summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt6
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi132
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-cdp.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-gpu.dtsi65
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi22
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig2
-rw-r--r--arch/arm64/configs/msmcortex_defconfig2
-rw-r--r--arch/arm64/mm/mmu.c22
-rw-r--r--drivers/char/adsprpc.c22
-rw-r--r--drivers/char/adsprpc_compat.c109
-rw-r--r--drivers/char/adsprpc_shared.h18
-rw-r--r--drivers/char/diag/diag_ipc_logging.h3
-rw-r--r--drivers/char/diag/diagfwd_cntl.c240
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c32
-rw-r--r--drivers/devfreq/arm-memlat-mon.c3
-rw-r--r--drivers/devfreq/bimc-bwmon.c3
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c8
-rw-r--r--drivers/iio/imu/inv_mpu/Kconfig63
-rw-r--r--drivers/iio/imu/inv_mpu/Makefile61
-rw-r--r--drivers/iio/imu/inv_mpu/README117
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c1072
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h236
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c258
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c421
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c752
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c466
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_common.c988
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_dts.c343
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_dts.h25
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_i2c.c556
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_iio.h1138
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_ring.c643
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_spi.c410
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c280
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/Kconfig13
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/Makefile6
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/inv_counters.c159
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/inv_counters.h76
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c29
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h1
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_util.c5
-rw-r--r--drivers/misc/qseecom.c9
-rw-r--r--drivers/mmc/card/block.c1
-rw-r--r--drivers/mmc/host/sdhci-msm.c33
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c20
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/supply/qcom/battery.c387
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c7
-rw-r--r--drivers/power/supply/qcom/smb-lib.c8
-rw-r--r--drivers/power/supply/qcom/smb-lib.h2
-rw-r--r--drivers/soc/qcom/hab/Kconfig4
-rw-r--r--drivers/soc/qcom/hab/Makefile21
-rw-r--r--drivers/soc/qcom/hab/ghs_comm.c141
-rw-r--r--drivers/soc/qcom/hab/hab.c550
-rw-r--r--drivers/soc/qcom/hab/hab.h135
-rw-r--r--drivers/soc/qcom/hab/hab_ghs.c214
-rw-r--r--drivers/soc/qcom/hab/hab_ghs.h30
-rw-r--r--drivers/soc/qcom/hab/hab_mem_linux.c57
-rw-r--r--drivers/soc/qcom/hab/hab_mimex.c125
-rw-r--r--drivers/soc/qcom/hab/hab_msg.c115
-rw-r--r--drivers/soc/qcom/hab/hab_open.c207
-rw-r--r--drivers/soc/qcom/hab/hab_parser.c29
-rw-r--r--drivers/soc/qcom/hab/hab_pchan.c29
-rw-r--r--drivers/soc/qcom/hab/hab_qvm.c59
-rw-r--r--drivers/soc/qcom/hab/hab_qvm.h3
-rw-r--r--drivers/soc/qcom/hab/hab_vchan.c165
-rw-r--r--drivers/soc/qcom/hab/khab.c8
-rw-r--r--drivers/soc/qcom/hab/qvm_comm.c13
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c48
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr.c31
-rw-r--r--drivers/soc/qcom/subsystem_restart.c14
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c6
-rw-r--r--drivers/uio/msm_sharedmem/msm_sharedmem.c32
-rw-r--r--drivers/usb/gadget/function/f_cdev.c3
-rw-r--r--include/linux/habmm.h2
-rw-r--r--include/linux/iio/imu/mpu.h124
-rw-r--r--include/linux/power_supply.h1
-rw-r--r--include/sound/q6adm-v2.h2
-rw-r--r--include/uapi/media/ais/msm_ais_isp.h12
-rw-r--r--kernel/power/hibernate.c3
-rw-r--r--net/packet/af_packet.c2
-rw-r--r--net/wireless/nl80211.c3
-rw-r--r--sound/core/rawmidi.c2
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c7
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c31
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c37
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c86
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h1
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c249
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c47
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c6
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c2
-rw-r--r--sound/soc/msm/qdsp6v2/q6voice.c8
-rw-r--r--sound/soc/msm/qdsp6v2/rtac.c4
-rw-r--r--sound/soc/msm/sdm660-internal.c2
95 files changed, 11292 insertions, 660 deletions
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index f01eae10bf4f..30a26a8965bc 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -183,6 +183,12 @@ Charger specific properties:
Value type: bool
Definition: Boolean flag which when present enables sw compensation for jeita
+- qcom,fcc-stepping-enable
+ Usage: optional
+ Value type: bool
+ Definition: Boolean flag which when present enables stepwise change in FCC.
+ The default stepping rate is 100mA/sec.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
new file mode 100644
index 000000000000..2bb0f6b5b116
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
@@ -0,0 +1,132 @@
+/* Copyright (c) 2018, 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.
+ */
+
+&mdss_mdp {
+ dsi_hx8399c_truly_vid: qcom,mdss_dsi_hx8399_truly_fhd_video {
+ qcom,mdss-dsi-panel-name =
+ "hx8399c video mode dsi truly panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <2160>;
+ qcom,mdss-dsi-h-front-porch = <42>;
+ qcom,mdss-dsi-h-back-porch = <42>;
+ qcom,mdss-dsi-h-pulse-width = <10>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <15>;
+ qcom,mdss-dsi-v-front-porch = <10>;
+ qcom,mdss-dsi-v-pulse-width = <3>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-pan-physical-width-dimension = <65>;
+ qcom,mdss-pan-physical-height-dimension = <129>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 39 01 00 00 00 00 04
+ b9 ff 83 99
+ 39 01 00 00 00 00 02
+ d2 88
+ 39 01 00 00 00 00 0c
+ b1 02 04 72 92 01
+ 32 aa 11 11 52 57
+ 39 01 00 00 00 00 10
+ b2 00 80 80 cc 05 07 5a
+ 11 10 10 00 1e 70 03 d4
+ 39 01 00 00 00 00 2d
+ b4 00 ff 59 59 01 ab 00
+ 00 09 00 03 05 00 28 03
+ 0b 0d 21 03 02 00 0c a3
+ 80 59 59 02 ab 00 00 09
+ 00 03 05 00 28 03 0b 0d
+ 02 00 0c a3 01
+ 39 01 00 00 05 00 22
+ d3 00 0c 03 03 00 00 10
+ 10 00 00 03 00 03 00 08
+ 78 08 78 00 00 00 00 00
+ 24 02 05 05 03 00 00 00
+ 05 40
+ 39 01 00 00 05 00 21
+ d5 20 20 19 19 18 18 02
+ 03 00 01 24 24 18 18 18
+ 18 24 24 00 00 00 00 00
+ 00 00 00 2f 2f 30 30 31
+ 31
+ 39 01 00 00 05 00 21
+ d6 24 24 18 18 19 19 01
+ 00 03 02 24 24 18 18 18
+ 18 20 20 40 40 40 40 40
+ 40 40 40 2f 2f 30 30 31
+ 31
+ 39 01 00 00 00 00 02
+ bd 00
+ 39 01 00 00 00 00 11
+ d8 aa aa aa aa aa aa aa
+ aa aa ba aa aa aa ba aa
+ aa
+ 39 01 00 00 00 00 02
+ bd 01
+ 39 01 00 00 00 00 11
+ d8 00 00 00 00 00 00 00
+ 00 82 ea aa aa 82 ea aa
+ aa
+ 39 01 00 00 00 00 02
+ bd 02
+ 39 01 00 00 00 00 09
+ d8 ff ff c0 3f ff ff c0
+ 3f
+ 39 01 00 00 00 00 02
+ bd 00
+ 39 01 00 00 05 00 37
+ e0 01 21 31 2d 66 6f 7b
+ 75 7a 81 86 89 8c 90 95
+ 97 9a a1 a2 aa 9e ad b0
+ 5b 57 63 7a 01 21 31 2d
+ 66 6f 7b 75 7a 81 86 89
+ 8c 90 95 97 9a a1 a2 aa
+ 9e ad b0 5b 57 63 7a
+ 39 01 00 00 00 00 03
+ b6 7e 7e
+ 39 01 00 00 00 00 02
+ cc 08
+ 05 01 00 00 96 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 32 00 02 28 00
+ 05 01 00 00 96 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-t-clk-post = <0x0e>;
+ qcom,mdss-dsi-t-clk-pre = <0x31>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-lp11-init;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ };
+};
+
diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
index 7db93928a369..70fc57e2594f 100644
--- a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
@@ -191,6 +191,13 @@
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+&dsi_hx8399c_truly_vid {
+ 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>;
+};
+
&mdss_dp_ctrl {
pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
index 5a571c2db634..953e3a20da0b 100644
--- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -605,6 +605,69 @@
qcom,bus-max = <0>;
};
};
+
+ qcom,gpu-pwrlevels-6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <122>;
+
+ qcom,initial-pwrlevel = <3>;
+
+ /* NOM */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <585000000>;
+ qcom,bus-freq = <12>;
+ qcom,bus-min = <11>;
+ qcom,bus-max = <12>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <465000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <11>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <370000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <9>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <266000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <6>;
+ };
+
+ /* Min SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <160000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 2cf4a1378778..b2f4a8ce47d3 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -26,6 +26,7 @@
#include "dsi-panel-truly-1080p-video.dtsi"
#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
#include "dsi-panel-lgd-incell-sw49106-fhd-video.dtsi"
+#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -337,3 +338,24 @@
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x30>;
};
+
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1c 08 09 05 03 04 a0];
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+};
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 031869a26722..9be3b7160ebe 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -614,6 +614,8 @@ CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
CONFIG_EXT4_FS_ENCRYPTION=y
CONFIG_EXT4_FS_ICE_ENCRYPTION=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index bd5435729d88..14fe16629069 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -637,6 +637,8 @@ CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
CONFIG_EXT4_FS_ENCRYPTION=y
CONFIG_EXT4_FS_ICE_ENCRYPTION=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index b30bdffa3066..24b0ed38ab7d 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -656,10 +656,16 @@ static phys_addr_t pgd_pgtable_alloc(void)
*/
void hotplug_paging(phys_addr_t start, phys_addr_t size)
{
-
struct page *pg;
- phys_addr_t pgd_phys = pgd_pgtable_alloc();
- pgd_t *pgd = pgd_set_fixmap(pgd_phys);
+ phys_addr_t pgd_phys;
+ pgd_t *pgd;
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_isolate_cpu(cpu);
+ pgd_phys = pgd_pgtable_alloc();
+ pgd = pgd_set_fixmap(pgd_phys);
memcpy(pgd, swapper_pg_dir, PAGE_SIZE);
@@ -675,6 +681,9 @@ void hotplug_paging(phys_addr_t start, phys_addr_t size)
pg = phys_to_page(pgd_phys);
pgtable_page_dtor(pg);
__free_pages(pg, 0);
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_unisolate_cpu_unlocked(cpu);
}
#ifdef CONFIG_MEMORY_HOTREMOVE
@@ -1028,7 +1037,11 @@ void remove_pagetable(unsigned long start, unsigned long end, bool direct)
unsigned long addr;
pgd_t *pgd;
pud_t *pud;
+ int cpu;
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_isolate_cpu(cpu);
for (addr = start; addr < end; addr = next) {
next = pgd_addr_end(addr, end);
@@ -1049,6 +1062,9 @@ void remove_pagetable(unsigned long start, unsigned long end, bool direct)
}
flush_tlb_all();
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_unisolate_cpu_unlocked(cpu);
}
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 18d98292a187..c0787608af56 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -2774,6 +2774,28 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num,
if (err)
goto bail;
break;
+ case FASTRPC_IOCTL_MMAP_64:
+ K_COPY_FROM_USER(err, 0, &p.mmap, param,
+ sizeof(p.mmap));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_mmap(fl, &p.mmap)));
+ if (err)
+ goto bail;
+ K_COPY_TO_USER(err, 0, param, &p.mmap, sizeof(p.mmap));
+ if (err)
+ goto bail;
+ break;
+ case FASTRPC_IOCTL_MUNMAP_64:
+ K_COPY_FROM_USER(err, 0, &p.munmap, param,
+ sizeof(p.munmap));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_munmap(fl,
+ &p.munmap)));
+ if (err)
+ goto bail;
+ break;
case FASTRPC_IOCTL_SETMODE:
switch ((uint32_t)ioctl_param) {
case FASTRPC_MODE_PARALLEL:
diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c
index fc6450336061..e1e061748f22 100644
--- a/drivers/char/adsprpc_compat.c
+++ b/drivers/char/adsprpc_compat.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, 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
@@ -36,6 +36,11 @@
_IOWR('R', 9, struct compat_fastrpc_ioctl_perf)
#define COMPAT_FASTRPC_IOCTL_INIT_ATTRS \
_IOWR('R', 10, struct compat_fastrpc_ioctl_init_attrs)
+#define COMPAT_FASTRPC_IOCTL_MMAP_64 \
+ _IOWR('R', 14, struct compat_fastrpc_ioctl_mmap_64)
+#define COMPAT_FASTRPC_IOCTL_MUNMAP_64 \
+ _IOWR('R', 15, struct compat_fastrpc_ioctl_munmap_64)
+
struct compat_remote_buf {
compat_uptr_t pv; /* buffer pointer */
@@ -72,11 +77,24 @@ struct compat_fastrpc_ioctl_mmap {
compat_uptr_t vaddrout; /* dsps virtual address */
};
+struct compat_fastrpc_ioctl_mmap_64 {
+ compat_int_t fd; /* ion fd */
+ compat_uint_t flags; /* flags for dsp to map with */
+ compat_u64 vaddrin; /* optional virtual address */
+ compat_size_t size; /* size */
+ compat_u64 vaddrout; /* dsps virtual address */
+};
+
struct compat_fastrpc_ioctl_munmap {
compat_uptr_t vaddrout; /* address to unmap */
compat_size_t size; /* size */
};
+struct compat_fastrpc_ioctl_munmap_64 {
+ compat_u64 vaddrout; /* address to unmap */
+ compat_size_t size; /* size */
+};
+
struct compat_fastrpc_ioctl_init {
compat_uint_t flags; /* one of FASTRPC_INIT_* macros */
compat_uptr_t file; /* pointer to elf file */
@@ -209,6 +227,28 @@ static int compat_get_fastrpc_ioctl_mmap(
return err;
}
+static int compat_get_fastrpc_ioctl_mmap_64(
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32,
+ struct fastrpc_ioctl_mmap __user *map)
+{
+ compat_uint_t u;
+ compat_int_t i;
+ compat_size_t s;
+ compat_u64 p;
+ int err;
+
+ err = get_user(i, &map32->fd);
+ err |= put_user(i, &map->fd);
+ err |= get_user(u, &map32->flags);
+ err |= put_user(u, &map->flags);
+ err |= get_user(p, &map32->vaddrin);
+ err |= put_user(p, &map->vaddrin);
+ err |= get_user(s, &map32->size);
+ err |= put_user(s, &map->size);
+
+ return err;
+}
+
static int compat_put_fastrpc_ioctl_mmap(
struct compat_fastrpc_ioctl_mmap __user *map32,
struct fastrpc_ioctl_mmap __user *map)
@@ -222,6 +262,19 @@ static int compat_put_fastrpc_ioctl_mmap(
return err;
}
+static int compat_put_fastrpc_ioctl_mmap_64(
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32,
+ struct fastrpc_ioctl_mmap __user *map)
+{
+ compat_u64 p;
+ int err;
+
+ err = get_user(p, &map->vaddrout);
+ err |= put_user(p, &map32->vaddrout);
+
+ return err;
+}
+
static int compat_get_fastrpc_ioctl_munmap(
struct compat_fastrpc_ioctl_munmap __user *unmap32,
struct fastrpc_ioctl_munmap __user *unmap)
@@ -238,6 +291,22 @@ static int compat_get_fastrpc_ioctl_munmap(
return err;
}
+static int compat_get_fastrpc_ioctl_munmap_64(
+ struct compat_fastrpc_ioctl_munmap_64 __user *unmap32,
+ struct fastrpc_ioctl_munmap __user *unmap)
+{
+ compat_u64 p;
+ compat_size_t s;
+ int err;
+
+ err = get_user(p, &unmap32->vaddrout);
+ err |= put_user(p, &unmap->vaddrout);
+ err |= get_user(s, &unmap32->size);
+ err |= put_user(s, &unmap->size);
+
+ return err;
+}
+
static int compat_get_fastrpc_ioctl_perf(
struct compat_fastrpc_ioctl_perf __user *perf32,
struct fastrpc_ioctl_perf __user *perf)
@@ -343,6 +412,27 @@ long compat_fastrpc_device_ioctl(struct file *filp, unsigned int cmd,
VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap(map32, map));
return err;
}
+ case COMPAT_FASTRPC_IOCTL_MMAP_64:
+ {
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32;
+ struct fastrpc_ioctl_mmap __user *map;
+ long ret;
+
+ map32 = compat_ptr(arg);
+ VERIFY(err, NULL != (map = compat_alloc_user_space(
+ sizeof(*map))));
+ if (err)
+ return -EFAULT;
+ VERIFY(err, 0 == compat_get_fastrpc_ioctl_mmap_64(map32, map));
+ if (err)
+ return err;
+ ret = filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MMAP_64,
+ (unsigned long)map);
+ if (ret)
+ return ret;
+ VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap_64(map32, map));
+ return err;
+ }
case COMPAT_FASTRPC_IOCTL_MUNMAP:
{
struct compat_fastrpc_ioctl_munmap __user *unmap32;
@@ -360,6 +450,23 @@ long compat_fastrpc_device_ioctl(struct file *filp, unsigned int cmd,
return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP,
(unsigned long)unmap);
}
+ case COMPAT_FASTRPC_IOCTL_MUNMAP_64:
+ {
+ struct compat_fastrpc_ioctl_munmap_64 __user *unmap32;
+ struct fastrpc_ioctl_munmap __user *unmap;
+
+ unmap32 = compat_ptr(arg);
+ VERIFY(err, NULL != (unmap = compat_alloc_user_space(
+ sizeof(*unmap))));
+ if (err)
+ return -EFAULT;
+ VERIFY(err, 0 == compat_get_fastrpc_ioctl_munmap_64(unmap32,
+ unmap));
+ if (err)
+ return err;
+ return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP_64,
+ (unsigned long)unmap);
+ }
case COMPAT_FASTRPC_IOCTL_INIT:
/* fall through */
case COMPAT_FASTRPC_IOCTL_INIT_ATTRS:
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index be8d1a536d6c..a88c668440c7 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,8 @@
#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke)
#define FASTRPC_IOCTL_MMAP _IOWR('R', 2, struct fastrpc_ioctl_mmap)
#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 3, struct fastrpc_ioctl_munmap)
+#define FASTRPC_IOCTL_MMAP_64 _IOWR('R', 14, struct fastrpc_ioctl_mmap_64)
+#define FASTRPC_IOCTL_MUNMAP_64 _IOWR('R', 15, struct fastrpc_ioctl_munmap_64)
#define FASTRPC_IOCTL_INVOKE_FD _IOWR('R', 4, struct fastrpc_ioctl_invoke_fd)
#define FASTRPC_IOCTL_SETMODE _IOWR('R', 5, uint32_t)
#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init)
@@ -171,6 +173,11 @@ struct fastrpc_ioctl_munmap {
size_t size; /* size */
};
+struct fastrpc_ioctl_munmap_64 {
+ uint64_t vaddrout; /* address to unmap */
+ size_t size; /* size */
+};
+
struct fastrpc_ioctl_mmap {
int fd; /* ion fd */
uint32_t flags; /* flags for dsp to map with */
@@ -179,6 +186,15 @@ struct fastrpc_ioctl_mmap {
uintptr_t vaddrout; /* dsps virtual address */
};
+
+struct fastrpc_ioctl_mmap_64 {
+ int fd; /* ion fd */
+ uint32_t flags; /* flags for dsp to map with */
+ uint64_t vaddrin; /* optional virtual address */
+ size_t size; /* size */
+ uint64_t vaddrout; /* dsps virtual address */
+};
+
struct fastrpc_ioctl_perf { /* kernel performance data */
uintptr_t data;
uint32_t numkeys;
diff --git a/drivers/char/diag/diag_ipc_logging.h b/drivers/char/diag/diag_ipc_logging.h
index b9958a433c46..4b8dd1b12c1c 100644
--- a/drivers/char/diag/diag_ipc_logging.h
+++ b/drivers/char/diag/diag_ipc_logging.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
#define DIAG_DEBUG_MASKS 0x0010
#define DIAG_DEBUG_POWER 0x0020
#define DIAG_DEBUG_BRIDGE 0x0040
+#define DIAG_DEBUG_CONTROL 0x0080
#define DIAG_DEBUG
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 10038e629e6c..d8ec09f90ec4 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -47,8 +47,11 @@ static void diag_mask_update_work_fn(struct work_struct *work)
void diag_cntl_channel_open(struct diagfwd_info *p_info)
{
- if (!p_info)
+ if (!p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid fwd_info structure\n");
return;
+ }
driver->mask_update |= PERIPHERAL_MASK(p_info->peripheral);
queue_work(driver->cntl_wq, &driver->mask_update_work);
diag_notify_md_client(p_info->peripheral, DIAG_STATUS_OPEN);
@@ -58,12 +61,18 @@ void diag_cntl_channel_close(struct diagfwd_info *p_info)
{
uint8_t peripheral;
- if (!p_info)
+ if (!p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid fwd_info structure\n");
return;
+ }
peripheral = p_info->peripheral;
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
driver->feature[peripheral].sent_feature_mask = 0;
driver->feature[peripheral].rcvd_feature_mask = 0;
@@ -88,8 +97,11 @@ static void diag_stm_update_work_fn(struct work_struct *work)
driver->stm_peripheral = 0;
mutex_unlock(&driver->cntl_lock);
- if (peripheral_mask == 0)
+ if (peripheral_mask == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Empty Peripheral mask\n");
return;
+ }
for (i = 0; i < NUM_PERIPHERALS; i++) {
if (!driver->feature[i].stm_support)
@@ -112,11 +124,18 @@ void diag_notify_md_client(uint8_t peripheral, int data)
struct pid *pid_struct;
struct task_struct *result;
- if (peripheral > NUM_PERIPHERALS)
+ if (peripheral > NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
- if (driver->logging_mode != DIAG_MEMORY_DEVICE_MODE)
+ if (driver->logging_mode != DIAG_MEMORY_DEVICE_MODE) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid logging_mode (%d)\n",
+ driver->logging_mode);
return;
+ }
mutex_lock(&driver->md_session_lock);
memset(&info, 0, sizeof(struct siginfo));
@@ -172,8 +191,12 @@ static void process_pd_status(uint8_t *buf, uint32_t len,
uint32_t pd;
int status = DIAG_STATUS_CLOSED;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < sizeof(*pd_msg))
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < sizeof(*pd_msg)) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, pd_msg_len = %d\n",
+ !buf, peripheral, len, (int)sizeof(*pd_msg));
return;
+ }
pd_msg = (struct diag_ctrl_msg_pd_status *)buf;
pd = pd_msg->pd_id;
@@ -183,8 +206,11 @@ static void process_pd_status(uint8_t *buf, uint32_t len,
static void enable_stm_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
mutex_lock(&driver->cntl_lock);
driver->feature[peripheral].stm_support = ENABLE_STM;
@@ -196,8 +222,11 @@ static void enable_stm_feature(uint8_t peripheral)
static void enable_socket_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_sockets)
driver->feature[peripheral].sockets_enabled = 1;
@@ -207,8 +236,11 @@ static void enable_socket_feature(uint8_t peripheral)
static void process_hdlc_encoding_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_apps_hdlc_encoding) {
driver->feature[peripheral].encode_hdlc =
@@ -221,8 +253,11 @@ static void process_hdlc_encoding_feature(uint8_t peripheral)
static void process_upd_header_untagging_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_apps_header_untagging) {
driver->feature[peripheral].untag_header =
@@ -248,8 +283,16 @@ static void process_command_deregistration(uint8_t *buf, uint32_t len,
* Perform Basic sanity. The len field is the size of the data payload.
* This doesn't include the header size.
*/
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral(%d) command deregistration packet processing started\n",
+ peripheral);
dereg = (struct diag_ctrl_cmd_dereg *)ptr;
ptr += header_len;
@@ -257,8 +300,8 @@ static void process_command_deregistration(uint8_t *buf, uint32_t len,
read_len += header_len - (2 * sizeof(uint32_t));
if (dereg->count_entries == 0) {
- pr_debug("diag: In %s, received reg tbl with no entries\n",
- __func__);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: received reg tbl with no entries\n");
return;
}
@@ -277,6 +320,9 @@ static void process_command_deregistration(uint8_t *buf, uint32_t len,
pr_err("diag: In %s, reading less than available, read_len: %d, len: %d count: %d\n",
__func__, read_len, len, dereg->count_entries);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral(%d) command deregistration packet processing complete\n",
+ peripheral);
}
static void process_command_registration(uint8_t *buf, uint32_t len,
uint8_t peripheral)
@@ -293,8 +339,15 @@ static void process_command_registration(uint8_t *buf, uint32_t len,
* Perform Basic sanity. The len field is the size of the data payload.
* This doesn't include the header size.
*/
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: peripheral(%d) command registration packet processing started\n",
+ peripheral);
reg = (struct diag_ctrl_cmd_reg *)ptr;
ptr += header_len;
@@ -302,7 +355,8 @@ static void process_command_registration(uint8_t *buf, uint32_t len,
read_len += header_len - (2 * sizeof(uint32_t));
if (reg->count_entries == 0) {
- pr_debug("diag: In %s, received reg tbl with no entries\n",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: In %s, received reg tbl with no entries\n",
__func__);
return;
}
@@ -322,6 +376,9 @@ static void process_command_registration(uint8_t *buf, uint32_t len,
pr_err("diag: In %s, reading less than available, read_len: %d, len: %d count: %d\n",
__func__, read_len, len, reg->count_entries);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: peripheral(%d) command registration packet processing complete\n",
+ peripheral);
}
static void diag_close_transport_work_fn(struct work_struct *work)
@@ -343,8 +400,11 @@ static void diag_close_transport_work_fn(struct work_struct *work)
static void process_socket_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
mutex_lock(&driver->cntl_lock);
driver->close_transport |= PERIPHERAL_MASK(peripheral);
@@ -375,15 +435,20 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
uint32_t feature_mask = 0;
uint8_t *ptr = buf;
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
header = (struct diag_ctrl_feature_mask *)ptr;
ptr += header_len;
feature_mask_len = header->feature_mask_len;
if (feature_mask_len == 0) {
- pr_debug("diag: In %s, received invalid feature mask from peripheral %d\n",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: In %s, received invalid feature mask from peripheral %d\n",
__func__, peripheral);
return;
}
@@ -396,6 +461,8 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
diag_cmd_remove_reg_by_proc(peripheral);
driver->feature[peripheral].rcvd_feature_mask = 1;
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Received feature mask for peripheral %d\n", peripheral);
for (i = 0; i < feature_mask_len && read_len < len; i++) {
feature_mask = *(uint8_t *)ptr;
@@ -425,6 +492,10 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
process_socket_feature(peripheral);
process_log_on_demand_feature(peripheral);
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Peripheral(%d) feature mask is processed\n",
+ peripheral);
}
static void process_last_event_report(uint8_t *buf, uint32_t len,
@@ -436,14 +507,23 @@ static void process_last_event_report(uint8_t *buf, uint32_t len,
uint32_t pkt_len = sizeof(uint32_t) + sizeof(uint16_t);
uint16_t event_size = 0;
- if (!buf || peripheral >= NUM_PERIPHERALS || len != pkt_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len != pkt_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, pkt_len = %d\n",
+ !buf, peripheral, len, pkt_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:started processing last event report for peripheral (%d)\n",
+ peripheral);
mutex_lock(&event_mask.lock);
header = (struct diag_ctrl_last_event_report *)ptr;
event_size = ((header->event_last_id / 8) + 1);
if (event_size >= driver->event_mask_size) {
- pr_debug("diag: In %s, receiving event mask size more that Apps can handle\n",
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: In %s, receiving event mask size more that Apps can handle\n",
__func__);
temp = krealloc(driver->event_mask->ptr, event_size,
GFP_KERNEL);
@@ -461,6 +541,9 @@ static void process_last_event_report(uint8_t *buf, uint32_t len,
driver->last_event_id = header->event_last_id;
err:
mutex_unlock(&event_mask.lock);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: last event report processed for peripheral (%d)\n",
+ peripheral);
}
static void process_log_range_report(uint8_t *buf, uint32_t len,
@@ -474,8 +557,15 @@ static void process_log_range_report(uint8_t *buf, uint32_t len,
struct diag_ctrl_log_range *log_range = NULL;
struct diag_log_mask_t *mask_ptr = NULL;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:started processing log range report for peripheral(%d)\n",
+ peripheral);
header = (struct diag_ctrl_log_range_report *)ptr;
ptr += header_len;
@@ -501,6 +591,9 @@ static void process_log_range_report(uint8_t *buf, uint32_t len,
mask_ptr->range = LOG_ITEMS_TO_SIZE(log_range->num_items);
mutex_unlock(&(mask_ptr->lock));
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: log range report processed for peripheral (%d)\n",
+ peripheral);
}
static int update_msg_mask_tbl_entry(struct diag_msg_mask_t *mask,
@@ -508,8 +601,12 @@ static int update_msg_mask_tbl_entry(struct diag_msg_mask_t *mask,
{
uint32_t temp_range;
- if (!mask || !range)
+ if (!mask || !range) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid %s\n",
+ (!mask ? "mask" : (!range ? "range" : " ")));
return -EIO;
+ }
if (range->ssid_last < range->ssid_first) {
pr_err("diag: In %s, invalid ssid range, first: %d, last: %d\n",
__func__, range->ssid_first, range->ssid_last);
@@ -541,8 +638,16 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len,
uint8_t *temp = NULL;
uint32_t min_len = header_len - sizeof(struct diag_ctrl_pkt_header_t);
- if (!buf || peripheral >= NUM_PERIPHERALS || len < min_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < min_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, min_len = %d\n",
+ !buf, peripheral, len, min_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: started processing ssid range for peripheral (%d)\n",
+ peripheral);
header = (struct diag_ctrl_ssid_range_report *)ptr;
ptr += header_len;
@@ -594,6 +699,9 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len,
driver->msg_mask_tbl_count += 1;
}
mutex_unlock(&driver->msg_mask_lock);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: processed ssid range for peripheral(%d)\n",
+ peripheral);
}
static void diag_build_time_mask_update(uint8_t *buf,
@@ -610,8 +718,12 @@ static void diag_build_time_mask_update(uint8_t *buf,
uint32_t *dest_ptr = NULL;
struct diag_msg_mask_t *build_mask = NULL;
- if (!range || !buf)
+ if (!range || !buf) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid %s\n",
+ (!range ? "range" : (!buf ? "buf" : " ")));
return;
+ }
if (range->ssid_last < range->ssid_first) {
pr_err("diag: In %s, invalid ssid range, first: %d, last: %d\n",
@@ -673,8 +785,16 @@ static void process_build_mask_report(uint8_t *buf, uint32_t len,
struct diag_ctrl_build_mask_report *header = NULL;
struct diag_ssid_range_t *range = NULL;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < header_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < header_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, header_len = %d\n",
+ !buf, peripheral, len, header_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: started processing build mask for peripheral(%d)\n",
+ peripheral);
header = (struct diag_ctrl_build_mask_report *)ptr;
ptr += header_len;
@@ -690,6 +810,8 @@ static void process_build_mask_report(uint8_t *buf, uint32_t len,
ptr += num_items * sizeof(uint32_t);
read_len += num_items * sizeof(uint32_t);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: processing build mask complete (%d)\n", peripheral);
}
void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
@@ -700,8 +822,10 @@ void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
uint8_t *ptr = buf;
struct diag_ctrl_pkt_header_t *ctrl_pkt = NULL;
- if (!buf || len <= 0 || !p_info)
+ if (!buf || len <= 0 || !p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid parameters\n");
return;
+ }
if (reg_dirty & PERIPHERAL_MASK(p_info->peripheral)) {
pr_err_ratelimited("diag: dropping command registration from peripheral %d\n",
@@ -711,6 +835,9 @@ void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
while (read_len + header_len < len) {
ctrl_pkt = (struct diag_ctrl_pkt_header_t *)ptr;
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral: %d: pkt_id: %d\n",
+ p_info->peripheral, ctrl_pkt->pkt_id);
switch (ctrl_pkt->pkt_id) {
case DIAG_CTRL_MSG_REG:
process_command_registration(ptr, ctrl_pkt->len,
@@ -745,13 +872,15 @@ void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
p_info->peripheral);
break;
default:
- pr_debug("diag: Control packet %d not supported\n",
- ctrl_pkt->pkt_id);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Control packet %d not supported\n",
+ ctrl_pkt->pkt_id);
}
ptr += header_len + ctrl_pkt->len;
read_len += header_len + ctrl_pkt->len;
}
-
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: control packet processing complete\n");
return;
}
@@ -969,15 +1098,16 @@ void diag_real_time_work_fn(struct work_struct *work)
for (i = 0; i < DIAG_NUM_PROC; i++) {
temp_real_time = diag_compute_real_time(i);
if (temp_real_time == driver->real_time_mode[i]) {
- pr_debug("diag: did not update real time mode on proc %d, already in the req mode %d",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: did not update real time mode on proc %d, already in the req mode %d\n",
i, temp_real_time);
continue;
}
if (i == DIAG_LOCAL_PROC) {
if (!send_update) {
- pr_debug("diag: In %s, cannot send real time mode pkt since one of the periperhal is in buffering mode\n",
- __func__);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: cannot send real time mode pkt since one of the periperhal is in buffering mode\n");
break;
}
for (j = 0; j < NUM_PERIPHERALS; j++)
@@ -1011,7 +1141,8 @@ void diag_real_time_work_fn(struct work_struct *work)
temp_real_time = MODE_NONREALTIME;
}
if (temp_real_time == driver->real_time_mode[i]) {
- pr_debug("diag: did not update real time mode on proc %d, already in the req mode %d",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: did not update real time mode on proc %d, already in the req mode %d\n",
i, temp_real_time);
continue;
}
@@ -1046,8 +1177,8 @@ static int __diag_send_real_time_update(uint8_t peripheral, int real_time,
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n", peripheral);
return err;
}
@@ -1194,8 +1325,9 @@ int diag_send_peripheral_buffering_mode(struct diag_buffering_mode_t *params)
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
driver->buffering_flag[params->peripheral] = 0;
return -EIO;
}
@@ -1260,8 +1392,9 @@ int diag_send_stm_state(uint8_t peripheral, uint8_t stm_control_data)
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
@@ -1290,15 +1423,17 @@ int diag_send_peripheral_drain_immediate(uint8_t pd,
struct diag_ctrl_drain_immediate_v2 ctrl_pkt_v2;
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
@@ -1355,8 +1490,9 @@ int diag_send_buffering_tx_mode_pkt(uint8_t peripheral,
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
@@ -1434,15 +1570,17 @@ int diag_send_buffering_wm_values(uint8_t peripheral,
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 6b74c0056d1b..bfdce051d405 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -728,6 +728,7 @@ static void diagfwd_cntl_read_done(struct diagfwd_info *fwd_info,
unsigned char *buf, int len)
{
if (!fwd_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid fwd_info\n");
diag_ws_release();
return;
}
@@ -748,8 +749,12 @@ static void diagfwd_cntl_read_done(struct diagfwd_info *fwd_info,
*/
diag_ws_on_copy_fail(DIAG_WS_MUX);
/* Reset the buffer in_busy value after processing the data */
- if (fwd_info->buf_1)
+ if (fwd_info->buf_1) {
atomic_set(&fwd_info->buf_1->in_busy, 0);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
+ }
diagfwd_queue_read(fwd_info);
diagfwd_queue_read(&peripheral_info[TYPE_DATA][fwd_info->peripheral]);
@@ -774,8 +779,12 @@ static void diagfwd_dci_read_done(struct diagfwd_info *fwd_info,
diag_dci_process_peripheral_data(fwd_info, (void *)buf, len);
/* Reset the buffer in_busy value after processing the data */
- if (fwd_info->buf_1)
+ if (fwd_info->buf_1) {
atomic_set(&fwd_info->buf_1->in_busy, 0);
+ DIAG_LOG(DIAG_DEBUG_DCI,
+ "Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
+ }
diagfwd_queue_read(fwd_info);
}
@@ -1106,8 +1115,11 @@ void *diagfwd_request_write_buf(struct diagfwd_info *fwd_info)
int index;
unsigned long flags;
+ if (!fwd_info)
+ return NULL;
spin_lock_irqsave(&fwd_info->write_buf_lock, flags);
- for (index = 0 ; index < NUM_WRITE_BUFFERS; index++) {
+ for (index = 0; (index < NUM_WRITE_BUFFERS) && fwd_info->buf_ptr[index];
+ index++) {
if (!atomic_read(&(fwd_info->buf_ptr[index]->in_busy))) {
atomic_set(&(fwd_info->buf_ptr[index]->in_busy), 1);
buf = fwd_info->buf_ptr[index]->data;
@@ -1529,7 +1541,8 @@ int diagfwd_write_buffer_done(struct diagfwd_info *fwd_info, const void *ptr)
if (!fwd_info || !ptr)
return found;
spin_lock_irqsave(&fwd_info->write_buf_lock, flags);
- for (index = 0; index < NUM_WRITE_BUFFERS; index++) {
+ for (index = 0; (index < NUM_WRITE_BUFFERS) && fwd_info->buf_ptr[index];
+ index++) {
if (fwd_info->buf_ptr[index]->data == ptr) {
atomic_set(&fwd_info->buf_ptr[index]->in_busy, 0);
found = 1;
@@ -1548,13 +1561,15 @@ void diagfwd_channel_read(struct diagfwd_info *fwd_info)
struct diagfwd_buf_t *temp_buf = NULL;
if (!fwd_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid fwd_info\n");
diag_ws_release();
return;
}
if (!fwd_info->inited || !atomic_read(&fwd_info->opened)) {
- pr_debug("diag: In %s, p: %d, t: %d, inited: %d, opened: %d ch_open: %d\n",
- __func__, fwd_info->peripheral, fwd_info->type,
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: p: %d, t: %d, inited: %d, opened: %d, ch_open: %d\n",
+ fwd_info->peripheral, fwd_info->type,
fwd_info->inited, atomic_read(&fwd_info->opened),
fwd_info->ch_open);
diag_ws_release();
@@ -1590,8 +1605,9 @@ void diagfwd_channel_read(struct diagfwd_info *fwd_info)
atomic_set(&temp_buf->in_busy, 1);
}
} else {
- pr_debug("diag: In %s, both buffers are empty for p: %d, t: %d\n",
- __func__, fwd_info->peripheral, fwd_info->type);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: both buffers are busy for p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
}
if (!read_buf) {
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index 4fb0a5ffda50..739d02300ac8 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016, 2018 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
@@ -352,6 +352,7 @@ static struct platform_driver arm_memlat_mon_driver = {
.name = "arm-memlat-mon",
.of_match_table = match_table,
.owner = THIS_MODULE,
+ .suppress_bind_attrs = true,
},
};
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 315d3a67e43e..4db5a29fa849 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016, 2018 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
@@ -669,6 +669,7 @@ static struct platform_driver bimc_bwmon_driver = {
.name = "bimc-bwmon",
.of_match_table = match_table,
.owner = THIS_MODULE,
+ .suppress_bind_attrs = true,
},
};
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 67261bc10e80..d72953f2df23 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -1274,10 +1274,10 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
tx->dir,
(SPS_IOVEC_FLAG_EOT |
SPS_IOVEC_FLAG_NWD));
- if (dma_desc_tx < 0) {
+ if (IS_ERR_OR_NULL(dma_desc_tx)) {
dev_err(ctrl->dev, "error dmaengine_prep_slave_sg tx:%ld\n",
PTR_ERR(dma_desc_tx));
- ret = PTR_ERR(dma_desc_tx);
+ ret = dma_desc_tx ? PTR_ERR(dma_desc_tx) : -ENOMEM;
goto dma_xfer_end;
}
@@ -1292,11 +1292,11 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
sg_rx_itr - sg_rx, rx->dir,
(SPS_IOVEC_FLAG_EOT |
SPS_IOVEC_FLAG_NWD));
- if (dma_desc_rx < 0) {
+ if (IS_ERR_OR_NULL(dma_desc_rx)) {
dev_err(ctrl->dev,
"error dmaengine_prep_slave_sg rx:%ld\n",
PTR_ERR(dma_desc_rx));
- ret = PTR_ERR(dma_desc_rx);
+ ret = dma_desc_rx ? PTR_ERR(dma_desc_rx) : -ENOMEM;
goto dma_xfer_end;
}
diff --git a/drivers/iio/imu/inv_mpu/Kconfig b/drivers/iio/imu/inv_mpu/Kconfig
new file mode 100644
index 000000000000..7505454f8763
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/Kconfig
@@ -0,0 +1,63 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices
+#
+
+config INV_MPU_IIO
+ tristate
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ select IIO_TRIGGER
+ select CRC32
+
+choice
+ prompt "Chip name"
+ depends on INV_MPU_IIO
+
+config INV_MPU_IIO_ICM20648
+ bool "ICM20648/ICM20948"
+ help
+ Select this if you are using a ICM20648/ICM20948 chip.
+
+config INV_MPU_IIO_ICM20608D
+ bool "ICM20608D/ICM20609/ICM20689"
+ help
+ Select this if you are using a ICM20608D/ICM20609/ICM20689 chip.
+
+config INV_MPU_IIO_ICM20602
+ bool "ICM20602"
+ help
+ Select this if you are using a ICM20602 chip.
+
+config INV_MPU_IIO_ICM20690
+ bool "ICM20690"
+ help
+ Select this if you are using a ICM20690 chip.
+
+config INV_MPU_IIO_IAM20680
+ bool "IAM20680"
+ help
+ Select this if you are using a IAM20680 chip.
+
+endchoice
+
+config INV_MPU_IIO_I2C
+ tristate "Invensense ICM20xxx devices (I2C)"
+ depends on I2C && !INV_MPU6050_IIO
+ select INV_MPU_IIO
+ default n
+ help
+ This driver supports Invensense ICM20xxx devices over I2C.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio-i2c.
+
+config INV_MPU_IIO_SPI
+ tristate "Invensense ICM20xxx devices (SPI)"
+ depends on SPI_MASTER && !INV_MPU6050_IIO
+ select INV_MPU_IIO
+ default n
+ help
+ This driver supports Invensense ICM20xxx devices over SPI.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio-spi.
+
+source "drivers/iio/imu/inv_mpu/inv_test/Kconfig"
diff --git a/drivers/iio/imu/inv_mpu/Makefile b/drivers/iio/imu/inv_mpu/Makefile
new file mode 100644
index 000000000000..dfc4c257ef73
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/Makefile
@@ -0,0 +1,61 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs += inv_mpu_common.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_timestamp.o
+inv-mpu-iio-objs += inv_mpu_dts.o
+
+# chip support
+ifeq ($(CONFIG_INV_MPU_IIO_ICM20648), y)
+inv-mpu-iio-objs += icm20648/inv_mpu_init.o
+inv-mpu-iio-objs += icm20648/inv_mpu_core.o
+inv-mpu-iio-objs += icm20648/inv_mpu_parsing.o
+inv-mpu-iio-objs += icm20648/inv_mpu_setup.o
+inv-mpu-iio-objs += icm20648/inv_mpu_dmp_fifo.o
+inv-mpu-iio-objs += icm20648/inv_slave_compass.o
+inv-mpu-iio-objs += icm20648/inv_slave_pressure.o
+inv-mpu-iio-objs += icm20648/inv_slave_als.o
+inv-mpu-iio-objs += icm20648/inv_mpu_load_dmp.o
+inv-mpu-iio-objs += icm20648/inv_mpu_selftest.o
+inv-mpu-iio-objs += dmp_support/inv_mpu_misc.o
+else ifeq ($(CONFIG_INV_MPU_IIO_ICM20690), y)
+inv-mpu-iio-objs += icm20690/inv_mpu_init_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_core_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_parsing_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_setup_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_selftest_20690.o
+inv-mpu-iio-objs += icm20690/inv_slave_compass.o
+else ifeq ($(CONFIG_INV_MPU_IIO_ICM20602), y)
+inv-mpu-iio-objs += icm20602/inv_mpu_init_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_core_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_parsing_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_setup_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_selftest_20602.o
+else ifeq ($(CONFIG_INV_MPU_IIO_ICM20608D), y)
+inv-mpu-iio-objs += icm20608d/inv_mpu_init_20608.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_core_20608.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_parsing_20608.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_setup_20608D.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_dmp_fifo.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_load_dmp.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_selftest_20608.o
+inv-mpu-iio-objs += dmp_support/inv_mpu_misc.o
+else ifeq ($(CONFIG_INV_MPU_IIO_IAM20680), y)
+inv-mpu-iio-objs += iam20680/inv_mpu_init_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_core_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_parsing_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_setup_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_selftest_20680.o
+endif
+
+# Bus support
+obj-$(CONFIG_INV_MPU_IIO_I2C) += inv-mpu-iio-i2c.o
+inv-mpu-iio-i2c-objs := inv_mpu_i2c.o
+obj-$(CONFIG_INV_MPU_IIO_SPI) += inv-mpu-iio-spi.o
+inv-mpu-iio-spi-objs := inv_mpu_spi.o
+
+obj-y += inv_test/
diff --git a/drivers/iio/imu/inv_mpu/README b/drivers/iio/imu/inv_mpu/README
new file mode 100644
index 000000000000..47ff5029ee6e
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/README
@@ -0,0 +1,117 @@
+Kernel driver inv-mpu-iio
+Author: InvenSense, Inc.
+
+
+Table of Contents
+=================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Dts file
+- Communicating with the Driver in Userspace
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The supported chips are listed in Kconfig and user selects an
+appropriate one from .e.g. menuconfig.
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows (kernel 3.10):
+- Copy mpu.h to <kernel_root>/include/linux/iio/imu/
+- Copy inv_mpu folder under <kernel_root>/drivers/iio/imu/
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ add "source "drivers/iio/imu/inv_mpu/Kconfig""
+ in <kernel_root>/drivers/iio/imu/Kconfig
+
+ add "obj-y += inv_mpu/"
+ in <kernel_root>/drivers/iio/imu/Makefile
+
+
+
+Dts file
+========
+In order to recognize the Invensense device on the I2C/SPI bus, dts(or dtsi)
+file must be modified.
+
+Example)
+ICM20648 + AK09911/BMP280/APDS9930 on AUX I2C
+
+ i2c@f9968000 {
+ /* Invensense */
+ mpu6515_acc@68 {
+ compatible = "inven,icm20648";
+ reg = <0x68>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <73 0x2>;
+ inven,vdd_ana-supply = <&pm8941_l17>;
+ inven,vcc_i2c-supply = <&pm8941_lvs1>;
+ inven,gpio_int1 = <&msmgpio 73 0x00>;
+ fs_range = <0x00>;
+ /* mount matrix */
+ axis_map_x = <1>;
+ axis_map_y = <0>;
+ axis_map_z = <2>;
+ negate_x = <0>;
+ negate_y = <0>;
+ negate_z = <1>;
+ poll_interval = <200>;
+ min_interval = <5>;
+ inven,secondary_reg = <0x0c>;
+ /* If no compass sensor,
+ * replace "compass" with "none"
+ */
+ inven,secondary_type = "compass";
+ inven,secondary_name = "ak09911";
+ inven,secondary_axis_map_x = <1>;
+ inven,secondary_axis_map_y = <0>;
+ inven,secondary_axis_map_z = <2>;
+ inven,secondary_negate_x = <1>;
+ inven,secondary_negate_y = <1>;
+ inven,secondary_negate_z = <1>;
+ /* If no pressure sensor,
+ * replace "pressure" with "none"
+ */
+ inven,aux_type = "pressure";
+ inven,aux_name = "bmp280";
+ inven,aux_reg = <0x76>;
+ /* If no ALS sensor
+ * replace "als" with "none"
+ */
+ inven,read_only_slave_type = "als";
+ inven,read_only_slave_name = "apds9930";
+ inven,read_only_slave_reg = <0x39>;
+ };
+ };
+
+
+Communicating with the Driver in Userspace
+==========================================
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found at:
+
+(I2C) /sys/devices/*.i2c/i2c-*/*-*/iio:device*
+(SPI) /sys/devices/*.spi/spi_master/spi*/spi*.*/iio:device*
+
+Group and Owner for all entries should be updated to system/system at
+boot time to allow userspace to access properly.
+
+
+License
+=======
+Copyright (C) 2018 InvenSense, Inc.
+
+This software is licensed under the terms of the GNU General Public
+License version 2, as published by the Free Software Foundation, and
+may be copied, distributed, and modified under those terms.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c
new file mode 100644
index 000000000000..b429f57be5ca
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+
+#include "../inv_mpu_iio.h"
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+ {128, "ICM20608D"},
+ {128, "ICM20690"},
+ {128, "ICM20602"},
+ {128, "IAM20680"},
+};
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static char debug_reg_addr = 0x6;
+#endif
+
+const char sensor_l_info[][30] = {
+ "SENSOR_L_ACCEL",
+ "SENSOR_L_GYRO",
+ "SENSOR_L_MAG",
+ "SENSOR_L_ALS",
+ "SENSOR_L_SIXQ",
+ "SENSOR_L_THREEQ",
+ "SENSOR_L_NINEQ",
+ "SENSOR_L_PEDQ",
+ "SENSOR_L_GEOMAG",
+ "SENSOR_L_PRESSURE",
+ "SENSOR_L_GYRO_CAL",
+ "SENSOR_L_MAG_CAL",
+ "SENSOR_L_EIS_GYRO",
+ "SENSOR_L_ACCEL_WAKE",
+ "SENSOR_L_GYRO_WAKE",
+ "SENSOR_L_MAG_WAKE",
+ "SENSOR_L_ALS_WAKE",
+ "SENSOR_L_SIXQ_WAKE",
+ "SENSOR_L_NINEQ_WAKE",
+ "SENSOR_L_PEDQ_WAKE",
+ "SENSOR_L_GEOMAG_WAKE",
+ "SENSOR_L_PRESSURE_WAKE",
+ "SENSOR_L_GYRO_CAL_WAKE",
+ "SENSOR_L_MAG_CAL_WAKE",
+ "SENSOR_L_NUM_MAX",
+};
+
+static int inv_set_accel_bias_reg(struct inv_mpu_state *st,
+ int accel_bias, int axis)
+{
+ int accel_reg_bias;
+ u8 addr;
+ u8 d[2];
+ int result = 0;
+
+ switch (axis) {
+ case 0:
+ /* X */
+ addr = REG_XA_OFFS_H;
+ break;
+ case 1:
+ /* Y */
+ addr = REG_YA_OFFS_H;
+ break;
+ case 2:
+ /* Z* */
+ addr = REG_ZA_OFFS_H;
+ break;
+ default:
+ result = -EINVAL;
+ goto accel_bias_set_err;
+ }
+
+ result = inv_plat_read(st, addr, 2, d);
+ if (result)
+ goto accel_bias_set_err;
+ accel_reg_bias = ((int)d[0] << 8) | d[1];
+
+ /* accel_bias is 2g scaled by 1<<16.
+ * Convert to 16g, and mask bit0 */
+ accel_reg_bias -= ((accel_bias / 8 / 65536) & ~1);
+
+ d[0] = (accel_reg_bias >> 8) & 0xff;
+ d[1] = (accel_reg_bias) & 0xff;
+ result = inv_plat_single_write(st, addr, d[0]);
+ if (result)
+ goto accel_bias_set_err;
+ result = inv_plat_single_write(st, addr + 1, d[1]);
+ if (result)
+ goto accel_bias_set_err;
+
+accel_bias_set_err:
+ return result;
+}
+
+static int inv_set_gyro_bias_reg(struct inv_mpu_state *st,
+ const int gyro_bias, int axis)
+{
+ int gyro_reg_bias;
+ u8 addr;
+ u8 d[2];
+ int result = 0;
+
+ switch (axis) {
+ case 0:
+ /* X */
+ addr = REG_XG_OFFS_USR_H;
+ break;
+ case 1:
+ /* Y */
+ addr = REG_YG_OFFS_USR_H;
+ break;
+ case 2:
+ /* Z */
+ addr = REG_ZG_OFFS_USR_H;
+ break;
+ default:
+ result = -EINVAL;
+ goto gyro_bias_set_err;
+ }
+
+ /* gyro_bias is 2000dps scaled by 1<<16.
+ * Convert to 1000dps */
+ gyro_reg_bias = (-gyro_bias * 2 / 65536);
+
+ d[0] = (gyro_reg_bias >> 8) & 0xff;
+ d[1] = (gyro_reg_bias) & 0xff;
+ result = inv_plat_single_write(st, addr, d[0]);
+ if (result)
+ goto gyro_bias_set_err;
+ result = inv_plat_single_write(st, addr + 1, d[1]);
+ if (result)
+ goto gyro_bias_set_err;
+
+gyro_bias_set_err:
+ return result;
+}
+
+static int _bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ result = inv_switch_power_in_lp(st, true);
+ if (result)
+ return result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto bias_store_fail;
+ switch (this_attr->address) {
+ case ATTR_ACCEL_X_OFFSET:
+ result = inv_set_accel_bias_reg(st, data, 0);
+ if (result)
+ goto bias_store_fail;
+ st->input_accel_bias[0] = data;
+ break;
+ case ATTR_ACCEL_Y_OFFSET:
+ result = inv_set_accel_bias_reg(st, data, 1);
+ if (result)
+ goto bias_store_fail;
+ st->input_accel_bias[1] = data;
+ break;
+ case ATTR_ACCEL_Z_OFFSET:
+ result = inv_set_accel_bias_reg(st, data, 2);
+ if (result)
+ goto bias_store_fail;
+ st->input_accel_bias[2] = data;
+ break;
+ case ATTR_GYRO_X_OFFSET:
+ result = inv_set_gyro_bias_reg(st, data, 0);
+ if (result)
+ goto bias_store_fail;
+ st->input_gyro_bias[0] = data;
+ break;
+ case ATTR_GYRO_Y_OFFSET:
+ result = inv_set_gyro_bias_reg(st, data, 1);
+ if (result)
+ goto bias_store_fail;
+ st->input_gyro_bias[1] = data;
+ break;
+ case ATTR_GYRO_Z_OFFSET:
+ result = inv_set_gyro_bias_reg(st, data, 2);
+ if (result)
+ goto bias_store_fail;
+ st->input_gyro_bias[2] = data;
+ break;
+ default:
+ break;
+ }
+
+bias_store_fail:
+ if (result)
+ return result;
+ result = inv_switch_power_in_lp(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _bias_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static ssize_t inv_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+ switch (this_attr->address) {
+ case ATTR_DMP_LP_EN_OFF:
+ st->chip_config.lp_en_mode_off = !!data;
+ inv_switch_power_in_lp(st, !!data);
+ break;
+ case ATTR_DMP_CLK_SEL:
+ st->chip_config.clk_sel = !!data;
+ inv_switch_power_in_lp(st, !!data);
+ break;
+ case ATTR_DEBUG_REG_ADDR:
+ debug_reg_addr = data;
+ break;
+ case ATTR_DEBUG_REG_WRITE:
+ inv_plat_single_write(st, debug_reg_addr, data);
+ break;
+ }
+ return count;
+}
+#endif
+
+static int _misc_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ result = inv_switch_power_in_lp(st, true);
+ if (result)
+ return result;
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+ switch (this_attr->address) {
+ case ATTR_GYRO_SCALE:
+ if (data > 3)
+ return -EINVAL;
+ st->chip_config.fsr = data;
+ result = inv_set_gyro_sf(st);
+ return result;
+ case ATTR_ACCEL_SCALE:
+ if (data > 3)
+ return -EINVAL;
+ st->chip_config.accel_fs = data;
+ result = inv_set_accel_sf(st);
+ return result;
+ default:
+ return -EINVAL;
+ }
+ st->trigger_state = MISC_TRIGGER;
+ result = set_inv_enable(indio_dev);
+
+ return result;
+}
+
+/*
+ * inv_misc_attr_store() - calling this function
+ */
+static ssize_t inv_misc_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _misc_attr_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_sensor_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->sensor_l[this_attr->address].rate);
+}
+
+static ssize_t inv_sensor_rate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data, rate, ind;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+ if (data <= 0) {
+ pr_err("sensor_rate_store: invalid data=%d\n", data);
+ return -EINVAL;
+ }
+ ind = this_attr->address;
+ rate = inv_rate_convert(st, ind, data);
+
+ pr_debug("sensor [%s] requested rate %d input [%d]\n",
+ sensor_l_info[ind], rate, data);
+
+ if (rate == st->sensor_l[ind].rate)
+ return count;
+ mutex_lock(&indio_dev->mlock);
+ st->sensor_l[ind].rate = rate;
+ st->trigger_state = DATA_TRIGGER;
+ inv_check_sensor_on(st);
+ result = set_inv_enable(indio_dev);
+ pr_debug("%s rate %d div %d\n", sensor_l_info[ind],
+ st->sensor_l[ind].rate, st->sensor_l[ind].div);
+ mutex_unlock(&indio_dev->mlock);
+
+ return count;
+}
+
+static ssize_t inv_sensor_on_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->sensor_l[this_attr->address].on);
+}
+
+static ssize_t inv_sensor_on_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data, on, ind;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+ if (data < 0) {
+ pr_err("sensor_on_store: invalid data=%d\n", data);
+ return -EINVAL;
+ }
+ ind = this_attr->address;
+ on = !!data;
+
+ pr_debug("sensor [%s] requested %s, input [%d]\n",
+ sensor_l_info[ind], (on == 1) ? "On" : "Off", data);
+
+ if (on == st->sensor_l[ind].on) {
+ pr_debug("sensor [%s] is already %s, input [%d]\n",
+ sensor_l_info[ind], (on == 1) ? "On" : "Off", data);
+ return count;
+ }
+
+ mutex_lock(&indio_dev->mlock);
+ st->sensor_l[ind].on = on;
+ st->trigger_state = RATE_TRIGGER;
+ inv_check_sensor_on(st);
+ result = set_inv_enable(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ pr_debug("Sensor [%s] is %s by sysfs\n",
+ sensor_l_info[ind], (on == 1) ? "On" : "Off");
+ return count;
+}
+
+static int inv_check_l_step(struct inv_mpu_state *st)
+{
+ if (st->step_counter_l_on || st->step_counter_wake_l_on)
+ st->ped.on = true;
+ else
+ st->ped.on = false;
+
+ return 0;
+}
+
+static int _basic_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ int result;
+ u32 power_on_data;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result || (data < 0))
+ return -EINVAL;
+
+ switch (this_attr->address) {
+ case ATTR_DMP_PED_ON:
+ if ((!!data) == st->ped.on)
+ return count;
+ st->ped.on = !!data;
+ break;
+ case ATTR_DMP_TILT_ENABLE:
+ if ((!!data) == st->chip_config.tilt_enable)
+ return count;
+ st->chip_config.tilt_enable = !!data;
+ pr_info("Tile %s\n",
+ st->chip_config.tilt_enable ==
+ 1 ? "Enabled" : "Disabled");
+ break;
+ case ATTR_DMP_PICK_UP_ENABLE:
+ if ((!!data) == st->chip_config.pick_up_enable) {
+ pr_info("Pick_up enable already %s\n",
+ st->chip_config.pick_up_enable ==
+ 1 ? "Enabled" : "Disabled");
+ return count;
+ }
+ st->chip_config.pick_up_enable = !!data;
+ pr_info("Pick up %s\n",
+ st->chip_config.pick_up_enable ==
+ 1 ? "Enable" : "Disable");
+ break;
+ case ATTR_IN_POWER_ON:
+ {
+ u8 p0[2];
+ u8 p1[2];
+
+ power_on_data = (u32)data;
+ p0[0] = (power_on_data & 0xff);
+ p0[1] = ((power_on_data >> 8) & 0xff);
+ p1[0] = ((power_on_data >> 16) & 0xff);
+ p1[1] = ((power_on_data >> 24) & 0xff);
+
+ if (st->bus_type == BUS_SPI) {
+ struct spi_transfer power_on;
+ struct spi_message msg;
+
+ memset(&power_on, 0, sizeof(struct spi_transfer));
+
+ power_on.bits_per_word = 8;
+ power_on.len = 2;
+
+ power_on.tx_buf = p0;
+ power_on.rx_buf = p1;
+ spi_message_init(&msg);
+ spi_message_add_tail(&power_on, &msg);
+ spi_sync(to_spi_device(st->dev), &msg);
+
+ } else if (st->bus_type == BUS_I2C) {
+ struct i2c_msg msgs[2];
+
+ p0[0] &= 0x7f;
+
+ msgs[0].addr = st->i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &p0[0];
+ msgs[0].len = 1;
+
+ msgs[1].addr = st->i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = &p1[1];
+ msgs[1].len = 1;
+
+ result = i2c_transfer(st->sl_handle, msgs, 2);
+ if (result < 2)
+ return -EIO;
+ }
+ st->power_on_data = ((p0[0] << 24) | (p0[1] << 16) |
+ (p1[0] << 8) | p1[1]);
+ return count;
+ }
+ case ATTR_DMP_EIS_ENABLE:
+ if ((!!data) == st->chip_config.eis_enable)
+ return count;
+ st->chip_config.eis_enable = !!data;
+ pr_info("Eis %s\n",
+ st->chip_config.eis_enable == 1 ? "Enable" : "Disable");
+ break;
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ st->step_detector_l_on = !!data;
+ break;
+ case ATTR_DMP_STEP_DETECTOR_WAKE_ON:
+ st->step_detector_wake_l_on = !!data;
+ break;
+ case ATTR_DMP_STEP_COUNTER_ON:
+ st->step_counter_l_on = !!data;
+ break;
+ case ATTR_DMP_STEP_COUNTER_WAKE_ON:
+ st->step_counter_wake_l_on = !!data;
+ break;
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ if (data == st->batch.timeout)
+ return count;
+ st->batch.timeout = data;
+ break;
+ default:
+ return -EINVAL;
+ };
+ inv_check_l_step(st);
+ inv_check_sensor_on(st);
+
+ st->trigger_state = EVENT_TRIGGER;
+ result = set_inv_enable(indio_dev);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/*
+ * inv_basic_attr_store()
+ */
+static ssize_t inv_basic_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _basic_attr_store(dev, attr, buf, count);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+/*
+ * inv_attr_show()
+ */
+static ssize_t inv_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ s8 *m;
+
+ switch (this_attr->address) {
+ case ATTR_GYRO_SCALE:
+ {
+ const s16 gyro_scale[] = { 250, 500, 1000, 2000 };
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ gyro_scale[st->chip_config.fsr]);
+ }
+ case ATTR_ACCEL_SCALE:
+ {
+ const s16 accel_scale[] = { 2, 4, 8, 16 };
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ accel_scale[st->chip_config.accel_fs]);
+ }
+ case ATTR_GYRO_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.gyro_enable);
+ case ATTR_ACCEL_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.accel_enable);
+ case ATTR_IN_POWER_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->power_on_data);
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->batch.timeout);
+ case ATTR_DMP_PED_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->ped.on);
+ case ATTR_DMP_TILT_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->chip_config.tilt_enable);
+ case ATTR_DMP_PICK_UP_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->chip_config.pick_up_enable);
+ case ATTR_DMP_EIS_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.eis_enable);
+ case ATTR_DMP_LP_EN_OFF:
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->chip_config.lp_en_mode_off);
+ case ATTR_DMP_STEP_COUNTER_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_counter_l_on);
+ case ATTR_DMP_STEP_COUNTER_WAKE_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_counter_wake_l_on);
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_detector_l_on);
+ case ATTR_DMP_STEP_DETECTOR_WAKE_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_detector_wake_l_on);
+ case ATTR_GYRO_MATRIX:
+ m = st->plat_data.orientation;
+ return snprintf(buf, MAX_WR_SZ, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
+ m[8]);
+ case ATTR_ACCEL_MATRIX:
+ m = st->plat_data.orientation;
+ return snprintf(buf, MAX_WR_SZ, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
+ m[8]);
+ case ATTR_GYRO_SF:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_sf);
+ case ATTR_ANGLVEL_X_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[0]);
+ case ATTR_ANGLVEL_Y_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[1]);
+ case ATTR_ANGLVEL_Z_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[2]);
+ case ATTR_ACCEL_X_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[0]);
+ case ATTR_ACCEL_Y_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[1]);
+ case ATTR_ACCEL_Z_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[2]);
+ case ATTR_GYRO_X_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[0]);
+ case ATTR_GYRO_Y_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[1]);
+ case ATTR_GYRO_Z_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[2]);
+ case ATTR_ACCEL_X_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[0]);
+ case ATTR_ACCEL_Y_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[1]);
+ case ATTR_ACCEL_Z_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[2]);
+ default:
+ return -EPERM;
+ }
+}
+
+static ssize_t inv_self_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int res;
+
+ mutex_lock(&indio_dev->mlock);
+ res = inv_hw_self_test(st);
+ set_inv_enable(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n", res);
+}
+
+
+/*
+ * inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ u8 data[2];
+ s32 temp;
+ int res;
+
+ mutex_lock(&indio_dev->mlock);
+ res = inv_plat_read(st, REG_RAW_TEMP, 2, data);
+ if (res)
+ return res;
+ mutex_unlock(&indio_dev->mlock);
+
+ temp = (s32)be16_to_cpup((__be16 *)(data)) * 10000;
+ temp = temp / TEMP_SENSITIVITY + TEMP_OFFSET;
+
+ return snprintf(buf, MAX_WR_SZ, "%d %lld\n", temp, get_time_ns());
+}
+
+/*
+ * inv_reg_dump_show() - Register dump for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ii;
+ char data;
+ int bytes_printed = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ bytes_printed += snprintf(buf + bytes_printed, MAX_WR_SZ, "bank 0\n");
+
+ for (ii = 0; ii < 0x7F; ii++) {
+ /* don't read fifo r/w register */
+ if ((ii == REG_MEM_R_W) || (ii == REG_FIFO_R_W))
+ data = 0;
+ else
+ inv_plat_read(st, ii, 1, &data);
+ bytes_printed += snprintf(buf + bytes_printed, MAX_WR_SZ,
+ "%#2x: %#2x\n", ii, data);
+ }
+ set_inv_enable(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+
+ return bytes_printed;
+}
+
+static ssize_t inv_flush_batch_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result, data;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = inv_flush_batch_data(indio_dev, data);
+ mutex_unlock(&indio_dev->mlock);
+
+ return count;
+}
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+};
+
+/* special run time sysfs entry, read only */
+static DEVICE_ATTR(debug_reg_dump, S_IRUGO | S_IWUSR, inv_reg_dump_show, NULL);
+static DEVICE_ATTR(out_temperature, S_IRUGO | S_IWUSR,
+ inv_temperature_show, NULL);
+static DEVICE_ATTR(misc_self_test, S_IRUGO | S_IWUSR, inv_self_test, NULL);
+
+static IIO_DEVICE_ATTR(info_anglvel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(info_accel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_ACCEL_MATRIX);
+
+static IIO_DEVICE_ATTR(info_gyro_sf, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_SF);
+/* write only sysfs */
+static DEVICE_ATTR(misc_flush_batch, S_IWUSR, NULL, inv_flush_batch_store);
+
+/* sensor on/off sysfs control */
+static IIO_DEVICE_ATTR(in_accel_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store, SENSOR_L_ACCEL);
+static IIO_DEVICE_ATTR(in_anglvel_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store, SENSOR_L_GYRO);
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(in_eis_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store,
+ SENSOR_L_EIS_GYRO);
+#endif
+static IIO_DEVICE_ATTR(in_accel_wake_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store,
+ SENSOR_L_ACCEL_WAKE);
+static IIO_DEVICE_ATTR(in_anglvel_wake_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store,
+ SENSOR_L_GYRO_WAKE);
+
+/* sensor rate sysfs control */
+static IIO_DEVICE_ATTR(in_accel_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_ACCEL);
+static IIO_DEVICE_ATTR(in_anglvel_rate, S_IRUGO | S_IWUSR, inv_sensor_rate_show,
+ inv_sensor_rate_store, SENSOR_L_GYRO);
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(in_eis_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_EIS_GYRO);
+#endif
+static IIO_DEVICE_ATTR(in_accel_wake_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_ACCEL_WAKE);
+static IIO_DEVICE_ATTR(in_anglvel_wake_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_GYRO_WAKE);
+
+static IIO_DEVICE_ATTR(misc_batchmode_timeout, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_BATCHMODE_TIMEOUT);
+
+/* engine scale */
+static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_misc_attr_store, ATTR_ACCEL_SCALE);
+static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_misc_attr_store, ATTR_GYRO_SCALE);
+
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(debug_lp_en_off, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DMP_LP_EN_OFF);
+static IIO_DEVICE_ATTR(debug_clock_sel, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DMP_CLK_SEL);
+static IIO_DEVICE_ATTR(debug_reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DEBUG_REG_WRITE);
+static IIO_DEVICE_ATTR(debug_reg_write_addr, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DEBUG_REG_ADDR);
+#endif
+
+static IIO_DEVICE_ATTR(in_accel_x_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ACCEL_X_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_y_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ACCEL_Y_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_z_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ACCEL_Z_ST_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ANGLVEL_X_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_y_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ANGLVEL_Y_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_z_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ANGLVEL_Z_ST_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_ACCEL_X_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_ACCEL_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_ACCEL_Z_OFFSET);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_GYRO_X_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_GYRO_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_GYRO_Z_OFFSET);
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(in_step_detector_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_STEP_DETECTOR_ON);
+static IIO_DEVICE_ATTR(in_step_detector_wake_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_STEP_DETECTOR_WAKE_ON);
+static IIO_DEVICE_ATTR(in_step_counter_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_basic_attr_store, ATTR_DMP_STEP_COUNTER_ON);
+static IIO_DEVICE_ATTR(in_step_counter_wake_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_STEP_COUNTER_WAKE_ON);
+
+static IIO_DEVICE_ATTR(event_tilt_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_TILT_ENABLE);
+
+static IIO_DEVICE_ATTR(event_eis_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_EIS_ENABLE);
+
+static IIO_DEVICE_ATTR(event_pick_up_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_PICK_UP_ENABLE);
+
+static IIO_DEVICE_ATTR(in_power_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_IN_POWER_ON);
+#endif
+
+static const struct attribute *inv_raw_attributes[] = {
+ &dev_attr_debug_reg_dump.attr,
+ &dev_attr_out_temperature.attr,
+ &dev_attr_misc_flush_batch.attr,
+ &dev_attr_misc_self_test.attr,
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ &iio_dev_attr_in_power_on.dev_attr.attr,
+#endif
+ &iio_dev_attr_in_accel_enable.dev_attr.attr,
+ &iio_dev_attr_in_accel_wake_enable.dev_attr.attr,
+ &iio_dev_attr_info_accel_matrix.dev_attr.attr,
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ &iio_dev_attr_misc_batchmode_timeout.dev_attr.attr,
+ &iio_dev_attr_in_accel_rate.dev_attr.attr,
+ &iio_dev_attr_in_accel_wake_rate.dev_attr.attr,
+};
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static const struct attribute *inv_debug_attributes[] = {
+ &iio_dev_attr_debug_lp_en_off.dev_attr.attr,
+ &iio_dev_attr_debug_clock_sel.dev_attr.attr,
+ &iio_dev_attr_debug_reg_write.dev_attr.attr,
+ &iio_dev_attr_debug_reg_write_addr.dev_attr.attr,
+};
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+ &iio_dev_attr_info_anglvel_matrix.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_enable.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_rate.dev_attr.attr,
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ &iio_dev_attr_in_eis_enable.dev_attr.attr,
+#endif
+ &iio_dev_attr_in_anglvel_wake_enable.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_scale.dev_attr.attr,
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ &iio_dev_attr_in_eis_rate.dev_attr.attr,
+#endif
+ &iio_dev_attr_in_anglvel_wake_rate.dev_attr.attr,
+ &iio_dev_attr_info_gyro_sf.dev_attr.attr,
+};
+
+static const struct attribute *inv_bias_attributes[] = {
+ &iio_dev_attr_in_accel_x_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_offset.dev_attr.attr,
+};
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static const struct attribute *inv_pedometer_attributes[] = {
+ &iio_dev_attr_event_tilt_enable.dev_attr.attr,
+ &iio_dev_attr_event_eis_enable.dev_attr.attr,
+ &iio_dev_attr_event_pick_up_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_counter_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_counter_wake_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_detector_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_detector_wake_enable.dev_attr.attr,
+};
+#endif
+
+static struct attribute *inv_attributes[ARRAY_SIZE(inv_raw_attributes) +
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ ARRAY_SIZE(inv_debug_attributes) +
+#endif
+ ARRAY_SIZE(inv_gyro_attributes) +
+ ARRAY_SIZE(inv_bias_attributes) +
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ ARRAY_SIZE(inv_pedometer_attributes) +
+#endif
+ + 1];
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "mpu",
+ .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &inv_attribute_group,
+};
+
+/*
+ * inv_check_chip_type() - check and setup chip type.
+ */
+int inv_check_chip_type(struct iio_dev *indio_dev, const char *name)
+{
+ int result;
+ int t_ind;
+ struct inv_chip_config_s *conf;
+ struct mpu_platform_data *plat;
+ struct inv_mpu_state *st;
+
+ st = iio_priv(indio_dev);
+ conf = &st->chip_config;
+ plat = &st->plat_data;
+
+ if (!strcmp(name, "iam20680"))
+ st->chip_type = IAM20680;
+ else
+ return -EPERM;
+ st->chip_config.has_gyro = 1;
+
+ st->hw = &hw_info[st->chip_type];
+ result = inv_mpu_initialize(st);
+ if (result)
+ return result;
+
+ t_ind = 0;
+ memcpy(&inv_attributes[t_ind], inv_raw_attributes,
+ sizeof(inv_raw_attributes));
+ t_ind += ARRAY_SIZE(inv_raw_attributes);
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ memcpy(&inv_attributes[t_ind], inv_pedometer_attributes,
+ sizeof(inv_pedometer_attributes));
+ t_ind += ARRAY_SIZE(inv_pedometer_attributes);
+#endif
+
+ memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+ sizeof(inv_gyro_attributes));
+ t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+ memcpy(&inv_attributes[t_ind], inv_bias_attributes,
+ sizeof(inv_bias_attributes));
+ t_ind += ARRAY_SIZE(inv_bias_attributes);
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ memcpy(&inv_attributes[t_ind], inv_debug_attributes,
+ sizeof(inv_debug_attributes));
+ t_ind += ARRAY_SIZE(inv_debug_attributes);
+#endif
+
+ inv_attributes[t_ind] = NULL;
+
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ indio_dev->info = &mpu_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(inv_check_chip_type);
+
+int inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+ // dummy
+ return 0;
+}
+EXPORT_SYMBOL_GPL(inv_create_dmp_sysfs);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device ICM20xxx driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h
new file mode 100644
index 000000000000..3f8ce71be024
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _INV_MPU_IIO_REG_20680_H_
+#define _INV_MPU_IIO_REG_20680_H_
+
+/* Uncomment when HAL does not support the algorithm library
+ * for calibration and sensor fusion not to expose unused
+ * sysfs entries */
+#define SUPPORT_ONLY_BASIC_FEATURES
+
+/* Uncomment to read data registers for sensor data instead of FIFO */
+//#define SENSOR_DATA_FROM_REGISTERS
+
+/* Uncomment to enable timer based batching */
+#define TIMER_BASED_BATCHING
+
+/* Polling (batch mode) can be enabled only when FIFO read */
+#if defined(SENSOR_DATA_FROM_REGISTERS)
+#undef TIMER_BASED_BATCHING
+#endif
+
+/*register and associated bit definition*/
+#define REG_XA_OFFS_H 0x77
+#define REG_YA_OFFS_H 0x7A
+#define REG_ZA_OFFS_H 0x7D
+#define REG_XG_OFFS_USR_H 0x13
+#define REG_YG_OFFS_USR_H 0x15
+#define REG_ZG_OFFS_USR_H 0x17
+#define REG_SAMPLE_RATE_DIV 0x19
+
+#define REG_CONFIG 0x1A
+#define EXT_SYNC_SET 8
+
+#define REG_GYRO_CONFIG 0x1B
+#define BITS_SELF_TEST_EN 0xE0
+#define SHIFT_GYRO_FS_SEL 0x03
+
+#define REG_ACCEL_CONFIG 0x1C
+#define SHIFT_ACCEL_FS 0x03
+
+#define REG_LP_MODE_CTRL 0x1E
+#define BIT_GYRO_CYCLE_EN 0x80
+
+#define REG_ACCEL_WOM_THR 0x1F
+#define REG_ACCEL_WOM_X_THR 0x20
+#define REG_ACCEL_WOM_Y_THR 0x21
+#define REG_ACCEL_WOM_Z_THR 0x22
+
+#define REG_ACCEL_MOT_THR 0x1F
+#define REG_ACCEL_MOT_DUR 0x20
+
+#define REG_ACCEL_CONFIG_2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+
+#define REG_FIFO_EN 0x23
+#define BITS_GYRO_FIFO_EN 0x70
+#define BIT_ACCEL_FIFO_EN 0x08
+
+#define REG_FSYNC_INT 0x36
+#define BIT_FSYNC_INT 0x80
+
+#define REG_INT_PIN_CFG 0x37
+
+#define REG_INT_ENABLE 0x38
+#define BIT_WOM_X_INT_EN 0x80
+#define BIT_WOM_Y_INT_EN 0x40
+#define BIT_WOM_Z_INT_EN 0x20
+#define BIT_WOM_ALL_INT_EN 0xE0
+#define BIT_FSYNC_INT_EN 0x8
+#define BIT_DATA_RDY_EN 0x1
+
+#define REG_INT_STATUS 0x3A
+#define BIT_WOM_X_INT 0x80
+#define BIT_WOM_Y_INT 0x40
+#define BIT_WOM_Z_INT 0x20
+
+#define REG_RAW_ACCEL 0x3B
+#define REG_RAW_TEMP 0x41
+#define REG_RAW_GYRO 0x43
+#define REG_EXT_SENS_DATA_00 0x49
+#define REG_EXT_SENS_DATA_08 0x51
+#define REG_EXT_SENS_DATA_09 0x52
+
+#define REG_ACCEL_INTEL_CTRL 0x69
+#define BIT_ACCEL_INTEL_EN 0x80
+#define BIT_ACCEL_INTEL_MODE 0x40
+
+#define REG_USER_CTRL 0x6A
+#define BIT_COND_RST 0x01
+#define BIT_FIFO_RST 0x04
+#define BIT_FIFO_EN 0x40
+
+#define REG_PWR_MGMT_1 0x6B
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_LP_EN 0x20
+#define BIT_CLK_PLL 0x01
+#define BIT_CLK_MASK 0x07
+
+#define REG_PWR_MGMT_2 0x6C
+#define BIT_PWR_ACCEL_STBY 0x38
+#define BIT_PWR_GYRO_STBY 0x07
+#define BIT_PWR_ALL_OFF 0x3F
+#define BIT_FIFO_LP_EN 0x80
+
+#define REG_MEM_BANK_SEL 0x6D
+#define REG_MEM_START_ADDR 0x6E
+#define REG_MEM_R_W 0x6F
+
+#define REG_FIFO_COUNT_H 0x72
+#define REG_FIFO_R_W 0x74
+#define REG_WHO_AM_I 0x75
+
+#define REG_6500_XG_ST_DATA 0x50
+#define REG_6500_XA_ST_DATA 0xD
+#define REG_6500_XA_OFFS_H 0x77
+#define REG_6500_YA_OFFS_H 0x7A
+#define REG_6500_ZA_OFFS_H 0x7D
+#define REG_6500_ACCEL_CONFIG2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+#define BIT_FIFO_SIZE_1K 0x40
+
+#define REG_LP_MODE_CFG 0x1E
+
+#define REG_6500_LP_ACCEL_ODR 0x1E
+#define REG_6500_ACCEL_WOM_THR 0x1F
+
+/* data output control reg 2 */
+#define ACCEL_ACCURACY_SET 0x4000
+#define GYRO_ACCURACY_SET 0x2000
+#define CPASS_ACCURACY_SET 0x1000
+
+/* data definitions */
+#define ACCEL_COVARIANCE 0
+#define BYTES_PER_SENSOR 6
+#define BYTES_FOR_TEMP 2
+#define FIFO_COUNT_BYTE 2
+#define HARDWARE_FIFO_SIZE 512
+#define FIFO_SIZE (HARDWARE_FIFO_SIZE * 7 / 10)
+#define POWER_UP_TIME 100
+#define REG_UP_TIME_USEC 100
+#define LEFT_OVER_BYTES 128
+#define IIO_BUFFER_BYTES 8
+#define BASE_SAMPLE_RATE 1000
+#define DRY_RUN_TIME 50
+#define INV_IAM20680_GYRO_START_TIME 35
+#define INV_IAM20680_ACCEL_START_TIME 30
+#define MODE_1K_INIT_SAMPLE 5
+#define FIRST_SAMPLE_BUF_MS 30
+
+#ifdef BIAS_CONFIDENCE_HIGH
+#define DEFAULT_ACCURACY 3
+#else
+#define DEFAULT_ACCURACY 1
+#endif
+
+/* temperature */
+#define TEMP_SENSITIVITY 32680 // 326.8 LSB/degC * 100
+#define TEMP_OFFSET 2500 // 25 degC * 100
+
+/* enum for sensor */
+enum INV_SENSORS {
+ SENSOR_ACCEL = 0,
+ SENSOR_TEMP,
+ SENSOR_GYRO,
+ SENSOR_COMPASS,
+ SENSOR_NUM_MAX,
+ SENSOR_INVALID,
+};
+
+enum inv_filter_e {
+ INV_FILTER_256HZ_NOLPF2 = 0,
+ INV_FILTER_188HZ,
+ INV_FILTER_98HZ,
+ INV_FILTER_42HZ,
+ INV_FILTER_20HZ,
+ INV_FILTER_10HZ,
+ INV_FILTER_5HZ,
+ INV_FILTER_2100HZ_NOLPF,
+ NUM_FILTER
+};
+
+#define MPU_DEFAULT_DMP_FREQ 200
+#define PEDOMETER_FREQ (MPU_DEFAULT_DMP_FREQ >> 2)
+#define SENSOR_FUSION_MIN_RATE 100
+#define GESTURE_ACCEL_RATE 50
+#define ESI_GYRO_RATE 1000
+#define MAX_FIFO_PACKET_READ 6
+#define MAX_BATCH_FIFO_SIZE FIFO_SIZE
+
+#define MIN_MST_ODR_CONFIG 4
+#define MAX_MST_ODR_CONFIG 5
+/* initial rate is important. For non-DMP mode, it is set as 4 1000/256*/
+#define MPU_INIT_SENSOR_RATE 4
+#define MAX_MST_NON_COMPASS_ODR_CONFIG 7
+#define THREE_AXES 3
+#define NINE_ELEM (THREE_AXES * THREE_AXES)
+#define MPU_TEMP_SHIFT 16
+
+#define DMP_DIVIDER (BASE_SAMPLE_RATE / MPU_DEFAULT_DMP_FREQ)
+#define DEFAULT_BATCH_RATE 400
+#define DEFAULT_BATCH_TIME (MSEC_PER_SEC / DEFAULT_BATCH_RATE)
+
+#define TEMPERATURE_SCALE 3340827L
+#define TEMPERATURE_OFFSET 1376256L
+#define SECONDARY_INIT_WAIT 100
+#define MPU_SOFT_REV_ADDR 0x86
+#define MPU_SOFT_REV_MASK 0xf
+#define SW_REV_LP_EN_MODE 4
+
+/* data limit definitions */
+#define MIN_FIFO_RATE 4
+#define MAX_FIFO_RATE MPU_DEFAULT_DMP_FREQ
+
+#define MAX_MPU_MEM 8192
+#define MAX_PRS_RATE 281
+
+enum inv_devices {
+ ICM20608D,
+ ICM20690,
+ ICM20602,
+ IAM20680,
+ INV_NUM_PARTS,
+};
+#endif
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c
new file mode 100644
index 000000000000..58bd8d073890
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+#include "../inv_mpu_iio.h"
+
+static int inv_calc_gyro_sf(s8 pll)
+{
+ int a, r;
+ int value, t;
+
+ t = 102870L + 81L * pll;
+ a = (1L << 30) / t;
+ r = (1L << 30) - a * t;
+ value = a * 797 * DMP_DIVIDER;
+ value += (s64) ((a * 1011387LL * DMP_DIVIDER) >> 20);
+ value += r * 797L * DMP_DIVIDER / t;
+ value += (s32) ((s64) ((r * 1011387LL * DMP_DIVIDER) >> 20)) / t;
+ value <<= 1;
+
+ return value;
+}
+
+static int inv_read_timebase(struct inv_mpu_state *st)
+{
+
+ inv_plat_single_write(st, REG_CONFIG, 3);
+
+ st->eng_info[ENGINE_ACCEL].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_ACCEL].base_time_1k = NSEC_PER_SEC;
+ /* talor expansion to calculate base time unit */
+ st->eng_info[ENGINE_GYRO].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_GYRO].base_time_1k = NSEC_PER_SEC;
+ st->eng_info[ENGINE_I2C].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_I2C].base_time_1k = NSEC_PER_SEC;
+
+ st->eng_info[ENGINE_ACCEL].orig_rate = BASE_SAMPLE_RATE;
+ st->eng_info[ENGINE_GYRO].orig_rate = BASE_SAMPLE_RATE;
+ st->eng_info[ENGINE_I2C].orig_rate = BASE_SAMPLE_RATE;
+
+ st->gyro_sf = inv_calc_gyro_sf(0);
+
+ return 0;
+}
+
+int inv_set_gyro_sf(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_plat_single_write(st, REG_GYRO_CONFIG,
+ st->chip_config.fsr << SHIFT_GYRO_FS_SEL);
+
+ return result;
+}
+
+int inv_set_accel_sf(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG,
+ st->chip_config.accel_fs << SHIFT_ACCEL_FS);
+ return result;
+}
+
+// dummy for 20602
+int inv_set_accel_intel(struct inv_mpu_state *st)
+{
+ return 0;
+}
+
+static void inv_init_sensor_struct(struct inv_mpu_state *st)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
+
+ st->sensor[SENSOR_ACCEL].sample_size = BYTES_PER_SENSOR;
+ st->sensor[SENSOR_TEMP].sample_size = BYTES_FOR_TEMP;
+ st->sensor[SENSOR_GYRO].sample_size = BYTES_PER_SENSOR;
+
+ st->sensor_l[SENSOR_L_SIXQ].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_PEDQ].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_SIXQ_WAKE].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_PEDQ_WAKE].base = SENSOR_GYRO;
+
+ st->sensor[SENSOR_ACCEL].a_en = true;
+ st->sensor[SENSOR_GYRO].a_en = false;
+
+ st->sensor[SENSOR_ACCEL].g_en = false;
+ st->sensor[SENSOR_GYRO].g_en = true;
+
+ st->sensor[SENSOR_ACCEL].c_en = false;
+ st->sensor[SENSOR_GYRO].c_en = false;
+
+ st->sensor[SENSOR_ACCEL].p_en = false;
+ st->sensor[SENSOR_GYRO].p_en = false;
+
+ st->sensor[SENSOR_ACCEL].engine_base = ENGINE_ACCEL;
+ st->sensor[SENSOR_GYRO].engine_base = ENGINE_GYRO;
+
+ st->sensor_l[SENSOR_L_ACCEL].base = SENSOR_ACCEL;
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].base = SENSOR_ACCEL;
+ st->sensor_l[SENSOR_L_GYRO].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_GYRO_CAL].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_EIS_GYRO].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_ACCEL_WAKE].base = SENSOR_ACCEL;
+ st->sensor_l[SENSOR_L_GYRO_WAKE].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_ACCEL].header = ACCEL_HDR;
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].header = ACCEL_HDR;
+ st->sensor_l[SENSOR_L_GYRO].header = GYRO_HDR;
+ st->sensor_l[SENSOR_L_GYRO_CAL].header = GYRO_CALIB_HDR;
+
+ st->sensor_l[SENSOR_L_EIS_GYRO].header = EIS_GYRO_HDR;
+ st->sensor_l[SENSOR_L_SIXQ].header = SIXQUAT_HDR;
+ st->sensor_l[SENSOR_L_THREEQ].header = LPQ_HDR;
+ st->sensor_l[SENSOR_L_NINEQ].header = NINEQUAT_HDR;
+ st->sensor_l[SENSOR_L_PEDQ].header = PEDQUAT_HDR;
+
+ st->sensor_l[SENSOR_L_ACCEL_WAKE].header = ACCEL_WAKE_HDR;
+ st->sensor_l[SENSOR_L_GYRO_WAKE].header = GYRO_WAKE_HDR;
+ st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].header = GYRO_CALIB_WAKE_HDR;
+ st->sensor_l[SENSOR_L_MAG_WAKE].header = COMPASS_WAKE_HDR;
+ st->sensor_l[SENSOR_L_MAG_CAL_WAKE].header = COMPASS_CALIB_WAKE_HDR;
+ st->sensor_l[SENSOR_L_SIXQ_WAKE].header = SIXQUAT_WAKE_HDR;
+ st->sensor_l[SENSOR_L_NINEQ_WAKE].header = NINEQUAT_WAKE_HDR;
+ st->sensor_l[SENSOR_L_PEDQ_WAKE].header = PEDQUAT_WAKE_HDR;
+
+ st->sensor_l[SENSOR_L_ACCEL].wake_on = false;
+ st->sensor_l[SENSOR_L_GYRO].wake_on = false;
+ st->sensor_l[SENSOR_L_GYRO_CAL].wake_on = false;
+ st->sensor_l[SENSOR_L_MAG].wake_on = false;
+ st->sensor_l[SENSOR_L_MAG_CAL].wake_on = false;
+ st->sensor_l[SENSOR_L_EIS_GYRO].wake_on = false;
+ st->sensor_l[SENSOR_L_SIXQ].wake_on = false;
+ st->sensor_l[SENSOR_L_NINEQ].wake_on = false;
+ st->sensor_l[SENSOR_L_PEDQ].wake_on = false;
+
+ st->sensor_l[SENSOR_L_ACCEL_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_GYRO_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_MAG_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_SIXQ_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_NINEQ_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_PEDQ_WAKE].wake_on = true;
+}
+
+static int inv_init_config(struct inv_mpu_state *st)
+{
+ int res, i;
+
+ st->batch.overflow_on = 0;
+ st->chip_config.fsr = MPU_INIT_GYRO_SCALE;
+ st->chip_config.accel_fs = MPU_INIT_ACCEL_SCALE;
+ st->ped.int_thresh = MPU_INIT_PED_INT_THRESH;
+ st->ped.step_thresh = MPU_INIT_PED_STEP_THRESH;
+ st->chip_config.low_power_gyro_on = 1;
+ st->eis.count_precision = NSEC_PER_MSEC;
+ st->firmware = 0;
+ st->fifo_count_mode = BYTE_MODE;
+#ifdef TIMER_BASED_BATCHING
+ st->batch_timeout = 0;
+ st->is_batch_timer_running = false;
+#endif
+
+ st->eng_info[ENGINE_GYRO].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_ACCEL].base_time = NSEC_PER_SEC;
+
+ inv_init_sensor_struct(st);
+ res = inv_read_timebase(st);
+ if (res)
+ return res;
+
+ res = inv_set_gyro_sf(st);
+ if (res)
+ return res;
+ res = inv_set_accel_sf(st);
+ if (res)
+ return res;
+ res = inv_set_accel_intel(st);
+ if (res)
+ return res;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].ts = 0;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].previous_ts = 0;
+
+ return res;
+}
+
+int inv_mpu_initialize(struct inv_mpu_state *st)
+{
+ u8 v;
+ int result;
+ struct inv_chip_config_s *conf;
+ struct mpu_platform_data *plat;
+
+ conf = &st->chip_config;
+ plat = &st->plat_data;
+
+ /* verify whoami */
+ result = inv_plat_read(st, REG_WHO_AM_I, 1, &v);
+ if (result)
+ return result;
+ pr_info("whoami= %x\n", v);
+ if (v == 0x00 || v == 0xff)
+ return -ENODEV;
+
+ /* reset to make sure previous state are not there */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
+ if (result)
+ return result;
+ usleep_range(REG_UP_TIME_USEC, REG_UP_TIME_USEC);
+ msleep(100);
+ /* toggle power state */
+ result = inv_set_power(st, false);
+ if (result)
+ return result;
+ result = inv_set_power(st, true);
+ if (result)
+ return result;
+
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
+ if (result)
+ return result;
+ result = inv_init_config(st);
+ if (result)
+ return result;
+
+ result = mem_r(MPU_SOFT_REV_ADDR, 1, &v);
+ pr_info("sw_rev=%x, res=%d\n", v, result);
+ if (result)
+ return result;
+ st->chip_config.lp_en_mode_off = 0;
+
+ pr_info("%s: Mask %X, v = %X, lp mode = %d\n", __func__,
+ MPU_SOFT_REV_MASK, v, st->chip_config.lp_en_mode_off);
+ result = inv_set_power(st, false);
+
+ pr_info("%s: initialize result is %d....\n", __func__, result);
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c
new file mode 100644
index 000000000000..0f17b6dd2c52
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/math64.h>
+
+#include "../inv_mpu_iio.h"
+
+static char iden[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
+
+static int inv_process_gyro(struct inv_mpu_state *st, u8 *d, u64 t)
+{
+ s16 raw[3];
+ s32 calib[3];
+ int i;
+#define BIAS_UNIT 2859
+
+ for (i = 0; i < 3; i++)
+ raw[i] = be16_to_cpup((__be16 *) (d + i * 2));
+
+ for (i = 0; i < 3; i++)
+ calib[i] = (raw[i] << 15);
+
+
+ inv_push_gyro_data(st, raw, calib, t);
+
+ return 0;
+}
+
+static int inv_check_fsync(struct inv_mpu_state *st, u8 fsync_status)
+{
+ u8 data[1];
+
+ if (!st->chip_config.eis_enable)
+ return 0;
+ inv_plat_read(st, REG_FSYNC_INT, 1, data);
+ if (data[0] & BIT_FSYNC_INT) {
+ pr_debug("fsync\n");
+ st->eis.eis_triggered = true;
+ st->eis.fsync_delay = 1;
+ st->eis.prev_state = 1;
+ st->eis.frame_count++;
+ st->eis.eis_frame = true;
+ }
+ st->header_count--;
+
+ return 0;
+}
+
+static int inv_push_sensor(struct inv_mpu_state *st, int ind, u64 t, u8 *d)
+{
+#ifdef ACCEL_BIAS_TEST
+ s16 acc[3], avg[3];
+#endif
+
+ switch (ind) {
+ case SENSOR_ACCEL:
+ inv_convert_and_push_8bytes(st, ind, d, t, iden);
+#ifdef ACCEL_BIAS_TEST
+ acc[0] = be16_to_cpup((__be16 *) (d));
+ acc[1] = be16_to_cpup((__be16 *) (d + 2));
+ acc[2] = be16_to_cpup((__be16 *) (d + 4));
+ if(inv_get_3axis_average(acc, avg, 0)){
+ pr_debug("accel 200 samples average = %5d, %5d, %5d\n", avg[0], avg[1], avg[2]);
+ }
+#endif
+ break;
+ case SENSOR_TEMP:
+ inv_check_fsync(st, d[1]);
+ break;
+ case SENSOR_GYRO:
+ inv_process_gyro(st, d, t);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int inv_push_20680_data(struct inv_mpu_state *st, u8 *d)
+{
+ u8 *dptr;
+ int i;
+
+ dptr = d;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ inv_get_dmp_ts(st, i);
+ if (st->sensor[i].send && (!st->ts_algo.first_sample)) {
+ st->sensor[i].sample_calib++;
+ inv_push_sensor(st, i, st->sensor[i].ts, dptr);
+ }
+ dptr += st->sensor[i].sample_size;
+ }
+ }
+ if (st->ts_algo.first_sample)
+ st->ts_algo.first_sample--;
+ st->header_count--;
+
+ return 0;
+}
+
+static int inv_process_20680_data(struct inv_mpu_state *st)
+{
+ int total_bytes, tmp, res, fifo_count, pk_size, i;
+ u8 *dptr, *d;
+ u8 data[14];
+ bool done_flag;
+ u8 v;
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ u8 reg;
+ int len;
+#endif
+
+ if(st->gesture_only_on && (!st->batch.timeout)) {
+ res = inv_plat_read(st, REG_INT_STATUS, 1, data);
+ if (res)
+ return res;
+ pr_debug("ges cnt=%d, statu=%x\n",
+ st->gesture_int_count, data[0]);
+ if (data[0] & (BIT_WOM_ALL_INT_EN)) {
+ if (!st->gesture_int_count) {
+ inv_switch_power_in_lp(st, true);
+ res = inv_plat_single_write(st, REG_INT_ENABLE,
+ BIT_WOM_ALL_INT_EN | BIT_DATA_RDY_EN);
+ if (res)
+ return res;
+ v = 0;
+ if (st->chip_config.gyro_enable)
+ v |= BITS_GYRO_FIFO_EN;
+
+ if (st->chip_config.accel_enable)
+ v |= BIT_ACCEL_FIFO_EN;
+ res = inv_plat_single_write(st, REG_FIFO_EN, v);
+ if (res)
+ return res;
+ /* First time wake up from WOM.
+ We don't need data in the FIFO */
+ res = inv_reset_fifo(st, true);
+ if (res)
+ return res;
+ res = inv_switch_power_in_lp(st, false);
+ st->gesture_int_count = WOM_DELAY_THRESHOLD;
+
+ return res;
+ }
+ st->gesture_int_count = WOM_DELAY_THRESHOLD;
+ } else {
+ if (!st->gesture_int_count) {
+ inv_switch_power_in_lp(st, true);
+ res = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ res = inv_plat_single_write(st, REG_INT_ENABLE,
+ BIT_WOM_ALL_INT_EN);
+ inv_switch_power_in_lp(st, false);
+
+ return res;
+ }
+ st->gesture_int_count--;
+ }
+ }
+
+ fifo_count = inv_get_last_run_time_non_dmp_record_mode(st);
+ pr_debug("fifc= %d\n", fifo_count);
+ if (!fifo_count) {
+ pr_debug("REG_FIFO_COUNT_H size is 0\n");
+ return 0;
+ }
+ pk_size = st->batch.pk_size;
+ if (!pk_size)
+ return -EINVAL;
+
+ if (fifo_count >= (HARDWARE_FIFO_SIZE / st->batch.pk_size)) {
+ pr_warn("fifo overflow pkt count=%d pkt sz=%d\n", fifo_count, st->batch.pk_size);
+ return -EOVERFLOW;
+ }
+
+ fifo_count *= st->batch.pk_size;
+ st->fifo_count = fifo_count;
+ d = st->fifo_data_store;
+ dptr = d;
+ total_bytes = fifo_count;
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ len = 0;
+ if (st->sensor[SENSOR_GYRO].on) {
+ reg = REG_RAW_GYRO;
+ len += BYTES_PER_SENSOR;
+ if (st->sensor[SENSOR_ACCEL].on && !st->sensor[SENSOR_TEMP].on)
+ len += BYTES_FOR_TEMP;
+ }
+ if (st->sensor[SENSOR_TEMP].on) {
+ reg = REG_RAW_TEMP;
+ len += BYTES_FOR_TEMP;
+ }
+ if (st->sensor[SENSOR_ACCEL].on) {
+ reg = REG_RAW_ACCEL;
+ len += BYTES_PER_SENSOR;
+ }
+
+ if (len == 0) {
+ pr_debug("No sensor is enabled\n");
+ return 0;
+ }
+
+ /* read data registers */
+ res = inv_plat_read(st, reg, len, data);
+ if (res < 0) {
+ pr_err("read data registers is failed\n");
+ return res;
+ }
+
+ /* copy sensor data to buffer as FIFO data format */
+ tmp = 0;
+ if (st->sensor[SENSOR_ACCEL].on) {
+ for (i = 0; i < BYTES_PER_SENSOR; i++)
+ dptr[i] = data[tmp + i];
+ dptr += BYTES_PER_SENSOR;
+ tmp += BYTES_PER_SENSOR;
+ }
+
+ if (st->sensor[SENSOR_TEMP].on) {
+ for (i = 0; i < BYTES_FOR_TEMP; i++)
+ dptr[i] = data[tmp + i];
+ dptr += BYTES_FOR_TEMP;
+ tmp += BYTES_FOR_TEMP;
+ }
+
+ if (st->sensor[SENSOR_GYRO].on) {
+ if (st->sensor[SENSOR_ACCEL].on && !st->sensor[SENSOR_TEMP].on)
+ tmp += BYTES_FOR_TEMP;
+ for (i = 0; i < BYTES_PER_SENSOR; i++)
+ dptr[i] = data[tmp + i];
+ }
+#else
+ while (total_bytes > 0) {
+ if (total_bytes < pk_size * MAX_FIFO_PACKET_READ)
+ tmp = total_bytes;
+ else
+ tmp = pk_size * MAX_FIFO_PACKET_READ;
+ res = inv_plat_read(st, REG_FIFO_R_W, tmp, dptr);
+ if (res < 0) {
+ pr_err("read REG_FIFO_R_W is failed\n");
+ return res;
+ }
+ pr_debug("inside: %x, %x, %x, %x, %x, %x, %x, %x\n", dptr[0], dptr[1], dptr[2],
+ dptr[3], dptr[4], dptr[5], dptr[6], dptr[7]);
+ pr_debug("insid2: %x, %x, %x, %x, %x, %x, %x, %x\n", dptr[8], dptr[9], dptr[10],
+ dptr[11], dptr[12], dptr[13], dptr[14], dptr[15]);
+
+ dptr += tmp;
+ total_bytes -= tmp;
+ }
+#endif /* SENSOR_DATA_FROM_REGISTERS */
+ dptr = d;
+ pr_debug("dd: %x, %x, %x, %x, %x, %x, %x, %x\n", d[0], d[1], d[2],
+ d[3], d[4], d[5], d[6], d[7]);
+ pr_debug("dd2: %x, %x, %x, %x, %x, %x, %x, %x\n", d[8], d[9], d[10],
+ d[11], d[12], d[13], d[14], d[15]);
+ total_bytes = fifo_count;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ st->sensor[i].count = total_bytes / pk_size;
+ }
+ }
+ st->header_count = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on)
+ st->header_count = max(st->header_count,
+ st->sensor[i].count);
+ }
+
+ st->ts_algo.calib_counter++;
+ inv_bound_timestamp(st);
+
+ dptr = d;
+ done_flag = false;
+
+ while (!done_flag) {
+ pr_debug("total%d, pk=%d\n", total_bytes, pk_size);
+ if (total_bytes >= pk_size) {
+ res = inv_push_20680_data(st, dptr);
+ if (res)
+ return res;
+ total_bytes -= pk_size;
+ dptr += pk_size;
+ } else {
+ done_flag = true;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * _inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+static void _inv_read_fifo(struct inv_mpu_state *st)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int result;
+
+ result = wait_event_interruptible_timeout(st->wait_queue,
+ st->resume_state, msecs_to_jiffies(300));
+ if (result <= 0)
+ return;
+ mutex_lock(&indio_dev->mlock);
+#ifdef TIMER_BASED_BATCHING
+ if (st->batch_timeout) {
+ if (inv_plat_single_write(st, REG_INT_ENABLE, st->int_en))
+ pr_err("REG_INT_ENABLE write error\n");
+ }
+#endif
+ st->wake_sensor_received = false;
+ result = inv_process_20680_data(st);
+ if (result)
+ goto err_reset_fifo;
+ mutex_unlock(&indio_dev->mlock);
+
+ if (st->wake_sensor_received)
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_timeout(&st->wake_lock, msecs_to_jiffies(200));
+#else
+ __pm_wakeup_event(&st->wake_lock, 200); /* 200 msecs */
+#endif
+ return;
+
+err_reset_fifo:
+ if ((!st->chip_config.gyro_enable) &&
+ (!st->chip_config.accel_enable) &&
+ (!st->chip_config.slave_enable) &&
+ (!st->chip_config.pressure_enable)) {
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+
+ return;
+ }
+
+ pr_err("error to reset fifo\n");
+ inv_switch_power_in_lp(st, true);
+ inv_reset_fifo(st, true);
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+
+ return;
+}
+
+irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+ struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
+
+ _inv_read_fifo(st);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef TIMER_BASED_BATCHING
+void inv_batch_work(struct work_struct *work)
+{
+ struct inv_mpu_state *st =
+ container_of(work, struct inv_mpu_state, batch_work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+
+ mutex_lock(&indio_dev->mlock);
+ if (inv_plat_single_write(st, REG_INT_ENABLE, st->int_en | BIT_DATA_RDY_EN))
+ pr_err("REG_INT_ENABLE write error\n");
+ mutex_unlock(&indio_dev->mlock);
+
+ return;
+}
+#endif
+
+int inv_flush_batch_data(struct iio_dev *indio_dev, int data)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifndef SENSOR_DATA_FROM_REGISTERS
+ if (st->chip_config.gyro_enable ||
+ st->chip_config.accel_enable ||
+ st->chip_config.slave_enable ||
+ st->chip_config.pressure_enable) {
+ st->wake_sensor_received = 0;
+ inv_process_20680_data(st);
+ if (st->wake_sensor_received)
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_timeout(&st->wake_lock, msecs_to_jiffies(200));
+#else
+ __pm_wakeup_event(&st->wake_lock, 200); /* 200 msecs */
+#endif
+ inv_switch_power_in_lp(st, false);
+ }
+#endif /* SENSOR_DATA_FROM_REGISTERS */
+ inv_push_marker_to_buffer(st, END_MARKER, data);
+
+ return 0;
+}
+
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c
new file mode 100644
index 000000000000..7a90b4d8b882
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c
@@ -0,0 +1,752 @@
+/*
+* Copyright (C) 2017-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include "../inv_mpu_iio.h"
+
+/* register settings */
+#define DEF_SELFTEST_GYRO_SENS (32768 / 250)
+/* wait time before collecting data */
+#define MAX_PACKETS 20
+#define SELFTEST_WAIT_TIME (MAX_PACKETS * 10)
+#define DEF_ST_STABLE_TIME 20
+#define DEF_GYRO_SCALE 131
+#define DEF_ST_PRECISION 1000
+#define DEF_ST_ACCEL_FS_MG 2000UL
+#define DEF_ST_SCALE 32768
+#define DEF_ST_TRY_TIMES 2
+#define DEF_ST_ACCEL_RESULT_SHIFT 1
+#define DEF_ST_SAMPLES 200
+
+#define DEF_ACCEL_ST_SHIFT_DELTA_MIN 500
+#define DEF_ACCEL_ST_SHIFT_DELTA_MAX 1500
+#define DEF_GYRO_CT_SHIFT_DELTA 500
+
+#define SENSOR_UP_TIME 30
+#define REG_UP_TIME 2
+
+#define DEF_ST_ACCEL_FS_MG 2000UL
+#define DEF_ACCEL_ST_SHIFT_DELTA 500
+#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \
+ / DEF_ST_ACCEL_FS_MG) * DEF_ST_PRECISION)
+#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \
+ / DEF_ST_ACCEL_FS_MG) * DEF_ST_PRECISION)
+
+#define THREE_AXIS 3
+#define DEF_ST_MPU6500_ACCEL_LPF 2
+#define DEF_SELFTEST_SAMPLE_RATE 0 /* 1000Hz */
+#define DEF_SELFTEST_SAMPLE_RATE_LP 3 /* 250Hz */
+#define DEF_SELFTEST_SAMPLE_RATE_ACC_LP 10 /* 250Hz LPOSC_CLKSEL */
+#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50
+#define DEF_SELFTEST_6500_ACCEL_FS (0 << 3)
+#define DEF_SELFTEST_GYRO_FS (0 << 3)
+#define DEF_ST_6500_STABLE_TIME 20
+#define BIT_ACCEL_OUT 0x08
+#define BITS_GYRO_OUT 0x70
+#define THREE_AXIS 3
+#define DEF_GYRO_WAIT_TIME 10
+#define DEF_GYRO_WAIT_TIME_LP 50
+
+/* Gyro Offset Max Value (dps) */
+#define DEF_GYRO_OFFSET_MAX 20
+/* Gyro Self Test Absolute Limits ST_AL (dps) */
+#define DEF_GYRO_ST_AL 60
+/* Accel Self Test Absolute Limits ST_AL (mg) */
+#define DEF_ACCEL_ST_AL_MIN 225
+#define DEF_ACCEL_ST_AL_MAX 675
+
+struct recover_regs {
+ u8 int_enable; /* REG_INT_ENABLE */
+ u8 fifo_en; /* REG_FIFO_EN */
+ u8 user_ctrl; /* REG_USER_CTRL */
+ u8 config; /* REG_CONFIG */
+ u8 gyro_config; /* REG_GYRO_CONFIG */
+ u8 accel_config; /* REG_ACCEL_CONFIG */
+ u8 accel_config_2; /* REG_ACCEL_CONFIG_2 */
+ u8 smplrt_div; /* REG_SAMPLE_RATE_DIV */
+ u8 lp_mode; /* REG_LP_MODE_CTRL */
+ u8 pwr_mgmt_1; /* REG_PWR_MGMT_1 */
+ u8 pwr_mgmt_2; /* REG_PWR_MGMT_2 */
+};
+
+static struct recover_regs saved_regs;
+
+static const u16 mpu_st_tb[256] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+static void inv_show_saved_setting(struct inv_mpu_state *st)
+{
+ pr_debug(" REG_INT_ENABLE : 0x%02X\n", saved_regs.int_enable);
+ pr_debug(" REG_FIFO_EN : 0x%02X\n", saved_regs.fifo_en);
+ pr_debug(" REG_USER_CTRL : 0x%02X\n", saved_regs.user_ctrl);
+ pr_debug(" REG_CONFIG : 0x%02X\n", saved_regs.config);
+ pr_debug(" REG_GYRO_CONFIG : 0x%02X\n", saved_regs.gyro_config);
+ pr_debug(" REG_ACCEL_CONFIG : 0x%02X\n", saved_regs.accel_config);
+ pr_debug(" REG_ACCEL_CONFIG_2 : 0x%02X\n", saved_regs.accel_config_2);
+ pr_debug(" REG_SAMPLE_RATE_DIV : 0x%02X\n", saved_regs.smplrt_div);
+ pr_debug(" REG_LP_MODE_CTRL : 0x%02X\n", saved_regs.lp_mode);
+ pr_debug(" REG_PWR_MGMT_1 : 0x%02X\n", saved_regs.pwr_mgmt_1);
+ pr_debug(" REG_PWR_MGMT_2 : 0x%02X\n", saved_regs.pwr_mgmt_2);
+}
+
+static int inv_save_setting(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_plat_read(st, REG_PWR_MGMT_1, 1,
+ &saved_regs.pwr_mgmt_1);
+ if (result)
+ return result;
+
+ /* wake up */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1,
+ (saved_regs.pwr_mgmt_1 & ~BIT_SLEEP));
+ if (result)
+ return result;
+
+ result = inv_plat_read(st, REG_INT_ENABLE, 1,
+ &saved_regs.int_enable);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_FIFO_EN, 1,
+ &saved_regs.fifo_en);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_USER_CTRL, 1,
+ &saved_regs.user_ctrl);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_CONFIG, 1,
+ &saved_regs.config);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_GYRO_CONFIG, 1,
+ &saved_regs.gyro_config);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_ACCEL_CONFIG, 1,
+ &saved_regs.accel_config);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_ACCEL_CONFIG_2, 1,
+ &saved_regs.accel_config_2);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_SAMPLE_RATE_DIV, 1,
+ &saved_regs.smplrt_div);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_LP_MODE_CTRL, 1,
+ &saved_regs.lp_mode);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_PWR_MGMT_2, 1,
+ &saved_regs.pwr_mgmt_2);
+ if (result)
+ return result;
+
+ inv_show_saved_setting(st);
+
+ return result;
+}
+
+static int inv_recover_setting(struct inv_mpu_state *st)
+{
+ int result;
+ /* Stop sensors */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_2,
+ BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+
+ /* Restore sensor configurations */
+ result = inv_plat_single_write(st, REG_INT_ENABLE,
+ saved_regs.int_enable);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_FIFO_EN,
+ saved_regs.fifo_en);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_USER_CTRL,
+ saved_regs.user_ctrl);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_CONFIG,
+ saved_regs.config);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_GYRO_CONFIG,
+ saved_regs.gyro_config);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG,
+ saved_regs.accel_config);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG_2,
+ saved_regs.accel_config_2);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
+ saved_regs.smplrt_div);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
+ saved_regs.lp_mode);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1,
+ saved_regs.pwr_mgmt_1);
+ if (result)
+ return result;
+
+ result = inv_plat_single_write(st, REG_PWR_MGMT_2,
+ saved_regs.pwr_mgmt_2);
+ if (result)
+ return result;
+
+ return result;
+}
+
+int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask)
+{
+ u8 data, mgmt_1;
+ int result;
+
+ if (BIT_PWR_GYRO_STBY == mask) {
+ result = inv_plat_read(st, REG_PWR_MGMT_1, 1, &mgmt_1);
+ if (result)
+ return result;
+ mgmt_1 &= ~BIT_CLK_MASK;
+ }
+
+ if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, mgmt_1);
+ if (result)
+ return result;
+ }
+
+ result = inv_plat_read(st, REG_PWR_MGMT_2, 1, &data);
+ if (result)
+ return result;
+ if (en)
+ data &= (~mask);
+ else
+ data |= mask;
+ data |= BIT_FIFO_LP_EN;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_2, data);
+ if (result)
+ return result;
+
+ if ((BIT_PWR_GYRO_STBY == mask) && en) {
+ /* only gyro on needs sensor up time */
+ msleep(SENSOR_UP_TIME);
+ /* after gyro is on & stable, switch internal clock to PLL */
+ mgmt_1 |= BIT_CLK_PLL;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, mgmt_1);
+ if (result)
+ return result;
+ }
+ if ((BIT_PWR_ACCEL_STBY == mask) && en)
+ msleep(REG_UP_TIME);
+
+ return 0;
+}
+
+int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val)
+{
+ int result;
+ u8 d;
+
+ d = ((val >> 8) & 0xff);
+ result = inv_plat_single_write(st, reg, d);
+ if (result)
+ return result;
+
+ d = (val & 0xff);
+ result = inv_plat_single_write(st, reg + 1, d);
+
+ return result;
+}
+
+/**
+* inv_check_gyro_self_test() - check gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+int inv_check_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ u8 regs[3];
+ int ret_val, result;
+ int otp_value_zero = 0;
+ int st_shift_prod[3], st_shift_cust[3], i;
+
+ ret_val = 0;
+ result = inv_plat_read(st, REG_6500_XG_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ if (st_shift_cust[i] < DEF_GYRO_CT_SHIFT_DELTA
+ * st_shift_prod[i])
+ ret_val = 1;
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ if (st_shift_cust[i] < DEF_GYRO_ST_AL *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ if (ret_val == 0) {
+ /* Self Test Pass/Fail Criteria C */
+ for (i = 0; i < 3; i++)
+ if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_accel_self_test() - check 6500 accel self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+int inv_check_accel_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ int ret_val, result;
+ int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+ int otp_value_zero = 0;
+
+ ret_val = 0;
+ result = inv_plat_read(st, REG_6500_XA_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test accel shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ st_shift_ratio[i] = abs(st_shift_cust[i] /
+ st_shift_prod[i] - DEF_ST_PRECISION);
+ if (st_shift_ratio[i] > DEF_ACCEL_ST_SHIFT_DELTA)
+ ret_val = 1;
+ }
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]);
+ if (st_shift_cust[i] < ACCEL_ST_AL_MIN ||
+ st_shift_cust[i] > ACCEL_ST_AL_MAX)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ return ret_val;
+}
+
+/*
+ * inv_do_test() - do the actual test of self testing
+ */
+int inv_do_test(struct inv_mpu_state *st, int self_test_flag,
+ int *gyro_result, int *accel_result, int lp_mode)
+{
+ int result, i, j, packet_size;
+ u8 data[BYTES_PER_SENSOR * 2], d, dd;
+ int fifo_count, packet_count, ind, s;
+
+ packet_size = BYTES_PER_SENSOR * 2;
+
+ /* disable interrupt */
+ result = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ if (result)
+ return result;
+ /* disable fifo reading */
+ result = inv_plat_single_write(st, REG_USER_CTRL, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* setup parameters */
+ result = inv_plat_single_write(st, REG_CONFIG, INV_FILTER_98HZ);
+ if (result)
+ return result;
+
+ /* gyro lp mode */
+ if (lp_mode == 1)
+ d = BIT_GYRO_CYCLE_EN;
+ else if (lp_mode == 2)
+ d = DEF_SELFTEST_SAMPLE_RATE_ACC_LP;
+ else
+ d = 0;
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL, d);
+ if (result)
+ return result;
+
+ /* config accel LPF register */
+ if (lp_mode == 2)
+ d = BIT_ACCEL_FCHOCIE_B;
+ else
+ d = DEF_ST_MPU6500_ACCEL_LPF;
+ result = inv_plat_single_write(st, REG_6500_ACCEL_CONFIG2, d);
+ if (result)
+ return result;
+
+ if (lp_mode) {
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
+ DEF_SELFTEST_SAMPLE_RATE_LP);
+ } else {
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
+ DEF_SELFTEST_SAMPLE_RATE);
+ }
+ if (result)
+ return result;
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+ result = inv_plat_single_write(st, REG_GYRO_CONFIG,
+ self_test_flag | DEF_SELFTEST_GYRO_FS);
+ if (result)
+ return result;
+
+ d = DEF_SELFTEST_6500_ACCEL_FS;
+ d |= self_test_flag;
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG, d);
+ if (result)
+ return result;
+
+ /* wait for the output to get stable */
+ msleep(DEF_ST_6500_STABLE_TIME);
+
+ /* enable FIFO reading */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_EN);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO */
+ d = BITS_GYRO_OUT | BIT_ACCEL_OUT;
+ for (i = 0; i < THREE_AXIS; i++) {
+ gyro_result[i] = 0;
+ accel_result[i] = 0;
+ }
+ s = 0;
+ while (s < 200 /*st->self_test.samples*/) {
+ /* Stop FIFO */
+ result = inv_plat_single_write(st, REG_USER_CTRL, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* enable FIFO reading */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_EN);
+ if (result)
+ return result;
+
+ /* accel lp mode */
+ dd = BIT_CLK_PLL;
+ if (lp_mode == 2)
+ dd |= BIT_LP_EN;
+ else
+ dd &= ~BIT_LP_EN;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, dd);
+ if (result)
+ return result;
+
+ result = inv_plat_single_write(st, REG_FIFO_EN, d);
+ if (result)
+ return result;
+ if (lp_mode)
+ mdelay(DEF_GYRO_WAIT_TIME_LP);
+ else
+ mdelay(DEF_GYRO_WAIT_TIME);
+
+ result = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ if (result)
+ return result;
+
+ result = inv_plat_read(st, REG_FIFO_COUNT_H,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ pr_debug("%s self_test fifo_count - %d\n",
+ st->hw->name, fifo_count);
+ packet_count = fifo_count / packet_size;
+ i = 0;
+ while ((i < packet_count) && (s < 200 /*st->self_test.samples*/)) {
+ short vals[3];
+ result = inv_plat_read(st, REG_FIFO_R_W,
+ packet_size, data);
+ if (result)
+ return result;
+ ind = 0;
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ accel_result[j] += vals[j];
+ }
+ ind += BYTES_PER_SENSOR;
+ pr_debug(
+ "%s self_test accel data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ gyro_result[j] += vals[j];
+ }
+ pr_debug("%s self_test gyro data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+
+ s++;
+ i++;
+ }
+ }
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ accel_result[j] = accel_result[j] / s;
+ accel_result[j] *= DEF_ST_PRECISION;
+ }
+ for (j = 0; j < THREE_AXIS; j++) {
+ gyro_result[j] = gyro_result[j] / s;
+ gyro_result[j] *= DEF_ST_PRECISION;
+ }
+
+ return 0;
+}
+
+
+int inv_power_up_self_test(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_switch_power_in_lp(st, true);
+
+ /* make sure no interrupts */
+ result = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (result)
+ return result;
+
+ if (result)
+ return result;
+ result = inv_switch_engine(st, true, BIT_PWR_ACCEL_STBY);
+ if (result)
+ return result;
+ result = inv_switch_engine(st, true, BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/*
+ * inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+ int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS];
+#if 0
+ int gyro_bias_regular_lp[THREE_AXIS];
+ int accel_bias_regular_lp[THREE_AXIS];
+ int dummy_bias_regular[THREE_AXIS];
+#endif
+ int test_times, i;
+ char accel_result, gyro_result;
+
+ result = inv_save_setting(st);
+ if (result)
+ return result;
+
+ result = inv_power_up_self_test(st);
+ if (result)
+ return result;
+ accel_result = 0;
+ gyro_result = 0;
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular,
+ accel_bias_regular, 0);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n",
+ st->hw->name, accel_bias_regular[0],
+ accel_bias_regular[1], accel_bias_regular[2]);
+ pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1],
+ gyro_bias_regular[2]);
+
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+ accel_bias_st, 0);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ break;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_st - %+d %+d %+d\n",
+ st->hw->name, accel_bias_st[0], accel_bias_st[1],
+ accel_bias_st[2]);
+ pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_st[0], gyro_bias_st[1],
+ gyro_bias_st[2]);
+
+#if 0
+ /* lp gyro mode */
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular_lp,
+ dummy_bias_regular, 1);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test gyro bias_regular lp - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_regular_lp[0], gyro_bias_regular_lp[1],
+ gyro_bias_regular_lp[2]);
+
+ /* lp accel mode */
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, dummy_bias_regular,
+ accel_bias_regular_lp, 2);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_regular lp - %+d %+d %+d\n",
+ st->hw->name, accel_bias_regular_lp[0],
+ accel_bias_regular_lp[1], accel_bias_regular_lp[2]);
+#endif
+
+ /* copy bias */
+ for (i = 0; i < 3; i++) {
+ /* gyro : LN bias as LN is default mode */
+ st->gyro_st_bias[i] = gyro_bias_regular[i] / DEF_ST_PRECISION;
+ /* accel : LN bias as LN is default mode */
+ st->accel_st_bias[i] = accel_bias_regular[i] / DEF_ST_PRECISION;
+ }
+
+ /* Check is done on continuous mode data */
+ accel_result = !inv_check_accel_self_test(st,
+ accel_bias_regular, accel_bias_st);
+ gyro_result = !inv_check_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+
+test_fail:
+ inv_recover_setting(st);
+ return (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c
new file mode 100644
index 000000000000..5e9cf8906d79
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c
@@ -0,0 +1,466 @@
+/*
+* Copyright (C) 2017-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+#include "../inv_mpu_iio.h"
+
+/* set LN mode for gyro regardless of conditions */
+#define USE_GYRO_LN_MODE
+
+static int inv_calc_engine_dur(struct inv_engine_info *ei)
+{
+ if (!ei->running_rate)
+ return -EINVAL;
+ ei->dur = ei->base_time / ei->orig_rate;
+ ei->dur *= ei->divider;
+
+ return 0;
+}
+
+static int inv_turn_on_fifo(struct inv_mpu_state *st)
+{
+ u8 int_en, fifo_en, mode, user;
+ int r;
+
+ r = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ if (r)
+ return r;
+ r = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
+ if (r)
+ return r;
+ fifo_en = 0;
+ int_en = 0;
+
+ if (st->gesture_only_on && (!st->batch.timeout)) {
+ st->gesture_int_count = WOM_DELAY_THRESHOLD;
+ int_en |= BIT_WOM_ALL_INT_EN;
+ }
+#ifdef TIMER_BASED_BATCHING
+ if (st->chip_config.eis_enable)
+ int_en |= BIT_FSYNC_INT_EN;
+ if (!st->batch_timeout) {
+ int_en |= BIT_DATA_RDY_EN;
+ }
+#else
+ if (st->batch.timeout) {
+ if(!st->batch.fifo_wm_th)
+ int_en = BIT_DATA_RDY_EN;
+ } else {
+ int_en = BIT_DATA_RDY_EN;
+ if (st->chip_config.eis_enable)
+ int_en |= BIT_FSYNC_INT_EN;
+ }
+#endif
+ if (st->sensor[SENSOR_GYRO].on)
+ fifo_en |= BITS_GYRO_FIFO_EN;
+
+ if (st->sensor[SENSOR_ACCEL].on)
+ fifo_en |= BIT_ACCEL_FIFO_EN;
+ r = inv_plat_single_write(st, REG_FIFO_EN, fifo_en);
+ if (r)
+ return r;
+ st->int_en = int_en;
+ r = inv_plat_single_write(st, REG_INT_ENABLE, int_en);
+ if (r)
+ return r;
+ if (st->gesture_only_on && (!st->batch.timeout)) {
+ mode = BIT_ACCEL_INTEL_EN | BIT_ACCEL_INTEL_MODE;
+ } else {
+ mode = 0;
+ }
+ r = inv_plat_single_write(st, REG_ACCEL_INTEL_CTRL, mode);
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ user = 0;
+#else
+ user = BIT_FIFO_EN;
+#endif
+ r = inv_plat_single_write(st, REG_USER_CTRL, user | st->i2c_dis);
+#ifdef TIMER_BASED_BATCHING
+ if (fifo_en && st->batch_timeout) {
+ if (st->is_batch_timer_running)
+ hrtimer_cancel(&st ->hr_batch_timer);
+ st->is_batch_timer_running = true;
+ hrtimer_start(&st ->hr_batch_timer,
+ ns_to_ktime(st->batch_timeout), HRTIMER_MODE_REL);
+ } else {
+ if (st->is_batch_timer_running)
+ hrtimer_cancel(&st ->hr_batch_timer);
+ st->is_batch_timer_running = false;
+ }
+#endif
+
+ return r;
+}
+
+/*
+ * inv_reset_fifo() - Reset FIFO related registers.
+ */
+int inv_reset_fifo(struct inv_mpu_state *st, bool turn_off)
+{
+ int r, i;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+ int dur_ms;
+
+ r = inv_turn_on_fifo(st);
+ if (r)
+ return r;
+
+ ts_algo->last_run_time = get_time_ns();
+ ts_algo->reset_ts = ts_algo->last_run_time;
+ if (st->mode_1k_on)
+ ts_algo->first_sample = MODE_1K_INIT_SAMPLE;
+ else
+ ts_algo->first_sample = 1;
+
+ dur_ms = st->smplrt_div + 1;
+ if ((ts_algo->first_sample * dur_ms) < FIRST_SAMPLE_BUF_MS)
+ ts_algo->first_sample = FIRST_SAMPLE_BUF_MS / dur_ms;
+ if (ts_algo->first_sample == 0)
+ ts_algo->first_sample = 1;
+
+ st->last_temp_comp_time = ts_algo->last_run_time;
+ st->left_over_size = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ st->sensor[i].calib_flag = 0;
+ st->sensor[i].sample_calib = 0;
+ st->sensor[i].time_calib = ts_algo->last_run_time;
+ }
+
+ ts_algo->calib_counter = 0;
+
+ return 0;
+}
+
+static int inv_turn_on_engine(struct inv_mpu_state *st)
+{
+ u8 v, w;
+ int r;
+ unsigned int wait_ms;
+
+ if (st->chip_config.gyro_enable | st->chip_config.accel_enable) {
+ w = 0;
+ if (!st->chip_config.gyro_enable)
+ w |= BIT_PWR_GYRO_STBY;
+ if (!st->chip_config.accel_enable)
+ w |= BIT_PWR_ACCEL_STBY;
+ } else if (st->chip_config.compass_enable) {
+ w = BIT_PWR_GYRO_STBY;
+ } else {
+ w = (BIT_PWR_GYRO_STBY | BIT_PWR_ACCEL_STBY);
+ }
+
+ r = inv_plat_read(st, REG_PWR_MGMT_2, 1, &v);
+ if (r)
+ return r;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_2, w);
+ if (r)
+ return r;
+
+ wait_ms = 0;
+ if (st->chip_config.gyro_enable
+ && (v & BIT_PWR_GYRO_STBY)) {
+ wait_ms = INV_IAM20680_GYRO_START_TIME;
+ }
+ if (st->chip_config.accel_enable
+ && (v & BIT_PWR_ACCEL_STBY)) {
+ if (INV_IAM20680_ACCEL_START_TIME > wait_ms)
+ wait_ms = INV_IAM20680_ACCEL_START_TIME;
+ }
+ if (wait_ms)
+ msleep(wait_ms);
+
+ if (st->chip_config.has_compass) {
+ if (st->chip_config.compass_enable)
+ r = st->slave_compass->resume(st);
+ else
+ r = st->slave_compass->suspend(st);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static int inv_setup_dmp_rate(struct inv_mpu_state *st)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ st->cntl |= st->sensor[i].output;
+ st->sensor[i].dur =
+ st->eng_info[st->sensor[i].engine_base].dur;
+ st->sensor[i].div = 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_state *st, int rate)
+{
+ const short hz[] = {188, 98, 42, 20, 10, 5};
+ const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+ INV_FILTER_42HZ, INV_FILTER_20HZ,
+ INV_FILTER_10HZ, INV_FILTER_5HZ};
+ int i, h, data, result;
+
+#ifdef USE_GYRO_LN_MODE
+ if (1) {
+#else
+ if (st->chip_config.eis_enable || st->ois.en || st->mode_1k_on) {
+#endif
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+ i++;
+ data = d[i];
+ data |= EXT_SYNC_SET;
+ result = inv_plat_single_write(st, REG_CONFIG, data);
+ if (result)
+ return result;
+
+ st->chip_config.lpf = data;
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL, 0);
+ } else {
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
+ BIT_GYRO_CYCLE_EN);
+ if (result)
+ return result;
+ data = 0;
+ result = inv_plat_single_write(st, REG_CONFIG, data | 3);
+ }
+
+ return result;
+}
+
+static int inv_set_div(struct inv_mpu_state *st, int a_d, int g_d)
+{
+ int result, div;
+
+ if (st->chip_config.gyro_enable)
+ div = g_d;
+ else
+ div = a_d;
+ if (st->chip_config.eis_enable)
+ div = 0;
+
+ st->smplrt_div = div;
+ pr_debug("div= %d\n", div);
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV, div);
+
+ return result;
+}
+
+// 20680 does not support batching
+static int inv_set_batch(struct inv_mpu_state *st)
+{
+#ifdef TIMER_BASED_BATCHING
+ u64 timeout;
+ int required_fifo_size;
+
+ if (st->batch.timeout) {
+ required_fifo_size = st->batch.timeout * st->eng_info[ENGINE_GYRO].running_rate
+ * st->batch.pk_size / 1000;
+ if (required_fifo_size > MAX_BATCH_FIFO_SIZE) {
+ required_fifo_size = MAX_BATCH_FIFO_SIZE;
+ timeout = (required_fifo_size / st->batch.pk_size) * (1000 / st->eng_info[ENGINE_GYRO].running_rate);
+ } else {
+ timeout = st->batch.timeout;
+ }
+ } else {
+ timeout = 1000 / st->eng_info[ENGINE_GYRO].running_rate;
+ }
+ if (timeout <= 1000 / st->eng_info[ENGINE_GYRO].running_rate)
+ st->batch_timeout = 0;
+ else
+ st->batch_timeout = timeout * 1000000; // ms to ns
+#endif
+ st->batch.fifo_wm_th = 0;
+
+ return 0;
+}
+
+static int inv_set_rate(struct inv_mpu_state *st)
+{
+ int g_d, a_d, result, i;
+
+ result = inv_setup_dmp_rate(st);
+ if (result)
+ return result;
+
+ g_d = st->eng_info[ENGINE_GYRO].divider - 1;
+ a_d = st->eng_info[ENGINE_ACCEL].divider - 1;
+ result = inv_set_div(st, a_d, g_d);
+ if (result)
+ return result;
+ result = inv_set_lpf(st, st->eng_info[ENGINE_GYRO].running_rate);
+ if (result)
+ return result;
+ // set ADLPF at this point not to change after accel is enabled
+ result = inv_set_accel_config2(st, false);
+ st->batch.pk_size = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on)
+ st->batch.pk_size += st->sensor[i].sample_size;
+ }
+
+ inv_set_batch(st);
+
+ return result;
+}
+
+static int inv_determine_engine(struct inv_mpu_state *st)
+{
+ int i;
+ bool a_en, g_en;
+ int accel_rate, gyro_rate;
+
+ a_en = false;
+ g_en = false;
+ gyro_rate = MPU_INIT_SENSOR_RATE;
+ accel_rate = MPU_INIT_SENSOR_RATE;
+ /* loop the streaming sensors to see which engine needs to be turned on
+ */
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ a_en |= st->sensor[i].a_en;
+ g_en |= st->sensor[i].g_en;
+ }
+ }
+
+ if (st->chip_config.eis_enable) {
+ g_en = true;
+ st->eis.frame_count = 0;
+ st->eis.fsync_delay = 0;
+ st->eis.gyro_counter = 0;
+ st->eis.voting_count = 0;
+ st->eis.voting_count_sub = 0;
+ gyro_rate = BASE_SAMPLE_RATE;
+ } else {
+ st->eis.eis_triggered = false;
+ st->eis.prev_state = false;
+ }
+
+ accel_rate = st->sensor[SENSOR_ACCEL].rate;
+ gyro_rate = max(gyro_rate, st->sensor[SENSOR_GYRO].rate);
+
+ st->ts_algo.clock_base = ENGINE_ACCEL;
+
+ if (g_en) {
+ /* gyro engine needs to be fastest */
+ if (a_en)
+ gyro_rate = max(gyro_rate, accel_rate);
+ accel_rate = gyro_rate;
+ st->ts_algo.clock_base = ENGINE_GYRO;
+ } else if (a_en) {
+ /* accel engine needs to be fastest if gyro engine is off */
+ gyro_rate = accel_rate;
+ st->ts_algo.clock_base = ENGINE_ACCEL;
+ }
+
+ st->eng_info[ENGINE_GYRO].running_rate = gyro_rate;
+ st->eng_info[ENGINE_ACCEL].running_rate = accel_rate;
+ if ((gyro_rate >= BASE_SAMPLE_RATE) ||
+ (accel_rate >= BASE_SAMPLE_RATE))
+ st->mode_1k_on = true;
+ else
+ st->mode_1k_on = false;
+ /* engine divider for pressure and compass is set later */
+ if (st->chip_config.eis_enable || st->mode_1k_on) {
+ st->eng_info[ENGINE_GYRO].divider = 1;
+ st->eng_info[ENGINE_ACCEL].divider = 1;
+ // need to update rate and div for 1khz mode
+ for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ ) {
+ if (st->sensor_l[i].on) {
+ st->sensor_l[i].counter = 0;
+ if (st->sensor_l[i].rate)
+ st->sensor_l[i].div =
+ BASE_SAMPLE_RATE
+ / st->sensor_l[i].rate;
+ else
+ st->sensor_l[i].div = 0xffff;
+ }
+ }
+ } else {
+ st->eng_info[ENGINE_GYRO].divider = BASE_SAMPLE_RATE /
+ st->eng_info[ENGINE_GYRO].running_rate;
+ st->eng_info[ENGINE_ACCEL].divider = BASE_SAMPLE_RATE /
+ st->eng_info[ENGINE_ACCEL].running_rate;
+ }
+
+ for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ )
+ st->sensor_l[i].counter = 0;
+
+ inv_calc_engine_dur(&st->eng_info[ENGINE_GYRO]);
+ inv_calc_engine_dur(&st->eng_info[ENGINE_ACCEL]);
+
+ pr_debug("gen: %d aen: %d grate: %d arate: %d\n",
+ g_en, a_en, gyro_rate, accel_rate);
+
+ st->chip_config.gyro_enable = g_en;
+ st->chip_config.accel_enable = a_en;
+
+ return 0;
+}
+
+/*
+ * set_inv_enable() - enable function.
+ */
+int set_inv_enable(struct iio_dev *indio_dev)
+{
+ int result;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ result = inv_switch_power_in_lp(st, true);
+ if (result)
+ return result;
+ inv_stop_interrupt(st);
+ inv_determine_engine(st);
+ result = inv_set_rate(st);
+ if (result) {
+ pr_err("inv_set_rate error\n");
+ return result;
+ }
+ result = inv_turn_on_engine(st);
+ if (result) {
+ pr_err("inv_turn_on_engine error\n");
+ return result;
+ }
+ result = inv_reset_fifo(st, false);
+ if (result)
+ return result;
+ result = inv_switch_power_in_lp(st, false);
+ if ((!st->chip_config.gyro_enable) &&
+ (!st->chip_config.accel_enable)) {
+ inv_set_power(st, false);
+ return 0;
+ }
+
+ return result;
+}
+/* dummy function for 20608D */
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en)
+{
+ return 0;
+}
+int inv_dmp_read(struct inv_mpu_state *st, int off, int size, u8 *buf)
+{
+ return 0;
+}
+int inv_firmware_load(struct inv_mpu_state *st)
+{
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_common.c b/drivers/iio/imu/inv_mpu/inv_mpu_common.c
new file mode 100644
index 000000000000..33db03418b92
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_common.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+#include "inv_mpu_iio.h"
+#ifdef CONFIG_RTC_INTF_ALARM
+#include <linux/android_alarm.h>
+#endif
+#include <linux/export.h>
+
+#ifdef CONFIG_RTC_INTF_ALARM
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+
+ /* get_monotonic_boottime(&ts); */
+
+ /* Workaround for some platform on which monotonic clock and
+ * Android SystemClock has a gap.
+ * Use ktime_to_timespec(alarm_get_elapsed_realtime()) instead of
+ * get_monotonic_boottime() for these platform
+ */
+
+ ts = ktime_to_timespec(alarm_get_elapsed_realtime());
+
+ return timespec_to_ns(&ts);
+}
+#else
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+
+ get_monotonic_boottime(&ts);
+
+ /* Workaround for some platform on which monotonic clock and
+ * Android SystemClock has a gap.
+ * Use ktime_to_timespec(alarm_get_elapsed_realtime()) instead of
+ * get_monotonic_boottime() for these platform
+ */
+ return timespec_to_ns(&ts);
+}
+
+#endif
+
+#ifdef ACCEL_BIAS_TEST
+int inv_get_3axis_average(s16 src[], s16 dst[], s16 reset)
+{
+#define BUFFER_SIZE 200
+ static s16 buffer[BUFFER_SIZE][3];
+ static s16 current_position = 0;
+ static s16 ready = 0;
+ int sum[3]= {0,};
+ int i;
+
+ if(reset){
+ current_position = 0;
+ ready = 0;
+ }
+ buffer[current_position][0] = src[0];
+ buffer[current_position][1] = src[1];
+ buffer[current_position][2] = src[2];
+ current_position++;
+ if(current_position == BUFFER_SIZE){
+ ready = 1;
+ current_position = 0;
+ }
+ if(ready){
+ for(i = 0 ; i < BUFFER_SIZE ; i++){
+ sum[0] += buffer[i][0];
+ sum[1] += buffer[i][1];
+ sum[2] += buffer[i][2];
+ }
+ dst[0] = sum[0]/BUFFER_SIZE;
+ dst[1] = sum[1]/BUFFER_SIZE;
+ dst[2] = sum[2]/BUFFER_SIZE;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+int inv_q30_mult(int a, int b)
+{
+#define DMP_MULTI_SHIFT 30
+ u64 temp;
+ int result;
+
+ temp = ((u64)a) * b;
+ result = (int)(temp >> DMP_MULTI_SHIFT);
+
+ return result;
+}
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) || \
+ defined(CONFIG_INV_MPU_IIO_ICM20690)
+/* inv_read_secondary(): set secondary registers for reading.
+ The chip must be set as bank 3 before calling.
+ */
+int inv_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len)
+{
+ int result;
+
+ result = inv_plat_single_write(st, st->slv_reg[ind].addr,
+ INV_MPU_BIT_I2C_READ | addr);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].reg, reg);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].ctrl,
+ INV_MPU_BIT_SLV_EN | len);
+
+ return result;
+}
+
+int inv_execute_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len, u8 *d)
+{
+ int result;
+
+ inv_set_bank(st, BANK_SEL_3);
+ result = inv_read_secondary(st, ind, addr, reg, len);
+ if (result)
+ return result;
+ inv_set_bank(st, BANK_SEL_0);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis |
+ BIT_I2C_MST_EN);
+ msleep(SECONDARY_INIT_WAIT);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_EXT_SLV_SENS_DATA_00, len, d);
+
+ return result;
+}
+
+/* inv_write_secondary(): set secondary registers for writing.
+ The chip must be set as bank 3 before calling.
+ */
+int inv_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v)
+{
+ int result;
+
+ result = inv_plat_single_write(st, st->slv_reg[ind].addr, addr);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].reg, reg);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].ctrl,
+ INV_MPU_BIT_SLV_EN | 1);
+
+ result = inv_plat_single_write(st, st->slv_reg[ind].d0, v);
+
+ return result;
+}
+
+int inv_execute_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v)
+{
+ int result;
+
+ inv_set_bank(st, BANK_SEL_3);
+ result = inv_write_secondary(st, ind, addr, reg, v);
+ if (result)
+ return result;
+ inv_set_bank(st, BANK_SEL_0);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis |
+ BIT_I2C_MST_EN);
+ msleep(SECONDARY_INIT_WAIT);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
+
+ return result;
+}
+
+int inv_set_bank(struct inv_mpu_state *st, u8 bank)
+{
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+ int r;
+
+ r = inv_plat_single_write(st, REG_BANK_SEL, bank);
+
+ return r;
+#else
+ return 0;
+#endif
+}
+#endif
+
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+/**
+ * inv_write_cntl() - Write control word to designated address.
+ * @st: Device driver instance.
+ * @wd: control word.
+ * @en: enable/disable.
+ * @cntl: control address to be written.
+ */
+int inv_write_cntl(struct inv_mpu_state *st, u16 wd, bool en, int cntl)
+{
+ int result;
+ u8 reg[2], d_out[2];
+
+ result = mem_r(cntl, 2, d_out);
+ if (result)
+ return result;
+ reg[0] = ((wd >> 8) & 0xff);
+ reg[1] = (wd & 0xff);
+ if (!en) {
+ d_out[0] &= ~reg[0];
+ d_out[1] &= ~reg[1];
+ } else {
+ d_out[0] |= reg[0];
+ d_out[1] |= reg[1];
+ }
+ result = mem_w(cntl, 2, d_out);
+
+ return result;
+}
+#endif
+
+int inv_set_power(struct inv_mpu_state *st, bool power_on)
+{
+ u8 d;
+ int r;
+
+ if ((!power_on) == st->chip_config.is_asleep)
+ return 0;
+
+ d = BIT_CLK_PLL;
+ if (!power_on)
+ d |= BIT_SLEEP;
+
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, d);
+ if (r)
+ return r;
+
+ if (power_on)
+ usleep_range(REG_UP_TIME_USEC, REG_UP_TIME_USEC);
+
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(inv_set_power);
+
+int inv_stop_interrupt(struct inv_mpu_state *st)
+{
+ int res;
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+ /* disable_irq_wake alone should work already. However,
+ it might need system configuration change. From driver side,
+ we will disable IRQ altogether for non-wakeup sensors. */
+ res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_read(st, REG_INT_ENABLE_2, 1, &st->int_en_2);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE_2, 0);
+ if (res)
+ return res;
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20608D)
+ res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (res)
+ return res;
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+ res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (res)
+ return res;
+#endif
+ return 0;
+}
+int inv_reenable_interrupt(struct inv_mpu_state *st)
+{
+ int res = 0;
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+ res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE_2, st->int_en_2);
+ if (res)
+ return res;
+#elif defined(CONFIG_INV_MPU_IIO_ICM20608D)
+ res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
+ if (res)
+ return res;
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+ res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
+ if (res)
+ return res;
+#endif
+ return res;
+}
+
+static int inv_lp_en_off_mode(struct inv_mpu_state *st, bool on)
+{
+ int r;
+
+ if (!st->chip_config.is_asleep)
+ return 0;
+
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_CLK_PLL);
+ st->chip_config.is_asleep = 0;
+
+ return r;
+}
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
+{
+ int r = 0;
+ u8 w;
+
+ if ((!st->chip_config.is_asleep) &&
+ ((!on) == st->chip_config.lp_en_set))
+ return 0;
+
+ w = BIT_CLK_PLL;
+ if ((!on) && (!st->eis.eis_triggered))
+ w |= BIT_LP_EN;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ st->chip_config.is_asleep = 0;
+ st->chip_config.lp_en_set = (!on);
+ return r;
+}
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+int inv_set_accel_config2(struct inv_mpu_state *st, bool cycle_mode)
+{
+ int cycle_freq[] = {275, 192, 111, 59};
+ int cont_freq[] = {219, 219, 99, 45, 22, 11, 6};
+ int i, r, rate;
+ u8 v;
+
+ v = 0;
+#ifdef CONFIG_INV_MPU_IIO_ICM20690
+ v |= BIT_FIFO_SIZE_1K;
+#endif
+ if (cycle_mode) {
+ rate = (st->eng_info[ENGINE_ACCEL].running_rate << 1);
+ i = ARRAY_SIZE(cycle_freq) - 1;
+ while (i > 0) {
+ if (rate < cycle_freq[i]) {
+ break;
+ }
+ i--;
+ }
+ r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, v |
+ (i << 4) | 7);
+ if (r)
+ return r;
+ } else {
+ rate = (st->eng_info[ENGINE_ACCEL].running_rate >> 1);
+ for (i = 1; i < ARRAY_SIZE(cont_freq); i++) {
+ if (rate >= cont_freq[i])
+ break;
+ }
+ if (i > 6)
+ i = 6;
+ r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, v | i);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
+{
+ int r = 0;
+ u8 w;
+ bool cond_check;
+
+ if ((!st->chip_config.is_asleep) &&
+ ((!on) == st->chip_config.lp_en_set))
+ return 0;
+ cond_check = (!on) && st->cycle_on;
+
+ w = BIT_CLK_PLL;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ if (cond_check) {
+ w |= BIT_LP_EN;
+ inv_set_accel_config2(st, true);
+ st->chip_config.lp_en_set = true;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ } else {
+ inv_set_accel_config2(st, false);
+#ifdef CONFIG_INV_MPU_IIO_ICM20690
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w | BIT_SLEEP);
+ if (r)
+ return r;
+#endif
+ st->chip_config.lp_en_set = false;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ msleep(10);
+ }
+ st->chip_config.is_asleep = 0;
+
+ return r;
+}
+#endif
+#ifdef CONFIG_INV_MPU_IIO_ICM20608D
+static int inv_set_accel_config2(struct inv_mpu_state *st)
+{
+ int cont_freq[] = {219, 219, 99, 45, 22, 11, 6};
+ int dec2_cfg = 0;
+ int i, r, rate;
+
+ rate = (st->eng_info[ENGINE_ACCEL].running_rate << 1);
+ i = 0;
+ if (!st->chip_config.eis_enable){
+ while ((rate < cont_freq[i]) && (i < ARRAY_SIZE(cont_freq) - 1))
+ i++;
+ dec2_cfg = 2<<4; //4x
+ }
+ r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, i | dec2_cfg);
+ if (r)
+ return r;
+ return 0;
+}
+static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
+{
+ int r = 0;
+ u8 w;
+
+ w = BIT_CLK_PLL;
+ if ((!on) && (!st->chip_config.eis_enable))
+ w |= BIT_LP_EN;
+ inv_set_accel_config2(st);
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+
+ return r;
+}
+#endif
+int inv_switch_power_in_lp(struct inv_mpu_state *st, bool on)
+{
+ int r;
+
+ if (st->chip_config.lp_en_mode_off)
+ r = inv_lp_en_off_mode(st, on);
+ else
+ r = inv_lp_en_on_mode(st, on);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(inv_switch_power_in_lp);
+
+int write_be16_to_mem(struct inv_mpu_state *st, u16 data, int addr)
+{
+ u8 d[2];
+
+ d[0] = (data >> 8) & 0xff;
+ d[1] = data & 0xff;
+
+ return mem_w(addr, sizeof(d), d);
+}
+
+int write_be32_to_mem(struct inv_mpu_state *st, u32 data, int addr)
+{
+ cpu_to_be32s(&data);
+ return mem_w(addr, sizeof(data), (u8 *)&data);
+}
+
+int read_be16_from_mem(struct inv_mpu_state *st, u16 *o, int addr)
+{
+ int result;
+ u8 d[2];
+
+ result = mem_r(addr, 2, (u8 *) &d);
+ *o = d[0] << 8 | d[1];
+
+ return result;
+}
+
+int read_be32_from_mem(struct inv_mpu_state *st, u32 *o, int addr)
+{
+ int result;
+ u32 d = 0;
+
+ result = mem_r(addr, 4, (u8 *) &d);
+ *o = be32_to_cpup((__be32 *)(&d));
+
+ return result;
+}
+
+int be32_to_int(u8 *d)
+{
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+u32 inv_get_cntr_diff(u32 curr_counter, u32 prev)
+{
+ u32 diff;
+
+ if (curr_counter > prev)
+ diff = curr_counter - prev;
+ else
+ diff = 0xffffffff - prev + curr_counter + 1;
+
+ return diff;
+}
+
+int inv_write_2bytes(struct inv_mpu_state *st, int addr, int data)
+{
+ u8 d[2];
+
+ if (data < 0 || data > USHRT_MAX)
+ return -EINVAL;
+
+ d[0] = (u8) ((data >> 8) & 0xff);
+ d[1] = (u8) (data & 0xff);
+
+ return mem_w(addr, ARRAY_SIZE(d), d);
+}
+
+
+
+int inv_process_eis(struct inv_mpu_state *st, u16 delay)
+{
+ int tmp1, tmp2, tmp3;
+
+ switch (st->eis.voting_state) {
+ case 0:
+ st->eis.gyro_counter_s[0] = st->eis.gyro_counter;
+ st->eis.fsync_delay_s[0] = delay - st->eis.fsync_delay;
+ st->eis.voting_count = 1;
+ st->eis.voting_count_sub = 0;
+ st->eis.voting_state = 1;
+ break;
+ case 1:
+ if (abs(st->eis.gyro_counter_s[0] -
+ st->eis.gyro_counter) <= 1) {
+ st->eis.voting_count++;
+ } else {
+ st->eis.gyro_counter_s[2] = st->eis.gyro_counter;
+ st->eis.voting_count_sub++;
+ st->eis.voting_state = 2;
+ }
+ if (st->eis.voting_count > 5)
+ st->eis.voting_state = 3;
+ break;
+ case 2:
+ tmp1 = abs(st->eis.gyro_counter_s[0] - st->eis.gyro_counter);
+ tmp2 = abs(st->eis.gyro_counter_s[2] - st->eis.gyro_counter);
+
+ if ((tmp1 < tmp2) && (tmp1 <= 1))
+ st->eis.voting_count++;
+ else
+ st->eis.voting_count_sub++;
+ if (st->eis.voting_count > 5) {
+ st->eis.voting_state = 3;
+ st->eis.voting_count = 0;
+ st->eis.voting_count_sub = 0;
+ }
+
+ if (st->eis.voting_count_sub > 5) {
+ st->eis.gyro_counter_s[0] = st->eis.gyro_counter;
+ st->eis.fsync_delay_s[0] = delay - st->eis.fsync_delay;
+ st->eis.voting_state = 1;
+ st->eis.voting_count = 1;
+ st->eis.voting_count_sub = 0;
+ }
+ break;
+ case 3:
+ tmp1 = abs(st->eis.gyro_counter_s[0] - st->eis.gyro_counter);
+ if (tmp1 == 1) {
+ st->eis.gyro_counter_s[1] = st->eis.gyro_counter;
+ st->eis.fsync_delay_s[1] = delay - st->eis.fsync_delay;
+ st->eis.voting_state = 4;
+ st->eis.voting_count_sub = 1;
+ st->eis.voting_count = 1;
+ }
+ break;
+ case 4:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[0]) {
+ tmp1 = delay - st->eis.fsync_delay;
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[0]);
+ if (tmp2 < 3) {
+ st->eis.voting_count++;
+ } else {
+ st->eis.fsync_delay_s[2] = tmp1;
+ st->eis.voting_count_sub = 1;
+ st->eis.voting_state = 5;
+ }
+ if (st->eis.voting_count > 5) {
+ st->eis.voting_count = 1;
+ st->eis.voting_state = 6;
+ }
+ }
+ break;
+ case 5:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[0]) {
+ tmp1 = delay - st->eis.fsync_delay;
+
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[0]);
+ tmp3 = abs(tmp1 - st->eis.fsync_delay_s[2]);
+ if ((tmp2 < tmp3) && (tmp2 < 3))
+ st->eis.voting_count++;
+ else
+ st->eis.voting_count_sub++;
+ if ((st->eis.voting_count > 5) &&
+ (st->eis.voting_count_sub
+ < st->eis.voting_count)) {
+ st->eis.voting_state = 6;
+ st->eis.voting_count = 1;
+ } else if (st->eis.voting_count_sub > 5) {
+ st->eis.fsync_delay_s[0] = tmp1;
+ st->eis.voting_state = 4;
+ st->eis.voting_count = 1;
+ }
+
+ }
+ break;
+ case 6:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[1]) {
+ tmp1 = delay - st->eis.fsync_delay;
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[1]);
+ if (tmp2 < 3) {
+ st->eis.voting_count++;
+ } else {
+ st->eis.fsync_delay_s[2] = tmp1;
+ st->eis.voting_count_sub = 1;
+ st->eis.voting_count = 1;
+ st->eis.voting_state = 7;
+ }
+ if (st->eis.voting_count > 5)
+ st->eis.voting_state = 8;
+ }
+ break;
+ case 7:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[1]) {
+ tmp1 = delay - st->eis.fsync_delay;
+
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[1]);
+ tmp3 = abs(tmp1 - st->eis.fsync_delay_s[2]);
+ if ((tmp2 < tmp3) && (tmp2 < 3))
+ st->eis.voting_count++;
+ else
+ st->eis.voting_count_sub++;
+ if ((st->eis.voting_count > 5) &&
+ (st->eis.voting_count_sub
+ < st->eis.voting_count)) {
+ st->eis.voting_state = 8;
+ } else if (st->eis.voting_count_sub > 5) {
+ st->eis.fsync_delay_s[1] = tmp1;
+ st->eis.voting_state = 6;
+ st->eis.voting_count = 1;
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("de= %d gc= %d\n", delay, st->eis.gyro_counter);
+ st->eis.fsync_delay = delay;
+ st->eis.gyro_counter = 0;
+
+ pr_debug("state=%d g1= %d d1= %d g2= %d d2= %d\n",
+ st->eis.voting_state,
+ st->eis.gyro_counter_s[0],
+ st->eis.fsync_delay_s[0],
+ st->eis.gyro_counter_s[1],
+ st->eis.fsync_delay_s[1]);
+
+ return 0;
+}
+
+int inv_rate_convert(struct inv_mpu_state *st, int ind, int data)
+{
+ int t, out, out1, out2;
+ int base_freq;
+
+ if (data <= MPU_DEFAULT_DMP_FREQ)
+ base_freq = MPU_DEFAULT_DMP_FREQ;
+ else
+ base_freq = BASE_SAMPLE_RATE;
+
+ t = base_freq / data;
+ if (!t)
+ t = 1;
+ out1 = base_freq / (t + 1);
+ out2 = base_freq / t;
+ if ((data - out1) * INV_ODR_BUFFER_MULTI < data)
+ out = out1;
+ else
+ out = out2;
+
+ return out;
+}
+
+static void inv_check_wake_non_wake(struct inv_mpu_state *st,
+ enum SENSOR_L wake, enum SENSOR_L non_wake)
+{
+ int tmp_rate;
+
+ if (!st->sensor_l[wake].on && !st->sensor_l[non_wake].on)
+ return;
+
+ tmp_rate = MPU_INIT_SENSOR_RATE;
+ if (st->sensor_l[wake].on)
+ tmp_rate = st->sensor_l[wake].rate;
+ if (st->sensor_l[non_wake].on)
+ tmp_rate = max(tmp_rate, st->sensor_l[non_wake].rate);
+ st->sensor_l[wake].rate = tmp_rate;
+ st->sensor_l[non_wake].rate = tmp_rate;
+}
+
+static void inv_check_wake_non_wake_divider(struct inv_mpu_state *st,
+ enum SENSOR_L wake, enum SENSOR_L non_wake)
+{
+ if (st->sensor_l[wake].on && st->sensor_l[non_wake].on)
+ st->sensor_l[non_wake].div = 0xffff;
+
+}
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+int inv_check_sensor_on(struct inv_mpu_state *st)
+{
+ int i, max_rate;
+ enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
+ SENSOR_L_MAG_WAKE};
+ enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
+ SENSOR_L_MAG};
+
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].rate = GESTURE_ACCEL_RATE;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].on = false;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
+
+ if ((st->step_detector_l_on
+ || st->step_detector_wake_l_on
+ || st->step_counter_l_on
+ || st->step_counter_wake_l_on
+ || st->chip_config.pick_up_enable
+ || st->chip_config.tilt_enable)
+ && (!st->sensor_l[SENSOR_L_ACCEL].on)
+ && (!st->sensor_l[SENSOR_L_ACCEL_WAKE].on))
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].on = true;
+ else
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].on = false;
+
+
+ st->chip_config.wake_on = false;
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on && st->sensor_l[i].rate) {
+ st->sensor[st->sensor_l[i].base].on = true;
+ st->chip_config.wake_on |= st->sensor_l[i].wake_on;
+ }
+ }
+ if (st->sensor_l[SENSOR_L_GESTURE_ACCEL].on &&
+ (!st->sensor[SENSOR_GYRO].on) &&
+ (!st->sensor[SENSOR_COMPASS].on))
+ st->gesture_only_on = true;
+ else
+ st->gesture_only_on = false;
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ st->sensor[st->sensor_l[i].base].rate =
+ max(st->sensor[st->sensor_l[i].base].rate,
+ st->sensor_l[i].rate);
+ }
+ }
+ max_rate = MPU_INIT_SENSOR_RATE;
+ if (st->chip_config.eis_enable) {
+ max_rate = ESI_GYRO_RATE;
+ st->sensor_l[SENSOR_L_EIS_GYRO].rate = ESI_GYRO_RATE;
+ }
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ max_rate = max(max_rate, st->sensor[i].rate);
+ }
+ }
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ st->sensor[i].rate = max_rate;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(wake); i++)
+ inv_check_wake_non_wake(st, wake[i], non_wake[i]);
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ if (st->sensor_l[i].rate)
+ st->sensor_l[i].div =
+ st->sensor[st->sensor_l[i].base].rate
+ / st->sensor_l[i].rate;
+ else
+ st->sensor_l[i].div = 0xffff;
+ pr_debug("sensor= %d, div= %d\n",
+ i, st->sensor_l[i].div);
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(wake); i++)
+ inv_check_wake_non_wake_divider(st, wake[i], non_wake[i]);
+
+ if (st->step_detector_wake_l_on ||
+ st->step_counter_wake_l_on ||
+ st->chip_config.pick_up_enable ||
+ st->chip_config.tilt_enable)
+ st->chip_config.wake_on = true;
+
+ return 0;
+}
+#else
+static void inv_do_check_sensor_on(struct inv_mpu_state *st,
+ enum SENSOR_L *wake,
+ enum SENSOR_L *non_wake, int sensor_size)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].on = false;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
+
+ st->chip_config.wake_on = false;
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on && st->sensor_l[i].rate) {
+ st->sensor[st->sensor_l[i].base].on = true;
+ st->chip_config.wake_on |= st->sensor_l[i].wake_on;
+ }
+ }
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ st->sensor[st->sensor_l[i].base].rate =
+ max(st->sensor[st->sensor_l[i].base].rate,
+ st->sensor_l[i].rate);
+ }
+ }
+ for (i = 0; i < sensor_size; i++)
+ inv_check_wake_non_wake(st, wake[i], non_wake[i]);
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ if (st->sensor_l[i].rate)
+ st->sensor_l[i].div =
+ st->sensor[st->sensor_l[i].base].rate
+ / st->sensor_l[i].rate;
+ else
+ st->sensor_l[i].div = 0xffff;
+ }
+ }
+ for (i = 0; i < sensor_size; i++)
+ inv_check_wake_non_wake_divider(st, wake[i], non_wake[i]);
+
+ if (st->step_detector_wake_l_on ||
+ st->step_counter_wake_l_on ||
+ st->chip_config.pick_up_enable ||
+ st->chip_config.tilt_enable ||
+ st->smd.on)
+ st->chip_config.wake_on = true;
+
+}
+#endif
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20608D)
+int inv_check_sensor_on(struct inv_mpu_state *st)
+{
+ enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
+ SENSOR_L_SIXQ_WAKE, SENSOR_L_PEDQ_WAKE,
+ SENSOR_L_GYRO_CAL_WAKE};
+ enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
+ SENSOR_L_SIXQ, SENSOR_L_PEDQ,
+ SENSOR_L_GYRO_CAL};
+
+ inv_do_check_sensor_on(st, wake, non_wake, ARRAY_SIZE(wake));
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+int inv_check_sensor_on(struct inv_mpu_state *st)
+{
+ enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
+ SENSOR_L_MAG_WAKE, SENSOR_L_ALS_WAKE,
+ SENSOR_L_SIXQ_WAKE, SENSOR_L_PEDQ_WAKE,
+ SENSOR_L_NINEQ_WAKE, SENSOR_L_GEOMAG_WAKE,
+ SENSOR_L_PRESSURE_WAKE,
+ SENSOR_L_GYRO_CAL_WAKE,
+ SENSOR_L_MAG_CAL_WAKE};
+ enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
+ SENSOR_L_MAG, SENSOR_L_ALS,
+ SENSOR_L_SIXQ, SENSOR_L_PEDQ,
+ SENSOR_L_NINEQ, SENSOR_L_GEOMAG,
+ SENSOR_L_PRESSURE,
+ SENSOR_L_GYRO_CAL,
+ SENSOR_L_MAG_CAL};
+
+ inv_do_check_sensor_on(st, wake, non_wake, ARRAY_SIZE(wake));
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+int inv_mpu_suspend(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ /* add code according to different request Start */
+ dev_info(st->dev, "%s suspend\n", st->hw->name);
+ mutex_lock(&indio_dev->mlock);
+
+ st->resume_state = false;
+ if (st->chip_config.wake_on) {
+ enable_irq_wake(st->irq);
+ } else {
+ inv_stop_interrupt(st);
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(inv_mpu_suspend);
+
+/*
+ * inv_mpu_complete(): complete method for this driver.
+ * This method can be modified according to the request of different
+ * customers. It basically undo everything suspend is doing
+ * and recover the chip to what it was before suspend. We use complete to
+ * make sure that alarm clock resume is finished. If we use resume, the
+ * alarm clock may not resume yet and get incorrect clock reading.
+ */
+void inv_mpu_complete(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ dev_info(st->dev, "%s resume\n", st->hw->name);
+ if (st->resume_state)
+ return;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (!st->chip_config.wake_on) {
+ inv_reenable_interrupt(st);
+ } else {
+ disable_irq_wake(st->irq);
+ }
+ /* resume state is used to synchronize read_fifo such that it won't
+ proceed unless resume is finished. */
+ st->resume_state = true;
+ /* resume flag is indicating that current clock reading is from resume,
+ it has up to 1 second drift and should do proper processing */
+ st->ts_algo.resume_flag = true;
+ mutex_unlock(&indio_dev->mlock);
+ wake_up_interruptible(&st->wait_queue);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(inv_mpu_complete);
+#endif
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_dts.c b/drivers/iio/imu/inv_mpu/inv_mpu_dts.c
new file mode 100644
index 000000000000..0b8b3fc29b0a
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_dts.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/export.h>
+
+#include <linux/iio/imu/mpu.h>
+#include "inv_mpu_dts.h"
+#include "inv_mpu_iio.h"
+
+#ifdef CONFIG_OF
+
+static int inv_mpu_power_on(struct mpu_platform_data *pdata)
+{
+ int err;
+
+ if (!IS_ERR(pdata->vdd_ana)) {
+ err = regulator_enable(pdata->vdd_ana);
+ if (err)
+ return err;
+ }
+ if (!IS_ERR(pdata->vdd_i2c)) {
+ err = regulator_enable(pdata->vdd_i2c);
+ if (err)
+ goto error_disable_vdd_ana;
+ }
+
+ return 0;
+
+error_disable_vdd_ana:
+ regulator_disable(pdata->vdd_ana);
+ return err;
+}
+
+static int inv_mpu_power_off(struct mpu_platform_data *pdata)
+{
+ if (!IS_ERR(pdata->vdd_ana))
+ regulator_disable(pdata->vdd_ana);
+ if (!IS_ERR(pdata->vdd_i2c))
+ regulator_disable(pdata->vdd_i2c);
+
+ return 0;
+}
+
+static int inv_parse_orientation_matrix(struct device *dev, s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix */
+ rc = of_property_read_u32(np, "axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix */
+ rc = of_property_read_u32(np, "axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix */
+ rc = of_property_read_u32(np, "axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+static int inv_parse_secondary_orientation_matrix(struct device *dev,
+ s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix */
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix */
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix */
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+static int inv_parse_secondary(struct device *dev,
+ struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,secondary_type", &name)) {
+ dev_err(dev, "Missing secondary type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "compass")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ } else if (!strcmp(name, "none")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,secondary_name", &name)) {
+ dev_err(dev, "Missing secondary name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "ak8963"))
+ pdata->sec_slave_id = COMPASS_ID_AK8963;
+ else if (!strcmp(name, "ak8975"))
+ pdata->sec_slave_id = COMPASS_ID_AK8975;
+ else if (!strcmp(name, "ak8972"))
+ pdata->sec_slave_id = COMPASS_ID_AK8972;
+ else if (!strcmp(name, "ak09911"))
+ pdata->sec_slave_id = COMPASS_ID_AK09911;
+ else if (!strcmp(name, "ak09912"))
+ pdata->sec_slave_id = COMPASS_ID_AK09912;
+ else if (!strcmp(name, "ak09916"))
+ pdata->sec_slave_id = COMPASS_ID_AK09916;
+ else
+ return -EINVAL;
+ rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary register\n");
+ return rc;
+ }
+ pdata->secondary_i2c_addr = temp_val;
+ rc = inv_parse_secondary_orientation_matrix(dev,
+ pdata->
+ secondary_orientation);
+
+ return rc;
+}
+
+static int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,aux_type", &name)) {
+ dev_err(dev, "Missing aux type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "pressure")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE;
+ } else if (!strcmp(name, "none")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,aux_name", &name)) {
+ dev_err(dev, "Missing aux name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "bmp280"))
+ pdata->aux_slave_id = PRESSURE_ID_BMP280;
+ else
+ return -EINVAL;
+
+ rc = of_property_read_u32(np, "inven,aux_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read aux register\n");
+ return rc;
+ }
+ pdata->aux_i2c_addr = temp_val;
+
+ return 0;
+}
+
+static int inv_parse_readonly_secondary(struct device *dev,
+ struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,read_only_slave_type", &name)) {
+ dev_err(dev, "Missing read only slave type type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "als")) {
+ pdata->read_only_slave_type = SECONDARY_SLAVE_TYPE_ALS;
+ } else if (!strcmp(name, "none")) {
+ pdata->read_only_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,read_only_slave_name", &name)) {
+ dev_err(dev, "Missing read only slave type name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "apds9930"))
+ pdata->read_only_slave_id = ALS_ID_APDS_9930;
+ else
+ return -EINVAL;
+
+ rc = of_property_read_u32(np, "inven,read_only_slave_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read read only slave reg register\n");
+ return rc;
+ }
+ pdata->read_only_i2c_addr = temp_val;
+
+ return 0;
+}
+
+int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+
+ rc = inv_parse_orientation_matrix(dev, pdata->orientation);
+ if (rc)
+ return rc;
+ rc = inv_parse_secondary(dev, pdata);
+ if (rc)
+ return rc;
+ inv_parse_aux(dev, pdata);
+
+ inv_parse_readonly_secondary(dev, pdata);
+
+ pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana");
+ if (IS_ERR(pdata->vdd_ana)) {
+ rc = PTR_ERR(pdata->vdd_ana);
+ dev_warn(dev, "regulator get failed vdd_ana-supply rc=%d\n", rc);
+ }
+ pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c");
+ if (IS_ERR(pdata->vdd_i2c)) {
+ rc = PTR_ERR(pdata->vdd_i2c);
+ dev_warn(dev, "regulator get failed vcc-i2c-supply rc=%d\n", rc);
+ }
+ pdata->power_on = inv_mpu_power_on;
+ pdata->power_off = inv_mpu_power_off;
+ dev_dbg(dev, "parse dt complete\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(invensense_mpu_parse_dt);
+
+#endif /* CONFIG_OF */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_dts.h b/drivers/iio/imu/inv_mpu/inv_mpu_dts.h
new file mode 100644
index 000000000000..90966febb930
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_dts.h
@@ -0,0 +1,25 @@
+/*
+* Copyright (C) 2012-2017 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#ifndef _INV_MPU_DTS_H_
+#define _INV_MPU_DTS_H_
+
+#include <linux/kernel.h>
+#include <linux/iio/imu/mpu.h>
+
+#ifdef CONFIG_OF
+int invensense_mpu_parse_dt(struct device *dev,
+ struct mpu_platform_data *pdata);
+#endif
+
+#endif /* #ifndef _INV_MPU_DTS_H_ */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu/inv_mpu_i2c.c
new file mode 100644
index 000000000000..e7838fce84a8
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_i2c.c
@@ -0,0 +1,556 @@
+/*
+* Copyright (C) 2012-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_mpu_dts.h"
+
+#define CONFIG_DYNAMIC_DEBUG_I2C 0
+
+/**
+ * inv_i2c_read_base() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @i2c_addr: i2c address of device.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE:This is not re-implementation of i2c_smbus_read because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 2);
+
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+ INV_I2C_INC_MPUWRITE(3);
+ INV_I2C_INC_MPUREAD(length);
+
+ return res;
+}
+
+/**
+ * inv_i2c_single_write_base() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @i2c_addr: I2C address of the device.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ * NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u8 data)
+{
+ u8 tmp[2];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = reg;
+ tmp[1] = data;
+
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ INV_I2C_INC_MPUWRITE(3);
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_i2c_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
+{
+ return inv_i2c_single_write_base(st, st->i2c_addr, reg, data);
+}
+
+static int inv_i2c_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
+{
+ return inv_i2c_read_base(st, st->i2c_addr, reg, len, data);
+}
+
+static int _memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = REG_MEM_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = REG_MEM_R_W;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (u8 *) buf;
+ msgs[2].len = len + 1;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len));
+
+#if CONFIG_DYNAMIC_DEBUG_I2C
+ {
+ char *write = 0;
+ pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
+ mpu_addr, bank[1], addr[1],
+ wr_pr_debug_begin(data, len, write),
+ wr_pr_debug_end(write), len);
+ }
+#endif
+
+ res = i2c_transfer(st->sl_handle, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+static int inv_i2c_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ int r, i, j;
+#define DMP_MEM_CMP_SIZE 16
+ u8 w[DMP_MEM_CMP_SIZE];
+ bool retry;
+
+ j = 0;
+ retry = true;
+ while ((j < 3) && retry) {
+ retry = false;
+ r = _memory_write(st, mpu_addr, mem_addr, len, data);
+ if (len < DMP_MEM_CMP_SIZE) {
+ r = mem_r(mem_addr, len, w);
+ for (i = 0; i < len; i++) {
+ if (data[i] != w[i]) {
+ pr_debug
+ ("error write=%x, len=%d,data=%x, w=%x, i=%d\n",
+ mem_addr, len, data[i], w[i], i);
+ retry = true;
+ }
+ }
+ }
+ j++;
+ }
+
+ return r;
+}
+
+static int inv_i2c_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ bank[0] = REG_MEM_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = REG_MEM_R_W;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(st->sl_handle, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+ INV_I2C_INC_MPUWRITE(3 + 3 + 3);
+ INV_I2C_INC_MPUREAD(len);
+
+#if CONFIG_DYNAMIC_DEBUG_I2C
+ {
+ char *read = 0;
+ pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
+ mpu_addr, bank[1], addr[1], len,
+ wr_pr_debug_begin(data, len, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+
+ return res;
+}
+
+/*
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_mpu_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENOSYS;
+ pr_err("I2c function error\n");
+ goto out_no_free;
+ }
+
+#ifdef KERNEL_VERSION_4_X
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#else
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#endif
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->sl_handle = client->adapter;
+ st->i2c_addr = client->addr;
+ st->write = inv_i2c_single_write;
+ st->read = inv_i2c_read;
+ st->mem_write = inv_i2c_mem_write;
+ st->mem_read = inv_i2c_mem_read;
+ st->dev = &client->dev;
+ st->bus_type = BUS_I2C;
+#ifdef CONFIG_OF
+ result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
+ if (result)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+
+ /* Power on device */
+ if (st->plat_data.power_on) {
+ result = st->plat_data.power_on(&st->plat_data);
+ if (result < 0) {
+ dev_err(st->dev, "power_on failed: %d\n", result);
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ }
+ pr_info("%s: power on here.\n", __func__);
+ }
+ pr_info("%s: power on.\n", __func__);
+
+ msleep(100);
+#else
+ if (dev_get_platdata(st->dev) == NULL)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
+#endif
+
+ /* power is turned on inside check chip type */
+ result = inv_check_chip_type(indio_dev, id->name);
+ if (result)
+#ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+#else
+ goto out_free;
+#endif
+
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = st->dev;
+ indio_dev->name = id->name;
+
+ st->irq = client->irq;
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+#ifdef KERNEL_VERSION_4_X
+ INV_I2C_SETIRQ(IRQ_MPU, st->irq);
+ result = devm_iio_device_register(st->dev, indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_unreg_ring;
+ }
+#else
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+ INV_I2C_SETIRQ(IRQ_MPU, client->irq);
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_ring;
+ }
+#endif
+
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ init_waitqueue_head(&st->wait_queue);
+ st->resume_state = true;
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
+#else
+ wakeup_source_init(&st->wake_lock, "inv_mpu");
+#endif
+ dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
+ indio_dev->name, INVENSENSE_DRIVER_VERSION);
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ pr_info("Data read from registers\n");
+#else
+ pr_info("Data read from FIFO\n");
+#endif
+#ifdef TIMER_BASED_BATCHING
+ pr_info("Timer based batching\n");
+#endif
+
+ return 0;
+#ifdef KERNEL_VERSION_4_X
+out_unreg_iio:
+ devm_iio_device_unregister(st->dev, indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ devm_iio_device_free(st->dev, indio_dev);
+out_no_free:
+#else
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+#endif
+ dev_err(st->dev, "%s failed %d\n", __func__, result);
+
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ inv_switch_power_in_lp(st, true);
+ dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
+ if (result)
+ dev_err(st->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = inv_set_power(st, false);
+ if (result)
+ dev_err(st->dev, "Failed to turn off %s\n",
+ st->hw->name);
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+}
+
+/*
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_unregister(st->dev, indio_dev);
+#else
+ iio_device_unregister(indio_dev);
+ iio_buffer_unregister(indio_dev);
+#endif
+ inv_mpu_unconfigure_ring(indio_dev);
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_free(st->dev, indio_dev);
+#else
+ iio_device_free(indio_dev);
+#endif
+ dev_info(st->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int inv_mpu_i2c_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+
+ return inv_mpu_suspend(indio_dev);
+}
+
+static void inv_mpu_i2c_complete(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+
+ inv_mpu_complete(indio_dev);
+}
+#endif
+
+static const struct dev_pm_ops inv_mpu_i2c_pmops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = inv_mpu_i2c_suspend,
+ .complete = inv_mpu_i2c_complete,
+#endif
+};
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+ {"icm20645", ICM20645},
+ {"icm10340", ICM10340},
+ {"icm20648", ICM20648},
+#else
+ {"icm20608d", ICM20608D},
+ {"icm20690", ICM20690},
+ {"icm20602", ICM20602},
+ {"iam20680", IAM20680},
+#endif
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio-i2c",
+ .pm = &inv_mpu_i2c_pmops,
+ },
+};
+module_i2c_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense I2C device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu/inv_mpu_iio.h
new file mode 100644
index 000000000000..9e7316558eae
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_iio.h
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (C) 2012-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0))
+#define KERNEL_VERSION_4_X
+#endif
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/iio/imu/mpu.h>
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#else
+#include <linux/pm_wakeup.h>
+#endif
+#include <linux/wait.h>
+
+#include <linux/iio/sysfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+#include "icm20648/dmp3Default.h"
+#endif
+#ifdef CONFIG_INV_MPU_IIO_ICM20608D
+#include "icm20608d/dmp3Default_20608D.h"
+#endif
+
+#include "inv_test/inv_counters.h"
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+#include "icm20648/inv_mpu_iio_reg_20648.h"
+#elif defined(CONFIG_INV_MPU_IIO_ICM20602)
+#include "icm20602/inv_mpu_iio_reg_20602.h"
+#elif defined(CONFIG_INV_MPU_IIO_ICM20608D)
+#include "icm20608d/inv_mpu_iio_reg_20608.h"
+#elif defined(CONFIG_INV_MPU_IIO_ICM20690)
+#include "icm20690/inv_mpu_iio_reg_20690.h"
+#elif defined(CONFIG_INV_MPU_IIO_IAM20680)
+#include "iam20680/inv_mpu_iio_reg_20680.h"
+#endif
+
+#define INVENSENSE_DRIVER_VERSION "8.1.2-simple-test1"
+
+/* #define DEBUG */
+
+/* #define ACCEL_BIAS_TEST */
+
+/* #define BIAS_CONFIDENCE_HIGH 1 */
+
+#define MAX_FIFO_READ_SIZE 128
+#define MAX_DMP_READ_SIZE 16
+
+/* data header defines */
+#define WAKE_HDR 0x8000
+
+#define ACCEL_HDR 1
+#define GYRO_HDR 2
+#define COMPASS_HDR 3
+#define ALS_HDR 4
+#define SIXQUAT_HDR 5
+#define NINEQUAT_HDR 6
+#define PEDQUAT_HDR 7
+#define GEOMAG_HDR 8
+#define PRESSURE_HDR 9
+#define GYRO_CALIB_HDR 10
+#define COMPASS_CALIB_HDR 11
+#define STEP_COUNTER_HDR 12
+#define STEP_DETECTOR_HDR 13
+#define STEP_COUNT_HDR 14
+#define ACTIVITY_HDR 15
+#define PICK_UP_HDR 16
+#define EMPTY_MARKER 17
+#define END_MARKER 18
+#define COMPASS_ACCURACY_HDR 19
+#define ACCEL_ACCURACY_HDR 20
+#define GYRO_ACCURACY_HDR 21
+#define EIS_GYRO_HDR 36
+#define EIS_CALIB_HDR 37
+#define LPQ_HDR 38
+
+#define ACCEL_WAKE_HDR (ACCEL_HDR | WAKE_HDR)
+#define GYRO_WAKE_HDR (GYRO_HDR | WAKE_HDR)
+#define COMPASS_WAKE_HDR (COMPASS_HDR | WAKE_HDR)
+#define ALS_WAKE_HDR (ALS_HDR | WAKE_HDR)
+#define SIXQUAT_WAKE_HDR (SIXQUAT_HDR | WAKE_HDR)
+#define NINEQUAT_WAKE_HDR (NINEQUAT_HDR | WAKE_HDR)
+#define PEDQUAT_WAKE_HDR (PEDQUAT_HDR | WAKE_HDR)
+#define GEOMAG_WAKE_HDR (GEOMAG_HDR | WAKE_HDR)
+#define PRESSURE_WAKE_HDR (PRESSURE_HDR | WAKE_HDR)
+#define GYRO_CALIB_WAKE_HDR (GYRO_CALIB_HDR | WAKE_HDR)
+#define COMPASS_CALIB_WAKE_HDR (COMPASS_CALIB_HDR | WAKE_HDR)
+#define STEP_COUNTER_WAKE_HDR (STEP_COUNTER_HDR | WAKE_HDR)
+#define STEP_DETECTOR_WAKE_HDR (STEP_DETECTOR_HDR | WAKE_HDR)
+
+/* init parameters */
+#define MPU_INIT_SMD_THLD 1500
+#define MPU_INIT_GYRO_SCALE 3
+#define MPU_INIT_ACCEL_SCALE 2
+#define MPU_INIT_PED_INT_THRESH 2
+#define MPU_INIT_PED_STEP_THRESH 6
+#define MPU_4X_TS_GYRO_SHIFT (3160000 / 2)
+#define DMP_START_ADDR_20645 0x900
+#define DMP_START_ADDR_20648 0x1000
+#define DMP_START_ADDR_10340 0x0a60
+#define DMP_START_ADDR_20608D 0x4B0
+#define MAX_WR_SZ 100
+#define WOM_DELAY_THRESHOLD 200
+#define INV_ODR_BUFFER_MULTI 20
+#define INV_ODR_OVER_FACTOR 20
+
+#define COVARIANCE_SIZE 14
+#define ACCEL_COVARIANCE_SIZE (COVARIANCE_SIZE * sizeof(int))
+
+enum inv_bus_type {
+ BUS_I2C = 0,
+ BUS_SPI,
+};
+
+struct inv_mpu_state;
+
+enum INV_ENGINE {
+ ENGINE_GYRO = 0,
+ ENGINE_ACCEL,
+ ENGINE_PRESSURE,
+ ENGINE_I2C,
+ ENGINE_NUM_MAX,
+};
+
+/**
+ * struct inv_hw_s - Other important hardware information.
+ * @num_reg: Number of registers on device.
+ * @name: name of the chip
+ */
+struct inv_hw_s {
+ u8 num_reg;
+ u8 *name;
+};
+
+/**
+ * struct inv_sensor - information for each sensor.
+ * @ts: this sensors timestamp.
+ * @ts_adj: sensor timestamp adjustment.
+ * @previous_ts: previous timestamp for this sensor.
+ * @dur: duration between samples in ns.
+ * @rate: sensor data rate.
+ * @sample_size: number of bytes for the sensor.
+ * @odr_addr: output data rate address in DMP.
+ * @counter_addr: output counter address in DMP.
+ * @output: output on/off control word.
+ * @time_calib: calibrate timestamp.
+ * @sample_calib: calibrate bytes accumulated.
+ * @div: divider in DMP mode.
+ * @calib_flag: calibrate flag used to improve the accuracy of estimation.
+ * @on: sensor on/off.
+ * @a_en: accel engine requirement.
+ * @g_en: gyro engine requirement.
+ * @c_en: compass_engine requirement.
+ * @p_en: pressure engine requirement.
+ * @engine_base: engine base for this sensor.
+ * @count: number of samples in one session.
+ * @send: decide whether to send this sample or not.
+ */
+struct inv_sensor {
+ u64 ts;
+ s64 ts_adj;
+ u64 previous_ts;
+ int dur;
+ int rate;
+ u8 sample_size;
+ int odr_addr;
+ int counter_addr;
+ u16 output;
+ u64 time_calib;
+ u32 sample_calib;
+ int div;
+ bool calib_flag;
+ bool on;
+ bool a_en;
+ bool g_en;
+ bool c_en;
+ bool p_en;
+ enum INV_ENGINE engine_base;
+ int count;
+ bool send;
+};
+
+/**
+ * struct inv_sensor - information for each sensor.
+ * @sample_size: number of bytes for the sensor.
+ * @output: output on/off control word.
+ * @on: sensor on/off.
+ * @header: accuracy header for communicate with HAL
+ *dd @count: number of samples in one session.
+ */
+struct inv_sensor_accuracy {
+ u16 output;
+ u8 sample_size;
+ bool on;
+ u16 header;
+};
+
+enum SENSOR_ACCURACY {
+ SENSOR_ACCEL_ACCURACY = 0,
+ SENSOR_GYRO_ACCURACY,
+ SENSOR_COMPASS_ACCURACY,
+ SENSOR_ACCURACY_NUM_MAX,
+};
+
+enum SENSOR_L {
+ SENSOR_L_ACCEL = 0,
+ SENSOR_L_GYRO,
+ SENSOR_L_MAG,
+ SENSOR_L_ALS,
+ SENSOR_L_SIXQ,
+ SENSOR_L_THREEQ,
+ SENSOR_L_NINEQ,
+ SENSOR_L_PEDQ,
+ SENSOR_L_GEOMAG,
+ SENSOR_L_PRESSURE,
+ SENSOR_L_GYRO_CAL,
+ SENSOR_L_MAG_CAL,
+ SENSOR_L_EIS_GYRO,
+ /*wake sensors */
+ SENSOR_L_ACCEL_WAKE = 13,
+ SENSOR_L_GYRO_WAKE,
+ SENSOR_L_MAG_WAKE,
+ SENSOR_L_ALS_WAKE,
+ SENSOR_L_SIXQ_WAKE,
+ SENSOR_L_NINEQ_WAKE,
+ SENSOR_L_PEDQ_WAKE,
+ SENSOR_L_GEOMAG_WAKE,
+ SENSOR_L_PRESSURE_WAKE,
+ SENSOR_L_GYRO_CAL_WAKE,
+ SENSOR_L_MAG_CAL_WAKE,
+ SENSOR_L_GESTURE_ACCEL,
+ SENSOR_L_NUM_MAX,
+};
+
+/**
+ * struct android_l_sensor - information for each android sensor.
+ * @ts: this sensors timestamp.
+ * @base: android sensor based on invensense sensor.
+ * @rate: output rate.
+ * @on: sensor on/off.
+ * @wake_on: wake on sensor is on/off.
+ * @div: divider for the output.
+ * @counter: counter works with the divider.
+ * @header: header for the output.
+ */
+struct android_l_sensor {
+ u64 ts;
+ enum INV_SENSORS base;
+ int rate;
+ bool on;
+ bool wake_on;
+ int div;
+ int counter;
+ u16 header;
+};
+
+/**
+ * struct inv_batch - information for batchmode.
+ * @on: normal batch mode on.
+ * @default_on: default batch on. This is optimization option.
+ * @overflow_on: overflow mode for batchmode.
+ * @wake_fifo_on: overflow for suspend mode.
+ * @step_only: mean only step detector data is batched.
+ * @post_isr_run: mean post isr has runned once.
+ * @counter: counter for batch mode.
+ * @timeout: nominal timeout value for batchmode in milliseconds.
+ * @max_rate: max rate for all batched sensors.
+ * @pk_size: packet size;
+ * @engine_base: engine base batch mode should stick to.
+ */
+struct inv_batch {
+ bool on;
+ bool default_on;
+ bool overflow_on;
+ bool wake_fifo_on;
+ bool step_only;
+ bool post_isr_run;
+ u32 counter;
+ u32 timeout;
+ u32 max_rate;
+ u32 pk_size;
+ u32 fifo_wm_th;
+ enum INV_ENGINE engine_base;
+};
+
+/**
+ * struct inv_chip_config_s - Cached chip configuration data.
+ * @fsr: Full scale range.
+ * @lpf: Digital low pass filter frequency.
+ * @accel_fs: accel full scale range.
+ * @accel_enable: enable accel functionality
+ * @gyro_enable: enable gyro functionality
+ * @compass_enable: enable compass functinality.
+ * @geomag_enable: enable geomag sensor functions.
+ * @als_enable: enable ALS functionality.
+ * @pressure_enable: eanble pressure functionality.
+ * @secondary_enable: secondary I2C bus enabled or not.
+ * @has_gyro: has gyro or not.
+ * @has_compass: has secondary I2C compass or not.
+ * @has_pressure: has secondary I2C pressure or not.
+ * @has_als: has secondary I2C als or not.
+ * @slave_enable: secondary I2C interface enabled or not.
+ * @normal_compass_measure: discard first compass data after reset.
+ * @is_asleep: 1 if chip is powered down.
+ * @lp_en_set: 1 if LP_EN bit is set;
+ * @lp_en_mode_off: debug mode that turns off LP_EN mode off.
+ * @clk_sel: debug_mode that turns on/off clock selection.
+ * @dmp_on: dmp is on/off.
+ * @dmp_event_int_on: dmp event interrupt on/off.
+ * @wom_on: WOM interrupt on. This is an internal variable.
+ * @step_indicator_on: step indicate bit added to the sensor or not.
+ * @tilt_enable: tilt enable.
+ * @pick_up_enable: pick up gesture enable.
+ * @step_detector_on: step detector on or not.
+ * @activity_on: turn on/off activity.
+ * @activity_eng_on: activity engine on/off.
+ * @firmware_loaded: flag indicate firmware loaded or not.
+ * @low_power_gyro_on: flag indicating low power gyro on/off.
+ * @wake_on: any wake on sensor is on/off.
+ * @compass_rate: compass engine rate. Determined by underlying data.
+ */
+struct inv_chip_config_s {
+ u32 fsr:2;
+ u32 lpf:3;
+ u32 accel_fs:2;
+ u32 accel_enable:1;
+ u32 gyro_enable:1;
+ u32 compass_enable:1;
+ u32 geomag_enable:1;
+ u32 als_enable:1;
+ u32 prox_enable:1;
+ u32 pressure_enable:1;
+ u32 has_gyro:1;
+ u32 has_compass:1;
+ u32 has_pressure:1;
+ u32 has_als:1;
+ u32 slave_enable:1;
+ u32 normal_compass_measure:1;
+ u32 is_asleep:1;
+ u32 lp_en_set:1;
+ u32 lp_en_mode_off:1;
+ u32 clk_sel:1;
+ u32 dmp_on:1;
+ u32 dmp_event_int_on:1;
+ u32 wom_on:1;
+ u32 step_indicator_on:1;
+ u32 tilt_enable:1;
+ u32 pick_up_enable:1;
+ u32 eis_enable:1;
+ u32 step_detector_on:1;
+ u32 activity_on:1;
+ u32 activity_eng_on:1;
+ u32 firmware_loaded:1;
+ u32 low_power_gyro_on:1;
+ u32 wake_on:1;
+ int compass_rate;
+};
+
+/**
+ * struct inv_temp_comp - temperature compensation structure.
+ * @t_lo: raw temperature in low temperature.
+ * @t_hi: raw temperature in high temperature.
+ * @b_lo: gyro bias in low temperature.
+ * @b_hi: gyro bias in high temperature.
+ * @has_low: flag indicate low temperature parameters is updated.
+ * @has_high: flag indicates high temperature parameters is updated.
+ * @slope: slope for temperature compensation.
+ */
+struct inv_temp_comp {
+ int t_lo;
+ int t_hi;
+ int b_lo[3];
+ int b_hi[3];
+ bool has_low;
+ bool has_high;
+ int slope[3];
+};
+
+/**
+ * struct inv_chip_info_s - Chip related information.
+ * @product_id: Product id.
+ * @product_revision: Product revision.
+ * @silicon_revision: Silicon revision.
+ * @software_revision: software revision.
+ * @compass_sens: compass sensitivity.
+ * @gyro_sens_trim: Gyro sensitivity trim factor.
+ * @accel_sens_trim: accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+ u8 product_id;
+ u8 product_revision;
+ u8 silicon_revision;
+ u8 software_revision;
+ u8 compass_sens[3];
+ u32 gyro_sens_trim;
+ u32 accel_sens_trim;
+};
+
+/**
+ * struct inv_smd significant motion detection structure.
+ * @threshold: accel threshold for motion detection.
+ * @delay: delay time to confirm 2nd motion.
+ * @delay2: delay window parameter.
+ * @on: smd on/off.
+ */
+struct inv_smd {
+ u32 threshold;
+ u32 delay;
+ u32 delay2;
+ bool on;
+};
+
+/**
+ * struct inv_ped pedometer related data structure.
+ * @step: steps taken.
+ * @time: time taken during the period.
+ * @last_step_time: last time the step is taken.
+ * @step_thresh: step threshold to show steps.
+ * @int_thresh: step threshold to generate interrupt.
+ * @int_on: pedometer interrupt enable/disable.
+ * @on: pedometer on/off.
+ * @engine_on: pedometer engine on/off.
+ */
+struct inv_ped {
+ u64 step;
+ u64 time;
+ u64 last_step_time;
+ u16 step_thresh;
+ u16 int_thresh;
+ bool int_on;
+ bool on;
+ bool engine_on;
+};
+
+/**
+ * struct inv_eis EIS related data structure.
+ * @prev_gyro: latest gyro data just before FSYNC triggerd
+ * @prev_timestamp: latest gyro timestamp just before FSYNC triggered
+ * @current_gyro: gyro data just after FSYNC triggerd
+ * @current_timestamp: gyro timestamp just after FSYNC triggered
+ * @fsync_timestamp: timestamp of FSYNC event
+ * @fsync_delay: delay time of FSYNC and Gyro data. DMP data of FSYNC event
+ * @eis_triggered: check fsync event is triggered or not.
+ * @eis_frame: current frame is eis frame;
+ * @current_sync: current frame contains fsync counter.
+ * @frame_count: frame count for synchronization.
+ */
+struct inv_eis {
+ int prev_gyro[3];
+ u64 prev_timestamp;
+ int current_gyro[3];
+ u64 current_timestamp;
+ u32 frame_dur;
+ u64 slope[3];
+ u64 fsync_timestamp;
+ u64 last_fsync_timestamp;
+ u16 fsync_delay;
+ bool eis_triggered;
+ bool eis_frame;
+ bool current_sync;
+ bool prev_state;
+ u32 frame_count;
+ int gyro_counter;
+ int gyro_counter_s[3];
+ int fsync_delay_s[3];
+ int voting_count;
+ int voting_count_sub;
+ int voting_state;
+ int count_precision;
+};
+
+enum TRIGGER_STATE {
+ DATA_TRIGGER = 0,
+ RATE_TRIGGER,
+ EVENT_TRIGGER,
+ MISC_TRIGGER,
+ DEBUG_TRIGGER,
+};
+
+enum inv_fifo_count_mode {
+ BYTE_MODE,
+ RECORD_MODE
+};
+
+/**
+ * struct inv_secondary_reg - secondary registers data structure.
+ * @addr: address of the slave.
+ * @reg: register address of slave.
+ * @ctrl: control register.
+ * @d0: data out register.
+ */
+struct inv_secondary_reg {
+ u8 addr;
+ u8 reg;
+ u8 ctrl;
+ u8 d0;
+};
+
+struct inv_secondary_set {
+ u8 delay_enable;
+ u8 delay_time;
+ u8 odr_config;
+};
+/**
+ * struct inv_engine_info - data structure for engines.
+ * @base_time: base time for each engine.
+ * @base_time_1k: base time when chip is running at 1K;
+ * @divider: divider used to downsample engine rate from original rate.
+ * @running_rate: the actually running rate of engine.
+ * @orig_rate: original rate for each engine before downsample.
+ * @dur: duration for one tick.
+ * @last_update_time: last update time.
+ */
+struct inv_engine_info {
+ u32 base_time;
+ u32 base_time_1k;
+ u32 divider;
+ u32 running_rate;
+ u32 orig_rate;
+ u32 dur;
+ u64 last_update_time;
+};
+
+struct inv_ois {
+ int gyro_fs;
+ int accel_fs;
+ bool en;
+};
+
+/**
+ * struct inv_timestamp_algo - timestamp algorithm .
+ * @last_run_time: last time the post ISR runs.
+ * @ts_for_calib: ts storage for calibration.
+ * @reset_ts: reset time.
+ * @dmp_ticks: dmp ticks storage for calibration.
+ * @start_dmp_counter: dmp counter when start a new session.
+ * @calib_counter: calibration counter for timestamp.
+ * @resume_flag: flag to indicate this is the first time after resume. time
+ could have up to 1 seconds difference.
+ * @clock_base: clock base to calculate the timestamp.
+ * @gyro_ts_shift: 9 K counter for EIS.
+ * @first_sample: first of 1K running should be dropped it affects timing
+ */
+struct inv_timestamp_algo {
+ u64 last_run_time;
+ u64 ts_for_calib;
+ u64 reset_ts;
+ u32 dmp_ticks;
+ u32 start_dmp_counter;
+ int calib_counter;
+ bool resume_flag;
+ enum INV_ENGINE clock_base;
+ u32 gyro_ts_shift;
+ u32 first_sample;
+};
+
+struct inv_mpu_slave;
+/**
+ * struct inv_mpu_state - Driver state variables.
+ * @dev: device address of the current bus, i2c or spi.
+ * @chip_config: Cached attribute information.
+ * @chip_info: Chip information from read-only registers.
+ * @smd: SMD data structure.
+ * @ped: pedometer data structure.
+ * @batch: batchmode data structure.
+ * @temp_comp: gyro temperature compensation structure.
+ * @slave_compass: slave compass.
+ * @slave_pressure: slave pressure.
+ * @slave_als: slave als.
+ * @slv_reg: slave register data structure.
+ * @ts_algo: timestamp algorithm data structure.
+ * @sec_set: slave register odr config.
+ * @eng_info: information for each engine.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @suspend_resume_sema: semaphore for suspend/resume.
+ * @wake_lock: wake lock of the system.
+ * @client: i2c client handle.
+ * @plat_data: platform data.
+ * @sl_handle: Handle to I2C port.
+ * @sensor{SENSOR_NUM_MAX]: sensor individual properties.
+ * @sensor_l[SENSOR_L_NUM_MAX]: android L sensors properties.
+ * @sensor_accuracy[SENSOR_ACCURACY_NUM_MAX]: sensor accuracy.
+ * @sensor_acurracy_flag: flag indiciate whether to check output accuracy.
+ * @irq: irq number store.
+ * @accel_bias: accel bias store.
+ * @gyro_bias: gyro bias store.
+ * @accel_st_bias: accel bias store, result of self-test.
+ * @gyro_st_bias: gyro bias store, result of self-test.
+ * @gyro_ois_st_bias: gyro bias store from ois self test result.
+ * @input_accel_dmp_bias[3]: accel bias for dmp.
+ * @input_gyro_dmp_bias[3]: gyro bias for dmp.
+ * @input_compass_dmp_bias[3]: compass bias for dmp.
+ * @input_accel_bias[3]: accel bias for offset register.
+ * @input_gyro_bias[3]: gyro bias for offset register.
+ * @fifo_data[8]: fifo data storage.
+ * @i2c_addr: i2c address.
+ * @header_count: header count in current FIFO.
+ * @step_det_count: number of step detectors in one batch.
+ * @gyro_sf: gyro scale factor.
+ * @left_over[LEFT_OVER_BYTES]: left over bytes storage.
+ * @left_over_size: left over size.
+ * @fifo_count: current fifo_count;
+ * @wake_sensor_received: wake up sensor received.
+ * @accel_cal_enable: accel calibration on/off
+ * @gyro_cal_enable: gyro calibration on/off
+ * @calib_compass_on: calibrate compass on.
+ * @debug_determine_engine_on: determine engine on/off.
+ * @poke_mode_on: poke mode on/off.
+ * @mode_1k_on: indicate 1K Hz mode is on.
+ * @poke_ts: time stamp for poke feature.
+ * @step_detector_base_ts: base time stamp for step detector calculation.
+ * @last_temp_comp_time: last time temperature compensation is done.
+ * @i2c_dis: disable I2C interface or not.
+ * @name: name for the chip.
+ * @gyro_st_data: gyro self test data.
+ * @accel_st_data: accel self test data.
+ * @secondary_name: name for the slave device in the secondary I2C.
+ * @compass_var: compass variance from DMP.
+ * @current_compass_matrix: matrix compass data multiplied to before soft iron.
+ * @final_compass_matrix: matrix compass data multiplied to before soft iron.
+ * @trigger_state: information that which part triggers set_inv_enable.
+ * @firmware: firmware data pointer.
+ * @accel_calib_threshold: accel calibration threshold;
+ * @accel_calib_rate: divider for accel calibration rate.
+ * @accel_covariance[COVARIANCE_SIZE]: accel covariance data;
+ * @kf: kfifo for activity store.
+ * @activity_size: size for activity.
+ * @cntl: control word for sensor enable.
+ * @cntl2: control word for sensor extension.
+ * @motion_event_cntl: control word for events.
+ * @dmp_image_size: dmp image size.
+ * @dmp_start_address: start address of dmp.
+ * @step_counter_l_on: step counter android L sensor on/off.
+ * @step_counter_wake_l_on: step counter android L sensor wake on/off .
+ * @step_detector_l_on: step detector android L sensor on/off.
+ * @step_detector_wake_l_on: step detector android L sensor wake on/off .
+ * @gesture_only_on: indicate it is gesture only.
+ * @mag_divider: mag divider when gyro/accel is faster than mag maximum rate.
+ * @special_mag_mode: for 20690, there is special mag mode need to be handled.
+ * @mag_start_flag: when mag divider is non zero, need to check the start.
+ * @prev_steps: previous steps sent to the user.
+ * @aut_key_in: authentication key input.
+ * @aut_key_out: authentication key output.
+ * @suspend_state: state variable to indicate that we are in suspend state.
+ * @secondary_gyro_on: DMP out signal to turn on gyro.
+ * @secondary_mag_on: DMP out signal to turn on mag.
+ * @secondary_prox_on: DMP out signal to turn on proximity.
+ * @secondary_switch: showing this setup is triggerred by secondary switch.
+ * @send_calib_gyro: flag to indicate to send calibrated gyro.
+ * @send_raw_compass: flag to send raw compass.
+ * @resume_state: flag to synchronize the processing of inv_read_fifo()
+ * @cycle_on: variable indicate accel cycle mode is on.
+ * @secondary_switch_data: secondary switch data for activity.
+ * @raw_gyro_data[6]: save raw gyro data.
+ * @raw_compass_data[3]: save raw compass data.
+ * @wait_queue: wait queue to wake up inv_read_fifo()
+ * @bac_drive_conf: bac drive configuration.
+ * @bac_walk_conf: bac walk configuration.
+ * @bac_smd_conf: bac smd configuration.
+ * @bac_bike_conf: bac bike configuration.
+ * @bac_run_conf: bac run configuration.
+ * @bac_still_conf: back still configuration.
+ * @power_on_data: power on data.
+ * @fifo_data_store: store of FIFO data.
+ * @int_en: store interrupt enable register data.
+ * @int_en2: store interrupt enable register 2 data.
+ * @gesture_int_count: interrupt count for gesture only mode.
+ * @smplrt_div: SMPLRT_DIV register value.
+ */
+struct inv_mpu_state {
+ struct device *dev;
+ int (*write)(struct inv_mpu_state *st, u8 reg, u8 data);
+ int (*read)(struct inv_mpu_state *st, u8 reg, int len, u8 *data);
+ int (*mem_write)(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data);
+ int (*mem_read)(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data);
+ struct inv_chip_config_s chip_config;
+ struct inv_chip_info_s chip_info;
+ struct inv_smd smd;
+ struct inv_ped ped;
+ struct inv_eis eis;
+ struct inv_batch batch;
+ struct inv_temp_comp temp_comp;
+ struct inv_mpu_slave *slave_compass;
+ struct inv_mpu_slave *slave_pressure;
+ struct inv_mpu_slave *slave_als;
+ struct inv_secondary_reg slv_reg[4];
+ struct inv_timestamp_algo ts_algo;
+ struct inv_secondary_set sec_set;
+ struct inv_engine_info eng_info[ENGINE_NUM_MAX];
+ const struct inv_hw_s *hw;
+ enum inv_devices chip_type;
+ enum inv_bus_type bus_type;
+ enum inv_fifo_count_mode fifo_count_mode;
+#ifdef CONFIG_HAS_WAKELOCK
+ struct wake_lock wake_lock;
+#else
+ struct wakeup_source wake_lock;
+#endif
+#ifdef TIMER_BASED_BATCHING
+ struct hrtimer hr_batch_timer;
+ u64 batch_timeout;
+ bool is_batch_timer_running;
+ struct work_struct batch_work;
+#endif
+ struct i2c_client *client;
+ struct mpu_platform_data plat_data;
+ void *sl_handle;
+ struct inv_sensor sensor[SENSOR_NUM_MAX];
+ struct android_l_sensor sensor_l[SENSOR_L_NUM_MAX];
+ struct inv_sensor_accuracy sensor_accuracy[SENSOR_ACCURACY_NUM_MAX];
+ struct inv_ois ois;
+ bool sensor_acurracy_flag[SENSOR_ACCURACY_NUM_MAX];
+ short irq;
+ int accel_bias[3];
+ int gyro_bias[3];
+ int accel_st_bias[3];
+ int accel_ois_st_bias[3];
+ int gyro_st_bias[3];
+ int gyro_ois_st_bias[3];
+ int input_accel_dmp_bias[3];
+ int input_gyro_dmp_bias[3];
+ int input_compass_dmp_bias[3];
+ int input_accel_bias[3];
+ int input_gyro_bias[3];
+ u8 fifo_data[8];
+ u8 i2c_addr;
+ int header_count;
+ int step_det_count;
+ s32 gyro_sf;
+ u8 left_over[LEFT_OVER_BYTES];
+ u32 left_over_size;
+ u32 fifo_count;
+ bool wake_sensor_received;
+ bool accel_cal_enable;
+ bool gyro_cal_enable;
+ bool calib_compass_on;
+ bool debug_determine_engine_on;
+ bool poke_mode_on;
+ bool mode_1k_on;
+ u64 poke_ts;
+ u64 step_detector_base_ts;
+ u64 last_temp_comp_time;
+ u8 i2c_dis;
+ u8 name[20];
+ u8 gyro_st_data[3];
+ u8 accel_st_data[3];
+ u8 secondary_name[20];
+ s32 compass_var;
+ int current_compass_matrix[9];
+ int final_compass_matrix[9];
+ enum TRIGGER_STATE trigger_state;
+ u8 *firmware;
+ int accel_calib_threshold;
+ int accel_calib_rate;
+ u32 accel_covariance[COVARIANCE_SIZE];
+ DECLARE_KFIFO(kf, u8, 128);
+ u32 activity_size;
+ int wom_thld;
+ u16 cntl;
+ u16 cntl2;
+ u16 motion_event_cntl;
+ int dmp_image_size;
+ int dmp_start_address;
+ bool step_counter_l_on;
+ bool step_counter_wake_l_on;
+ bool step_detector_l_on;
+ bool step_detector_wake_l_on;
+ bool gesture_only_on;
+ bool mag_start_flag;
+ int mag_divider;
+ bool special_mag_mode;
+ int prev_steps;
+ u32 curr_steps;
+ int aut_key_in;
+ int aut_key_out;
+ bool secondary_gyro_on;
+ bool secondary_mag_on;
+ bool secondary_prox_on;
+ bool secondary_switch;
+ bool send_calib_gyro;
+ bool send_raw_compass;
+ bool send_raw_gyro;
+ bool resume_state;
+ bool cycle_on;
+ int secondary_switch_data;
+ u8 raw_gyro_data[6];
+ u32 raw_compass_data[3];
+ wait_queue_head_t wait_queue;
+ u32 bac_drive_conf;
+ u32 bac_walk_conf;
+ u32 bac_smd_conf;
+ u32 bac_bike_conf;
+ u32 bac_run_conf;
+ u32 bac_still_conf;
+ u32 power_on_data;
+ u8 fifo_data_store[HARDWARE_FIFO_SIZE + LEFT_OVER_BYTES];
+ u8 int_en;
+ u8 int_en_2;
+ u8 gesture_int_count;
+ u8 smplrt_div;
+};
+
+/**
+ * struct inv_mpu_slave - MPU slave structure.
+ * @st_upper: compass self test upper limit.
+ * @st_lower: compass self test lower limit.
+ * @scale: compass scale.
+ * @rate_scale: decide how fast a compass can read.
+ * @min_read_time: minimum time between each reading.
+ * @self_test: self test method of the slave.
+ * @set_scale: set scale of slave
+ * @get_scale: read scale back of the slave.
+ * @suspend: suspend operation.
+ * @resume: resume operation.
+ * @setup: setup chip. initialization.
+ * @combine_data: combine raw data into meaningful data.
+ * @read_data: read external sensor and output
+ * @get_mode: get current chip mode.
+ * @set_lpf: set low pass filter.
+ * @set_fs: set full scale
+ * @prev_ts: last time it is read.
+ */
+struct inv_mpu_slave {
+ const short *st_upper;
+ const short *st_lower;
+ int scale;
+ int rate_scale;
+ int min_read_time;
+ int (*self_test) (struct inv_mpu_state *);
+ int (*set_scale) (struct inv_mpu_state *, int scale);
+ int (*get_scale) (struct inv_mpu_state *, int *val);
+ int (*suspend) (struct inv_mpu_state *);
+ int (*resume) (struct inv_mpu_state *);
+ int (*setup) (struct inv_mpu_state *);
+ int (*combine_data) (u8 *in, short *out);
+ int (*read_data) (struct inv_mpu_state *, short *out);
+ int (*get_mode) (void);
+ int (*set_lpf) (struct inv_mpu_state *, int rate);
+ int (*set_fs) (struct inv_mpu_state *, int fs);
+ u64 prev_ts;
+};
+
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_MPU_SCAN_TIMESTAMP,
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+ ATTR_DMP_GYRO_X_DMP_BIAS,
+ ATTR_DMP_GYRO_Y_DMP_BIAS,
+ ATTR_DMP_GYRO_Z_DMP_BIAS,
+ ATTR_DMP_GYRO_CAL_ENABLE,
+ ATTR_DMP_ACCEL_X_DMP_BIAS,
+ ATTR_DMP_ACCEL_Y_DMP_BIAS,
+ ATTR_DMP_ACCEL_Z_DMP_BIAS,
+ ATTR_DMP_MAGN_X_DMP_BIAS,
+ ATTR_DMP_MAGN_Y_DMP_BIAS,
+ ATTR_DMP_MAGN_Z_DMP_BIAS,
+ ATTR_DMP_MAGN_ACCURACY,
+ ATTR_GYRO_X_OFFSET,
+ ATTR_GYRO_Y_OFFSET,
+ ATTR_GYRO_Z_OFFSET,
+ ATTR_ACCEL_X_OFFSET,
+ ATTR_ACCEL_Y_OFFSET,
+ ATTR_ACCEL_Z_OFFSET,
+ ATTR_DMP_SC_AUTH,
+ ATTR_DMP_EIS_AUTH,
+ ATTR_DMP_ACCEL_CAL_ENABLE,
+ ATTR_DMP_PED_INT_ON,
+ ATTR_DMP_PED_STEP_THRESH,
+ ATTR_DMP_PED_INT_THRESH,
+ ATTR_DMP_PED_ON,
+ ATTR_DMP_SMD_ENABLE,
+ ATTR_DMP_TILT_ENABLE,
+ ATTR_DMP_PICK_UP_ENABLE,
+ ATTR_DMP_EIS_ENABLE,
+ ATTR_DMP_PEDOMETER_STEPS,
+ ATTR_DMP_PEDOMETER_TIME,
+ ATTR_DMP_PEDOMETER_COUNTER,
+ ATTR_DMP_LOW_POWER_GYRO_ON,
+ ATTR_DMP_LP_EN_OFF,
+ ATTR_DMP_CLK_SEL,
+ ATTR_DMP_DEBUG_MEM_READ,
+ ATTR_DMP_DEBUG_MEM_WRITE,
+ ATTR_DEBUG_REG_WRITE,
+ ATTR_DEBUG_WRITE_CFG,
+ ATTR_DEBUG_REG_ADDR,
+ ATTR_WOM_THLD,
+ /* *****above this line, are DMP features, power needs on/off */
+ /* *****below this line, are DMP features, no power needed */
+ ATTR_IN_POWER_ON,
+ ATTR_DMP_ON,
+ ATTR_DMP_EVENT_INT_ON,
+ ATTR_DMP_STEP_COUNTER_ON,
+ ATTR_DMP_STEP_COUNTER_WAKE_ON,
+ ATTR_DMP_BATCHMODE_TIMEOUT,
+ ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL,
+ ATTR_DMP_STEP_DETECTOR_ON,
+ ATTR_DMP_STEP_DETECTOR_WAKE_ON,
+ ATTR_DMP_ACTIVITY_ON,
+ ATTR_DMP_IN_ANGLVEL_ACCURACY_ENABLE,
+ ATTR_DMP_IN_ACCEL_ACCURACY_ENABLE,
+ ATTR_DMP_DEBUG_DETERMINE_ENGINE_ON,
+ ATTR_DMP_MISC_GYRO_RECALIBRATION,
+ ATTR_DMP_MISC_ACCEL_RECALIBRATION,
+ ATTR_DMP_PARAMS_ACCEL_CALIBRATION_THRESHOLD,
+ ATTR_DMP_PARAMS_ACCEL_CALIBRATION_RATE,
+ ATTR_GYRO_SCALE,
+ ATTR_ACCEL_SCALE,
+ ATTR_COMPASS_SCALE,
+ ATTR_COMPASS_SENSITIVITY_X,
+ ATTR_COMPASS_SENSITIVITY_Y,
+ ATTR_COMPASS_SENSITIVITY_Z,
+ ATTR_GYRO_ENABLE,
+ ATTR_ACCEL_ENABLE,
+ ATTR_COMPASS_ENABLE,
+ ATTR_FIRMWARE_LOADED,
+ ATTR_POKE_MODE,
+ ATTR_ANGLVEL_X_CALIBBIAS,
+ ATTR_ANGLVEL_Y_CALIBBIAS,
+ ATTR_ANGLVEL_Z_CALIBBIAS,
+ ATTR_ACCEL_X_CALIBBIAS,
+ ATTR_ACCEL_Y_CALIBBIAS,
+ ATTR_ACCEL_Z_CALIBBIAS,
+ ATTR_ANGLVEL_X_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Y_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Z_ST_CALIBBIAS,
+ ATTR_ANGLVEL_X_OIS_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Y_OIS_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Z_OIS_ST_CALIBBIAS,
+ ATTR_ACCEL_X_ST_CALIBBIAS,
+ ATTR_ACCEL_Y_ST_CALIBBIAS,
+ ATTR_ACCEL_Z_ST_CALIBBIAS,
+ ATTR_ACCEL_X_OIS_ST_CALIBBIAS,
+ ATTR_ACCEL_Y_OIS_ST_CALIBBIAS,
+ ATTR_ACCEL_Z_OIS_ST_CALIBBIAS,
+ ATTR_GYRO_MATRIX,
+ ATTR_ACCEL_MATRIX,
+ ATTR_COMPASS_MATRIX,
+ ATTR_FSYNC_FRAME_COUNT,
+ ATTR_SECONDARY_NAME,
+ ATTR_GYRO_SF,
+ ATTR_BAC_DRIVE_CONFIDENCE,
+ ATTR_BAC_WALK_CONFIDENCE,
+ ATTR_BAC_SMD_CONFIDENCE,
+ ATTR_BAC_BIKE_CONFIDENCE,
+ ATTR_BAC_STILL_CONFIDENCE,
+ ATTR_BAC_RUN_CONFIDENCE,
+ IN_OIS_ACCEL_FS,
+ IN_OIS_GYRO_FS,
+ IN_OIS_ENABLE,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+#ifdef CONFIG_PM_SLEEP
+int inv_mpu_suspend(struct iio_dev *indio_dev);
+void inv_mpu_complete(struct iio_dev *indio_dev);
+#endif
+
+int inv_get_pedometer_steps(struct inv_mpu_state *st, int *ped);
+int inv_get_pedometer_time(struct inv_mpu_state *st, int *ped);
+int inv_read_pedometer_counter(struct inv_mpu_state *st);
+
+int inv_dmp_read(struct inv_mpu_state *st, int off, int size, u8 *buf);
+int inv_firmware_load(struct inv_mpu_state *st);
+
+int set_inv_enable(struct iio_dev *indio_dev);
+
+int inv_mpu_setup_compass_slave(struct inv_mpu_state *st);
+int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st);
+int inv_mpu_setup_als_slave(struct inv_mpu_state *st);
+int inv_mpu_initialize(struct inv_mpu_state *st);
+int inv_set_accel_sf(struct inv_mpu_state *st);
+int inv_set_gyro_sf(struct inv_mpu_state *st);
+s64 get_time_ns(void);
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i, u8 r, u16 l, u8 *d);
+int inv_i2c_single_write_base(struct inv_mpu_state *st, u16 i, u8 r, u8 d);
+int write_be32_to_mem(struct inv_mpu_state *st, u32 data, int addr);
+int write_be16_to_mem(struct inv_mpu_state *st, u16 data, int addr);
+int read_be32_from_mem(struct inv_mpu_state *st, u32 *o, int addr);
+int read_be16_from_mem(struct inv_mpu_state *st, u16 *o, int addr);
+u32 inv_get_cntr_diff(u32 curr_counter, u32 prev);
+int inv_write_2bytes(struct inv_mpu_state *st, int k, int data);
+int inv_set_bank(struct inv_mpu_state *st, u8 bank);
+int inv_set_power(struct inv_mpu_state *st, bool power_on);
+int inv_switch_power_in_lp(struct inv_mpu_state *st, bool on);
+#ifndef CONFIG_INV_MPU_IIO_ICM20608D
+int inv_set_accel_config2(struct inv_mpu_state *st, bool cycle_mode);
+#endif
+int inv_stop_dmp(struct inv_mpu_state *st);
+int inv_reset_fifo(struct inv_mpu_state *st, bool turn_off);
+int inv_create_dmp_sysfs(struct iio_dev *ind);
+int inv_check_chip_type(struct iio_dev *indio_dev, const char *name);
+int inv_write_compass_matrix(struct inv_mpu_state *st, int *adj);
+irqreturn_t inv_read_fifo(int irq, void *dev_id);
+#ifdef TIMER_BASED_BATCHING
+void inv_batch_work(struct work_struct *work);
+#endif
+int inv_flush_batch_data(struct iio_dev *indio_dev, int data);
+static inline int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr,
+ u16 mem_addr, u32 len, u8 const *data)
+{
+ int ret = -1;
+
+ if (st->mem_write)
+ ret = st->mem_write(st, mpu_addr, mem_addr, len, data);
+
+ return ret;
+}
+static inline int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr,
+ u16 mem_addr, u32 len, u8 *data)
+{
+ int ret = -1;
+
+ if (st->mem_read)
+ ret = st->mem_read(st, mpu_addr, mem_addr, len, data);
+
+ return ret;
+}
+int inv_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len);
+int inv_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v);
+int inv_execute_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v);
+int inv_execute_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len, u8 *d);
+
+int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr,
+ u64 t, int *q, s16 accur);
+int inv_push_gyro_data(struct inv_mpu_state *st, s16 *raw, s32 *calib, u64 t);
+int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d);
+int inv_push_8bytes_kf(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d);
+
+void inv_push_step_indicator(struct inv_mpu_state *st, u64 t);
+int inv_send_steps(struct inv_mpu_state *st, int step, u64 t);
+int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr, int data);
+
+int inv_check_sensor_on(struct inv_mpu_state *st);
+int inv_write_cntl(struct inv_mpu_state *st, u16 wd, bool en, int cntl);
+
+int inv_get_packet_size(struct inv_mpu_state *st, u16 hdr,
+ u32 *pk_size, u8 *dptr);
+int inv_parse_packet(struct inv_mpu_state *st, u16 hdr, u8 *dptr);
+int inv_pre_parse_packet(struct inv_mpu_state *st, u16 hdr, u8 *dptr);
+int inv_process_dmp_data(struct inv_mpu_state *st);
+
+int be32_to_int(u8 *d);
+void inv_convert_and_push_16bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m);
+void inv_convert_and_push_8bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m);
+int inv_get_dmp_ts(struct inv_mpu_state *st, int i);
+int inv_process_step_det(struct inv_mpu_state *st, u8 *dptr);
+int inv_process_eis(struct inv_mpu_state *st, u16 delay);
+int inv_rate_convert(struct inv_mpu_state *st, int ind, int data);
+
+int inv_setup_dmp_firmware(struct inv_mpu_state *st);
+/* used to print i2c data using pr_debug */
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string);
+char *wr_pr_debug_end(char *string);
+
+int inv_hw_self_test(struct inv_mpu_state *st);
+int inv_q30_mult(int a, int b);
+#ifdef ACCEL_BIAS_TEST
+int inv_get_3axis_average(s16 src[], s16 dst[], s16 reset);
+#endif
+
+static inline int inv_plat_single_write(struct inv_mpu_state *st,
+ u8 reg, u8 data)
+{
+ int ret = -1;
+
+ if (st->write)
+ ret = st->write(st, reg, data);
+
+ return ret;
+}
+static inline int inv_plat_read(struct inv_mpu_state *st, u8 reg,
+ int len, u8 *data)
+{
+ int ret = -1;
+
+ if (st->read)
+ ret = st->read(st, reg, len, data);
+
+ return ret;
+}
+irqreturn_t inv_read_fifo(int , void *);
+
+int inv_stop_interrupt(struct inv_mpu_state *st);
+int inv_reenable_interrupt(struct inv_mpu_state *st);
+
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en);
+int inv_dataout_control1(struct inv_mpu_state *st, u16 cntl1);
+int inv_dataout_control2(struct inv_mpu_state *st, u16 cntl2);
+int inv_motion_interrupt_control(struct inv_mpu_state *st,
+ u16 motion_event_cntl);
+
+int inv_bound_timestamp(struct inv_mpu_state *st);
+int inv_update_dmp_ts(struct inv_mpu_state *st, int ind);
+int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st);
+
+#define mem_w(a, b, c) mpu_memory_write(st, st->i2c_addr, a, b, c)
+#define mem_r(a, b, c) mpu_memory_read(st, st->i2c_addr, a, b, c)
+
+#endif /* #ifndef _INV_MPU_IIO_H_ */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu/inv_mpu_ring.c
new file mode 100644
index 000000000000..3e5bccbea0df
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_ring.c
@@ -0,0 +1,643 @@
+/*
+* Copyright (C) 2012-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/math64.h>
+#include <linux/miscdevice.h>
+
+#include "inv_mpu_iio.h"
+
+static void inv_push_timestamp(struct iio_dev *indio_dev, u64 t)
+{
+ u8 buf[IIO_BUFFER_BYTES];
+ struct inv_mpu_state *st;
+
+ st = iio_priv(indio_dev);
+ if (st->poke_mode_on)
+ memcpy(buf, &st->poke_ts, sizeof(t));
+ else
+ memcpy(buf, &t, sizeof(t));
+ iio_push_to_buffers(indio_dev, buf);
+}
+
+int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr, int data)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(&buf[4], &data, sizeof(data));
+ iio_push_to_buffers(indio_dev, buf);
+
+ return 0;
+}
+static int inv_calc_precision(struct inv_mpu_state *st)
+{
+ int diff;
+ int init;
+
+ if (st->eis.voting_state != 8)
+ return 0;
+ diff = abs(st->eis.fsync_delay_s[1] - st->eis.fsync_delay_s[0]);
+ init = 0;
+ if (diff)
+ init = st->sensor[SENSOR_GYRO].dur / diff;
+
+ if (abs(init - NSEC_PER_USEC) < (NSEC_PER_USEC >> 3))
+ st->eis.count_precision = init;
+ else
+ st->eis.voting_state = 0;
+
+ pr_debug("dur= %d prc= %d\n", st->sensor[SENSOR_GYRO].dur,
+ st->eis.count_precision);
+
+ return 0;
+}
+
+static s64 calc_frame_ave(struct inv_mpu_state *st, int delay)
+{
+ s64 ts;
+
+ ts = st->eis.current_timestamp - delay;
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
+ ts -= st->ts_algo.gyro_ts_shift;
+#endif
+ pr_debug("shift= %d ts = %lld\n", st->ts_algo.gyro_ts_shift, ts);
+
+ return ts;
+}
+
+static void inv_push_eis_ring(struct inv_mpu_state *st, int *q, bool sync,
+ s64 t)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ struct inv_eis *eis = &st->eis;
+ u8 buf[IIO_BUFFER_BYTES];
+ int tmp, ii;
+
+ buf[0] = (EIS_GYRO_HDR & 0xff);
+ buf[1] = (EIS_GYRO_HDR >> 8);
+ memcpy(buf + 4, &q[0], sizeof(q[0]));
+ iio_push_to_buffers(indio_dev, buf);
+ for (ii = 0; ii < 2; ii++)
+ memcpy(buf + 4 * ii, &q[ii + 1], sizeof(q[ii]));
+ iio_push_to_buffers(indio_dev, buf);
+ tmp = eis->frame_count;
+ if (sync)
+ tmp |= 0x80000000;
+ memcpy(buf, &tmp, sizeof(tmp));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+}
+static int inv_do_interpolation_gyro(struct inv_mpu_state *st, int *prev,
+ s64 prev_t, int *curr, s64 curr_t, s64 t, bool trigger)
+{
+ int i;
+ int out[3];
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
+ prev_t -= st->ts_algo.gyro_ts_shift;
+ prev_t += MPU_4X_TS_GYRO_SHIFT;
+ curr_t -= st->ts_algo.gyro_ts_shift;
+ curr_t += MPU_4X_TS_GYRO_SHIFT;
+#endif
+ if ((t > prev_t) && (t < curr_t)) {
+ for (i = 0; i < 3; i++)
+ out[i] = (int)div_s64((s64)(curr[i] - prev[i]) *
+ (s64)(t - prev_t), curr_t - prev_t) + prev[i];
+ } else if (t < prev_t) {
+ for (i = 0; i < 3; i++)
+ out[i] = prev[i];
+ } else {
+ for (i = 0; i < 3; i++)
+ out[i] = curr[i];
+ }
+ pr_debug("prev= %lld t = %lld curr= %lld\n", prev_t, t, curr_t);
+ pr_debug("prev = %d, %d, %d\n", prev[0], prev[1], prev[2]);
+ pr_debug("curr = %d, %d, %d\n", curr[0], curr[1], curr[2]);
+ pr_debug("out = %d, %d, %d\n", out[0], out[1], out[2]);
+ inv_push_eis_ring(st, out, trigger, t);
+
+ return 0;
+}
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
+static void inv_handle_triggered_eis(struct inv_mpu_state *st)
+{
+ struct inv_eis *eis = &st->eis;
+ int delay;
+
+ if (st->eis.eis_frame) {
+ inv_calc_precision(st);
+ delay = ((int)st->eis.fsync_delay) * st->eis.count_precision;
+ eis->fsync_timestamp = calc_frame_ave(st, delay);
+ inv_do_interpolation_gyro(st,
+ st->eis.prev_gyro, st->eis.prev_timestamp,
+ st->eis.current_gyro, st->eis.current_timestamp,
+ eis->fsync_timestamp, true);
+ pr_debug("fsync=%lld, curr=%lld, delay=%d\n",
+ eis->fsync_timestamp, eis->current_timestamp, delay);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp - st->ts_algo.gyro_ts_shift
+ + MPU_4X_TS_GYRO_SHIFT);
+ eis->last_fsync_timestamp = eis->fsync_timestamp;
+ } else {
+ pr_debug("cur= %lld\n", st->eis.current_timestamp);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp - st->ts_algo.gyro_ts_shift
+ + MPU_4X_TS_GYRO_SHIFT);
+ }
+}
+#else
+static void inv_handle_triggered_eis(struct inv_mpu_state *st)
+{
+ struct inv_eis *eis = &st->eis;
+ int delay;
+
+ if ((st->eis.eis_frame && (st->eis.fsync_delay != 5)) ||
+ (st->eis.eis_frame && (st->eis.fsync_delay == 5) &&
+ (!st->eis.current_sync))
+ ) {
+ inv_calc_precision(st);
+ delay = ((int)st->eis.fsync_delay) * st->eis.count_precision;
+ eis->fsync_timestamp = calc_frame_ave(st, delay);
+ inv_do_interpolation_gyro(st,
+ st->eis.prev_gyro, st->eis.prev_timestamp,
+ st->eis.current_gyro, st->eis.current_timestamp,
+ eis->fsync_timestamp, true);
+ pr_debug("fsync=%lld, curr=%lld, delay=%d\n",
+ eis->fsync_timestamp, eis->current_timestamp, delay);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp);
+ eis->last_fsync_timestamp = eis->fsync_timestamp;
+ st->eis.eis_frame = false;
+ } else {
+ st->eis.current_sync = false;
+ pr_debug("cur= %lld\n", st->eis.current_timestamp);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp);
+ }
+}
+#endif
+static void inv_push_eis_buffer(struct inv_mpu_state *st, u64 t, int *q)
+{
+ int ii;
+
+ if (st->eis.eis_triggered) {
+ for (ii = 0; ii < 3; ii++)
+ st->eis.prev_gyro[ii] = st->eis.current_gyro[ii];
+ st->eis.prev_timestamp = st->eis.current_timestamp;
+
+ for (ii = 0; ii < 3; ii++)
+ st->eis.current_gyro[ii] = q[ii];
+ st->eis.current_timestamp = t;
+ inv_handle_triggered_eis(st);
+ } else {
+ for (ii = 0; ii < 3; ii++)
+ st->eis.current_gyro[ii] = q[ii];
+ st->eis.current_timestamp = t;
+ }
+}
+static int inv_push_16bytes_final(struct inv_mpu_state *st, int j,
+ s32 *q, u64 t, s16 accur)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int ii;
+
+ memcpy(buf, &st->sensor_l[j].header, sizeof(st->sensor_l[j].header));
+ memcpy(buf + 2, &accur, sizeof(accur));
+ memcpy(buf + 4, &q[0], sizeof(q[0]));
+ iio_push_to_buffers(indio_dev, buf);
+ for (ii = 0; ii < 2; ii++)
+ memcpy(buf + 4 * ii, &q[ii + 1], sizeof(q[ii]));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+ st->sensor_l[j].counter = 0;
+ if (st->sensor_l[j].wake_on)
+ st->wake_sensor_received = true;
+
+ return 0;
+}
+int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 sensor,
+ u64 t, int *q, s16 accur)
+{
+ int j;
+
+ for (j = 0; j < SENSOR_L_NUM_MAX; j++) {
+ if (st->sensor_l[j].on && (st->sensor_l[j].base == sensor)) {
+ st->sensor_l[j].counter++;
+ if ((st->sensor_l[j].div != 0xffff) &&
+ (st->sensor_l[j].counter >=
+ st->sensor_l[j].div)) {
+ pr_debug(
+ "Sensor_l = %d sensor = %d header [%04X] div [%d] ts [%lld] %d %d %d\n",
+ j, sensor,
+ st->sensor_l[j].header,
+ st->sensor_l[j].div,
+ t, q[0], q[1], q[2]);
+ inv_push_16bytes_final(st, j, q, t, accur);
+ }
+ }
+ }
+ return 0;
+}
+
+void inv_convert_and_push_16bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m)
+{
+ int i, j;
+ s32 in[3], out[3];
+
+ for (i = 0; i < 3; i++)
+ in[i] = be32_to_int(d + i * 4);
+ /* multiply with orientation matrix can be optimized like this */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (m[i * 3 + j])
+ out[i] = in[j] * m[i * 3 + j];
+
+ inv_push_16bytes_buffer(st, hdr, t, out, 0);
+}
+
+void inv_convert_and_push_8bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m)
+{
+ int i, j;
+ s16 in[3], out[3];
+
+ for (i = 0; i < 3; i++)
+ in[i] = be16_to_cpup((__be16 *) (d + i * 2));
+
+ /* multiply with orientation matrix can be optimized like this */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (m[i * 3 + j])
+ out[i] = in[j] * m[i * 3 + j];
+
+ inv_push_8bytes_buffer(st, hdr, t, out);
+}
+
+int inv_push_special_8bytes_buffer(struct inv_mpu_state *st,
+ u16 hdr, u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int j;
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(&buf[2], &d[0], sizeof(d[0]));
+ for (j = 0; j < 2; j++)
+ memcpy(&buf[4 + j * 2], &d[j + 1], sizeof(d[j]));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+
+ return 0;
+}
+
+static int inv_s16_gyro_push(struct inv_mpu_state *st, int i, s16 *raw, u64 t)
+{
+ if (st->sensor_l[i].on) {
+ st->sensor_l[i].counter++;
+ if ((st->sensor_l[i].div != 0xffff) &&
+ (st->sensor_l[i].counter >= st->sensor_l[i].div)) {
+ inv_push_special_8bytes_buffer(st,
+ st->sensor_l[i].header, t, raw);
+ st->sensor_l[i].counter = 0;
+ if (st->sensor_l[i].wake_on)
+ st->wake_sensor_received = true;
+ }
+ }
+
+ return 0;
+}
+
+static int inv_s32_gyro_push(struct inv_mpu_state *st, int i, s32 *calib, u64 t)
+{
+ if (st->sensor_l[i].on) {
+ st->sensor_l[i].counter++;
+ if ((st->sensor_l[i].div != 0xffff) &&
+ (st->sensor_l[i].counter >= st->sensor_l[i].div)) {
+ inv_push_16bytes_final(st, i, calib, t, 0);
+ st->sensor_l[i].counter = 0;
+ if (st->sensor_l[i].wake_on)
+ st->wake_sensor_received = true;
+ }
+ }
+
+ return 0;
+}
+
+int inv_push_gyro_data(struct inv_mpu_state *st, s16 *raw, s32 *calib, u64 t)
+{
+ int gyro_data[] = {SENSOR_L_GYRO, SENSOR_L_GYRO_WAKE};
+ int calib_data[] = {SENSOR_L_GYRO_CAL, SENSOR_L_GYRO_CAL_WAKE};
+ int i;
+
+ if (st->sensor_l[SENSOR_L_EIS_GYRO].on)
+ inv_push_eis_buffer(st, t, calib);
+
+ for (i = 0; i < 2; i++)
+ inv_s16_gyro_push(st, gyro_data[i], raw, t);
+ for (i = 0; i < 2; i++)
+ inv_s32_gyro_push(st, calib_data[i], calib, t);
+
+ return 0;
+}
+int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 sensor, u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int ii, j;
+
+ if ((sensor == STEP_DETECTOR_HDR) ||
+ (sensor == STEP_DETECTOR_WAKE_HDR)) {
+ memcpy(buf, &sensor, sizeof(sensor));
+ memcpy(&buf[2], &d[0], sizeof(d[0]));
+ for (j = 0; j < 2; j++)
+ memcpy(&buf[4 + j * 2], &d[j + 1], sizeof(d[j]));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+ if (sensor == STEP_DETECTOR_WAKE_HDR)
+ st->wake_sensor_received = true;
+ return 0;
+ }
+ for (ii = 0; ii < SENSOR_L_NUM_MAX; ii++) {
+ if (st->sensor_l[ii].on &&
+ (st->sensor_l[ii].base == sensor) &&
+ (st->sensor_l[ii].div != 0xffff)) {
+ st->sensor_l[ii].counter++;
+ if (st->sensor_l[ii].counter >= st->sensor_l[ii].div) {
+ pr_debug(
+ "Sensor_l = %d sensor = %d header [%04X] div [%d] ts [%lld] %d %d %d\n",
+ ii, sensor, st->sensor_l[ii].header,
+ st->sensor_l[ii].div, t, d[0], d[1], d[2]);
+
+ memcpy(buf, &st->sensor_l[ii].header,
+ sizeof(st->sensor_l[ii].header));
+ memcpy(&buf[2], &d[0], sizeof(d[0]));
+ for (j = 0; j < 2; j++)
+ memcpy(&buf[4 + j * 2], &d[j + 1],
+ sizeof(d[j]));
+
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+ st->sensor_l[ii].counter = 0;
+ if (st->sensor_l[ii].wake_on)
+ st->wake_sensor_received = true;
+ }
+ }
+ }
+
+ return 0;
+}
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+/* Implemented activity to string function for BAC test */
+#define TILT_DETECTED 0x1000
+#define NONE 0x00
+#define DRIVE 0x01
+#define WALK 0x02
+#define RUN 0x04
+#define BIKE 0x08
+#define TILT 0x10
+#define STILL 0x20
+#define DRIVE_WALK (DRIVE | WALK)
+#define DRIVE_RUN (DRIVE | RUN)
+
+char *act_string(s16 data)
+{
+ data &= (~TILT);
+ switch (data) {
+ case NONE:
+ return "None";
+ case DRIVE:
+ return "Drive";
+ case WALK:
+ return "Walk";
+ case RUN:
+ return "Run";
+ case BIKE:
+ return "Bike";
+ case STILL:
+ return "Still";
+ case DRIVE_WALK:
+ return "drive and walk";
+ case DRIVE_RUN:
+ return "drive and run";
+ default:
+ return "Unknown";
+ }
+ return "Unknown";
+}
+
+char *inv_tilt_check(s16 data)
+{
+ if (data & TILT)
+ return "Tilt";
+ else
+ return "None";
+}
+
+int inv_push_8bytes_kf(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int i;
+
+ if (st->chip_config.activity_on) {
+ memcpy(buf, &hdr, sizeof(hdr));
+ for (i = 0; i < 3; i++)
+ memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i]));
+
+ kfifo_in(&st->kf, buf, IIO_BUFFER_BYTES);
+ memcpy(buf, &t, sizeof(t));
+ kfifo_in(&st->kf, buf, IIO_BUFFER_BYTES);
+ st->activity_size += IIO_BUFFER_BYTES * 2;
+ }
+ if (st->chip_config.tilt_enable) {
+ pr_debug("d[0] = %04X, [%X : %s] to [%X : %s]",
+ d[0], d[0] & 0x00FF,
+ inv_tilt_check(d[0] & 0x00FF),
+ (d[0] & 0xFF00) >> 8, inv_tilt_check((d[0] & 0xFF00) >> 8));
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "poll_tilt");
+ }
+
+ pr_debug("d[0] = %04X, [%X : %s] to [%X : %s]", d[0], d[0] & 0x00FF,
+ act_string(d[0] & 0x00FF),
+ (d[0] & 0xFF00) >> 8, act_string((d[0] & 0xFF00) >> 8));
+
+ read_be32_from_mem(st, &st->bac_drive_conf, BAC_DRIVE_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_walk_conf, BAC_WALK_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_smd_conf, BAC_SMD_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_bike_conf, BAC_BIKE_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_still_conf, BAC_STILL_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_run_conf, BAC_RUN_CONFIDENCE);
+
+ return 0;
+}
+#endif
+
+int inv_send_steps(struct inv_mpu_state *st, int step, u64 ts)
+{
+ s16 s[3];
+
+ s[0] = 0;
+ s[1] = (s16) (step & 0xffff);
+ s[2] = (s16) ((step >> 16) & 0xffff);
+ if (st->step_counter_l_on)
+ inv_push_special_8bytes_buffer(st, STEP_COUNTER_HDR, ts, s);
+ if (st->step_counter_wake_l_on) {
+ inv_push_special_8bytes_buffer(st, STEP_COUNTER_WAKE_HDR,
+ ts, s);
+ st->wake_sensor_received = true;
+ }
+ return 0;
+}
+
+void inv_push_step_indicator(struct inv_mpu_state *st, u64 t)
+{
+ s16 sen[3];
+#define STEP_INDICATOR_HEADER 0x0001
+
+ sen[0] = 0;
+ sen[1] = 0;
+ sen[2] = 0;
+ inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER, t, sen);
+}
+
+/*
+ * inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+#ifdef TIMER_BASED_BATCHING
+static enum hrtimer_restart inv_batch_timer_handler(struct hrtimer *timer)
+{
+ struct inv_mpu_state *st =
+ container_of(timer, struct inv_mpu_state, hr_batch_timer);
+
+ if (st->chip_config.gyro_enable || st->chip_config.accel_enable) {
+ hrtimer_forward_now(&st->hr_batch_timer,
+ ns_to_ktime(st->batch_timeout));
+ schedule_work(&st->batch_work);
+ return HRTIMER_RESTART;
+ }
+ st->is_batch_timer_running = 0;
+ return HRTIMER_NORESTART;
+}
+#endif
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+#ifdef KERNEL_VERSION_4_X
+ devm_free_irq(st->dev, st->irq, st);
+ devm_iio_kfifo_free(st->dev, indio_dev->buffer);
+#else
+ free_irq(st->irq, st);
+ iio_kfifo_free(indio_dev->buffer);
+#endif
+};
+EXPORT_SYMBOL_GPL(inv_mpu_unconfigure_ring);
+
+#ifndef KERNEL_VERSION_4_X
+static int inv_predisable(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static int inv_preenable(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+ .preenable = &inv_preenable,
+ .predisable = &inv_predisable,
+};
+#endif
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring;
+
+#ifdef TIMER_BASED_BATCHING
+ /* configure hrtimer */
+ hrtimer_init(&st->hr_batch_timer, CLOCK_BOOTTIME, HRTIMER_MODE_REL);
+ st->hr_batch_timer.function = inv_batch_timer_handler;
+ INIT_WORK(&st->batch_work, inv_batch_work);
+#endif
+#ifdef KERNEL_VERSION_4_X
+ ring = devm_iio_kfifo_allocate(st->dev);
+ if (!ring)
+ return -ENOMEM;
+ ring->scan_timestamp = true;
+ iio_device_attach_buffer(indio_dev, ring);
+ ret = devm_request_threaded_irq(st->dev,
+ st->irq,
+ inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ "inv_irq",
+ st);
+ if (ret) {
+ devm_iio_kfifo_free(st->dev, ring);
+ return ret;
+ }
+
+ // this mode does not use ops
+ indio_dev->modes = INDIO_ALL_BUFFER_MODES;
+
+ return ret;
+#else
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring)
+ return -ENOMEM;
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
+ ret = request_threaded_irq(st->irq,
+ inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ "inv_irq",
+ st);
+ if (ret)
+ goto error_iio_sw_rb_free;
+
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ return 0;
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+
+ return ret;
+#endif
+}
+EXPORT_SYMBOL_GPL(inv_mpu_configure_ring);
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu/inv_mpu_spi.c
new file mode 100644
index 000000000000..fb916788a9df
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_spi.c
@@ -0,0 +1,410 @@
+/*
+* Copyright (C) 2012-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_mpu_dts.h"
+
+#define INV_SPI_READ 0x80
+
+static int inv_spi_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
+{
+ struct spi_message msg;
+ int res;
+ u8 d[2];
+ struct spi_transfer xfers = {
+ .tx_buf = d,
+ .bits_per_word = 8,
+ .len = 2,
+ };
+
+ pr_debug("reg_write: reg=0x%x data=0x%x\n", reg, data);
+ d[0] = reg;
+ d[1] = data;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers, &msg);
+ res = spi_sync(to_spi_device(st->dev), &msg);
+
+ return res;
+}
+
+static int inv_spi_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
+{
+ struct spi_message msg;
+ int res;
+ u8 d[1];
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = d,
+ .bits_per_word = 8,
+ .len = 1,
+ },
+ {
+ .rx_buf = data,
+ .bits_per_word = 8,
+ .len = len,
+ }
+ };
+
+ if (!data)
+ return -EINVAL;
+
+ d[0] = (reg | INV_SPI_READ);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ res = spi_sync(to_spi_device(st->dev), &msg);
+
+ if (len ==1)
+ pr_debug("reg_read: reg=0x%x length=%d data=0x%x\n",
+ reg, len, data[0]);
+ else
+ pr_debug("reg_read: reg=0x%x length=%d d0=0x%x d1=0x%x\n",
+ reg, len, data[0], data[1]);
+
+ return res;
+
+}
+
+static int inv_spi_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ struct spi_message msg;
+ u8 buf[258];
+ int res;
+
+ struct spi_transfer xfers = {
+ .tx_buf = buf,
+ .bits_per_word = 8,
+ .len = len + 1,
+ };
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len > (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
+ inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
+
+ buf[0] = REG_MEM_R_W;
+ memcpy(buf + 1, data, len);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers, &msg);
+ res = spi_sync(to_spi_device(st->dev), &msg);
+
+ return res;
+}
+
+static int inv_spi_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len > 256)
+ return -EINVAL;
+
+ res = inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
+ res = inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
+ res = inv_plat_read(st, REG_MEM_R_W, len, data);
+
+ return res;
+}
+
+/*
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct inv_mpu_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+#ifdef KERNEL_VERSION_4_X
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#else
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#endif
+ st = iio_priv(indio_dev);
+ st->write = inv_spi_single_write;
+ st->read = inv_spi_read;
+ st->mem_write = inv_spi_mem_write;
+ st->mem_read = inv_spi_mem_read;
+ st->dev = &spi->dev;
+ st->irq = spi->irq;
+#if !defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ && !defined(CONFIG_INV_MPU_IIO_IAM20680)
+ st->i2c_dis = BIT_I2C_IF_DIS;
+#endif
+ st->bus_type = BUS_SPI;
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = id->name;
+
+#ifdef CONFIG_OF
+ result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
+ if (result)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ /* Power on device */
+ if (st->plat_data.power_on) {
+ result = st->plat_data.power_on(&st->plat_data);
+ if (result < 0) {
+ dev_err(st->dev, "power_on failed: %d\n", result);
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ }
+ pr_info("%s: power on here.\n", __func__);
+ }
+ pr_info("%s: power on.\n", __func__);
+
+ msleep(100);
+#else
+ if (dev_get_platdata(st->dev) == NULL)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
+#endif
+
+ /* power is turned on inside check chip type */
+ result = inv_check_chip_type(indio_dev, id->name);
+ if (result)
+#ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+#else
+ goto out_free;
+#endif
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+#ifdef KERNEL_VERSION_4_X
+ result = devm_iio_device_register(st->dev, indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_unreg_ring;
+ }
+#else
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_ring;
+ }
+#endif
+
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ init_waitqueue_head(&st->wait_queue);
+ st->resume_state = true;
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
+#else
+ wakeup_source_init(&st->wake_lock, "inv_mpu");
+#endif
+ dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
+ indio_dev->name, INVENSENSE_DRIVER_VERSION);
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ pr_info("Data read from registers\n");
+#else
+ pr_info("Data read from FIFO\n");
+#endif
+#ifdef TIMER_BASED_BATCHING
+ pr_info("Timer based batching\n");
+#endif
+
+ return 0;
+#ifdef KERNEL_VERSION_4_X
+out_unreg_iio:
+ devm_iio_device_unregister(st->dev, indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ devm_iio_device_free(st->dev, indio_dev);
+out_no_free:
+#else
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+#endif
+ dev_err(st->dev, "%s failed %d\n", __func__, result);
+
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ inv_switch_power_in_lp(st, true);
+ dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
+ if (result)
+ dev_err(st->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = inv_set_power(st, false);
+ if (result)
+ dev_err(st->dev, "Failed to turn off %s\n",
+ st->hw->name);
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+}
+
+/*
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_unregister(st->dev, indio_dev);
+#else
+ iio_device_unregister(indio_dev);
+ iio_buffer_unregister(indio_dev);
+#endif
+ inv_mpu_unconfigure_ring(indio_dev);
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_free(st->dev, indio_dev);
+#else
+ iio_device_free(indio_dev);
+#endif
+ dev_info(st->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int inv_mpu_spi_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
+
+ return inv_mpu_suspend(indio_dev);
+}
+
+static void inv_mpu_spi_complete(struct device *dev)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
+
+ inv_mpu_complete(indio_dev);
+}
+#endif
+
+static const struct dev_pm_ops inv_mpu_spi_pmops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = inv_mpu_spi_suspend,
+ .complete = inv_mpu_spi_complete,
+#endif
+};
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct spi_device_id inv_mpu_id[] = {
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+ {"icm20645", ICM20645},
+ {"icm10340", ICM10340},
+ {"icm20648", ICM20648},
+#else
+ {"icm20608d", ICM20608D},
+ {"icm20690", ICM20690},
+ {"icm20602", ICM20602},
+ {"iam20680", IAM20680},
+#endif
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, inv_mpu_id);
+
+static struct spi_driver inv_mpu_driver = {
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio-spi",
+ .pm = &inv_mpu_spi_pmops,
+ },
+};
+module_spi_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense SPI device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c b/drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c
new file mode 100644
index 000000000000..2cc721b18596
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/math64.h>
+
+#include "inv_mpu_iio.h"
+
+#define INV_TIME_CALIB_THRESHOLD_1 2
+
+#define MIN_DELAY (3 * NSEC_PER_MSEC)
+#define JITTER_THRESH ( 1 * NSEC_PER_MSEC)
+
+int inv_update_dmp_ts(struct inv_mpu_state *st, int ind)
+{
+ int i;
+ u32 counter;
+ u64 ts;
+ enum INV_ENGINE en_ind;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+ u32 base_time;
+ u64 cal_period;
+
+ if (st->mode_1k_on)
+ cal_period = (NSEC_PER_SEC >> 2);
+ else
+ cal_period = 2 * NSEC_PER_SEC;
+
+ ts = ts_algo->last_run_time - st->sensor[ind].time_calib;
+ counter = st->sensor[ind].sample_calib;
+ en_ind = st->sensor[ind].engine_base;
+ if (en_ind != ts_algo->clock_base)
+ return 0;
+ /* we average over 2 seconds period to do the timestamp calculation */
+ if (ts < cal_period)
+ return 0;
+ /* this is the first time we do timestamp averaging, return */
+ /* after resume from suspend, the clock of linux has up to 1 seconds
+ drift. We should start from the resume clock instead of using clock
+ before resume */
+ if ((!st->sensor[ind].calib_flag) || ts_algo->resume_flag) {
+ st->sensor[ind].sample_calib = 0;
+ st->sensor[ind].time_calib = ts_algo->last_run_time;
+ st->sensor[ind].calib_flag = 1;
+ ts_algo->resume_flag = false;
+
+ return 0;
+ }
+ /* if the sample number in current FIFO is not zero and between now and
+ last update time is more than 2 seconds, we do calculation */
+ if ((counter > 0) &&
+ (ts_algo->last_run_time - st->eng_info[en_ind].last_update_time >
+ cal_period)) {
+ /* duration for each sensor */
+ st->sensor[ind].dur = (u32) div_u64(ts, counter);
+ /* engine duration derived from each sensor */
+ if (st->sensor[ind].div)
+ st->eng_info[en_ind].dur = st->sensor[ind].dur /
+ st->sensor[ind].div;
+ else
+ pr_err("sensor %d divider zero!\n", ind);
+ /* update base time for each sensor */
+ if (st->eng_info[en_ind].divider) {
+ base_time = (st->eng_info[en_ind].dur /
+ st->eng_info[en_ind].divider) *
+ st->eng_info[en_ind].orig_rate;
+ if (st->mode_1k_on)
+ st->eng_info[en_ind].base_time_1k = base_time;
+ else
+ st->eng_info[en_ind].base_time = base_time;
+ } else {
+ pr_err("engine %d divider zero!\n", en_ind);
+ }
+
+ st->eng_info[en_ind].last_update_time = ts_algo->last_run_time;
+ /* update all the sensors duration based on the same engine */
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on &&
+ (st->sensor[i].engine_base == en_ind))
+ st->sensor[i].dur = st->sensor[i].div *
+ st->eng_info[en_ind].dur;
+ }
+
+ }
+ st->sensor[ind].sample_calib = 0;
+ st->sensor[ind].time_calib = ts_algo->last_run_time;
+
+ return 0;
+}
+/**
+ * int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st)
+ * This is the function to get last run time in non dmp and record mode.
+ * This function will update the last_run_time, which is important parameter
+ * in overall timestamp algorithm.
+ * return value: this function returns fifo count value.
+*/
+int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st)
+{
+ long long t_pre, t_post, dur;
+ int fifo_count;
+#ifndef SENSOR_DATA_FROM_REGISTERS
+ int res;
+ u8 data[2];
+#endif
+
+ t_pre = get_time_ns();
+#ifndef SENSOR_DATA_FROM_REGISTERS
+ res = inv_plat_read(st, REG_FIFO_COUNT_H, FIFO_COUNT_BYTE, data);
+ if (res) {
+ pr_info("read REG_FIFO_COUNT_H failed= %d\n", res);
+ return 0;
+ }
+#endif
+ t_post = get_time_ns();
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ if (st->fifo_count_mode == BYTE_MODE)
+ fifo_count = st->batch.pk_size;
+ else
+ fifo_count = 1;
+#else
+ fifo_count = be16_to_cpup((__be16 *) (data));
+#endif
+ pr_debug("fifc=%d\n", fifo_count);
+ if (!fifo_count)
+ return 0;
+ if (st->special_mag_mode && (fifo_count == 2)) {
+ pr_debug("special trigger\n");
+ fifo_count = 1;
+ }
+
+ /* In non DMP mode, either gyro or accel duration is the duration
+ for each sample */
+ if (st->chip_config.gyro_enable)
+ dur = st->eng_info[ENGINE_GYRO].dur;
+ else
+ dur = st->eng_info[ENGINE_ACCEL].dur;
+
+ if (st->fifo_count_mode == BYTE_MODE) {
+ fifo_count /= st->batch.pk_size;
+ }
+
+ /* In record mode, each number in fifo_count is 1 record or 1 sample */
+ st->ts_algo.last_run_time += dur * fifo_count;
+ if (st->ts_algo.last_run_time < t_pre)
+ st->ts_algo.last_run_time = t_pre;
+ if (st->ts_algo.last_run_time > t_post)
+ st->ts_algo.last_run_time = t_post;
+
+ return fifo_count;
+}
+
+int inv_get_dmp_ts(struct inv_mpu_state *st, int i)
+{
+ u64 current_time;
+ int expected_lower_duration, expected_upper_duration;
+
+ current_time = get_time_ns();
+
+ st->sensor[i].ts += st->sensor[i].dur + st->sensor[i].ts_adj;
+
+ if (st->sensor[i].ts < st->sensor[i].previous_ts)
+ st->sensor[i].ts = st->sensor[i].previous_ts + st->sensor[i].dur;
+
+ //hifi sensor limits ts jitter to +/- 2%
+ expected_upper_duration = st->eng_info[st->sensor[i].engine_base].divider * 1020000;
+ expected_lower_duration = st->eng_info[st->sensor[i].engine_base].divider * 980000;
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) || defined(CONFIG_INV_MPU_IIO_ICM20690) || defined(CONFIG_INV_MPU_IIO_IAM20680)
+ if (st->sensor[i].ts < st->sensor[i].previous_ts + expected_lower_duration)
+ st->sensor[i].ts = st->sensor[i].previous_ts + expected_lower_duration;
+ if (st->sensor[i].ts > st->sensor[i].previous_ts + expected_upper_duration)
+ st->sensor[i].ts = st->sensor[i].previous_ts + expected_upper_duration;
+#endif
+ if (st->sensor[i].ts > current_time )
+ st->sensor[i].ts = current_time;
+
+ st->sensor[i].previous_ts = st->sensor[i].ts;
+
+ pr_debug("ts=%lld, reset=%lld\n", st->sensor[i].ts, st->ts_algo.reset_ts);
+ if (st->sensor[i].ts < st->ts_algo.reset_ts) {
+ pr_debug("less than reset\n");
+ st->sensor[i].send = false;
+ } else {
+ st->sensor[i].send = true;
+ }
+
+ if (st->header_count == 1)
+ inv_update_dmp_ts(st, i);
+
+ return 0;
+}
+
+static void process_sensor_bounding(struct inv_mpu_state *st, int i)
+{
+ s64 elaps_time, thresh1, thresh2;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+ u32 dur;
+
+ elaps_time = ((u64) (st->sensor[i].dur)) * st->sensor[i].count;
+ thresh1 = ts_algo->last_run_time - elaps_time;
+
+ dur = max(st->sensor[i].dur, (int)MIN_DELAY);
+ thresh2 = thresh1 - dur;
+ if (thresh1 < 0)
+ thresh1 = 0;
+ if (thresh2 < 0)
+ thresh2 = 0;
+ st->sensor[i].ts_adj = 0;
+ if ((ts_algo->calib_counter >= INV_TIME_CALIB_THRESHOLD_1) &&
+ (!ts_algo->resume_flag)) {
+ if (st->sensor[i].ts < thresh2)
+ st->sensor[i].ts_adj = thresh2 - st->sensor[i].ts;
+ } else if ((ts_algo->calib_counter >=
+ INV_TIME_CALIB_THRESHOLD_1) && ts_algo->resume_flag) {
+ if (st->sensor[i].ts < thresh2)
+ st->sensor[i].ts = ts_algo->last_run_time -
+ elaps_time - JITTER_THRESH;
+ } else {
+ st->sensor[i].ts = ts_algo->last_run_time - elaps_time -
+ JITTER_THRESH;
+ st->sensor[i].previous_ts = st->sensor[i].ts;
+ }
+
+ if (st->sensor[i].ts > thresh1)
+ st->sensor[i].ts_adj = thresh1 - st->sensor[i].ts;
+ pr_debug("cali=%d\n", st->ts_algo.calib_counter);
+ pr_debug("adj= %lld\n", st->sensor[i].ts_adj);
+ pr_debug("dur= %d count= %d last= %lld\n", st->sensor[i].dur,
+ st->sensor[i].count, ts_algo->last_run_time);
+ if (st->sensor[i].ts_adj && (st->sensor[i].count > 1))
+ st->sensor[i].ts_adj = div_s64(st->sensor[i].ts_adj,
+ st->sensor[i].count);
+}
+/* inv_bound_timestamp (struct inv_mpu_state *st)
+ The purpose this function is to give a generic bound to each
+ sensor timestamp. The timestamp cannot exceed current time.
+ The timestamp cannot backwards one sample time either, otherwise, there
+ would be another sample in between. Using this principle, we can bound
+ the sensor samples */
+int inv_bound_timestamp(struct inv_mpu_state *st)
+{
+ int i;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ if (st->sensor[i].count) {
+ process_sensor_bounding(st, i);
+ } else if (ts_algo->calib_counter <
+ INV_TIME_CALIB_THRESHOLD_1) {
+ st->sensor[i].ts = ts_algo->reset_ts;
+ st->sensor[i].previous_ts = st->sensor[i].ts;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_mpu/inv_test/Kconfig b/drivers/iio/imu/inv_mpu/inv_test/Kconfig
new file mode 100644
index 000000000000..a4dfd95db886
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/Kconfig
@@ -0,0 +1,13 @@
+#
+# Kconfig for Invensense IIO testing hooks
+#
+
+config INV_TESTING
+ boolean "Invensense IIO testing hooks"
+ depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO
+ default n
+ help
+ This flag enables display of additional testing information from the
+ Invensense IIO drivers
+ It also enables the I2C counters facility to perform IO profiling.
+ Some additional sysfs entries will appear when this flag is enabled.
diff --git a/drivers/iio/imu/inv_mpu/inv_test/Makefile b/drivers/iio/imu/inv_mpu/inv_test/Makefile
new file mode 100644
index 000000000000..4f0edd3de901
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense IIO testing hooks.
+#
+
+obj-$(CONFIG_INV_TESTING) += inv_counters.o
+
diff --git a/drivers/iio/imu/inv_mpu/inv_test/inv_counters.c b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.c
new file mode 100644
index 000000000000..f60337caeeed
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kdev_t.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include "inv_counters.h"
+
+static int mpu_irq;
+static int accel_irq;
+static int compass_irq;
+
+struct inv_counters {
+ uint32_t i2c_tempreads;
+ uint32_t i2c_mpureads;
+ uint32_t i2c_mpuwrites;
+ uint32_t i2c_accelreads;
+ uint32_t i2c_accelwrites;
+ uint32_t i2c_compassreads;
+ uint32_t i2c_compasswrites;
+ uint32_t i2c_compassirq;
+ uint32_t i2c_accelirq;
+};
+
+static struct inv_counters Counters;
+
+static ssize_t i2c_counters_show(struct class *cls,
+ struct class_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
+ jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
+ mpu_irq ? kstat_irqs(mpu_irq) : 0,
+ Counters.i2c_tempreads,
+ Counters.i2c_mpureads, Counters.i2c_mpuwrites,
+ accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
+ Counters.i2c_accelreads, Counters.i2c_accelwrites,
+ compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
+ Counters.i2c_compassreads, Counters.i2c_compasswrites);
+}
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
+{
+ switch (type) {
+ case IRQ_MPU:
+ mpu_irq = irq;
+ break;
+ case IRQ_ACCEL:
+ accel_irq = irq;
+ break;
+ case IRQ_COMPASS:
+ compass_irq = irq;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq);
+
+void inv_iio_counters_tempread(int count)
+{
+ Counters.i2c_tempreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
+
+void inv_iio_counters_mpuread(int count)
+{
+ Counters.i2c_mpureads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
+
+void inv_iio_counters_mpuwrite(int count)
+{
+ Counters.i2c_mpuwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
+
+void inv_iio_counters_accelread(int count)
+{
+ Counters.i2c_accelreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
+
+void inv_iio_counters_accelwrite(int count)
+{
+ Counters.i2c_accelwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
+
+void inv_iio_counters_compassread(int count)
+{
+ Counters.i2c_compassreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
+
+void inv_iio_counters_compasswrite(int count)
+{
+ Counters.i2c_compasswrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
+
+void inv_iio_counters_compassirq(void)
+{
+ Counters.i2c_compassirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
+
+void inv_iio_counters_accelirq(void)
+{
+ Counters.i2c_accelirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
+
+static struct class_attribute inv_class_attr[] = {
+ __ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
+ __ATTR_NULL
+};
+
+static struct class inv_counters_class = {
+ .name = "inv_counters",
+ .owner = THIS_MODULE,
+ .class_attrs = (struct class_attribute *) &inv_class_attr
+};
+
+static int __init inv_counters_init(void)
+{
+ memset(&Counters, 0, sizeof(Counters));
+
+ return class_register(&inv_counters_class);
+}
+
+static void __exit inv_counters_exit(void)
+{
+ class_unregister(&inv_counters_class);
+}
+
+module_init(inv_counters_init);
+module_exit(inv_counters_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GESL");
+MODULE_DESCRIPTION("inv_counters debug support");
+
diff --git a/drivers/iio/imu/inv_mpu/inv_test/inv_counters.h b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.h
new file mode 100644
index 000000000000..62f76279e703
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_INV_TESTING
+
+enum irqtype {
+ IRQ_MPU,
+ IRQ_ACCEL,
+ IRQ_COMPASS
+};
+
+#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
index bfccb06407f7..f135cfcd6ccd 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
@@ -2838,7 +2838,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev,
msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_reset_framedrop(vfe_dev, stream_info);
rc = msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
if (rc < 0) {
pr_err("%s: No buffer for stream%d\n", __func__,
@@ -3586,6 +3585,11 @@ int msm_isp_axi_output_cfg(struct vfe_device *vfe_dev, void *arg)
pstream_info, plane_idx);
}
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
+ vfe_dev->vfe_base, pstream_info,
+ pCmd->output_path_cfg[axi_src_idx].framedrop_pattern,
+ pCmd->output_path_cfg[axi_src_idx].framedrop_period);
+
if (axi_src_idx <= PIX_ENCODER && axi_src_idx <= IDEAL_RAW) {
if (axi_src_idx == CAMIF_RAW) {
vfe_dev->axi_data.src_info[VFE_PIX_0].
@@ -3626,6 +3630,29 @@ int msm_isp_axi_output_cfg(struct vfe_device *vfe_dev, void *arg)
return rc;
}
+void msm_isp_framedrop_update(struct vfe_device *vfe_dev, void *arg)
+{
+ struct msm_vfe_axi_framedrop_update *pCmd = arg;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *pstream_info;
+
+ pr_debug("%s: entry\n", __func__);
+
+ if (pCmd->stream_src < VFE_AXI_SRC_MAX) {
+
+ pstream_info = &axi_data->stream_info[pCmd->stream_src];
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
+ vfe_dev->vfe_base, pstream_info,
+ pCmd->framedrop_pattern,
+ pCmd->framedrop_period);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, SRC_TO_INTF(pstream_info->stream_src));
+ }
+
+ pr_debug("%s: exit\n", __func__);
+}
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
index 7babd750a05a..d695c4c0edf3 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
@@ -84,6 +84,7 @@ int msm_isp_axi_restart(struct vfe_device *vfe_dev,
int msm_isp_axi_output_cfg(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_framedrop_update(struct vfe_device *vfe_dev, void *arg);
void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
enum msm_vfe_input_src frame_src);
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_util.c
index 64a3c7cde26b..a9b6e5e6a861 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_util.c
@@ -1073,6 +1073,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
rc = msm_isp_camif_cfg(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
+ case VIDIOC_MSM_ISP_FRAMEDROP_UPDATE:
+ mutex_lock(&vfe_dev->core_mutex);
+ msm_isp_framedrop_update(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
case MSM_SD_NOTIFY_FREEZE:
vfe_dev->isp_sof_debug = 0;
vfe_dev->isp_raw0_debug = 0;
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index ce47780e5936..f0140e8bbe68 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -8729,6 +8729,7 @@ exit_unreg_chrdev_region:
static int qseecom_remove(struct platform_device *pdev)
{
struct qseecom_registered_kclient_list *kclient = NULL;
+ struct qseecom_registered_kclient_list *kclient_tmp = NULL;
unsigned long flags = 0;
int ret = 0;
int i;
@@ -8738,10 +8739,8 @@ static int qseecom_remove(struct platform_device *pdev)
atomic_set(&qseecom.qseecom_state, QSEECOM_STATE_NOT_READY);
spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
- list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
- list) {
- if (!kclient)
- goto exit_irqrestore;
+ list_for_each_entry_safe(kclient, kclient_tmp,
+ &qseecom.registered_kclient_list_head, list) {
/* Break the loop if client handle is NULL */
if (!kclient->handle)
@@ -8765,7 +8764,7 @@ exit_free_kc_handle:
kzfree(kclient->handle);
exit_free_kclient:
kzfree(kclient);
-exit_irqrestore:
+
spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
if (qseecom.qseos_version > QSEEE_VERSION_00)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 13e0df67d3b7..0747f22ce56c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3995,6 +3995,7 @@ cmdq_switch:
pr_err("%s: %s: mmc_blk_cmdq_switch failed: %d\n",
mmc_hostname(host), __func__, err);
ret = err;
+ goto out;
}
cmdq_unhalt:
err = mmc_cmdq_halt(host, false);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 9d68f01a2487..81a781c1f9d6 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -802,19 +802,23 @@ static int msm_init_cm_dll(struct sdhci_host *host)
| CORE_CK_OUT_EN), host->ioaddr +
msm_host_offset->CORE_DLL_CONFIG);
- wait_cnt = 50;
- /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
- while (!(readl_relaxed(host->ioaddr +
- msm_host_offset->CORE_DLL_STATUS) & CORE_DLL_LOCK)) {
- /* max. wait for 50us sec for LOCK bit to be set */
- if (--wait_cnt == 0) {
- pr_err("%s: %s: DLL failed to LOCK\n",
- mmc_hostname(mmc), __func__);
- rc = -ETIMEDOUT;
- goto out;
+ /* For hs400es mode, no need to wait for core dll lock */
+ if (!(msm_host->enhanced_strobe &&
+ mmc_card_strobe(msm_host->mmc->card))) {
+ wait_cnt = 50;
+ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
+ while (!(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_STATUS) & CORE_DLL_LOCK)) {
+ /* max. wait for 50us sec for LOCK bit to be set */
+ if (--wait_cnt == 0) {
+ pr_err("%s: %s: DLL failed to LOCK\n",
+ mmc_hostname(mmc), __func__);
+ rc = -ETIMEDOUT;
+ goto out;
+ }
+ /* wait for 1us before polling again */
+ udelay(1);
}
- /* wait for 1us before polling again */
- udelay(1);
}
out:
@@ -3167,7 +3171,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
| CORE_HC_SELECT_IN_EN), host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC);
}
- if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533) {
+ /* No need to check for DLL lock for HS400es mode */
+ if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533 &&
+ !((card && mmc_card_strobe(card) &&
+ msm_host->enhanced_strobe))) {
/*
* Poll on DLL_LOCK and DDR_DLL_LOCK bits in
* CORE_DLL_STATUS to be set. This should get set
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 826d449edbd2..e470183fc3b5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -2115,8 +2115,10 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys)
goto fail_dma_mapping;
}
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2130,8 +2132,10 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys)
return;
fail_sps_transfer:
+ spin_lock_bh(&sys->spinlock);
list_del(&rx_pkt->link);
rx_len_cached = --sys->len;
+ spin_unlock_bh(&sys->spinlock);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
@@ -2171,8 +2175,10 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
goto fail_dma_mapping;
}
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2185,9 +2191,11 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
return;
fail_sps_transfer:
+ spin_lock_bh(&sys->spinlock);
rx_len_cached = --sys->len;
list_del(&rx_pkt->link);
INIT_LIST_HEAD(&rx_pkt->link);
+ spin_unlock_bh(&sys->spinlock);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
@@ -2219,7 +2227,9 @@ static void ipa_fast_replenish_rx_cache(struct ipa_sys_context *sys)
}
rx_pkt = sys->repl.cache[curr];
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2278,6 +2288,7 @@ static void ipa_cleanup_rx(struct ipa_sys_context *sys)
u32 head;
u32 tail;
+ spin_lock_bh(&sys->spinlock);
list_for_each_entry_safe(rx_pkt, r,
&sys->head_desc_list, link) {
list_del(&rx_pkt->link);
@@ -2295,6 +2306,7 @@ static void ipa_cleanup_rx(struct ipa_sys_context *sys)
sys->free_skb(rx_pkt->data.skb);
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
}
+ spin_unlock_bh(&sys->spinlock);
if (sys->repl.cache) {
head = atomic_read(&sys->repl.head_idx);
@@ -2970,8 +2982,10 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
+ spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
return;
}
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
@@ -2979,6 +2993,7 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
link);
list_del(&rx_pkt_expected->link);
sys->len--;
+ spin_unlock_bh(&sys->spinlock);
if (size)
rx_pkt_expected->len = size;
rx_skb = rx_pkt_expected->data.skb;
@@ -2999,8 +3014,10 @@ static void ipa_wlan_wq_rx_common(struct ipa_sys_context *sys, u32 size)
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
+ spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
return;
}
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
@@ -3008,6 +3025,7 @@ static void ipa_wlan_wq_rx_common(struct ipa_sys_context *sys, u32 size)
link);
list_del(&rx_pkt_expected->link);
sys->len--;
+ spin_unlock_bh(&sys->spinlock);
if (size)
rx_pkt_expected->len = size;
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 2e9ff2afcba2..1974d6ee032b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -305,6 +305,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(pd_voltage_max),
POWER_SUPPLY_ATTR(pd_voltage_min),
POWER_SUPPLY_ATTR(sdp_current_max),
+ POWER_SUPPLY_ATTR(fcc_stepper_enable),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index e068bec8b85e..6d5308b3dd0b 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -40,12 +40,14 @@
#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER"
#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
+#define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER"
struct pl_data {
int pl_mode;
int slave_pct;
int taper_pct;
int slave_fcc_ua;
+ int main_fcc_ua;
int restricted_current;
bool restricted_charging_enabled;
struct votable *fcc_votable;
@@ -59,6 +61,7 @@ struct pl_data {
struct work_struct pl_disable_forever_work;
struct delayed_work pl_taper_work;
struct delayed_work pl_awake_work;
+ struct delayed_work fcc_step_update_work;
struct power_supply *main_psy;
struct power_supply *pl_psy;
struct power_supply *batt_psy;
@@ -66,6 +69,13 @@ struct pl_data {
int charge_type;
int total_settled_ua;
int pl_settled_ua;
+ int fcc_step_update;
+ int main_step_fcc_dir;
+ int main_step_fcc_count;
+ int main_step_fcc_residual;
+ int parallel_step_fcc_dir;
+ int parallel_step_fcc_count;
+ int parallel_step_fcc_residual;
struct class qcom_batt_class;
struct wakeup_source *pl_ws;
struct notifier_block nb;
@@ -380,6 +390,10 @@ done:
* FCC *
**********/
#define EFFICIENCY_PCT 80
+#define FCC_STEP_SIZE_UA 100000
+#define FCC_STEP_UPDATE_DELAY_MS 1000
+#define STEP_UP 1
+#define STEP_DOWN -1
static void get_fcc_split(struct pl_data *chip, int total_ua,
int *master_ua, int *slave_ua)
{
@@ -432,6 +446,43 @@ static void get_fcc_split(struct pl_data *chip, int total_ua,
*slave_ua = (*slave_ua * chip->taper_pct) / 100;
}
+static void get_fcc_step_update_params(struct pl_data *chip, int main_fcc_ua,
+ int parallel_fcc_ua)
+{
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ /* Read current FCC of main charger */
+ rc = power_supply_get_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get main charger current fcc, rc=%d\n", rc);
+ return;
+ }
+ chip->main_fcc_ua = pval.intval;
+
+ chip->main_step_fcc_dir = (main_fcc_ua > pval.intval) ?
+ STEP_UP : STEP_DOWN;
+ chip->main_step_fcc_count = abs((main_fcc_ua - pval.intval) /
+ FCC_STEP_SIZE_UA);
+ chip->main_step_fcc_residual = (main_fcc_ua - pval.intval) %
+ FCC_STEP_SIZE_UA;
+
+ chip->parallel_step_fcc_dir = (parallel_fcc_ua > chip->slave_fcc_ua) ?
+ STEP_UP : STEP_DOWN;
+ chip->parallel_step_fcc_count = abs((parallel_fcc_ua -
+ chip->slave_fcc_ua) / FCC_STEP_SIZE_UA);
+ chip->parallel_step_fcc_residual = (parallel_fcc_ua -
+ chip->slave_fcc_ua) % FCC_STEP_SIZE_UA;
+
+ pr_debug("Main FCC Stepper parameters: main_step_direction: %d, main_step_count: %d, main_residual_fcc: %d\n",
+ chip->main_step_fcc_dir, chip->main_step_fcc_count,
+ chip->main_step_fcc_residual);
+ pr_debug("Parallel FCC Stepper parameters: parallel_step_direction: %d, parallel_step_count: %d, parallel_residual_fcc: %d\n",
+ chip->parallel_step_fcc_dir, chip->parallel_step_fcc_count,
+ chip->parallel_step_fcc_residual);
+}
+
static int pl_fcc_vote_callback(struct votable *votable, void *data,
int total_fcc_ua, const char *client)
{
@@ -445,80 +496,305 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data,
if (!chip->main_psy)
return 0;
+ if (!chip->batt_psy) {
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (!chip->batt_psy)
+ return 0;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't read FCC step update status, rc=%d\n",
+ rc);
+ return rc;
+ }
+ chip->fcc_step_update = pval.intval;
+ pr_debug("FCC Stepper %s\n",
+ pval.intval ? "enabled" : "disabled");
+ }
+
+ if (chip->fcc_step_update)
+ cancel_delayed_work_sync(&chip->fcc_step_update_work);
+
+
if (chip->pl_mode == POWER_SUPPLY_PL_NONE
|| get_effective_result_locked(chip->pl_disable_votable)) {
+ if (chip->fcc_step_update) {
+ vote(chip->pl_awake_votable, FCC_STEPPER_VOTER,
+ true, 0);
+ get_fcc_step_update_params(chip, total_fcc_ua, 0);
+ schedule_delayed_work(&chip->fcc_step_update_work, 0);
+
+ return 0;
+ }
pval.intval = total_fcc_ua;
rc = power_supply_set_property(chip->main_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0)
pr_err("Couldn't set main fcc, rc=%d\n", rc);
+
return rc;
}
if (chip->pl_mode != POWER_SUPPLY_PL_NONE) {
get_fcc_split(chip, total_fcc_ua,
&master_fcc_ua, &slave_fcc_ua);
+ if (chip->fcc_step_update) {
+ vote(chip->pl_awake_votable, FCC_STEPPER_VOTER,
+ true, 0);
+ get_fcc_step_update_params(chip, master_fcc_ua,
+ slave_fcc_ua);
+ schedule_delayed_work(&chip->fcc_step_update_work, 0);
+ } else {
+ /*
+ * If there is an increase in slave share
+ * (Also handles parallel enable case)
+ * Set Main ICL then slave FCC
+ * else
+ * (Also handles parallel disable case)
+ * Set slave ICL then main FCC.
+ */
+ if (slave_fcc_ua > chip->slave_fcc_ua) {
+ pval.intval = master_fcc_ua;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Could not set main fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
- /*
- * If there is an increase in slave share
- * (Also handles parallel enable case)
- * Set Main ICL then slave FCC
- * else
- * (Also handles parallel disable case)
- * Set slave ICL then main FCC.
- */
- if (slave_fcc_ua > chip->slave_fcc_ua) {
- pval.intval = master_fcc_ua;
- rc = power_supply_set_property(chip->main_psy,
+ pval.intval = slave_fcc_ua;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
- if (rc < 0) {
- pr_err("Could not set main fcc, rc=%d\n", rc);
- return rc;
+ if (rc < 0) {
+ pr_err("Couldn't set parallel fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ chip->slave_fcc_ua = slave_fcc_ua;
+ } else {
+ pval.intval = slave_fcc_ua;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set parallel fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ chip->slave_fcc_ua = slave_fcc_ua;
+
+ pval.intval = master_fcc_ua;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Could not set main fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
}
+ }
+ }
+
+ pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
+ master_fcc_ua, slave_fcc_ua,
+ (master_fcc_ua * 100) / total_fcc_ua,
+ (slave_fcc_ua * 100) / total_fcc_ua);
+
+ return 0;
+}
+
+static void fcc_step_update_work(struct work_struct *work)
+{
+ struct pl_data *chip = container_of(work,
+ struct pl_data, fcc_step_update_work.work);
+ union power_supply_propval pval = {0, };
+ int reschedule_ms = 0, rc = 0;
+ int main_fcc = chip->main_fcc_ua;
+ int parallel_fcc = chip->slave_fcc_ua;
+
+ if (!chip->usb_psy) {
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy) {
+ pr_err("Couldn't get usb psy\n");
+ return;
+ }
+ }
+
+ /* Check whether USB is present or not */
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get USB Present status, rc=%d\n", rc);
+ return;
+ }
+
+ /*
+ * If USB is not present, then disable parallel and
+ * Main FCC to the effective value of FCC votable and exit.
+ */
+ if (!pval.intval) {
+ /* Disable parallel */
+ parallel_fcc = 0;
+ pval.intval = 1;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0)
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+
+ main_fcc = get_effective_result_locked(chip->fcc_votable);
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set main charger fcc, rc=%d\n", rc);
+ return;
+ }
+
+ goto stepper_exit;
+ }
+
+ if (chip->main_step_fcc_count) {
+ main_fcc += (FCC_STEP_SIZE_UA * chip->main_step_fcc_dir);
+ chip->main_step_fcc_count--;
+ reschedule_ms = FCC_STEP_UPDATE_DELAY_MS;
+ } else if (chip->main_step_fcc_residual) {
+ main_fcc += chip->main_step_fcc_residual;
+ chip->main_step_fcc_residual = 0;
+ }
- pval.intval = slave_fcc_ua;
+ if (chip->parallel_step_fcc_count) {
+ parallel_fcc += (FCC_STEP_SIZE_UA *
+ chip->parallel_step_fcc_dir);
+ chip->parallel_step_fcc_count--;
+ reschedule_ms = FCC_STEP_UPDATE_DELAY_MS;
+ } else if (chip->parallel_step_fcc_residual) {
+ parallel_fcc += chip->parallel_step_fcc_residual;
+ chip->parallel_step_fcc_residual = 0;
+ }
+
+ if (chip->pl_mode == POWER_SUPPLY_PL_NONE ||
+ get_effective_result_locked(chip->pl_disable_votable)) {
+ /* Set Parallel FCC */
+ pval.intval = parallel_fcc;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set parallel charger fcc, rc=%d\n",
+ rc);
+ return;
+ }
+
+ /* Set main FCC */
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set main charger fcc, rc=%d\n", rc);
+ return;
+ }
+
+ if (parallel_fcc < MINIMUM_PARALLEL_FCC_UA) {
+ pval.intval = 1;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+ return;
+ }
+ }
+ } else {
+ if (parallel_fcc < chip->slave_fcc_ua) {
+ pval.intval = parallel_fcc;
rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0) {
- pr_err("Couldn't set parallel fcc, rc=%d\n",
- rc);
- return rc;
+ pr_err("Couldn't set parallel charger fcc, rc=%d\n",
+ rc);
+ return;
}
- chip->slave_fcc_ua = slave_fcc_ua;
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set main charger fcc, rc=%d\n",
+ rc);
+ return;
+ }
} else {
- pval.intval = slave_fcc_ua;
- rc = power_supply_set_property(chip->pl_psy,
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0) {
- pr_err("Couldn't set parallel fcc, rc=%d\n",
- rc);
- return rc;
+ pr_err("Couldn't set main charger fcc, rc=%d\n",
+ rc);
+ return;
}
- chip->slave_fcc_ua = slave_fcc_ua;
-
- pval.intval = master_fcc_ua;
- rc = power_supply_set_property(chip->main_psy,
+ pval.intval = parallel_fcc;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0) {
- pr_err("Could not set main fcc, rc=%d\n", rc);
- return rc;
+ pr_err("Couldn't set parallel charger fcc, rc=%d\n",
+ rc);
+ return;
+ }
+ }
+
+ rc = power_supply_get_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get slave suspend status, rc=%d\n",
+ rc);
+ return;
+ }
+
+ /*
+ * Enable parallel charger only if it was disabled earlier and
+ * configured slave fcc is greater than or equal to 100mA.
+ */
+ if (pval.intval == 1 && parallel_fcc >= 100000) {
+ pval.intval = 0;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+ return;
}
+
+ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) ||
+ (chip->pl_mode ==
+ POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ split_settled(chip);
}
}
- pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
- master_fcc_ua, slave_fcc_ua,
- (master_fcc_ua * 100) / total_fcc_ua,
- (slave_fcc_ua * 100) / total_fcc_ua);
+stepper_exit:
+ chip->main_fcc_ua = main_fcc;
+ chip->slave_fcc_ua = parallel_fcc;
- return 0;
+ if (reschedule_ms) {
+ schedule_delayed_work(&chip->fcc_step_update_work,
+ msecs_to_jiffies(reschedule_ms));
+ pr_debug("Rescheduling FCC_STEPPER work\n");
+ } else {
+ vote(chip->pl_awake_votable, FCC_STEPPER_VOTER, false, 0);
+ }
}
#define PARALLEL_FLOAT_VOLTAGE_DELTA_UV 50000
@@ -660,6 +936,9 @@ static int pl_disable_vote_callback(struct votable *votable,
chip->total_settled_ua = 0;
chip->pl_settled_ua = 0;
+ /* Cancel FCC step change work */
+ cancel_delayed_work_sync(&chip->fcc_step_update_work);
+
if (!pl_disable) { /* enable */
/* keep system awake to talk to slave charger through i2c */
cancel_delayed_work_sync(&chip->pl_awake_work);
@@ -685,16 +964,19 @@ static int pl_disable_vote_callback(struct votable *votable,
* PARALLEL_PSY_VOTER keeps it disabled unless a pl_psy
* is seen.
*/
- pval.intval = 0;
- rc = power_supply_set_property(chip->pl_psy,
+ if (!chip->fcc_step_update) {
+ pval.intval = 0;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
- if (rc < 0)
- pr_err("Couldn't change slave suspend state rc=%d\n",
- rc);
+ if (rc < 0)
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
- if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
- || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
- split_settled(chip);
+ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) ||
+ (chip->pl_mode ==
+ POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ split_settled(chip);
+ }
/*
* we could have been enabled while in taper mode,
* start the taper work if so
@@ -715,15 +997,18 @@ static int pl_disable_vote_callback(struct votable *votable,
|| (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
split_settled(chip);
- /* pl_psy may be NULL while in the disable branch */
- if (chip->pl_psy) {
- pval.intval = 1;
- rc = power_supply_set_property(chip->pl_psy,
+ if (!chip->fcc_step_update) {
+ /* pl_psy may be NULL while in the disable branch */
+ if (chip->pl_psy) {
+ pval.intval = 1;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
- if (rc < 0)
- pr_err("Couldn't change slave suspend state rc=%d\n",
- rc);
+ if (rc < 0)
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+ }
}
+
rerun_election(chip->fcc_votable);
rerun_election(chip->fv_votable);
@@ -1118,6 +1403,7 @@ int qcom_batt_init(void)
INIT_DELAYED_WORK(&chip->pl_taper_work, pl_taper_work);
INIT_WORK(&chip->pl_disable_forever_work, pl_disable_forever_work);
INIT_DELAYED_WORK(&chip->pl_awake_work, pl_awake_work);
+ INIT_DELAYED_WORK(&chip->fcc_step_update_work, fcc_step_update_work);
rc = pl_register_notifier(chip);
if (rc < 0) {
@@ -1172,6 +1458,7 @@ void qcom_batt_deinit(void)
cancel_delayed_work_sync(&chip->pl_taper_work);
cancel_work_sync(&chip->pl_disable_forever_work);
cancel_delayed_work_sync(&chip->pl_awake_work);
+ cancel_delayed_work_sync(&chip->fcc_step_update_work);
power_supply_unreg_notifier(&chip->nb);
destroy_votable(chip->pl_enable_votable_indirect);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 02b1204789bf..8e57bf9d2c31 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -325,6 +325,9 @@ static int smb2_parse_dt(struct smb2 *chip)
if (rc < 0)
chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS;
+ chg->fcc_stepper_mode = of_property_read_bool(node,
+ "qcom,fcc-stepping-enable");
+
return 0;
}
@@ -941,6 +944,7 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_RERUN_AICL,
POWER_SUPPLY_PROP_DP_DM,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -1049,6 +1053,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
rc = smblib_get_prop_batt_charge_counter(chg, val);
break;
+ case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE:
+ val->intval = chg->fcc_stepper_mode;
+ break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 8eee480e4380..e96523a4d43e 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -3360,7 +3360,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
rc = smblib_request_dpdm(chg, true);
if (rc < 0)
smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
-
+ if (chg->fcc_stepper_mode)
+ vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0);
/* Schedule work to enable parallel charger */
vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
schedule_delayed_work(&chg->pl_enable_work,
@@ -3379,6 +3380,11 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
}
}
+ /* Force 1500mA FCC on removal */
+ if (chg->fcc_stepper_mode)
+ vote(chg->fcc_votable, FCC_STEPPER_VOTER,
+ true, 1500000);
+
rc = smblib_request_dpdm(chg, false);
if (rc < 0)
smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 0de99b9da7bd..4475ccc21a2a 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -67,6 +67,7 @@ enum print_reason {
#define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER"
#define WBC_VOTER "WBC_VOTER"
#define OV_VOTER "OV_VOTER"
+#define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -346,6 +347,7 @@ struct smb_charger {
u8 float_cfg;
bool use_extcon;
bool otg_present;
+ bool fcc_stepper_mode;
/* workaround flag */
u32 wa_flags;
diff --git a/drivers/soc/qcom/hab/Kconfig b/drivers/soc/qcom/hab/Kconfig
index 2e4f5114e29f..2e6126f3734e 100644
--- a/drivers/soc/qcom/hab/Kconfig
+++ b/drivers/soc/qcom/hab/Kconfig
@@ -5,3 +5,7 @@ config MSM_HAB
Required for drivers to use the HAB API to communicate with the host
OS.
+config MSM_AGL
+ bool "Enable built-in hab config"
+ help
+ Use built-in configuration to setup hab driver.
diff --git a/drivers/soc/qcom/hab/Makefile b/drivers/soc/qcom/hab/Makefile
index 0ad19931776c..945ae52de196 100644
--- a/drivers/soc/qcom/hab/Makefile
+++ b/drivers/soc/qcom/hab/Makefile
@@ -8,9 +8,24 @@ msm_hab-objs = \
hab_mimex.o \
hab_mem_linux.o \
hab_pipe.o \
- qvm_comm.o \
- hab_qvm.o \
hab_parser.o \
khab_test.o
-obj-$(CONFIG_MSM_HAB) += msm_hab.o
+ifdef CONFIG_GHS_VMM
+msm_hab_hyp-objs = \
+ ghs_comm.o \
+ hab_ghs.o
+
+ifndef CONFIG_MSM_AGL
+ccflags-y += -DHABMM_HC_VMID
+endif
+
+else
+ifdef CONFIG_MSM_GVM_QUIN
+msm_hab_hyp-objs = \
+ qvm_comm.o \
+ hab_qvm.o
+endif
+endif
+
+obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_hyp.o
diff --git a/drivers/soc/qcom/hab/ghs_comm.c b/drivers/soc/qcom/hab/ghs_comm.c
new file mode 100644
index 000000000000..825f33a23858
--- /dev/null
+++ b/drivers/soc/qcom/hab/ghs_comm.c
@@ -0,0 +1,141 @@
+/* Copyright (c) 2018, 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 "hab.h"
+#include "hab_ghs.h"
+
+int physical_channel_read(struct physical_channel *pchan,
+ void *payload,
+ size_t read_size)
+{
+ struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
+
+ /* size in header is only for payload excluding the header itself */
+ if (dev->read_size < read_size + sizeof(struct hab_header)) {
+ pr_warn("read %zd is less than requested %zd plus header %zd\n",
+ dev->read_size, read_size, sizeof(struct hab_header));
+ read_size = dev->read_size;
+ }
+
+ /* always skip the header */
+ memcpy(payload, (unsigned char *)dev->read_data +
+ sizeof(struct hab_header) + dev->read_offset, read_size);
+ dev->read_offset += read_size;
+
+ return read_size;
+}
+
+int physical_channel_send(struct physical_channel *pchan,
+ struct hab_header *header,
+ void *payload)
+{
+ int sizebytes = HAB_HEADER_GET_SIZE(*header);
+ struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
+ GIPC_Result result;
+ uint8_t *msg;
+
+ spin_lock_bh(&dev->io_lock);
+
+ result = GIPC_PrepareMessage(dev->endpoint, sizebytes+sizeof(*header),
+ (void **)&msg);
+ if (result == GIPC_Full) {
+ spin_unlock_bh(&dev->io_lock);
+ /* need to wait for space! */
+ pr_err("failed to reserve send msg for %zd bytes\n",
+ sizebytes+sizeof(*header));
+ return -EBUSY;
+ } else if (result != GIPC_Success) {
+ spin_unlock_bh(&dev->io_lock);
+ pr_err("failed to send due to error %d\n", result);
+ return -ENOMEM;
+ }
+
+ if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) {
+ struct timeval tv;
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)payload;
+
+ do_gettimeofday(&tv);
+ pstat->tx_sec = tv.tv_sec;
+ pstat->tx_usec = tv.tv_usec;
+ }
+
+ memcpy(msg, header, sizeof(*header));
+
+ if (sizebytes)
+ memcpy(msg+sizeof(*header), payload, sizebytes);
+
+ result = GIPC_IssueMessage(dev->endpoint, sizebytes+sizeof(*header),
+ header->id_type_size);
+ spin_unlock_bh(&dev->io_lock);
+ if (result != GIPC_Success) {
+ pr_err("send error %d, sz %zd, prot %x\n",
+ result, sizebytes+sizeof(*header),
+ header->id_type_size);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+void physical_channel_rx_dispatch(unsigned long physical_channel)
+{
+ struct hab_header header;
+ struct physical_channel *pchan =
+ (struct physical_channel *)physical_channel;
+ struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
+ GIPC_Result result;
+
+ uint32_t events;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pchan->rxbuf_lock, flags);
+ events = kgipc_dequeue_events(dev->endpoint);
+ spin_unlock_irqrestore(&pchan->rxbuf_lock, flags);
+
+ if (events & (GIPC_EVENT_RESET))
+ pr_err("hab gipc %s remote vmid %d RESET\n",
+ dev->name, pchan->vmid_remote);
+ if (events & (GIPC_EVENT_RESETINPROGRESS))
+ pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n",
+ dev->name, pchan->vmid_remote);
+
+ if (events & (GIPC_EVENT_RECEIVEREADY)) {
+ spin_lock_bh(&pchan->rxbuf_lock);
+ while (1) {
+ dev->read_size = 0;
+ dev->read_offset = 0;
+ result = GIPC_ReceiveMessage(dev->endpoint,
+ dev->read_data,
+ GIPC_RECV_BUFF_SIZE_BYTES,
+ &dev->read_size,
+ &header.id_type_size);
+
+ if (result == GIPC_Success || dev->read_size > 0) {
+ /* handle corrupted msg? */
+ hab_msg_recv(pchan, dev->read_data);
+ continue;
+ } else if (result == GIPC_Empty) {
+ /* no more pending msg */
+ break;
+ }
+ pr_err("recv unhandled result %d, size %zd\n",
+ result, dev->read_size);
+ break;
+ }
+ spin_unlock_bh(&pchan->rxbuf_lock);
+ }
+
+ if (events & (GIPC_EVENT_SENDREADY))
+ pr_debug("kgipc send ready\n");
+}
diff --git a/drivers/soc/qcom/hab/hab.c b/drivers/soc/qcom/hab/hab.c
index 52de57b766f2..48d61870f776 100644
--- a/drivers/soc/qcom/hab/hab.c
+++ b/drivers/soc/qcom/hab/hab.c
@@ -16,11 +16,13 @@
.name = __name__,\
.id = __id__,\
.pchannels = LIST_HEAD_INIT(hab_devices[__num__].pchannels),\
- .pchan_lock = __MUTEX_INITIALIZER(hab_devices[__num__].pchan_lock),\
+ .pchan_lock = __SPIN_LOCK_UNLOCKED(hab_devices[__num__].pchan_lock),\
.openq_list = LIST_HEAD_INIT(hab_devices[__num__].openq_list),\
.openlock = __SPIN_LOCK_UNLOCKED(&hab_devices[__num__].openlock)\
}
+static const char hab_info_str[] = "Change: 16239527 Revision: #65";
+
/*
* The following has to match habmm definitions, order does not matter if
* hab config does not care either. When hab config is not present, the default
@@ -54,6 +56,8 @@ static struct hab_device hab_devices[] = {
struct hab_driver hab_driver = {
.ndevices = ARRAY_SIZE(hab_devices),
.devp = hab_devices,
+ .uctx_list = LIST_HEAD_INIT(hab_driver.uctx_list),
+ .drvlock = __SPIN_LOCK_UNLOCKED(hab_driver.drvlock),
};
struct uhab_context *hab_ctx_alloc(int kernel)
@@ -77,6 +81,7 @@ struct uhab_context *hab_ctx_alloc(int kernel)
rwlock_init(&ctx->exp_lock);
rwlock_init(&ctx->ctx_lock);
+ INIT_LIST_HEAD(&ctx->pending_open);
kref_init(&ctx->refcount);
ctx->import_ctx = habmem_imp_hyp_open();
if (!ctx->import_ctx) {
@@ -86,14 +91,53 @@ struct uhab_context *hab_ctx_alloc(int kernel)
}
ctx->kernel = kernel;
+ spin_lock_bh(&hab_driver.drvlock);
+ list_add_tail(&ctx->node, &hab_driver.uctx_list);
+ hab_driver.ctx_cnt++;
+ ctx->lb_be = hab_driver.b_loopback_be; /* loopback only */
+ hab_driver.b_loopback_be = ~hab_driver.b_loopback_be; /* loopback only*/
+ spin_unlock_bh(&hab_driver.drvlock);
+ pr_debug("ctx %pK live %d loopback be %d\n",
+ ctx, hab_driver.ctx_cnt, ctx->lb_be);
+
return ctx;
}
+/* ctx can only be freed when all the vchan releases the refcnt */
void hab_ctx_free(struct kref *ref)
{
struct uhab_context *ctx =
container_of(ref, struct uhab_context, refcount);
struct hab_export_ack_recvd *ack_recvd, *tmp;
+ struct virtual_channel *vchan;
+ struct physical_channel *pchan;
+ int i;
+ struct uhab_context *ctxdel, *ctxtmp;
+ struct hab_open_node *node;
+ struct export_desc *exp, *exp_tmp;
+
+ /* garbage-collect exp/imp buffers */
+ write_lock(&ctx->exp_lock);
+ list_for_each_entry_safe(exp, exp_tmp, &ctx->exp_whse, node) {
+ list_del(&exp->node);
+ pr_debug("potential leak exp %d vcid %X recovered\n",
+ exp->export_id, exp->vcid_local);
+ habmem_hyp_revoke(exp->payload, exp->payload_count);
+ habmem_remove_export(exp);
+ }
+ write_unlock(&ctx->exp_lock);
+
+ spin_lock_bh(&ctx->imp_lock);
+ list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) {
+ list_del(&exp->node);
+ ctx->import_total--;
+ pr_debug("leaked imp %d vcid %X for ctx is collected total %d\n",
+ exp->export_id, exp->vcid_local,
+ ctx->import_total);
+ habmm_imp_hyp_unmap(ctx->import_ctx, exp, ctx->kernel);
+ kfree(exp);
+ }
+ spin_unlock_bh(&ctx->imp_lock);
habmem_imp_hyp_close(ctx->import_ctx, ctx->kernel);
@@ -102,9 +146,70 @@ void hab_ctx_free(struct kref *ref)
kfree(ack_recvd);
}
+ /* walk vchan list to find the leakage */
+ spin_lock_bh(&hab_driver.drvlock);
+ hab_driver.ctx_cnt--;
+ list_for_each_entry_safe(ctxdel, ctxtmp, &hab_driver.uctx_list, node) {
+ if (ctxdel == ctx)
+ list_del(&ctxdel->node);
+ }
+ spin_unlock_bh(&hab_driver.drvlock);
+ pr_debug("live ctx %d refcnt %d kernel %d close %d owner %d\n",
+ hab_driver.ctx_cnt, get_refcnt(ctx->refcount),
+ ctx->kernel, ctx->closing, ctx->owner);
+
+ /* check vchans in this ctx */
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry(vchan, &ctx->vchannels, node) {
+ pr_warn("leak vchan id %X cnt %X remote %d in ctx\n",
+ vchan->id, get_refcnt(vchan->refcount),
+ vchan->otherend_id);
+ }
+ write_unlock(&ctx->ctx_lock);
+
+ /* check pending open */
+ if (ctx->pending_cnt)
+ pr_warn("potential leak of pendin_open nodes %d\n",
+ ctx->pending_cnt);
+
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry(node, &ctx->pending_open, node) {
+ pr_warn("leak pending open vcid %X type %d subid %d openid %d\n",
+ node->request.xdata.vchan_id, node->request.type,
+ node->request.xdata.sub_id,
+ node->request.xdata.open_id);
+ }
+ write_unlock(&ctx->ctx_lock);
+
+ /* check vchans belong to this ctx in all hab/mmid devices */
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ struct hab_device *habdev = &hab_driver.devp[i];
+
+ spin_lock_bh(&habdev->pchan_lock);
+ list_for_each_entry(pchan, &habdev->pchannels, node) {
+
+ /* check vchan ctx owner */
+ write_lock(&pchan->vchans_lock);
+ list_for_each_entry(vchan, &pchan->vchannels, pnode) {
+ if (vchan->ctx == ctx) {
+ pr_warn("leak vcid %X cnt %d pchan %s local %d remote %d\n",
+ vchan->id,
+ get_refcnt(vchan->refcount),
+ pchan->name, pchan->vmid_local,
+ pchan->vmid_remote);
+ }
+ }
+ write_unlock(&pchan->vchans_lock);
+ }
+ spin_unlock_bh(&habdev->pchan_lock);
+ }
kfree(ctx);
}
+/*
+ * caller needs to call vchan_put() afterwards. this is used to refcnt
+ * the local ioctl access based on ctx
+ */
struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid,
struct uhab_context *ctx)
{
@@ -140,14 +245,14 @@ struct hab_device *find_hab_device(unsigned int mm_id)
* frontend backend
* send(INIT) wait(INIT)
* wait(INIT_ACK) send(INIT_ACK)
- * send(ACK) wait(ACK)
+ * send(INIT_DONE) wait(INIT_DONE)
*/
struct virtual_channel *frontend_open(struct uhab_context *ctx,
unsigned int mm_id,
int dom_id)
{
- int ret, open_id = 0;
+ int ret, ret2, open_id = 0;
struct physical_channel *pchan = NULL;
struct hab_device *dev;
struct virtual_channel *vchan = NULL;
@@ -155,6 +260,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
struct hab_open_request request;
struct hab_open_request *recv_request;
int sub_id = HAB_MMID_GET_MINOR(mm_id);
+ struct hab_open_node pending_open = { { 0 } };
dev = find_hab_device(mm_id);
if (dev == NULL) {
@@ -163,6 +269,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
goto err;
}
+ /* guest can find its own id */
pchan = hab_pchan_find_domid(dev, dom_id);
if (!pchan) {
pr_err("hab_pchan_find_domid failed: dom_id=%d\n", dom_id);
@@ -170,44 +277,83 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
goto err;
}
- vchan = hab_vchan_alloc(ctx, pchan);
+ open_id = atomic_inc_return(&open_id_counter);
+ vchan = hab_vchan_alloc(ctx, pchan, open_id);
if (!vchan) {
pr_err("vchan alloc failed\n");
ret = -ENOMEM;
goto err;
- }
+ } else
/* Send Init sequence */
- open_id = atomic_inc_return(&open_id_counter);
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT, pchan,
vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = HAB_API_VER;
ret = hab_open_request_send(&request);
if (ret) {
pr_err("hab_open_request_send failed: %d\n", ret);
goto err;
}
+ pending_open.request = request;
+
+ /* during wait app could be terminated */
+ hab_open_pending_enter(ctx, pchan, &pending_open);
+
/* Wait for Init-Ack sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_ACK, pchan,
0, sub_id, open_id);
+ /* wait forever */
ret = hab_open_listen(ctx, dev, &request, &recv_request, 0);
- if (ret || !recv_request) {
- pr_err("hab_open_listen failed: %d\n", ret);
+ if (!ret && recv_request && ((recv_request->xdata.ver_fe & 0xFFFF0000)
+ != (recv_request->xdata.ver_be & 0xFFFF0000))) {
+ /* version check */
+ pr_err("hab major version mismatch fe %X be %X on mmid %d\n",
+ recv_request->xdata.ver_fe,
+ recv_request->xdata.ver_be, mm_id);
+
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+ ret = -EPROTO;
+ goto err;
+ } else if (ret || !recv_request) {
+ pr_err("hab_open_listen failed: %d, send cancel vcid %x subid %d openid %d\n",
+ ret, vchan->id,
+ sub_id, open_id);
+ /* send cancel to BE due to FE's local close */
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_CANCEL,
+ pchan, vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = HAB_API_VER;
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send init_cancel failed %d on vcid %x\n", ret2,
+ vchan->id);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+
+ if (ret != -EINTR)
+ ret = -EINVAL;
goto err;
}
- vchan->otherend_id = recv_request->vchan_id;
- hab_open_request_free(recv_request);
+ /* remove pending open locally after good pairing */
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+
+ pr_debug("hab version match fe %X be %X on mmid %d\n",
+ recv_request->xdata.ver_fe, recv_request->xdata.ver_be,
+ mm_id);
- vchan->session_id = open_id;
- pr_debug("vchan->session_id:%d\n", vchan->session_id);
+ vchan->otherend_id = recv_request->xdata.vchan_id;
+ hab_open_request_free(recv_request);
/* Send Ack sequence */
- hab_open_request_init(&request, HAB_PAYLOAD_TYPE_ACK, pchan,
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_DONE, pchan,
0, sub_id, open_id);
+ request.xdata.ver_fe = HAB_API_VER;
ret = hab_open_request_send(&request);
- if (ret)
+ if (ret) {
+ pr_err("failed to send init-done vcid %x remote %x openid %d\n",
+ vchan->id, vchan->otherend_id, vchan->session_id);
goto err;
+ }
hab_pchan_put(pchan);
@@ -222,10 +368,10 @@ err:
}
struct virtual_channel *backend_listen(struct uhab_context *ctx,
- unsigned int mm_id)
+ unsigned int mm_id, int timeout)
{
- int ret;
- int open_id;
+ int ret, ret2;
+ int open_id, ver_fe;
int sub_id = HAB_MMID_GET_MINOR(mm_id);
struct physical_channel *pchan = NULL;
struct hab_device *dev;
@@ -233,6 +379,7 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
struct hab_open_request request;
struct hab_open_request *recv_request;
uint32_t otherend_vchan_id;
+ struct hab_open_node pending_open = { { 0 } };
dev = find_hab_device(mm_id);
if (dev == NULL) {
@@ -245,19 +392,50 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
/* Wait for Init sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT,
NULL, 0, sub_id, 0);
- ret = hab_open_listen(ctx, dev, &request, &recv_request, 0);
+ /* cancel should not happen at this moment */
+ ret = hab_open_listen(ctx, dev, &request, &recv_request,
+ timeout);
if (ret || !recv_request) {
- pr_err("hab_open_listen failed: %d\n", ret);
+ if (!ret && !recv_request)
+ ret = -EINVAL;
+ if (-EAGAIN == ret) {
+ ret = -ETIMEDOUT;
+ } else {
+ /* device is closed */
+ pr_err("open request wait failed ctx closing %d\n",
+ ctx->closing);
+ }
+ goto err;
+ } else if (!ret && recv_request &&
+ ((recv_request->xdata.ver_fe & 0xFFFF0000) !=
+ (HAB_API_VER & 0xFFFF0000))) {
+ int ret2;
+ /* version check */
+ pr_err("version mismatch fe %X be %X on mmid %d\n",
+ recv_request->xdata.ver_fe, HAB_API_VER, mm_id);
+ hab_open_request_init(&request,
+ HAB_PAYLOAD_TYPE_INIT_ACK,
+ NULL, 0, sub_id, recv_request->xdata.open_id);
+ request.xdata.ver_be = HAB_API_VER;
+ /* reply to allow FE to bail out */
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send FE version mismatch failed mmid %d sub %d\n",
+ mm_id, sub_id);
+ ret = -EPROTO;
goto err;
}
- otherend_vchan_id = recv_request->vchan_id;
- open_id = recv_request->open_id;
+ /* guest id from guest */
+ otherend_vchan_id = recv_request->xdata.vchan_id;
+ open_id = recv_request->xdata.open_id;
+ ver_fe = recv_request->xdata.ver_fe;
pchan = recv_request->pchan;
hab_pchan_get(pchan);
hab_open_request_free(recv_request);
+ recv_request = NULL;
- vchan = hab_vchan_alloc(ctx, pchan);
+ vchan = hab_vchan_alloc(ctx, pchan, open_id);
if (!vchan) {
ret = -ENOMEM;
goto err;
@@ -265,23 +443,64 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
vchan->otherend_id = otherend_vchan_id;
- vchan->session_id = open_id;
- pr_debug("vchan->session_id:%d\n", vchan->session_id);
-
/* Send Init-Ack sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_ACK,
pchan, vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = ver_fe; /* carry over */
+ request.xdata.ver_be = HAB_API_VER;
ret = hab_open_request_send(&request);
if (ret)
goto err;
+ pending_open.request = request;
+ /* wait only after init-ack is sent */
+ hab_open_pending_enter(ctx, pchan, &pending_open);
+
/* Wait for Ack sequence */
- hab_open_request_init(&request, HAB_PAYLOAD_TYPE_ACK,
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_DONE,
pchan, 0, sub_id, open_id);
- ret = hab_open_listen(ctx, dev, &request, &recv_request, 0);
-
- if (ret != -EAGAIN)
+ ret = hab_open_listen(ctx, dev, &request, &recv_request,
+ HAB_HS_TIMEOUT);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+ if (ret && recv_request &&
+ recv_request->type == HAB_PAYLOAD_TYPE_INIT_CANCEL) {
+ pr_err("listen cancelled vcid %x subid %d openid %d ret %d\n",
+ request.xdata.vchan_id, request.xdata.sub_id,
+ request.xdata.open_id, ret);
+
+ /* FE cancels this session.
+ * So BE has to cancel its too
+ */
+ hab_open_request_init(&request,
+ HAB_PAYLOAD_TYPE_INIT_CANCEL, pchan,
+ vchan->id, sub_id, open_id);
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send init_ack failed %d on vcid %x\n",
+ ret2, vchan->id);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+
+ ret = -ENODEV; /* open request cancelled remotely */
break;
+ } else if (ret != -EAGAIN) {
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+ break; /* received something. good case! */
+ }
+
+ /* stay in the loop retry */
+ pr_warn("retry open ret %d vcid %X remote %X sub %d open %d\n",
+ ret, vchan->id, vchan->otherend_id, sub_id, open_id);
+
+ /* retry path starting here. free previous vchan */
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_CANCEL,
+ pchan, vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = ver_fe;
+ request.xdata.ver_be = HAB_API_VER;
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send init_ack failed %d on vcid %x\n", ret2,
+ vchan->id);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
hab_vchan_put(vchan);
vchan = NULL;
@@ -290,7 +509,7 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
}
if (ret || !recv_request) {
- pr_err("backend_listen failed: %d\n", ret);
+ pr_err("backend mmid %d listen error %d\n", mm_id, ret);
ret = -EINVAL;
goto err;
}
@@ -299,7 +518,8 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
hab_pchan_put(pchan);
return vchan;
err:
- pr_err("listen on mmid %d failed\n", mm_id);
+ if (ret != -ETIMEDOUT)
+ pr_err("listen on mmid %d failed\n", mm_id);
if (vchan)
hab_vchan_put(vchan);
if (pchan)
@@ -318,8 +538,9 @@ long hab_vchan_send(struct uhab_context *ctx,
struct hab_header header = HAB_HEADER_INITIALIZER;
int nonblocking_flag = flags & HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING;
- if (sizebytes > HAB_MAX_MSG_SIZEBYTES) {
- pr_err("Message too large, %lu bytes\n", sizebytes);
+ if (sizebytes > HAB_HEADER_SIZE_MASK) {
+ pr_err("Message too large, %lu bytes, max is %d\n",
+ sizebytes, HAB_HEADER_SIZE_MASK);
return -EINVAL;
}
@@ -330,11 +551,17 @@ long hab_vchan_send(struct uhab_context *ctx,
}
HAB_HEADER_SET_SIZE(header, sizebytes);
- if (flags & HABMM_SOCKET_SEND_FLAGS_XING_VM_STAT)
+ if (flags & HABMM_SOCKET_SEND_FLAGS_XING_VM_STAT) {
HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_PROFILE);
- else
+ if (sizebytes < sizeof(struct habmm_xing_vm_stat)) {
+ pr_err("wrong profiling buffer size %zd, expect %zd\n",
+ sizebytes,
+ sizeof(struct habmm_xing_vm_stat));
+ return -EINVAL;
+ }
+ } else {
HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_MSG);
-
+ }
HAB_HEADER_SET_ID(header, vchan->otherend_id);
HAB_HEADER_SET_SESSION_ID(header, vchan->session_id);
@@ -347,8 +574,6 @@ long hab_vchan_send(struct uhab_context *ctx,
schedule();
}
-
-
err:
if (vchan)
hab_vchan_put(vchan);
@@ -403,23 +628,22 @@ bool hab_is_loopback(void)
int hab_vchan_open(struct uhab_context *ctx,
unsigned int mmid,
int32_t *vcid,
+ int32_t timeout,
uint32_t flags)
{
struct virtual_channel *vchan = NULL;
struct hab_device *dev;
- pr_debug("Open mmid=%d, loopback mode=%d, loopback num=%d\n",
- mmid, hab_driver.b_loopback, hab_driver.loopback_num);
+ pr_debug("Open mmid=%d, loopback mode=%d, loopback be ctx %d\n",
+ mmid, hab_driver.b_loopback, ctx->lb_be);
if (!vcid)
return -EINVAL;
if (hab_is_loopback()) {
- if (!hab_driver.loopback_num) {
- hab_driver.loopback_num = 1;
- vchan = backend_listen(ctx, mmid);
+ if (ctx->lb_be) {
+ vchan = backend_listen(ctx, mmid, timeout);
} else {
- hab_driver.loopback_num = 0;
vchan = frontend_open(ctx, mmid, LOOPBACK_DOM);
}
} else {
@@ -427,28 +651,37 @@ int hab_vchan_open(struct uhab_context *ctx,
if (dev) {
struct physical_channel *pchan =
- hab_pchan_find_domid(dev, HABCFG_VMID_DONT_CARE);
-
- if (pchan->is_be)
- vchan = backend_listen(ctx, mmid);
- else
- vchan = frontend_open(ctx, mmid,
- HABCFG_VMID_DONT_CARE);
+ hab_pchan_find_domid(dev,
+ HABCFG_VMID_DONT_CARE);
+ if (pchan) {
+ if (pchan->is_be)
+ vchan = backend_listen(ctx, mmid,
+ timeout);
+ else
+ vchan = frontend_open(ctx, mmid,
+ HABCFG_VMID_DONT_CARE);
+ } else {
+ pr_err("open on nonexistent pchan (mmid %x)",
+ mmid);
+ return -ENODEV;
+ }
} else {
pr_err("failed to find device, mmid %d\n", mmid);
}
}
if (IS_ERR(vchan)) {
- pr_err("vchan open failed over mmid=%d\n", mmid);
+ if (-ETIMEDOUT != PTR_ERR(vchan) && -EAGAIN != PTR_ERR(vchan))
+ pr_err("vchan open failed mmid=%d\n", mmid);
return PTR_ERR(vchan);
}
- pr_debug("vchan id %x, remote id %x\n",
- vchan->id, vchan->otherend_id);
+ pr_debug("vchan id %x remote id %x session %d\n", vchan->id,
+ vchan->otherend_id, vchan->session_id);
write_lock(&ctx->ctx_lock);
list_add_tail(&vchan->node, &ctx->vchannels);
+ ctx->vcnt++;
write_unlock(&ctx->ctx_lock);
*vcid = vchan->id;
@@ -469,17 +702,6 @@ void hab_send_close_msg(struct virtual_channel *vchan)
}
}
-static void hab_vchan_close_impl(struct kref *ref)
-{
- struct virtual_channel *vchan =
- container_of(ref, struct virtual_channel, usagecnt);
-
- list_del(&vchan->node);
- hab_vchan_stop_notify(vchan);
- hab_vchan_put(vchan);
-}
-
-
void hab_vchan_close(struct uhab_context *ctx, int32_t vcid)
{
struct virtual_channel *vchan, *tmp;
@@ -490,11 +712,29 @@ void hab_vchan_close(struct uhab_context *ctx, int32_t vcid)
write_lock(&ctx->ctx_lock);
list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
if (vchan->id == vcid) {
- kref_put(&vchan->usagecnt, hab_vchan_close_impl);
+ write_unlock(&ctx->ctx_lock);
+ pr_debug("vcid %x remote %x session %d refcnt %d\n",
+ vchan->id, vchan->otherend_id,
+ vchan->session_id, get_refcnt(vchan->refcount));
+ /*
+ * only set when vc close is called locally by user
+ * explicity. Used to block remote msg. if forked once
+ * before, this local close is skipped due to child
+ * usage. if forked but not closed locally, the local
+ * context could NOT be closed, vchan can be prolonged
+ * by arrived remote msgs
+ */
+ if (vchan->forked)
+ vchan->forked = 0;
+ else {
+ vchan->closed = 1;
+ hab_vchan_stop_notify(vchan);
+ }
+ hab_vchan_put(vchan); /* there is a lock inside */
+ write_lock(&ctx->ctx_lock);
break;
}
}
-
write_unlock(&ctx->ctx_lock);
}
@@ -511,7 +751,7 @@ static int hab_initialize_pchan_entry(struct hab_device *mmid_device,
char pchan_name[MAX_VMID_NAME_SIZE];
struct physical_channel *pchan = NULL;
int ret;
- int vmid = is_be ? vmid_remote : vmid_local;
+ int vmid = is_be ? vmid_remote : vmid_local; /* used for naming only */
if (!mmid_device) {
pr_err("habdev %pK, vmid local %d, remote %d, is be %d\n",
@@ -541,7 +781,11 @@ static int hab_initialize_pchan_entry(struct hab_device *mmid_device,
return ret;
}
-static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
+/*
+ * generate pchan list based on hab settings table.
+ * return status 0: success, otherwise failure
+ */
+static int hab_generate_pchan(struct local_vmid *settings, int i, int j)
{
int k, ret = 0;
@@ -657,6 +901,7 @@ static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
break;
}
+ return ret;
}
/*
@@ -665,7 +910,7 @@ static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
*/
static int hab_generate_pchan_list(struct local_vmid *settings)
{
- int i, j;
+ int i, j, ret = 0;
/* scan by valid VMs, then mmid */
pr_debug("self vmid is %d\n", settings->self);
@@ -677,24 +922,34 @@ static int hab_generate_pchan_list(struct local_vmid *settings)
for (j = 1; j <= HABCFG_MMID_AREA_MAX; j++) {
if (HABCFG_GET_MMID(settings, i, j)
!= HABCFG_VMID_INVALID)
- hab_generate_pchan(settings, i, j);
+ ret = hab_generate_pchan(settings,
+ i, j);
}
}
}
-
- return 0;
+ return ret;
}
/*
* This function checks hypervisor plug-in readiness, read in hab configs,
* and configure pchans
*/
+#ifdef HABMM_HC_VMID
+#define DEFAULT_GVMID 3
+#else
+#define DEFAULT_GVMID 2
+#endif
+
int do_hab_parse(void)
{
int result;
int i;
struct hab_device *device;
- int pchan_total = 0;
+
+ /* single GVM is 2, multigvm is 2 or 3. GHS LV-GVM 2, LA-GVM 3 */
+ int default_gvmid = DEFAULT_GVMID;
+
+ pr_debug("hab parse starts for %s\n", hab_info_str);
/* first check if hypervisor plug-in is ready */
result = hab_hypervisor_register();
@@ -703,7 +958,10 @@ int do_hab_parse(void)
return result;
}
- /* Initialize open Q before first pchan starts */
+ /*
+ * Initialize open Q before first pchan starts.
+ * Each is for one pchan list
+ */
for (i = 0; i < hab_driver.ndevices; i++) {
device = &hab_driver.devp[i];
init_waitqueue_head(&device->openq);
@@ -712,12 +970,12 @@ int do_hab_parse(void)
/* read in hab config and create pchans*/
memset(&hab_driver.settings, HABCFG_VMID_INVALID,
sizeof(hab_driver.settings));
-
result = hab_parse(&hab_driver.settings);
if (result) {
- pr_warn("hab_parse failed and use the default settings\n");
- fill_default_gvm_settings(&hab_driver.settings, 2,
- MM_AUD_START, MM_ID_MAX);
+ pr_err("hab config open failed, prepare default gvm %d settings\n",
+ default_gvmid);
+ fill_default_gvm_settings(&hab_driver.settings, default_gvmid,
+ MM_AUD_START, MM_ID_MAX);
}
/* now generate hab pchan list */
@@ -725,6 +983,7 @@ int do_hab_parse(void)
if (result) {
pr_err("generate pchan list failed, ret %d\n", result);
} else {
+ int pchan_total = 0;
for (i = 0; i < hab_driver.ndevices; i++) {
device = &hab_driver.devp[i];
pchan_total += device->pchan_cnt;
@@ -736,6 +995,48 @@ int do_hab_parse(void)
return result;
}
+int get_refcnt(struct kref ref)
+{
+ return ref.refcount.counter;
+}
+
+void hab_hypervisor_unregister_common(void)
+{
+ int status, i;
+ struct uhab_context *ctx;
+ struct virtual_channel *vchan;
+
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ struct hab_device *habdev = &hab_driver.devp[i];
+ struct physical_channel *pchan, *pchan_tmp;
+
+ list_for_each_entry_safe(pchan, pchan_tmp,
+ &habdev->pchannels, node) {
+ status = habhyp_commdev_dealloc(pchan);
+ if (status) {
+ pr_err("failed to free pchan %pK, i %d, ret %d\n",
+ pchan, i, status);
+ }
+ }
+ }
+
+ /* detect leaking uctx */
+ spin_lock_bh(&hab_driver.drvlock);
+ list_for_each_entry(ctx, &hab_driver.uctx_list, node) {
+ pr_warn("leaking ctx owner %d refcnt %d kernel %d\n",
+ ctx->owner, get_refcnt(ctx->refcount), ctx->kernel);
+ /* further check vchan leak */
+ read_lock(&ctx->ctx_lock);
+ list_for_each_entry(vchan, &ctx->vchannels, node) {
+ pr_warn("leaking vchan id %X remote %X refcnt %d\n",
+ vchan->id, vchan->otherend_id,
+ get_refcnt(vchan->refcount));
+ }
+ read_unlock(&ctx->ctx_lock);
+ }
+ spin_unlock_bh(&hab_driver.drvlock);
+}
+
static int hab_open(struct inode *inodep, struct file *filep)
{
int result = 0;
@@ -749,7 +1050,10 @@ static int hab_open(struct inode *inodep, struct file *filep)
return -ENOMEM;
}
+ ctx->owner = task_pid_nr(current);
filep->private_data = ctx;
+ pr_debug("ctx owner %d refcnt %d\n", ctx->owner,
+ get_refcnt(ctx->refcount));
return result;
}
@@ -758,25 +1062,50 @@ static int hab_release(struct inode *inodep, struct file *filep)
{
struct uhab_context *ctx = filep->private_data;
struct virtual_channel *vchan, *tmp;
+ struct hab_open_node *node;
if (!ctx)
return 0;
- pr_debug("inode %pK, filep %pK\n", inodep, filep);
+ pr_debug("inode %pK, filep %pK ctx %pK\n", inodep, filep, ctx);
write_lock(&ctx->ctx_lock);
-
+ /* notify remote side on vchan closing */
list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
- list_del(&vchan->node);
+ list_del(&vchan->node); /* vchan is not in this ctx anymore */
hab_vchan_stop_notify(vchan);
- hab_vchan_put(vchan);
+ write_unlock(&ctx->ctx_lock);
+ if (!vchan->closed) {
+ pr_warn("potential leak vc %pK %x remote %x session %d refcnt %d\n",
+ vchan, vchan->id, vchan->otherend_id,
+ vchan->session_id,
+ get_refcnt(vchan->refcount));
+ hab_vchan_put(vchan); /* there is a lock inside */
+ }
+ write_lock(&ctx->ctx_lock);
}
+ /* notify remote side on pending open */
+ list_for_each_entry(node, &ctx->pending_open, node) {
+ /* no touch to the list itself. it is allocated on the stack */
+ if (hab_open_cancel_notify(&node->request))
+ pr_err("failed to send open cancel vcid %x subid %d openid %d pchan %s\n",
+ node->request.xdata.vchan_id,
+ node->request.xdata.sub_id,
+ node->request.xdata.open_id,
+ node->request.pchan->habdev->name);
+ }
write_unlock(&ctx->ctx_lock);
hab_ctx_put(ctx);
filep->private_data = NULL;
+ /* ctx leak check */
+ if (get_refcnt(ctx->refcount))
+ pr_warn("pending ctx release owner %d refcnt %d total %d\n",
+ ctx->owner, get_refcnt(ctx->refcount),
+ hab_driver.ctx_cnt);
+
return 0;
}
@@ -809,7 +1138,9 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
case IOCTL_HAB_VC_OPEN:
open_param = (struct hab_open *)data;
ret = hab_vchan_open(ctx, open_param->mmid,
- &open_param->vcid, open_param->flags);
+ &open_param->vcid,
+ open_param->timeout,
+ open_param->flags);
break;
case IOCTL_HAB_VC_CLOSE:
close_param = (struct hab_close *)data;
@@ -858,6 +1189,9 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
recv_param->sizebytes = 0;
ret = -EFAULT;
}
+ } else if (ret && msg) {
+ pr_warn("vcid %X recv failed %d and msg is still of %zd bytes\n",
+ recv_param->vcid, (int)ret, msg->sizebytes);
}
if (msg)
@@ -879,22 +1213,22 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
info_param = (struct hab_info *)data;
if (!info_param->names || !info_param->namesize ||
info_param->namesize > sizeof(names)) {
- pr_err("wrong vm info vcid %X, names %llX, sz %d\n",
- info_param->vcid, info_param->names,
- info_param->namesize);
+ pr_err("wrong param for vm info vcid %X, names %llX, sz %d\n",
+ info_param->vcid, info_param->names,
+ info_param->namesize);
ret = -EINVAL;
break;
}
ret = hab_vchan_query(ctx, info_param->vcid,
(uint64_t *)&info_param->ids,
- names, info_param->namesize, 0);
+ names, info_param->namesize, 0);
if (!ret) {
if (copy_to_user((void __user *)info_param->names,
names,
info_param->namesize)) {
pr_err("copy_to_user failed: vc=%x size=%d\n",
- info_param->vcid,
- info_param->namesize*2);
+ info_param->vcid,
+ info_param->namesize*2);
info_param->namesize = 0;
ret = -EFAULT;
}
@@ -904,7 +1238,7 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
ret = -ENOIOCTLCMD;
}
- if (ret == 0 && _IOC_SIZE(cmd) && (cmd & IOC_OUT))
+ if (_IOC_SIZE(cmd) && (cmd & IOC_OUT))
if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) {
pr_err("copy_to_user failed: cmd=%x\n", cmd);
ret = -EFAULT;
@@ -955,6 +1289,26 @@ static const struct dma_map_ops hab_dma_ops = {
.unmap_sg = hab_unmap_sg,
};
+static int hab_power_down_callback(
+ struct notifier_block *nfb, unsigned long action, void *data)
+{
+
+ switch (action) {
+ case SYS_DOWN:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ pr_debug("reboot called %ld\n", action);
+ hab_hypervisor_unregister(); /* only for single VM guest */
+ break;
+ }
+ pr_debug("reboot called %ld done\n", action);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block hab_reboot_notifier = {
+ .notifier_call = hab_power_down_callback,
+};
+
static int __init hab_init(void)
{
int result;
@@ -997,6 +1351,10 @@ static int __init hab_init(void)
goto err;
}
+ result = register_reboot_notifier(&hab_reboot_notifier);
+ if (result)
+ pr_err("failed to register reboot notifier %d\n", result);
+
/* read in hab config, then configure pchans */
result = do_hab_parse();
@@ -1007,12 +1365,10 @@ static int __init hab_init(void)
result = -ENOMEM;
hab_hypervisor_unregister();
goto err;
- }
-
- set_dma_ops(hab_driver.dev, &hab_dma_ops);
-
- return result;
+ } else
+ set_dma_ops(hab_driver.dev, &hab_dma_ops);
}
+ return result;
err:
if (!IS_ERR_OR_NULL(hab_driver.dev))
@@ -1037,6 +1393,8 @@ static void __exit hab_exit(void)
class_destroy(hab_driver.class);
cdev_del(&hab_driver.cdev);
unregister_chrdev_region(dev, 1);
+ unregister_reboot_notifier(&hab_reboot_notifier);
+ pr_debug("hab exit called\n");
}
subsys_initcall(hab_init);
diff --git a/drivers/soc/qcom/hab/hab.h b/drivers/soc/qcom/hab/hab.h
index facb0a068221..d1aa88e3978e 100644
--- a/drivers/soc/qcom/hab/hab.h
+++ b/drivers/soc/qcom/hab/hab.h
@@ -16,7 +16,7 @@
#ifdef pr_fmt
#undef pr_fmt
#endif
-#define pr_fmt(fmt) "|hab:%s:%d|" fmt, __func__, __LINE__
+#define pr_fmt(fmt) "hab:%s:%d " fmt, __func__, __LINE__
#include <linux/types.h>
@@ -41,16 +41,19 @@
#include <linux/uaccess.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
+#include <linux/jiffies.h>
+#include <linux/reboot.h>
enum hab_payload_type {
HAB_PAYLOAD_TYPE_MSG = 0x0,
HAB_PAYLOAD_TYPE_INIT,
HAB_PAYLOAD_TYPE_INIT_ACK,
- HAB_PAYLOAD_TYPE_ACK,
+ HAB_PAYLOAD_TYPE_INIT_DONE,
HAB_PAYLOAD_TYPE_EXPORT,
HAB_PAYLOAD_TYPE_EXPORT_ACK,
HAB_PAYLOAD_TYPE_PROFILE,
HAB_PAYLOAD_TYPE_CLOSE,
+ HAB_PAYLOAD_TYPE_INIT_CANCEL,
HAB_PAYLOAD_TYPE_MAX,
};
#define LOOPBACK_DOM 0xFF
@@ -128,21 +131,21 @@ struct hab_header {
/* "Size" of the HAB_HEADER_ID and HAB_VCID_ID must match */
#define HAB_HEADER_SIZE_SHIFT 0
#define HAB_HEADER_TYPE_SHIFT 16
-#define HAB_HEADER_ID_SHIFT 24
+#define HAB_HEADER_ID_SHIFT 20
#define HAB_HEADER_SIZE_MASK 0x0000FFFF
-#define HAB_HEADER_TYPE_MASK 0x00FF0000
-#define HAB_HEADER_ID_MASK 0xFF000000
+#define HAB_HEADER_TYPE_MASK 0x000F0000
+#define HAB_HEADER_ID_MASK 0xFFF00000
#define HAB_HEADER_INITIALIZER {0}
#define HAB_MMID_GET_MAJOR(mmid) (mmid & 0xFFFF)
#define HAB_MMID_GET_MINOR(mmid) ((mmid>>16) & 0xFF)
#define HAB_VCID_ID_SHIFT 0
-#define HAB_VCID_DOMID_SHIFT 8
-#define HAB_VCID_MMID_SHIFT 16
-#define HAB_VCID_ID_MASK 0x000000FF
-#define HAB_VCID_DOMID_MASK 0x0000FF00
-#define HAB_VCID_MMID_MASK 0xFFFF0000
+#define HAB_VCID_DOMID_SHIFT 12
+#define HAB_VCID_MMID_SHIFT 20
+#define HAB_VCID_ID_MASK 0x00000FFF
+#define HAB_VCID_DOMID_MASK 0x000FF000
+#define HAB_VCID_MMID_MASK 0xFFF00000
#define HAB_VCID_GET_ID(vcid) \
(((vcid) & HAB_VCID_ID_MASK) >> HAB_VCID_ID_SHIFT)
@@ -182,12 +185,14 @@ struct hab_header {
#define HAB_HEADER_GET_SESSION_ID(header) ((header).session_id)
+#define HAB_HS_TIMEOUT (10*1000*1000)
+
struct physical_channel {
+ struct list_head node;
char name[MAX_VMID_NAME_SIZE];
int is_be;
struct kref refcount;
struct hab_device *habdev;
- struct list_head node;
struct idr vchan_idr;
spinlock_t vid_lock;
@@ -195,38 +200,44 @@ struct physical_channel {
spinlock_t expid_lock;
void *hyp_data;
- int dom_id;
- int vmid_local;
+ int dom_id; /* BE role: remote vmid; FE role: don't care */
+ int vmid_local; /* from DT or hab_config */
int vmid_remote;
- char vmname_local[12];
+ char vmname_local[12]; /* from DT */
char vmname_remote[12];
int closed;
spinlock_t rxbuf_lock;
- /* vchans over this pchan */
+ /* debug only */
+ uint32_t sequence_tx;
+ uint32_t sequence_rx;
+
+ /* vchans on this pchan */
struct list_head vchannels;
+ int vcnt;
rwlock_t vchans_lock;
};
-
+/* this payload has to be used together with type */
struct hab_open_send_data {
int vchan_id;
int sub_id;
int open_id;
+ int ver_fe;
+ int ver_be;
+ int reserved;
};
struct hab_open_request {
int type;
struct physical_channel *pchan;
- int vchan_id;
- int sub_id;
- int open_id;
+ struct hab_open_send_data xdata;
};
struct hab_open_node {
struct hab_open_request request;
struct list_head node;
- int age;
+ int64_t age; /* sec */
};
struct hab_export_ack {
@@ -247,20 +258,25 @@ struct hab_message {
uint32_t data[];
};
+/* for all the pchans of same kind */
struct hab_device {
char name[MAX_VMID_NAME_SIZE];
- unsigned int id;
+ uint32_t id;
struct list_head pchannels;
int pchan_cnt;
- struct mutex pchan_lock;
- struct list_head openq_list;
+ spinlock_t pchan_lock;
+ struct list_head openq_list; /* received */
spinlock_t openlock;
wait_queue_head_t openq;
+ int openq_cnt;
};
struct uhab_context {
+ struct list_head node; /* managed by the driver */
struct kref refcount;
+
struct list_head vchannels;
+ int vcnt;
struct list_head exp_whse;
uint32_t export_total;
@@ -276,9 +292,15 @@ struct uhab_context {
void *import_ctx;
+ struct list_head pending_open; /* sent to remote */
+ int pending_cnt;
+
rwlock_t ctx_lock;
int closing;
int kernel;
+ int owner;
+
+ int lb_be; /* loopback only */
};
/*
@@ -297,7 +319,7 @@ struct local_vmid {
};
struct hab_driver {
- struct device *dev;
+ struct device *dev; /* mmid dev list */
struct cdev cdev;
dev_t major;
struct class *class;
@@ -305,33 +327,30 @@ struct hab_driver {
struct hab_device *devp;
struct uhab_context *kctx;
+ struct list_head uctx_list;
+ int ctx_cnt;
+ spinlock_t drvlock;
+
struct local_vmid settings; /* parser results */
int b_server_dom;
- int loopback_num;
+ int b_loopback_be; /* only allow 2 apps simultaneously 1 fe 1 be */
int b_loopback;
void *hyp_priv; /* hypervisor plug-in storage */
};
struct virtual_channel {
- struct work_struct work;
/*
* refcount is used to track the references from hab core to the virtual
* channel such as references from physical channels,
* i.e. references from the "other" side
*/
struct kref refcount;
- /*
- * usagecnt is used to track the clients who are using this virtual
- * channel such as local clients, client sowftware etc,
- * i.e. references from "this" side
- */
- struct kref usagecnt;
struct physical_channel *pchan;
struct uhab_context *ctx;
- struct list_head node;
- struct list_head pnode;
+ struct list_head node; /* for ctx */
+ struct list_head pnode; /* for pchan */
struct list_head rx_list;
wait_queue_head_t rx_queue;
spinlock_t rx_lock;
@@ -339,6 +358,14 @@ struct virtual_channel {
int otherend_id;
int otherend_closed;
uint32_t session_id;
+
+ /*
+ * set when local close() is called explicitly. vchan could be
+ * used in hab-recv-msg() path (2) then close() is called (1).
+ * this is same case as close is not called and no msg path
+ */
+ int closed;
+ int forked; /* if fork is detected and assume only once */
};
/*
@@ -351,12 +378,15 @@ struct export_desc {
int readonly;
uint64_t import_index;
- struct virtual_channel *vchan;
+ struct virtual_channel *vchan; /* vchan could be freed earlier */
+ struct uhab_context *ctx;
+ struct physical_channel *pchan;
int32_t vcid_local;
int32_t vcid_remote;
int domid_local;
int domid_remote;
+ int flags;
struct list_head node;
void *kva;
@@ -365,7 +395,8 @@ struct export_desc {
} __packed;
int hab_vchan_open(struct uhab_context *ctx,
- unsigned int mmid, int32_t *vcid, uint32_t flags);
+ unsigned int mmid, int32_t *vcid,
+ int32_t timeout, uint32_t flags);
void hab_vchan_close(struct uhab_context *ctx,
int32_t vcid);
long hab_vchan_send(struct uhab_context *ctx,
@@ -401,13 +432,17 @@ int habmem_hyp_grant_user(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata);
+ void *ppdata,
+ int *compressed,
+ int *compressed_size);
int habmem_hyp_grant(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata);
+ void *ppdata,
+ int *compressed,
+ int *compressed_size);
int habmem_hyp_revoke(void *expdata, uint32_t count);
@@ -417,7 +452,7 @@ void habmem_imp_hyp_close(void *priv, int kernel);
int habmem_imp_hyp_map(void *imp_ctx, struct hab_import *param,
struct export_desc *exp, int kernel);
-int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp);
+int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp, int kernel);
int habmem_imp_hyp_mmap(struct file *flip, struct vm_area_struct *vma);
@@ -427,7 +462,7 @@ void hab_msg_free(struct hab_message *message);
int hab_msg_dequeue(struct virtual_channel *vchan,
struct hab_message **msg, int *rsize, unsigned int flags);
-void hab_msg_recv(struct physical_channel *pchan,
+int hab_msg_recv(struct physical_channel *pchan,
struct hab_header *header);
void hab_open_request_init(struct hab_open_request *request,
@@ -447,7 +482,7 @@ int hab_open_listen(struct uhab_context *ctx,
int ms_timeout);
struct virtual_channel *hab_vchan_alloc(struct uhab_context *ctx,
- struct physical_channel *pchan);
+ struct physical_channel *pchan, int openid);
struct virtual_channel *hab_vchan_get(struct physical_channel *pchan,
struct hab_header *header);
void hab_vchan_put(struct virtual_channel *vchan);
@@ -482,6 +517,7 @@ static inline void hab_ctx_put(struct uhab_context *ctx)
void hab_send_close_msg(struct virtual_channel *vchan);
int hab_hypervisor_register(void);
void hab_hypervisor_unregister(void);
+void hab_hypervisor_unregister_common(void);
int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
int vmid_remote, struct hab_device *mmid_device);
int habhyp_commdev_dealloc(void *commdev);
@@ -496,7 +532,7 @@ int physical_channel_send(struct physical_channel *pchan,
void physical_channel_rx_dispatch(unsigned long physical_channel);
-int loopback_pchan_create(char *dev_name);
+int loopback_pchan_create(struct hab_device *dev, char *pchan_name);
int hab_parse(struct local_vmid *settings);
@@ -512,6 +548,21 @@ int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,
struct hab_device *find_hab_device(unsigned int mm_id);
+int get_refcnt(struct kref ref);
+
+int hab_open_pending_enter(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending);
+
+int hab_open_pending_exit(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending);
+
+int hab_open_cancel_notify(struct hab_open_request *request);
+
+int hab_open_receive_cancel(struct physical_channel *pchan,
+ size_t sizebytes);
+
/* Global singleton HAB instance */
extern struct hab_driver hab_driver;
diff --git a/drivers/soc/qcom/hab/hab_ghs.c b/drivers/soc/qcom/hab/hab_ghs.c
new file mode 100644
index 000000000000..11fd6bb92a90
--- /dev/null
+++ b/drivers/soc/qcom/hab/hab_ghs.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2018, 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 "hab.h"
+#include "hab_ghs.h"
+
+static const char * const dt_gipc_path_name[] = {
+ "testgipc1",
+ "testgipc2",
+ "testgipc3",
+ "testgipc4",
+ "testgipc5",
+ "testgipc6",
+ "testgipc7",
+ "testgipc8",
+ "testgipc9",
+ "testgipc10",
+ "testgipc11",
+ "testgipc12",
+ "testgipc13",
+ "testgipc14",
+ "testgipc15",
+ "testgipc16",
+ "testgipc17",
+ "testgipc18",
+ "testgipc19",
+ "testgipc20",
+ "testgipc21",
+ "testgipc22",
+};
+
+static struct ghs_vmm_plugin_info_s {
+ const char **dt_name;
+ int curr;
+ int probe_cnt;
+} ghs_vmm_plugin_info = {
+ dt_gipc_path_name,
+ 0,
+ ARRAY_SIZE(dt_gipc_path_name),
+};
+
+static void ghs_irq_handler(void *cookie)
+{
+ struct physical_channel *pchan = cookie;
+ struct ghs_vdev *dev =
+ (struct ghs_vdev *) (pchan ? pchan->hyp_data : NULL);
+
+ if (dev)
+ tasklet_schedule(&dev->task);
+}
+
+/* static struct physical_channel *habhyp_commdev_alloc(int id) */
+int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote,
+ struct hab_device *mmid_device)
+{
+ struct ghs_vdev *dev = NULL;
+ struct physical_channel *pchan = NULL;
+ struct physical_channel **ppchan = (struct physical_channel **)commdev;
+ int ret = 0;
+
+ if (ghs_vmm_plugin_info.curr > ghs_vmm_plugin_info.probe_cnt) {
+ pr_err("too many commdev alloc %d, supported is %d\n",
+ ghs_vmm_plugin_info.curr,
+ ghs_vmm_plugin_info.probe_cnt);
+ ret = -ENOENT;
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ pr_err("allocate struct ghs_vdev failed %zu bytes on pchan %s\n",
+ sizeof(*dev), name);
+ goto err;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+ spin_lock_init(&dev->io_lock);
+
+ /*
+ * TODO: ExtractEndpoint is in ghs_comm.c because it blocks.
+ * Extrace and Request should be in roughly the same spot
+ */
+ if (is_be) {
+ /* role is backend */
+ dev->be = 1;
+ } else {
+ /* role is FE */
+ struct device_node *gvh_dn;
+
+ gvh_dn = of_find_node_by_path("/aliases");
+ if (gvh_dn) {
+ const char *ep_path = NULL;
+ struct device_node *ep_dn;
+
+ ret = of_property_read_string(gvh_dn,
+ ghs_vmm_plugin_info.dt_name[ghs_vmm_plugin_info.curr],
+ &ep_path);
+ if (ret)
+ pr_err("failed to read endpoint string ret %d\n",
+ ret);
+ of_node_put(gvh_dn);
+
+ ep_dn = of_find_node_by_path(ep_path);
+ if (ep_dn) {
+ dev->endpoint = kgipc_endpoint_alloc(ep_dn);
+ of_node_put(ep_dn);
+ if (IS_ERR(dev->endpoint)) {
+ ret = PTR_ERR(dev->endpoint);
+ pr_err("KGIPC alloc failed id: %d, ret: %d\n",
+ ghs_vmm_plugin_info.curr, ret);
+ goto err;
+ } else {
+ pr_debug("gipc ep found for %d\n",
+ ghs_vmm_plugin_info.curr);
+ }
+ } else {
+ pr_err("of_parse_phandle failed id: %d\n",
+ ghs_vmm_plugin_info.curr);
+ ret = -ENOENT;
+ goto err;
+ }
+ } else {
+ pr_err("of_find_compatible_node failed id: %d\n",
+ ghs_vmm_plugin_info.curr);
+ ret = -ENOENT;
+ goto err;
+ }
+ }
+ /* add pchan into the mmid_device list */
+ pchan = hab_pchan_alloc(mmid_device, vmid_remote);
+ if (!pchan) {
+ pr_err("hab_pchan_alloc failed for %s, cnt %d\n",
+ mmid_device->name, mmid_device->pchan_cnt);
+ ret = -ENOMEM;
+ goto err;
+ }
+ pchan->closed = 0;
+ pchan->hyp_data = (void *)dev;
+ pchan->is_be = is_be;
+ strlcpy(dev->name, name, sizeof(dev->name));
+ *ppchan = pchan;
+ dev->read_data = kmalloc(GIPC_RECV_BUFF_SIZE_BYTES, GFP_KERNEL);
+ if (!dev->read_data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ tasklet_init(&dev->task, physical_channel_rx_dispatch,
+ (unsigned long) pchan);
+
+ ret = kgipc_endpoint_start_with_irq_callback(dev->endpoint,
+ ghs_irq_handler,
+ pchan);
+ if (ret) {
+ pr_err("irq alloc failed id: %d %s, ret: %d\n",
+ ghs_vmm_plugin_info.curr, name, ret);
+ goto err;
+ } else
+ pr_debug("ep irq handler started for %d %s, ret %d\n",
+ ghs_vmm_plugin_info.curr, name, ret);
+ /* this value could be more than devp total */
+ ghs_vmm_plugin_info.curr++;
+ return 0;
+err:
+ hab_pchan_put(pchan);
+ kfree(dev);
+ return ret;
+}
+
+int habhyp_commdev_dealloc(void *commdev)
+{
+ struct physical_channel *pchan = (struct physical_channel *)commdev;
+ struct ghs_vdev *dev = pchan->hyp_data;
+
+ kgipc_endpoint_free(dev->endpoint);
+ kfree(dev->read_data);
+ kfree(dev);
+
+ if (get_refcnt(pchan->refcount) > 1) {
+ pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
+ pchan->name, pchan->vcnt, get_refcnt(pchan->refcount));
+ }
+ hab_pchan_put(pchan);
+ return 0;
+}
+
+void hab_hypervisor_unregister(void)
+{
+ pr_debug("total %d\n", hab_driver.ndevices);
+
+ hab_hypervisor_unregister_common();
+
+ ghs_vmm_plugin_info.curr = 0;
+}
+
+int hab_hypervisor_register(void)
+{
+ int ret = 0;
+
+ hab_driver.b_server_dom = 0;
+
+ return ret;
+}
diff --git a/drivers/soc/qcom/hab/hab_ghs.h b/drivers/soc/qcom/hab/hab_ghs.h
new file mode 100644
index 000000000000..54812480ebaa
--- /dev/null
+++ b/drivers/soc/qcom/hab/hab_ghs.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __HAB_GHS_H
+#define __HAB_GHS_H
+
+#include <ghs_vmm/kgipc.h>
+#define GIPC_RECV_BUFF_SIZE_BYTES (32*1024)
+
+struct ghs_vdev {
+ int be;
+ void *read_data; /* buffer to receive from gipc */
+ size_t read_size;
+ int read_offset;
+ GIPC_Endpoint endpoint;
+ spinlock_t io_lock;
+ char name[32];
+ struct tasklet_struct task;
+};
+#endif /* __HAB_GHS_H */
diff --git a/drivers/soc/qcom/hab/hab_mem_linux.c b/drivers/soc/qcom/hab/hab_mem_linux.c
index a779067ee4c4..74ee88a037af 100644
--- a/drivers/soc/qcom/hab/hab_mem_linux.c
+++ b/drivers/soc/qcom/hab/hab_mem_linux.c
@@ -82,8 +82,6 @@ static int habmem_get_dma_pages_from_va(unsigned long address,
goto err;
}
- pr_debug("vma flags %lx\n", vma->vm_flags);
-
/* Look for the fd that matches this the vma file */
fd = iterate_fd(current->files, 0, match_file, vma->vm_file);
if (fd == 0) {
@@ -111,7 +109,6 @@ static int habmem_get_dma_pages_from_va(unsigned long address,
for_each_sg(sg_table->sgl, s, sg_table->nents, i) {
page = sg_page(s);
- pr_debug("sgl length %d\n", s->length);
for (j = page_offset; j < (s->length >> PAGE_SHIFT); j++) {
pages[rc] = nth_page(page, j);
@@ -205,7 +202,9 @@ int habmem_hyp_grant_user(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata)
+ void *ppdata,
+ int *compressed,
+ int *compressed_size)
{
int i, ret = 0;
struct grantable *item = (struct grantable *)ppdata;
@@ -239,7 +238,8 @@ int habmem_hyp_grant_user(unsigned long address,
for (i = 0; i < page_count; i++)
item[i].pfn = page_to_pfn(pages[i]);
} else {
- pr_err("get %d user pages failed: %d\n", page_count, ret);
+ pr_err("get %d user pages failed %d flags %d\n",
+ page_count, ret, flags);
}
vfree(pages);
@@ -256,7 +256,9 @@ int habmem_hyp_grant(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata)
+ void *ppdata,
+ int *compressed,
+ int *compressed_size)
{
int i;
struct grantable *item;
@@ -310,7 +312,7 @@ void habmem_imp_hyp_close(void *imp_ctx, int kernel)
list_del(&pglist->list);
priv->cnt--;
- vfree(pglist->pages);
+ kfree(pglist->pages);
kfree(pglist);
}
@@ -460,19 +462,19 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
unsigned long pfn;
int i, j, k = 0;
pgprot_t prot = PAGE_KERNEL;
- int32_t fd;
+ int32_t fd, size;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
if (!pfn_table || !priv)
return -EINVAL;
-
- pages = vmalloc(exp->payload_count * sizeof(struct page *));
+ size = exp->payload_count * sizeof(struct page *);
+ pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
- vfree(pages);
+ kfree(pages);
return -ENOMEM;
}
@@ -503,7 +505,7 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
exp_info.priv = pglist;
pglist->dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(pglist->dmabuf)) {
- vfree(pages);
+ kfree(pages);
kfree(pglist);
return PTR_ERR(pglist->dmabuf);
}
@@ -511,7 +513,7 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
fd = dma_buf_fd(pglist->dmabuf, O_CLOEXEC);
if (fd < 0) {
dma_buf_put(pglist->dmabuf);
- vfree(pages);
+ kfree(pages);
kfree(pglist);
return -EINVAL;
}
@@ -539,17 +541,18 @@ static int habmem_imp_hyp_map_kva(void *imp_ctx,
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
- int i, j, k = 0;
+ int i, j, k = 0, size;
pgprot_t prot = PAGE_KERNEL;
if (!pfn_table || !priv)
return -EINVAL;
- pages = vmalloc(exp->payload_count * sizeof(struct page *));
+ size = exp->payload_count * sizeof(struct page *);
+ pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
- vfree(pages);
+ kfree(pages);
return -ENOMEM;
}
@@ -575,7 +578,7 @@ static int habmem_imp_hyp_map_kva(void *imp_ctx,
pglist->kva = vmap(pglist->pages, pglist->npages, VM_MAP, prot);
if (pglist->kva == NULL) {
- vfree(pages);
+ kfree(pages);
kfree(pglist);
pr_err("%ld pages vmap failed\n", pglist->npages);
return -ENOMEM;
@@ -607,18 +610,18 @@ static int habmem_imp_hyp_map_uva(void *imp_ctx,
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
- int i, j, k = 0;
+ int i, j, k = 0, size;
if (!pfn_table || !priv)
return -EINVAL;
-
- pages = vmalloc(exp->payload_count * sizeof(struct page *));
+ size = exp->payload_count * sizeof(struct page *);
+ pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
- vfree(pages);
+ kfree(pages);
return -ENOMEM;
}
@@ -670,7 +673,7 @@ int habmem_imp_hyp_map(void *imp_ctx, struct hab_import *param,
return ret;
}
-int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp)
+int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp, int kernel)
{
struct importer_context *priv = imp_ctx;
struct pages_list *pglist, *tmp;
@@ -679,11 +682,8 @@ int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp)
write_lock(&priv->implist_lock);
list_for_each_entry_safe(pglist, tmp, &priv->imp_list, list) {
if (pglist->export_id == exp->export_id &&
- pglist->vcid == exp->vcid_remote) {
+ pglist->vcid == exp->vcid_remote) {
found = 1;
- }
-
- if (found) {
list_del(&pglist->list);
priv->cnt--;
break;
@@ -705,7 +705,7 @@ int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp)
if (pglist->dmabuf)
dma_buf_put(pglist->dmabuf);
- vfree(pglist->pages);
+ kfree(pglist->pages);
kfree(pglist);
return 0;
@@ -719,9 +719,6 @@ int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma)
struct pages_list *pglist;
int bfound = 0;
- pr_debug("mmap request start %lX, len %ld, index %lX\n",
- vma->vm_start, length, vma->vm_pgoff);
-
read_lock(&imp_ctx->implist_lock);
list_for_each_entry(pglist, &imp_ctx->imp_list, list) {
if (pglist->index == vma->vm_pgoff) {
diff --git a/drivers/soc/qcom/hab/hab_mimex.c b/drivers/soc/qcom/hab/hab_mimex.c
index 00fbeabed4bb..86d763f65657 100644
--- a/drivers/soc/qcom/hab/hab_mimex.c
+++ b/drivers/soc/qcom/hab/hab_mimex.c
@@ -28,7 +28,7 @@
static int hab_export_ack_find(struct uhab_context *ctx,
- struct hab_export_ack *expect_ack)
+ struct hab_export_ack *expect_ack, struct virtual_channel *vchan)
{
int ret = 0;
struct hab_export_ack_recvd *ack_recvd, *tmp;
@@ -36,9 +36,10 @@ static int hab_export_ack_find(struct uhab_context *ctx,
spin_lock_bh(&ctx->expq_lock);
list_for_each_entry_safe(ack_recvd, tmp, &ctx->exp_rxq, node) {
- if (ack_recvd->ack.export_id == expect_ack->export_id &&
+ if ((ack_recvd->ack.export_id == expect_ack->export_id &&
ack_recvd->ack.vcid_local == expect_ack->vcid_local &&
- ack_recvd->ack.vcid_remote == expect_ack->vcid_remote) {
+ ack_recvd->ack.vcid_remote == expect_ack->vcid_remote)
+ || vchan->otherend_closed) {
list_del(&ack_recvd->node);
kfree(ack_recvd);
ret = 1;
@@ -57,15 +58,17 @@ static int hab_export_ack_find(struct uhab_context *ctx,
}
static int hab_export_ack_wait(struct uhab_context *ctx,
- struct hab_export_ack *expect_ack)
+ struct hab_export_ack *expect_ack, struct virtual_channel *vchan)
{
int ret;
ret = wait_event_interruptible_timeout(ctx->exp_wq,
- hab_export_ack_find(ctx, expect_ack),
- HZ);
+ hab_export_ack_find(ctx, expect_ack, vchan),
+ HAB_HS_TIMEOUT);
if (!ret || (ret == -ERESTARTSYS))
ret = -EAGAIN;
+ else if (vchan->otherend_closed)
+ ret = -ENODEV;
else if (ret > 0)
ret = 0;
return ret;
@@ -86,7 +89,7 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan,
if (!vchan || !sizebytes)
return NULL;
- exp = vmalloc(sizebytes);
+ exp = kzalloc(sizebytes, GFP_KERNEL);
if (!exp)
return NULL;
@@ -103,6 +106,8 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan,
exp->vcid_remote = vchan->otherend_id;
exp->domid_local = -1; /* dom id, provided on the importer */
exp->domid_remote = vchan->pchan->dom_id;
+ exp->ctx = vchan->ctx;
+ exp->pchan = vchan->pchan;
ctx = vchan->ctx;
write_lock(&ctx->exp_lock);
@@ -118,19 +123,22 @@ void habmem_remove_export(struct export_desc *exp)
struct physical_channel *pchan;
struct uhab_context *ctx;
- if (!exp || !exp->vchan || !exp->vchan->ctx || !exp->vchan->pchan)
+ if (!exp || !exp->ctx || !exp->pchan) {
+ pr_err("failed to find valid info in exp %pK ctx %pK pchan %pK\n",
+ exp, exp->ctx, exp->pchan);
return;
+ }
- ctx = exp->vchan->ctx;
+ ctx = exp->ctx;
ctx->export_total--;
- pchan = exp->vchan->pchan;
+ pchan = exp->pchan;
spin_lock(&pchan->expid_lock);
idr_remove(&pchan->expid_idr, exp->export_id);
spin_unlock(&pchan->expid_lock);
- vfree(exp);
+ kfree(exp);
}
static int compress_pfns(void **pfns, int npages, unsigned int *data_size)
@@ -148,7 +156,7 @@ static int compress_pfns(void **pfns, int npages, unsigned int *data_size)
new_table->first_pfn = item[0].pfn;
for (i = 1; i < npages; i++) {
if (item[i].pfn-1 == item[i-1].pfn) {
- region_size++;
+ region_size++; /* continuous pfn */
} else {
new_table->region[j].size = region_size;
new_table->region[j].space = item[i].pfn -
@@ -208,7 +216,12 @@ static int habmem_export_vchan(struct uhab_context *ctx,
expected_ack.export_id = exp->export_id;
expected_ack.vcid_local = exp->vcid_local;
expected_ack.vcid_remote = exp->vcid_remote;
- ret = hab_export_ack_wait(ctx, &expected_ack);
+ ret = hab_export_ack_wait(ctx, &expected_ack, vchan);
+ if (ret != 0) {
+ pr_err("failed to receive remote export ack %d on vc %x\n",
+ ret, vchan->id);
+ return ret;
+ }
*export_id = exp->export_id;
@@ -225,12 +238,11 @@ int hab_mem_export(struct uhab_context *ctx,
uint32_t export_id = 0;
struct virtual_channel *vchan;
int page_count;
+ int compressed = 0;
- if (!ctx || !param || param->sizebytes > HAB_MAX_EXPORT_SIZE)
+ if (!ctx || !param)
return -EINVAL;
- pr_debug("vc %X, mem size %d\n", param->vcid, param->sizebytes);
-
vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
if (!vchan || !vchan->pchan) {
ret = -ENODEV;
@@ -249,13 +261,17 @@ int hab_mem_export(struct uhab_context *ctx,
page_count,
param->flags,
vchan->pchan->dom_id,
- pdata_exp);
+ pdata_exp,
+ &compressed,
+ &pdata_size);
} else {
ret = habmem_hyp_grant_user((unsigned long)param->buffer,
page_count,
param->flags,
vchan->pchan->dom_id,
- pdata_exp);
+ pdata_exp,
+ &compressed,
+ &pdata_size);
}
if (ret < 0) {
pr_err("habmem_hyp_grant failed size=%d ret=%d\n",
@@ -263,7 +279,8 @@ int hab_mem_export(struct uhab_context *ctx,
goto err;
}
- compress_pfns(&pdata_exp, page_count, &pdata_size);
+ if (!compressed)
+ compress_pfns(&pdata_exp, page_count, &pdata_size);
ret = habmem_export_vchan(ctx,
vchan,
@@ -287,14 +304,23 @@ int hab_mem_unexport(struct uhab_context *ctx,
{
int ret = 0, found = 0;
struct export_desc *exp, *tmp;
+ struct virtual_channel *vchan;
if (!ctx || !param)
return -EINVAL;
+ /* refcnt on the access */
+ vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
+ if (!vchan || !vchan->pchan) {
+ ret = -ENODEV;
+ goto err_novchan;
+ }
+
write_lock(&ctx->exp_lock);
list_for_each_entry_safe(exp, tmp, &ctx->exp_whse, node) {
- if ((param->exportid == exp->export_id) &&
- (param->vcid == exp->vcid_local)) {
+ if (param->exportid == exp->export_id &&
+ param->vcid == exp->vcid_local) {
+ /* same vchan guarantees the pchan for idr */
list_del(&exp->node);
found = 1;
break;
@@ -302,15 +328,22 @@ int hab_mem_unexport(struct uhab_context *ctx,
}
write_unlock(&ctx->exp_lock);
- if (!found)
- return -EINVAL;
+ if (!found) {
+ ret = -EINVAL;
+ goto err_novchan;
+ }
ret = habmem_hyp_revoke(exp->payload, exp->payload_count);
if (ret) {
pr_err("Error found in revoke grant with ret %d", ret);
- return ret;
+ goto err_novchan;
}
habmem_remove_export(exp);
+
+err_novchan:
+ if (vchan)
+ hab_vchan_put(vchan);
+
return ret;
}
@@ -320,14 +353,24 @@ int hab_mem_import(struct uhab_context *ctx,
{
int ret = 0, found = 0;
struct export_desc *exp = NULL;
+ struct virtual_channel *vchan;
if (!ctx || !param)
return -EINVAL;
+ vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
+ if (!vchan || !vchan->pchan) {
+ ret = -ENODEV;
+ goto err_imp;
+ }
+
spin_lock_bh(&ctx->imp_lock);
list_for_each_entry(exp, &ctx->imp_whse, node) {
if ((exp->export_id == param->exportid) &&
(param->vcid == exp->vcid_remote)) {
+ /* only allow import on the vchan recevied from
+ * remote
+ */
found = 1;
break;
}
@@ -338,27 +381,24 @@ int hab_mem_import(struct uhab_context *ctx,
pr_err("Fail to get export descriptor from export id %d\n",
param->exportid);
ret = -ENODEV;
- return ret;
+ goto err_imp;
}
- pr_debug("call map id: %d pcnt %d remote_dom %d 1st_ref:0x%X\n",
- exp->export_id, exp->payload_count, exp->domid_local,
- *((uint32_t *)exp->payload));
-
ret = habmem_imp_hyp_map(ctx->import_ctx, param, exp, kernel);
if (ret) {
pr_err("Import fail ret:%d pcnt:%d rem:%d 1st_ref:0x%X\n",
ret, exp->payload_count,
exp->domid_local, *((uint32_t *)exp->payload));
- return ret;
+ goto err_imp;
}
exp->import_index = param->index;
exp->kva = kernel ? (void *)param->kva : NULL;
- pr_debug("import index %llx, kva or fd %llx, kernel %d\n",
- exp->import_index, param->kva, kernel);
+err_imp:
+ if (vchan)
+ hab_vchan_put(vchan);
return ret;
}
@@ -369,20 +409,26 @@ int hab_mem_unimport(struct uhab_context *ctx,
{
int ret = 0, found = 0;
struct export_desc *exp = NULL, *exp_tmp;
+ struct virtual_channel *vchan;
if (!ctx || !param)
return -EINVAL;
+ vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
+ if (!vchan || !vchan->pchan) {
+ if (vchan)
+ hab_vchan_put(vchan);
+ return -ENODEV;
+ }
+
spin_lock_bh(&ctx->imp_lock);
list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) {
- if ((exp->export_id == param->exportid) &&
- (param->vcid == exp->vcid_remote)) {
+ if (exp->export_id == param->exportid &&
+ param->vcid == exp->vcid_remote) {
+ /* same vchan is expected here */
list_del(&exp->node);
ctx->import_total--;
found = 1;
-
- pr_debug("found id:%d payload cnt:%d kernel:%d\n",
- exp->export_id, exp->payload_count, kernel);
break;
}
}
@@ -391,7 +437,7 @@ int hab_mem_unimport(struct uhab_context *ctx,
if (!found)
ret = -EINVAL;
else {
- ret = habmm_imp_hyp_unmap(ctx->import_ctx, exp);
+ ret = habmm_imp_hyp_unmap(ctx->import_ctx, exp, kernel);
if (ret) {
pr_err("unmap fail id:%d pcnt:%d vcid:%d\n",
exp->export_id, exp->payload_count, exp->vcid_remote);
@@ -400,5 +446,8 @@ int hab_mem_unimport(struct uhab_context *ctx,
kfree(exp);
}
+ if (vchan)
+ hab_vchan_put(vchan);
+
return ret;
}
diff --git a/drivers/soc/qcom/hab/hab_msg.c b/drivers/soc/qcom/hab/hab_msg.c
index d904cdee838c..9d5ee134c94e 100644
--- a/drivers/soc/qcom/hab/hab_msg.c
+++ b/drivers/soc/qcom/hab/hab_msg.c
@@ -78,13 +78,14 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg,
} else {
pr_err("rcv buffer too small %d < %zd\n",
*rsize, message->sizebytes);
- *rsize = 0;
+ *rsize = message->sizebytes;
message = NULL;
- ret = -EINVAL;
+ ret = -EOVERFLOW; /* come back again */
}
}
spin_unlock_bh(&vchan->rx_lock);
} else
+ /* no message received, retain the original status */
*rsize = 0;
*msg = message;
@@ -142,7 +143,7 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
return -ENOMEM;
if (sizeof(ack_recvd->ack) != sizebytes)
- pr_err("exp ack size %lu is not as arrived %zu\n",
+ pr_err("exp ack size %zu is not as arrived %zu\n",
sizeof(ack_recvd->ack), sizebytes);
if (physical_channel_read(pchan,
@@ -150,11 +151,6 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
sizebytes) != sizebytes)
return -EIO;
- pr_debug("receive export id %d, local vc %X, vd remote %X\n",
- ack_recvd->ack.export_id,
- ack_recvd->ack.vcid_local,
- ack_recvd->ack.vcid_remote);
-
spin_lock_bh(&ctx->expq_lock);
list_add_tail(&ack_recvd->node, &ctx->exp_rxq);
spin_unlock_bh(&ctx->expq_lock);
@@ -162,10 +158,21 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
return 0;
}
-void hab_msg_recv(struct physical_channel *pchan,
+static void hab_msg_drop(struct physical_channel *pchan, size_t sizebytes)
+{
+ uint8_t *data = NULL;
+
+ data = kmalloc(sizebytes, GFP_ATOMIC);
+ if (data == NULL)
+ return;
+ physical_channel_read(pchan, data, sizebytes);
+ kfree(data);
+}
+
+int hab_msg_recv(struct physical_channel *pchan,
struct hab_header *header)
{
- int ret;
+ int ret = 0;
struct hab_message *message;
struct hab_device *dev = pchan->habdev;
size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
@@ -179,7 +186,8 @@ void hab_msg_recv(struct physical_channel *pchan,
/* get the local virtual channel if it isn't an open message */
if (payload_type != HAB_PAYLOAD_TYPE_INIT &&
payload_type != HAB_PAYLOAD_TYPE_INIT_ACK &&
- payload_type != HAB_PAYLOAD_TYPE_ACK) {
+ payload_type != HAB_PAYLOAD_TYPE_INIT_DONE &&
+ payload_type != HAB_PAYLOAD_TYPE_INIT_CANCEL) {
/* sanity check the received message */
if (payload_type >= HAB_PAYLOAD_TYPE_MAX ||
@@ -189,29 +197,42 @@ void hab_msg_recv(struct physical_channel *pchan,
payload_type, vchan_id, sizebytes, session_id);
}
+ /*
+ * need both vcid and session_id to be accurate.
+ * this is from pchan instead of ctx
+ */
vchan = hab_vchan_get(pchan, header);
if (!vchan) {
- pr_debug("vchan is not found, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ pr_info("vchan is not found, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
payload_type, vchan_id, sizebytes, session_id);
- if (sizebytes)
- pr_err("message is dropped\n");
-
- return;
+ if (sizebytes) {
+ hab_msg_drop(pchan, sizebytes);
+ pr_err("message %d dropped no vchan, session id %d\n",
+ payload_type, session_id);
+ }
+ return -EINVAL;
} else if (vchan->otherend_closed) {
hab_vchan_put(vchan);
- pr_debug("vchan remote is closed, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ pr_info("vchan remote is closed payload type %d, vchan id %x, sizebytes %zx, session %d\n",
payload_type, vchan_id, sizebytes, session_id);
-
- if (sizebytes)
- pr_err("message is dropped\n");
-
- return;
+ if (sizebytes) {
+ hab_msg_drop(pchan, sizebytes);
+ pr_err("message %d dropped remote close, session id %d\n",
+ payload_type, session_id);
+ }
+ return -ENODEV;
}
} else {
if (sizebytes != sizeof(struct hab_open_send_data)) {
- pr_err("Invalid open request received, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ pr_err("Invalid open request received type %d, vcid %x, szbytes %zx, session %d\n",
payload_type, vchan_id, sizebytes, session_id);
+ if (sizebytes) {
+ hab_msg_drop(pchan, sizebytes);
+ pr_err("message %d dropped unknown reason, session id %d\n",
+ payload_type, session_id);
+ }
+ return -ENODEV;
}
}
@@ -226,7 +247,7 @@ void hab_msg_recv(struct physical_channel *pchan,
case HAB_PAYLOAD_TYPE_INIT:
case HAB_PAYLOAD_TYPE_INIT_ACK:
- case HAB_PAYLOAD_TYPE_ACK:
+ case HAB_PAYLOAD_TYPE_INIT_DONE:
ret = hab_open_request_add(pchan, sizebytes, payload_type);
if (ret) {
pr_err("open request add failed, ret %d, payload type %d, sizebytes %zx\n",
@@ -236,6 +257,16 @@ void hab_msg_recv(struct physical_channel *pchan,
wake_up_interruptible(&dev->openq);
break;
+ case HAB_PAYLOAD_TYPE_INIT_CANCEL:
+ pr_info("remote open cancel header vcid %X session %d local %d remote %d\n",
+ vchan_id, session_id, pchan->vmid_local,
+ pchan->vmid_remote);
+ ret = hab_open_receive_cancel(pchan, sizebytes);
+ if (ret)
+ pr_err("open cancel handling failed ret %d vcid %X session %d\n",
+ ret, vchan_id, session_id);
+ break;
+
case HAB_PAYLOAD_TYPE_EXPORT:
exp_desc = kzalloc(sizebytes, GFP_ATOMIC);
if (!exp_desc)
@@ -243,7 +274,10 @@ void hab_msg_recv(struct physical_channel *pchan,
if (physical_channel_read(pchan, exp_desc, sizebytes) !=
sizebytes) {
- vfree(exp_desc);
+ pr_err("corrupted exp expect %zd bytes vcid %X remote %X open %d!\n",
+ sizebytes, vchan->id,
+ vchan->otherend_id, vchan->session_id);
+ kfree(exp_desc);
break;
}
@@ -265,36 +299,33 @@ void hab_msg_recv(struct physical_channel *pchan,
case HAB_PAYLOAD_TYPE_CLOSE:
/* remote request close */
- pr_debug("remote side request close\n");
- pr_debug(" vchan id %X, other end %X, session %d\n",
- vchan->id, vchan->otherend_id, session_id);
+ pr_info("remote request close vcid %pK %X other id %X session %d refcnt %d\n",
+ vchan, vchan->id, vchan->otherend_id,
+ session_id, get_refcnt(vchan->refcount));
hab_vchan_stop(vchan);
break;
case HAB_PAYLOAD_TYPE_PROFILE:
do_gettimeofday(&tv);
-
/* pull down the incoming data */
message = hab_msg_alloc(pchan, sizebytes);
- if (!message) {
- pr_err("msg alloc failed\n");
- break;
+ if (!message)
+ pr_err("failed to allocate msg Arrived msg will be lost\n");
+ else {
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)message->data;
+ pstat->rx_sec = tv.tv_sec;
+ pstat->rx_usec = tv.tv_usec;
+ hab_msg_queue(vchan, message);
}
-
- ((uint64_t *)message->data)[2] = tv.tv_sec;
- ((uint64_t *)message->data)[3] = tv.tv_usec;
- hab_msg_queue(vchan, message);
break;
default:
- pr_err("unknown msg is received\n");
- pr_err("payload type %d, vchan id %x\n",
- payload_type, vchan_id);
- pr_err("sizebytes %zx, session %d\n",
- sizebytes, session_id);
-
+ pr_err("unknown msg received, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ payload_type, vchan_id, sizebytes, session_id);
break;
}
if (vchan)
hab_vchan_put(vchan);
+ return ret;
}
diff --git a/drivers/soc/qcom/hab/hab_open.c b/drivers/soc/qcom/hab/hab_open.c
index 35f3281604e2..f740a43c1973 100644
--- a/drivers/soc/qcom/hab/hab_open.c
+++ b/drivers/soc/qcom/hab/hab_open.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -12,6 +12,8 @@
*/
#include "hab.h"
+#define HAB_OPEN_REQ_EXPIRE_TIME_S (3600*10)
+
void hab_open_request_init(struct hab_open_request *request,
int type,
struct physical_channel *pchan,
@@ -21,57 +23,55 @@ void hab_open_request_init(struct hab_open_request *request,
{
request->type = type;
request->pchan = pchan;
- request->vchan_id = vchan_id;
- request->sub_id = sub_id;
- request->open_id = open_id;
+ request->xdata.vchan_id = vchan_id;
+ request->xdata.sub_id = sub_id;
+ request->xdata.open_id = open_id;
}
int hab_open_request_send(struct hab_open_request *request)
{
struct hab_header header = HAB_HEADER_INITIALIZER;
- struct hab_open_send_data data;
HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data));
HAB_HEADER_SET_TYPE(header, request->type);
- data.vchan_id = request->vchan_id;
- data.open_id = request->open_id;
- data.sub_id = request->sub_id;
-
- return physical_channel_send(request->pchan, &header, &data);
+ return physical_channel_send(request->pchan, &header, &request->xdata);
}
+/* called when remote sends in open-request */
int hab_open_request_add(struct physical_channel *pchan,
size_t sizebytes, int request_type)
{
struct hab_open_node *node;
struct hab_device *dev = pchan->habdev;
- struct hab_open_send_data data;
struct hab_open_request *request;
+ struct timeval tv;
node = kzalloc(sizeof(*node), GFP_ATOMIC);
if (!node)
return -ENOMEM;
- if (physical_channel_read(pchan, &data, sizebytes) != sizebytes)
+ request = &node->request;
+ if (physical_channel_read(pchan, &request->xdata, sizebytes)
+ != sizebytes)
return -EIO;
- request = &node->request;
- request->type = request_type;
- request->pchan = pchan;
- request->vchan_id = data.vchan_id;
- request->sub_id = data.sub_id;
- request->open_id = data.open_id;
- node->age = 0;
+ request->type = request_type;
+ request->pchan = pchan;
+
+ do_gettimeofday(&tv);
+ node->age = tv.tv_sec + HAB_OPEN_REQ_EXPIRE_TIME_S +
+ tv.tv_usec/1000000;
hab_pchan_get(pchan);
spin_lock_bh(&dev->openlock);
list_add_tail(&node->node, &dev->openq_list);
+ dev->openq_cnt++;
spin_unlock_bh(&dev->openlock);
-
return 0;
}
+/* local only */
static int hab_open_request_find(struct uhab_context *ctx,
struct hab_device *dev,
struct hab_open_request *listen,
@@ -79,6 +79,7 @@ static int hab_open_request_find(struct uhab_context *ctx,
{
struct hab_open_node *node, *tmp;
struct hab_open_request *request;
+ struct timeval tv;
int ret = 0;
if (ctx->closing ||
@@ -91,21 +92,27 @@ static int hab_open_request_find(struct uhab_context *ctx,
if (list_empty(&dev->openq_list))
goto done;
+ do_gettimeofday(&tv);
+
list_for_each_entry_safe(node, tmp, &dev->openq_list, node) {
request = (struct hab_open_request *)node;
- if (request->type == listen->type &&
- (request->sub_id == listen->sub_id) &&
- (!listen->open_id ||
- request->open_id == listen->open_id) &&
+ if ((request->type == listen->type ||
+ request->type == HAB_PAYLOAD_TYPE_INIT_CANCEL) &&
+ (request->xdata.sub_id == listen->xdata.sub_id) &&
+ (!listen->xdata.open_id ||
+ request->xdata.open_id == listen->xdata.open_id) &&
(!listen->pchan ||
request->pchan == listen->pchan)) {
list_del(&node->node);
+ dev->openq_cnt--;
*recv_request = request;
ret = 1;
break;
}
- node->age++;
- if (node->age > Q_AGE_THRESHOLD) {
+ if (node->age < (int64_t)tv.tv_sec + tv.tv_usec/1000000) {
+ pr_warn("open request type %d sub %d open %d\n",
+ request->type, request->xdata.sub_id,
+ request->xdata.sub_id);
list_del(&node->node);
hab_open_request_free(request);
}
@@ -121,7 +128,8 @@ void hab_open_request_free(struct hab_open_request *request)
if (request) {
hab_pchan_put(request->pchan);
kfree(request);
- }
+ } else
+ pr_err("empty request found\n");
}
int hab_open_listen(struct uhab_context *ctx,
@@ -132,22 +140,153 @@ int hab_open_listen(struct uhab_context *ctx,
{
int ret = 0;
- if (!ctx || !listen || !recv_request)
+ if (!ctx || !listen || !recv_request) {
+ pr_err("listen failed ctx %pK listen %pK request %pK\n",
+ ctx, listen, recv_request);
return -EINVAL;
+ }
*recv_request = NULL;
- if (ms_timeout > 0) {
+ if (ms_timeout > 0) { /* be case */
+ ms_timeout = msecs_to_jiffies(ms_timeout);
ret = wait_event_interruptible_timeout(dev->openq,
hab_open_request_find(ctx, dev, listen, recv_request),
ms_timeout);
- if (!ret || (-ERESTARTSYS == ret))
- ret = -EAGAIN;
- else if (ret > 0)
- ret = 0;
- } else {
+ if (!ret || (-ERESTARTSYS == ret)) {
+ pr_warn("something failed in open listen ret %d\n",
+ ret);
+ ret = -EAGAIN; /* condition not met */
+ } else if (ret > 0)
+ ret = 0; /* condition met */
+ } else { /* fe case */
ret = wait_event_interruptible(dev->openq,
hab_open_request_find(ctx, dev, listen, recv_request));
+ if (ctx->closing) {
+ pr_warn("local closing during open ret %d\n", ret);
+ ret = -ENODEV;
+ } else if (-ERESTARTSYS == ret) {
+ pr_warn("local interrupted ret %d\n", ret);
+ ret = -EINTR;
+ }
+ }
+
+ return ret;
+}
+
+/* called when receives remote's cancel init from FE or init-ack from BE */
+int hab_open_receive_cancel(struct physical_channel *pchan,
+ size_t sizebytes)
+{
+ struct hab_device *dev = pchan->habdev;
+ struct hab_open_send_data data;
+ struct hab_open_request *request;
+ struct hab_open_node *node, *tmp;
+ int bfound = 0;
+ struct timeval tv;
+
+ if (physical_channel_read(pchan, &data, sizebytes) != sizebytes)
+ return -EIO;
+
+ spin_lock_bh(&dev->openlock);
+ list_for_each_entry_safe(node, tmp, &dev->openq_list, node) {
+ request = &node->request;
+ /* check if open request has been serviced or not */
+ if ((request->type == HAB_PAYLOAD_TYPE_INIT ||
+ request->type == HAB_PAYLOAD_TYPE_INIT_ACK) &&
+ (request->xdata.sub_id == data.sub_id) &&
+ (request->xdata.open_id == data.open_id) &&
+ (request->xdata.vchan_id == data.vchan_id)) {
+ list_del(&node->node);
+ dev->openq_cnt--;
+ pr_info("open cancelled on pchan %s vcid %x subid %d openid %d\n",
+ pchan->name, data.vchan_id,
+ data.sub_id, data.open_id);
+ /* found un-serviced open request, delete it */
+ bfound = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&dev->openlock);
+
+ if (!bfound) {
+ pr_info("init waiting is in-flight. vcid %x sub %d open %d\n",
+ data.vchan_id, data.sub_id, data.open_id);
+ /* add cancel to the openq to let the waiting open bail out */
+ node = kzalloc(sizeof(*node), GFP_ATOMIC);
+ if (!node)
+ return -ENOMEM;
+
+ request = &node->request;
+ request->type = HAB_PAYLOAD_TYPE_INIT_CANCEL;
+ request->pchan = pchan;
+ request->xdata.vchan_id = data.vchan_id;
+ request->xdata.sub_id = data.sub_id;
+ request->xdata.open_id = data.open_id;
+
+ do_gettimeofday(&tv);
+ node->age = tv.tv_sec + HAB_OPEN_REQ_EXPIRE_TIME_S +
+ tv.tv_usec/1000000;
+ /* put when this node is handled in open path */
+ hab_pchan_get(pchan);
+
+ spin_lock_bh(&dev->openlock);
+ list_add_tail(&node->node, &dev->openq_list);
+ dev->openq_cnt++;
+ spin_unlock_bh(&dev->openlock);
+
+ wake_up_interruptible(&dev->openq);
+ }
+
+ return 0;
+}
+
+/* calls locally to send cancel pending open to remote */
+int hab_open_cancel_notify(struct hab_open_request *request)
+{
+ struct hab_header header = HAB_HEADER_INITIALIZER;
+
+ HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data));
+ HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_INIT_CANCEL);
+
+ return physical_channel_send(request->pchan, &header, &request->xdata);
+}
+
+int hab_open_pending_enter(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending)
+{
+ write_lock(&ctx->ctx_lock);
+ list_add_tail(&pending->node, &ctx->pending_open);
+ ctx->pending_cnt++;
+ write_unlock(&ctx->ctx_lock);
+
+ return 0;
+}
+
+int hab_open_pending_exit(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending)
+{
+ struct hab_open_node *node, *tmp;
+ int ret = -ENOENT;
+
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry_safe(node, tmp, &ctx->pending_open, node) {
+ if ((node->request.type == pending->request.type) &&
+ (node->request.pchan
+ == pending->request.pchan) &&
+ (node->request.xdata.vchan_id
+ == pending->request.xdata.vchan_id) &&
+ (node->request.xdata.sub_id
+ == pending->request.xdata.sub_id) &&
+ (node->request.xdata.open_id
+ == pending->request.xdata.open_id)) {
+ list_del(&node->node);
+ ctx->pending_cnt--;
+ ret = 0;
+ }
}
+ write_unlock(&ctx->ctx_lock);
return ret;
}
diff --git a/drivers/soc/qcom/hab/hab_parser.c b/drivers/soc/qcom/hab/hab_parser.c
index da0a4a3830a7..c332587e2b47 100644
--- a/drivers/soc/qcom/hab/hab_parser.c
+++ b/drivers/soc/qcom/hab/hab_parser.c
@@ -30,7 +30,7 @@ static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start,
for (j = mmid_start; j < mmid_start + mmid_range; j++) {
/* sanity check */
if (tbl[i].mmid[j] != HABCFG_VMID_INVALID) {
- pr_err("overwrite previous setting, i %d, j %d, be %d\n",
+ pr_err("overwrite previous setting vmid %d, mmid %d, be %d\n",
i, j, tbl[i].is_listener[j]);
}
tbl[i].mmid[j] = j;
@@ -43,28 +43,23 @@ static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start,
void dump_settings(struct local_vmid *settings)
{
- int i, j;
-
pr_debug("self vmid is %d\n", settings->self);
- for (i = 0; i < HABCFG_VMID_MAX; i++) {
- pr_debug("remote vmid %d\n",
- settings->vmid_mmid_list[i].vmid);
- for (j = 0; j <= HABCFG_MMID_AREA_MAX; j++) {
- pr_debug("mmid %d, is_be %d\n",
- settings->vmid_mmid_list[i].mmid[j],
- settings->vmid_mmid_list[i].is_listener[j]);
- }
- }
}
int fill_default_gvm_settings(struct local_vmid *settings, int vmid_local,
- int mmid_start, int mmid_end) {
+ int mmid_start, int mmid_end)
+{
+ int32_t be = HABCFG_BE_FALSE;
+ int32_t range = 1;
+ int32_t vmremote = 0; /* default to host[0] as local is guest[2] */
+
settings->self = vmid_local;
/* default gvm always talks to host as vm0 */
- return fill_vmid_mmid_tbl(settings->vmid_mmid_list, 0, 1,
- mmid_start/100, (mmid_end-mmid_start)/100+1, HABCFG_BE_FALSE);
+ return fill_vmid_mmid_tbl(settings->vmid_mmid_list, vmremote, range,
+ mmid_start/100, (mmid_end-mmid_start)/100+1, be);
}
+/* device tree based parser */
static int hab_parse_dt(struct local_vmid *settings)
{
int result, i;
@@ -151,6 +146,10 @@ static int hab_parse_dt(struct local_vmid *settings)
return 0;
}
+/*
+ * 0: successful
+ * negative: various failure core
+ */
int hab_parse(struct local_vmid *settings)
{
int ret;
diff --git a/drivers/soc/qcom/hab/hab_pchan.c b/drivers/soc/qcom/hab/hab_pchan.c
index 36bc29b7bd0c..8a9a6dfd1e0c 100644
--- a/drivers/soc/qcom/hab/hab_pchan.c
+++ b/drivers/soc/qcom/hab/hab_pchan.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -35,10 +35,10 @@ hab_pchan_alloc(struct hab_device *habdev, int otherend_id)
rwlock_init(&pchan->vchans_lock);
spin_lock_init(&pchan->rxbuf_lock);
- mutex_lock(&habdev->pchan_lock);
+ spin_lock_bh(&habdev->pchan_lock);
list_add_tail(&pchan->node, &habdev->pchannels);
habdev->pchan_cnt++;
- mutex_unlock(&habdev->pchan_lock);
+ spin_unlock_bh(&habdev->pchan_lock);
return pchan;
}
@@ -47,11 +47,26 @@ static void hab_pchan_free(struct kref *ref)
{
struct physical_channel *pchan =
container_of(ref, struct physical_channel, refcount);
+ struct virtual_channel *vchan;
- mutex_lock(&pchan->habdev->pchan_lock);
+ pr_debug("pchan %s refcnt %d\n", pchan->name,
+ get_refcnt(pchan->refcount));
+
+ spin_lock_bh(&pchan->habdev->pchan_lock);
list_del(&pchan->node);
pchan->habdev->pchan_cnt--;
- mutex_unlock(&pchan->habdev->pchan_lock);
+ spin_unlock_bh(&pchan->habdev->pchan_lock);
+
+ /* check vchan leaking */
+ read_lock(&pchan->vchans_lock);
+ list_for_each_entry(vchan, &pchan->vchannels, pnode) {
+ /* no logging on the owner. it might have been gone */
+ pr_warn("leaking vchan id %X remote %X refcnt %d\n",
+ vchan->id, vchan->otherend_id,
+ get_refcnt(vchan->refcount));
+ }
+ read_unlock(&pchan->vchans_lock);
+
kfree(pchan->hyp_data);
kfree(pchan);
}
@@ -61,7 +76,7 @@ hab_pchan_find_domid(struct hab_device *dev, int dom_id)
{
struct physical_channel *pchan;
- mutex_lock(&dev->pchan_lock);
+ spin_lock_bh(&dev->pchan_lock);
list_for_each_entry(pchan, &dev->pchannels, node)
if (pchan->dom_id == dom_id || dom_id == HABCFG_VMID_DONT_CARE)
break;
@@ -75,7 +90,7 @@ hab_pchan_find_domid(struct hab_device *dev, int dom_id)
if (pchan && !kref_get_unless_zero(&pchan->refcount))
pchan = NULL;
- mutex_unlock(&dev->pchan_lock);
+ spin_unlock_bh(&dev->pchan_lock);
return pchan;
}
diff --git a/drivers/soc/qcom/hab/hab_qvm.c b/drivers/soc/qcom/hab/hab_qvm.c
index 9aa41320a33f..129d1deeb2f0 100644
--- a/drivers/soc/qcom/hab/hab_qvm.c
+++ b/drivers/soc/qcom/hab/hab_qvm.c
@@ -71,14 +71,14 @@ static struct qvm_plugin_info {
static irqreturn_t shm_irq_handler(int irq, void *_pchan)
{
irqreturn_t rc = IRQ_NONE;
- struct physical_channel *pchan = _pchan;
+ struct physical_channel *pchan = (struct physical_channel *) _pchan;
struct qvm_channel *dev =
(struct qvm_channel *) (pchan ? pchan->hyp_data : NULL);
if (dev && dev->guest_ctrl) {
int status = dev->guest_ctrl->status;
- if (status & dev->idx) {
+ if (status & 0xffff) {/*source bitmask indicator*/
rc = IRQ_HANDLED;
tasklet_schedule(&dev->task);
}
@@ -95,13 +95,14 @@ static uint64_t get_guest_factory_paddr(struct qvm_channel *dev,
int i;
pr_debug("name = %s, factory paddr = 0x%lx, irq %d, pages %d\n",
- name, factory_addr, irq, pages);
+ name, factory_addr, irq, pages);
dev->guest_factory = (struct guest_shm_factory *)factory_addr;
if (dev->guest_factory->signature != GUEST_SHM_SIGNATURE) {
pr_err("signature error: %ld != %llu, factory addr %lx\n",
GUEST_SHM_SIGNATURE, dev->guest_factory->signature,
factory_addr);
+ iounmap(dev->guest_factory);
return 0;
}
@@ -120,6 +121,7 @@ static uint64_t get_guest_factory_paddr(struct qvm_channel *dev,
/* See if we successfully created/attached to the region. */
if (dev->guest_factory->status != GSS_OK) {
pr_err("create failed: %d\n", dev->guest_factory->status);
+ iounmap(dev->guest_factory);
return 0;
}
@@ -180,6 +182,7 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
{
struct qvm_channel *dev = NULL;
struct qvm_plugin_info *qvm_priv = hab_driver.hyp_priv;
+ uint64_t paddr;
struct physical_channel **pchan = (struct physical_channel **)commdev;
int ret = 0, coid = 0, channel = 0;
char *shmdata;
@@ -187,7 +190,6 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
hab_pipe_calc_required_bytes(PIPE_SHMEM_SIZE);
uint32_t pipe_alloc_pages =
(pipe_alloc_size + PAGE_SIZE - 1) / PAGE_SIZE;
- uint64_t paddr;
int temp;
int total_pages;
struct page **pages;
@@ -196,8 +198,10 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
pipe_alloc_size);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
spin_lock_init(&dev->io_lock);
@@ -208,7 +212,7 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
pipe_alloc_pages);
qvm_priv->curr++;
if (qvm_priv->curr > qvm_priv->probe_cnt) {
- pr_err("factory setting %d overflow probed cnt %d\n",
+ pr_err("pchan guest factory setting %d overflow probed cnt %d\n",
qvm_priv->curr, qvm_priv->probe_cnt);
ret = -1;
goto err;
@@ -261,17 +265,18 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
dev->coid = coid;
ret = create_dispatcher(*pchan);
- if (ret)
+ if (ret < 0)
goto err;
return ret;
err:
+ pr_err("habhyp_commdev_alloc failed\n");
+
kfree(dev);
if (*pchan)
hab_pchan_put(*pchan);
- pr_err("habhyp_commdev_alloc failed: %d\n", ret);
return ret;
}
@@ -280,6 +285,13 @@ int habhyp_commdev_dealloc(void *commdev)
struct physical_channel *pchan = (struct physical_channel *)commdev;
struct qvm_channel *dev = pchan->hyp_data;
+ dev->guest_ctrl->detach = 0;
+
+ if (get_refcnt(pchan->refcount) > 1) {
+ pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
+ pchan->name, pchan->vcnt,
+ get_refcnt(pchan->refcount));
+ }
kfree(dev);
hab_pchan_put(pchan);
@@ -302,25 +314,13 @@ int hab_hypervisor_register(void)
void hab_hypervisor_unregister(void)
{
- int status, i;
-
- for (i = 0; i < hab_driver.ndevices; i++) {
- struct hab_device *dev = &hab_driver.devp[i];
- struct physical_channel *pchan;
-
- list_for_each_entry(pchan, &dev->pchannels, node) {
- status = habhyp_commdev_dealloc(pchan);
- if (status) {
- pr_err("failed to free pchan %pK, i %d, ret %d\n",
- pchan, i, status);
- }
- }
- }
+ hab_hypervisor_unregister_common();
qvm_priv_info.probe_cnt = 0;
qvm_priv_info.curr = 0;
}
+/* this happens before hypervisor register */
static int hab_shmem_probe(struct platform_device *pdev)
{
int irq = 0;
@@ -373,19 +373,6 @@ static int hab_shmem_remove(struct platform_device *pdev)
static void hab_shmem_shutdown(struct platform_device *pdev)
{
- int i;
- struct qvm_channel *dev;
- struct physical_channel *pchan;
- struct hab_device *hab_dev;
-
- for (i = 0; i < hab_driver.ndevices; i++) {
- hab_dev = &hab_driver.devp[i];
- pr_debug("detaching %s\n", hab_dev->name);
- list_for_each_entry(pchan, &hab_dev->pchannels, node) {
- dev = (struct qvm_channel *)pchan->hyp_data;
- dev->guest_ctrl->detach = 0;
- }
- }
}
static const struct of_device_id hab_shmem_match_table[] = {
diff --git a/drivers/soc/qcom/hab/hab_qvm.h b/drivers/soc/qcom/hab/hab_qvm.h
index b483f4c21331..fe7cb0bbda0a 100644
--- a/drivers/soc/qcom/hab/hab_qvm.h
+++ b/drivers/soc/qcom/hab/hab_qvm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -36,6 +36,7 @@ struct qvm_channel {
int channel;
int coid;
+ /* Guest VM */
unsigned int guest_intr;
unsigned int guest_iid;
unsigned int factory_addr;
diff --git a/drivers/soc/qcom/hab/hab_vchan.c b/drivers/soc/qcom/hab/hab_vchan.c
index 2db4db8f321b..d127bcca19f8 100644
--- a/drivers/soc/qcom/hab/hab_vchan.c
+++ b/drivers/soc/qcom/hab/hab_vchan.c
@@ -13,7 +13,8 @@
#include "hab.h"
struct virtual_channel *
-hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
+hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan,
+ int openid)
{
int id;
struct virtual_channel *vchan;
@@ -28,11 +29,13 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
/* This should be the first thing we do in this function */
idr_preload(GFP_KERNEL);
spin_lock_bh(&pchan->vid_lock);
- id = idr_alloc(&pchan->vchan_idr, vchan, 1, 256, GFP_NOWAIT);
+ id = idr_alloc(&pchan->vchan_idr, vchan, 1,
+ (HAB_VCID_ID_MASK >> HAB_VCID_ID_SHIFT) + 1, GFP_NOWAIT);
spin_unlock_bh(&pchan->vid_lock);
idr_preload_end();
- if (id < 0) {
+ if (id <= 0) {
+ pr_err("idr failed %d\n", id);
kfree(vchan);
return NULL;
}
@@ -40,8 +43,11 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
hab_pchan_get(pchan);
vchan->pchan = pchan;
+ /* vchan need both vcid and openid to be properly located */
+ vchan->session_id = openid;
write_lock(&pchan->vchans_lock);
list_add_tail(&vchan->pnode, &pchan->vchannels);
+ pchan->vcnt++;
write_unlock(&pchan->vchans_lock);
vchan->id = ((id << HAB_VCID_ID_SHIFT) & HAB_VCID_ID_MASK) |
((pchan->habdev->id << HAB_VCID_MMID_SHIFT) &
@@ -53,7 +59,7 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
init_waitqueue_head(&vchan->rx_queue);
kref_init(&vchan->refcount);
- kref_init(&vchan->usagecnt);
+
vchan->otherend_closed = pchan->closed;
hab_ctx_get(ctx);
@@ -65,11 +71,9 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
static void
hab_vchan_free(struct kref *ref)
{
- int found;
struct virtual_channel *vchan =
container_of(ref, struct virtual_channel, refcount);
struct hab_message *message, *msg_tmp;
- struct export_desc *exp, *exp_tmp;
struct physical_channel *pchan = vchan->pchan;
struct uhab_context *ctx = vchan->ctx;
struct virtual_channel *vc, *vc_tmp;
@@ -81,73 +85,84 @@ hab_vchan_free(struct kref *ref)
}
spin_unlock_bh(&vchan->rx_lock);
- do {
- found = 0;
- write_lock(&ctx->exp_lock);
- list_for_each_entry_safe(exp, exp_tmp, &ctx->exp_whse, node) {
- if (exp->vcid_local == vchan->id) {
- list_del(&exp->node);
- found = 1;
- break;
- }
- }
- write_unlock(&ctx->exp_lock);
- if (found) {
- habmem_hyp_revoke(exp->payload, exp->payload_count);
- habmem_remove_export(exp);
- }
- } while (found);
-
- do {
- found = 0;
- spin_lock_bh(&ctx->imp_lock);
- list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) {
- if (exp->vcid_remote == vchan->id) {
- list_del(&exp->node);
- found = 1;
- break;
- }
- }
- spin_unlock_bh(&ctx->imp_lock);
- if (found) {
- habmm_imp_hyp_unmap(ctx->import_ctx, exp);
- ctx->import_total--;
- kfree(exp);
- }
- } while (found);
-
- spin_lock_bh(&pchan->vid_lock);
- idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
- spin_unlock_bh(&pchan->vid_lock);
+ /* the release vchan from ctx was done earlier in vchan close() */
+ hab_ctx_put(ctx); /* now ctx is not needed from this vchan's view */
+ vchan->ctx = NULL;
+ /* release vchan from pchan. no more msg for this vchan */
write_lock(&pchan->vchans_lock);
list_for_each_entry_safe(vc, vc_tmp, &pchan->vchannels, pnode) {
if (vchan == vc) {
list_del(&vc->pnode);
+ /* the ref is held in case of pchan is freed */
+ pchan->vcnt--;
break;
}
}
write_unlock(&pchan->vchans_lock);
- hab_pchan_put(pchan);
- hab_ctx_put(ctx);
+ /* release idr at the last so same idr will not be used early */
+ spin_lock_bh(&pchan->vid_lock);
+ idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
+ spin_unlock_bh(&pchan->vid_lock);
+
+ hab_pchan_put(pchan); /* no more need for pchan from this vchan */
kfree(vchan);
}
+/*
+ * only for msg recv path to retrieve vchan from vcid and openid based on
+ * pchan's vchan list
+ */
struct virtual_channel*
hab_vchan_get(struct physical_channel *pchan, struct hab_header *header)
{
struct virtual_channel *vchan;
uint32_t vchan_id = HAB_HEADER_GET_ID(*header);
uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header);
+ size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
+ uint32_t payload_type = HAB_HEADER_GET_TYPE(*header);
spin_lock_bh(&pchan->vid_lock);
vchan = idr_find(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan_id));
- if (vchan)
- if ((vchan->session_id != session_id) ||
- (!kref_get_unless_zero(&vchan->refcount)))
+ if (vchan) {
+ if (vchan->session_id != session_id)
+ /*
+ * skipped if session is different even vcid
+ * is the same
+ */
+ vchan = NULL;
+ else if (!vchan->otherend_id /*&& !vchan->session_id*/) {
+ /*
+ * not paired vchan can be fetched right after it is
+ * alloc'ed. so it has to be skipped during search
+ * for remote msg
+ */
+ pr_warn("vcid %x is not paired yet session %d refcnt %d type %d sz %zd\n",
+ vchan->id, vchan->otherend_id,
+ get_refcnt(vchan->refcount),
+ payload_type, sizebytes);
+ vchan = NULL;
+ } else if (!kref_get_unless_zero(&vchan->refcount)) {
+ /*
+ * this happens when refcnt is already zero
+ * (put from other thread) or there is an actual error
+ */
+ pr_err("failed to inc vcid %pK %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
+ vchan, vchan->id, vchan->otherend_id,
+ vchan->session_id, get_refcnt(vchan->refcount),
+ vchan_id, session_id, payload_type, sizebytes);
+ vchan = NULL;
+ } else if (vchan->otherend_closed || vchan->closed) {
+ pr_err("closed already remote %d local %d vcid %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
+ vchan->otherend_closed, vchan->closed,
+ vchan->id, vchan->otherend_id,
+ vchan->session_id, get_refcnt(vchan->refcount),
+ vchan_id, session_id, payload_type, sizebytes);
vchan = NULL;
+ }
+ }
spin_unlock_bh(&pchan->vid_lock);
return vchan;
@@ -158,6 +173,7 @@ void hab_vchan_stop(struct virtual_channel *vchan)
if (vchan) {
vchan->otherend_closed = 1;
wake_up(&vchan->rx_queue);
+ wake_up_interruptible(&vchan->ctx->exp_wq);
}
}
@@ -184,23 +200,36 @@ int hab_vchan_find_domid(struct virtual_channel *vchan)
return vchan ? vchan->pchan->dom_id : -1;
}
-static void
-hab_vchan_free_deferred(struct work_struct *work)
-{
- struct virtual_channel *vchan =
- container_of(work, struct virtual_channel, work);
-
- hab_vchan_free(&vchan->refcount);
-}
-
-static void
-hab_vchan_schedule_free(struct kref *ref)
+/* this sould be only called once after refcnt is zero */
+static void hab_vchan_schedule_free(struct kref *ref)
{
- struct virtual_channel *vchan =
+ struct virtual_channel *vchanin =
container_of(ref, struct virtual_channel, refcount);
+ struct uhab_context *ctx = vchanin->ctx;
+ struct virtual_channel *vchan, *tmp;
+ int bnotify = 0;
+
+ /*
+ * similar logic is in ctx free. if ctx free runs first,
+ * this is skipped
+ */
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
+ if (vchan == vchanin) {
+ pr_debug("vchan free refcnt = %d\n",
+ get_refcnt(vchan->refcount));
+ ctx->vcnt--;
+ list_del(&vchan->node);
+ bnotify = 1;
+ break;
+ }
+ }
+ write_unlock(&ctx->ctx_lock);
- INIT_WORK(&vchan->work, hab_vchan_free_deferred);
- schedule_work(&vchan->work);
+ if (bnotify)
+ hab_vchan_stop_notify(vchan);
+
+ hab_vchan_free(ref);
}
void hab_vchan_put(struct virtual_channel *vchan)
@@ -210,17 +239,23 @@ void hab_vchan_put(struct virtual_channel *vchan)
}
int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,
- char *names, size_t name_size, uint32_t flags)
+ char *names, size_t name_size, uint32_t flags)
{
struct virtual_channel *vchan = hab_get_vchan_fromvcid(vcid, ctx);
+ if (!vchan)
+ return -EINVAL;
- if (!vchan || vchan->otherend_closed)
+ if (vchan->otherend_closed) {
+ hab_vchan_put(vchan);
return -ENODEV;
+ }
*ids = vchan->pchan->vmid_local |
((uint64_t)vchan->pchan->vmid_remote) << 32;
names[0] = 0;
names[name_size/2] = 0;
+ hab_vchan_put(vchan);
+
return 0;
}
diff --git a/drivers/soc/qcom/hab/khab.c b/drivers/soc/qcom/hab/khab.c
index ba77e5e9cca2..c4acf12fd553 100644
--- a/drivers/soc/qcom/hab/khab.c
+++ b/drivers/soc/qcom/hab/khab.c
@@ -10,13 +10,14 @@
* GNU General Public License for more details.
*
*/
-#include <linux/module.h>
#include "hab.h"
+#include <linux/module.h>
int32_t habmm_socket_open(int32_t *handle, uint32_t mm_ip_id,
uint32_t timeout, uint32_t flags)
{
- return hab_vchan_open(hab_driver.kctx, mm_ip_id, handle, flags);
+ return hab_vchan_open(hab_driver.kctx, mm_ip_id, handle,
+ timeout, flags);
}
EXPORT_SYMBOL(habmm_socket_open);
@@ -55,6 +56,9 @@ int32_t habmm_socket_recv(int32_t handle, void *dst_buff, uint32_t *size_bytes,
if (ret == 0 && msg)
memcpy(dst_buff, msg->data, msg->sizebytes);
+ else if (ret && msg)
+ pr_warn("vcid %X recv failed %d but msg is still received %zd bytes\n",
+ handle, ret, msg->sizebytes);
if (msg)
hab_msg_free(msg);
diff --git a/drivers/soc/qcom/hab/qvm_comm.c b/drivers/soc/qcom/hab/qvm_comm.c
index 41e34be9ac21..04381232b26a 100644
--- a/drivers/soc/qcom/hab/qvm_comm.c
+++ b/drivers/soc/qcom/hab/qvm_comm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,7 +43,6 @@ int physical_channel_send(struct physical_channel *pchan,
int sizebytes = HAB_HEADER_GET_SIZE(*header);
struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data;
int total_size = sizeof(*header) + sizebytes;
- struct timeval tv;
if (total_size > dev->pipe_ep->tx_info.sh_buf->size)
return -EINVAL; /* too much data for ring */
@@ -67,9 +66,13 @@ int physical_channel_send(struct physical_channel *pchan,
}
if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) {
+ struct timeval tv;
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)payload;
+
do_gettimeofday(&tv);
- ((uint64_t *)payload)[0] = tv.tv_sec;
- ((uint64_t *)payload)[1] = tv.tv_usec;
+ pstat->tx_sec = tv.tv_sec;
+ pstat->tx_usec = tv.tv_usec;
}
if (sizebytes) {
@@ -102,7 +105,7 @@ void physical_channel_rx_dispatch(unsigned long data)
break; /* no data available */
if (header.signature != HAB_HEAD_SIGNATURE) {
- pr_err("HAB signature mismatch, expect %X, received %X, id_type_size %X, session %X, sequence %X\n",
+ pr_err("HAB signature mismatch expect %X received %X, id_type_size %X session %X sequence %X\n",
HAB_HEAD_SIGNATURE, header.signature,
header.id_type_size,
header.session_id,
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
index 7288c79de428..c41050adae5a 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
@@ -1038,10 +1038,8 @@ static struct device *msm_bus_device_init(
bus_node = kzalloc(sizeof(struct msm_bus_node_device_type), GFP_KERNEL);
if (!bus_node) {
- MSM_BUS_ERR("%s:Bus node alloc failed\n", __func__);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
+ ret = -ENOMEM;
+ goto err_device_init;
}
bus_dev = &bus_node->dev;
device_initialize(bus_dev);
@@ -1049,47 +1047,37 @@ static struct device *msm_bus_device_init(
node_info = devm_kzalloc(bus_dev,
sizeof(struct msm_bus_node_info_type), GFP_KERNEL);
if (!node_info) {
- MSM_BUS_ERR("%s:Bus node info alloc failed\n", __func__);
- devm_kfree(bus_dev, bus_node);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
+ ret = -ENOMEM;
+ goto err_put_device;
}
bus_node->node_info = node_info;
bus_node->ap_owned = pdata->ap_owned;
bus_dev->of_node = pdata->of_node;
- if (msm_bus_copy_node_info(pdata, bus_dev) < 0) {
- devm_kfree(bus_dev, bus_node);
- devm_kfree(bus_dev, node_info);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
- }
+ ret = msm_bus_copy_node_info(pdata, bus_dev);
+ if (ret)
+ goto err_put_device;
bus_dev->bus = &msm_bus_type;
dev_set_name(bus_dev, bus_node->node_info->name);
ret = device_add(bus_dev);
- if (ret < 0) {
+ if (ret) {
MSM_BUS_ERR("%s: Error registering device %d",
__func__, pdata->node_info->id);
- devm_kfree(bus_dev, bus_node);
- devm_kfree(bus_dev, node_info->dev_connections);
- devm_kfree(bus_dev, node_info->connections);
- devm_kfree(bus_dev, node_info->black_connections);
- devm_kfree(bus_dev, node_info->black_listed_connections);
- devm_kfree(bus_dev, node_info);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
+ goto err_put_device;
}
device_create_file(bus_dev, &dev_attr_bw);
INIT_LIST_HEAD(&bus_node->devlist);
-
-exit_device_init:
return bus_dev;
+
+err_put_device:
+ put_device(bus_dev);
+ bus_dev = NULL;
+ kfree(bus_node);
+err_device_init:
+ return ERR_PTR(ret);
}
static int msm_bus_setup_dev_conn(struct device *bus_dev, void *data)
@@ -1284,10 +1272,10 @@ static int msm_bus_device_probe(struct platform_device *pdev)
node_dev = msm_bus_device_init(&pdata->info[i]);
- if (!node_dev) {
+ if (IS_ERR(node_dev)) {
MSM_BUS_ERR("%s: Error during dev init for %d",
__func__, pdata->info[i].node_info->id);
- ret = -ENXIO;
+ ret = PTR_ERR(node_dev);
goto exit_device_probe;
}
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
index fefc348c0027..8cd86915be98 100644
--- a/drivers/soc/qcom/qdsp6v2/apr.c
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -45,7 +45,8 @@ static void *apr_pkt_ctx;
static wait_queue_head_t dsp_wait;
static wait_queue_head_t modem_wait;
static bool is_modem_up;
-static bool is_initial_boot;
+static bool is_initial_modem_boot;
+static bool is_initial_adsp_boot;
/* Subsystem restart: QDSP6 data, functions */
static struct workqueue_struct *apr_reset_workqueue;
static void apr_reset_deregister(struct work_struct *work);
@@ -909,21 +910,28 @@ static int apr_notifier_service_cb(struct notifier_block *this,
* recovery notifications during initial boot
* up since everything is expected to be down.
*/
- if (is_initial_boot) {
- is_initial_boot = false;
- break;
- }
- if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+ if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) {
+ if (is_initial_modem_boot) {
+ is_initial_modem_boot = false;
+ break;
+ }
apr_modem_down(opcode);
- else
+ } else {
+ if (is_initial_adsp_boot) {
+ is_initial_adsp_boot = false;
+ break;
+ }
apr_adsp_down(opcode);
+ }
break;
case AUDIO_NOTIFIER_SERVICE_UP:
- is_initial_boot = false;
- if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+ if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) {
+ is_initial_modem_boot = false;
apr_modem_up();
- else
+ } else {
+ is_initial_adsp_boot = false;
apr_adsp_up();
+ }
break;
default:
break;
@@ -965,7 +973,8 @@ static int __init apr_init(void)
if (!apr_pkt_ctx)
pr_err("%s: Unable to create ipc log context\n", __func__);
- is_initial_boot = true;
+ is_initial_modem_boot = true;
+ is_initial_adsp_boot = true;
subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
&adsp_service_nb);
subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index ae249f382339..ea94456ccef8 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -1083,7 +1083,7 @@ int subsystem_restart_dev(struct subsys_device *dev)
{
const char *name;
- if (!get_device(&dev->dev))
+ if ((!dev) || !get_device(&dev->dev))
return -ENODEV;
if (!try_module_get(dev->owner)) {
@@ -1177,11 +1177,21 @@ EXPORT_SYMBOL(subsystem_crashed);
void subsys_set_crash_status(struct subsys_device *dev,
enum crash_status crashed)
{
+ if (!dev) {
+ pr_err("Invalid subsystem device\n");
+ return;
+ }
+
dev->crashed = crashed;
}
enum crash_status subsys_get_crash_status(struct subsys_device *dev)
{
+ if (!dev) {
+ pr_err("Invalid subsystem device\n");
+ return CRASH_STATUS_NO_CRASH;
+ }
+
return dev->crashed;
}
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 2ad4cc7a4785..a2ead280ac4e 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_system_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -754,8 +754,10 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
{
int i;
for (i = 0; i < num_orders; i++)
- if (pools[i])
+ if (pools[i]) {
ion_page_pool_destroy(pools[i]);
+ pools[i] = NULL;
+ }
}
/**
diff --git a/drivers/uio/msm_sharedmem/msm_sharedmem.c b/drivers/uio/msm_sharedmem/msm_sharedmem.c
index b10c40b3e1fc..84623c9b41d3 100644
--- a/drivers/uio/msm_sharedmem/msm_sharedmem.c
+++ b/drivers/uio/msm_sharedmem/msm_sharedmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, 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
@@ -75,6 +75,24 @@ static int sharedmem_mmap(struct uio_info *info, struct vm_area_struct *vma)
return result;
}
+static void free_shared_ram_perms(u32 client_id, phys_addr_t addr, u32 size)
+{
+ int ret;
+ u32 source_vmlist[2] = {VMID_HLOS, VMID_MSS_MSA};
+ int dest_vmids[1] = {VMID_HLOS};
+ int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
+
+ if (client_id != MPSS_RMTS_CLIENT_ID)
+ return;
+
+ ret = hyp_assign_phys(addr, size, source_vmlist, 2, dest_vmids,
+ dest_perms, 1);
+ if (ret != 0) {
+ pr_err("hyp_assign_phys failed IPA=0x016%pa size=%u err=%d\n",
+ &addr, size, ret);
+ }
+}
+
/* Setup the shared ram permissions.
* This function currently supports the mpss client only.
*/
@@ -184,6 +202,17 @@ out:
return ret;
}
+static void msm_sharedmem_shutdown(struct platform_device *pdev)
+{
+ struct uio_info *info = dev_get_drvdata(&pdev->dev);
+
+ phys_addr_t shared_mem_addr = info->mem[0].addr;
+ u32 shared_mem_size = info->mem[0].size;
+
+ free_shared_ram_perms(MPSS_RMTS_CLIENT_ID, shared_mem_addr,
+ shared_mem_size);
+}
+
static int msm_sharedmem_remove(struct platform_device *pdev)
{
struct uio_info *info = dev_get_drvdata(&pdev->dev);
@@ -202,6 +231,7 @@ MODULE_DEVICE_TABLE(of, msm_sharedmem_of_match);
static struct platform_driver msm_sharedmem_driver = {
.probe = msm_sharedmem_probe,
.remove = msm_sharedmem_remove,
+ .shutdown = msm_sharedmem_shutdown,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c
index 34ec15ab9010..233221fed424 100644
--- a/drivers/usb/gadget/function/f_cdev.c
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2013-2018, The Linux Foundation. All rights reserved.
* Linux Foundation chooses to take subject only to the GPLv2 license terms,
* and distributes only under these terms.
*
@@ -1251,6 +1251,7 @@ ssize_t f_cdev_write(struct file *file,
ret = -EFAULT;
} else {
req->length = xfer_size;
+ req->zero = 1;
ret = usb_ep_queue(in, req, GFP_KERNEL);
if (ret) {
pr_err("EP QUEUE failed:%d\n", ret);
diff --git a/include/linux/habmm.h b/include/linux/habmm.h
index 842cd27fd372..cd4e2506f9ee 100644
--- a/include/linux/habmm.h
+++ b/include/linux/habmm.h
@@ -14,7 +14,7 @@
#ifndef HABMM_H
#define HABMM_H
-#include <uapi/linux/habmmid.h>
+#include "linux/habmmid.h"
#define HAB_API_VER_DEF(_MAJOR_, _MINOR_) \
((_MAJOR_&0xFF)<<16 | (_MINOR_&0xFFF))
diff --git a/include/linux/iio/imu/mpu.h b/include/linux/iio/imu/mpu.h
new file mode 100644
index 000000000000..4dbb86cad6d1
--- /dev/null
+++ b/include/linux/iio/imu/mpu.h
@@ -0,0 +1,124 @@
+/*
+* Copyright (C) 2012-2017 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#endif
+
+enum secondary_slave_type {
+ SECONDARY_SLAVE_TYPE_NONE,
+ SECONDARY_SLAVE_TYPE_ACCEL,
+ SECONDARY_SLAVE_TYPE_COMPASS,
+ SECONDARY_SLAVE_TYPE_PRESSURE,
+ SECONDARY_SLAVE_TYPE_ALS,
+
+ SECONDARY_SLAVE_TYPE_TYPES
+};
+
+enum ext_slave_id {
+ ID_INVALID = 0,
+ GYRO_ID_MPU3050,
+ GYRO_ID_MPU6050A2,
+ GYRO_ID_MPU6050B1,
+ GYRO_ID_MPU6050B1_NO_ACCEL,
+ GYRO_ID_ITG3500,
+
+ ACCEL_ID_LIS331,
+ ACCEL_ID_LSM303DLX,
+ ACCEL_ID_LIS3DH,
+ ACCEL_ID_KXSD9,
+ ACCEL_ID_KXTF9,
+ ACCEL_ID_BMA150,
+ ACCEL_ID_BMA222,
+ ACCEL_ID_BMA250,
+ ACCEL_ID_ADXL34X,
+ ACCEL_ID_MMA8450,
+ ACCEL_ID_MMA845X,
+ ACCEL_ID_MPU6050,
+
+ COMPASS_ID_AK8963,
+ COMPASS_ID_AK8975,
+ COMPASS_ID_AK8972,
+ COMPASS_ID_AMI30X,
+ COMPASS_ID_AMI306,
+ COMPASS_ID_YAS529,
+ COMPASS_ID_YAS530,
+ COMPASS_ID_HMC5883,
+ COMPASS_ID_LSM303DLH,
+ COMPASS_ID_LSM303DLM,
+ COMPASS_ID_MMC314X,
+ COMPASS_ID_HSCDTD002B,
+ COMPASS_ID_HSCDTD004A,
+ COMPASS_ID_MLX90399,
+ COMPASS_ID_AK09911,
+ COMPASS_ID_AK09912,
+ COMPASS_ID_AK09916,
+
+ PRESSURE_ID_BMP085,
+ PRESSURE_ID_BMP280,
+
+ ALS_ID_APDS_9900,
+ ALS_ID_APDS_9930,
+ ALS_ID_TSL_2772,
+};
+
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
+/**
+ * struct mpu_platform_data - Platform data for the mpu driver
+ * @int_config: Bits [7:3] of the int config register.
+ * @level_shifter: 0: VLogic, 1: VDD
+ * @orientation: Orientation matrix of the gyroscope
+ * @sec_slave_type: secondary slave device type, can be compass, accel, etc
+ * @sec_slave_id: id of the secondary slave device
+ * @secondary_i2c_address: secondary device's i2c address
+ * @secondary_orientation: secondary device's orientation matrix
+ * @aux_slave_type: auxiliary slave. Another slave device type
+ * @aux_slave_id: auxiliary slave ID.
+ * @aux_i2c_addr: auxiliary device I2C address.
+ * @read_only_slave_type: read only slave type.
+ * @read_only_slave_id: read only slave device ID.
+ * @read_only_i2c_addr: read only slave device address.
+ *
+ * Contains platform specific information on how to configure the MPU3050 to
+ * work on this platform. The orientation matricies are 3x3 rotation matricies
+ * that are applied to the data to rotate from the mounting orientation to the
+ * platform orientation. The values must be one of 0, 1, or -1 and each row and
+ * column should have exactly 1 non-zero value.
+ */
+struct mpu_platform_data {
+ __u8 int_config;
+ __u8 level_shifter;
+ __s8 orientation[9];
+ enum secondary_slave_type sec_slave_type;
+ enum ext_slave_id sec_slave_id;
+ __u16 secondary_i2c_addr;
+ __s8 secondary_orientation[9];
+ enum secondary_slave_type aux_slave_type;
+ enum ext_slave_id aux_slave_id;
+ __u16 aux_i2c_addr;
+ enum secondary_slave_type read_only_slave_type;
+ enum ext_slave_id read_only_slave_id;
+ __u16 read_only_i2c_addr;
+#ifdef CONFIG_OF
+ int (*power_on)(struct mpu_platform_data *);
+ int (*power_off)(struct mpu_platform_data *);
+ struct regulator *vdd_ana;
+ struct regulator *vdd_i2c;
+#endif
+};
+
+#endif /* __MPU_H_ */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 64d0797cc3a7..6828063842df 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -260,6 +260,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 4545f2cd3826..f04daf310182 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -128,7 +128,7 @@ int adm_pack_and_set_one_pp_param(int port_id, int copp_idx,
int adm_open(int port, int path, int rate, int mode, int topology,
int perf_mode, uint16_t bits_per_sample,
- int app_type, int acdbdev_id);
+ int app_type, int acdbdev_id, u32 copp_token);
int adm_map_rtac_block(struct rtac_cal_block_data *cal_block);
diff --git a/include/uapi/media/ais/msm_ais_isp.h b/include/uapi/media/ais/msm_ais_isp.h
index 2b4f0bfeb8c2..649173dd4404 100644
--- a/include/uapi/media/ais/msm_ais_isp.h
+++ b/include/uapi/media/ais/msm_ais_isp.h
@@ -936,6 +936,13 @@ struct msm_vfe_axi_output_plane_cfg {
uint32_t frame_increment;
};
+struct msm_vfe_axi_framedrop_update {
+ enum msm_vfe_axi_stream_src stream_src;
+
+ uint8_t framedrop_period;
+ uint32_t framedrop_pattern;
+};
+
struct msm_vfe_axi_output_path_cfg {
uint8_t enable;
@@ -1003,6 +1010,7 @@ enum msm_isp_ioctl_cmd_code {
MSM_ISP_SET_CLK_STATUS,
MSM_ISP_CMD_EXT,
+ MSM_ISP_FRAMEDROP_UPDATE,
};
@@ -1130,6 +1138,10 @@ enum msm_isp_ioctl_cmd_code {
_IOWR('V', MSM_ISP_AXI_OUTPUT_CFG, \
struct msm_vfe_axi_output_cfg)
+#define VIDIOC_MSM_ISP_FRAMEDROP_UPDATE \
+ _IOWR('V', MSM_ISP_FRAMEDROP_UPDATE, \
+ struct msm_vfe_axi_output_cfg)
+
#define VIDIOC_MSM_ISP_CAMIF_CFG \
_IOWR('V', MSM_ISP_CAMIF_CFG, \
struct msm_vfe_camif_cfg)
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 1535c46808f1..29771c69383b 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -30,6 +30,7 @@
#include <linux/genhd.h>
#include <linux/ktime.h>
#include <trace/events/power.h>
+#include <soc/qcom/boot_stats.h>
#include "power.h"
@@ -469,6 +470,7 @@ static int resume_target_kernel(bool platform_mode)
touch_softlockup_watchdog();
syscore_resume();
+ place_marker("PM: Image Restoration failed!");
Enable_irqs:
local_irq_enable();
@@ -705,6 +707,7 @@ int hibernate(void)
pm_restore_gfp_mask();
} else {
pr_debug("PM: Image restored successfully.\n");
+ place_marker("PM: Image restored!");
}
Free_bitmaps:
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 392d4e2c0a24..e9a2ff863d9b 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4156,6 +4156,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
/* Added to avoid minimal code churn */
struct tpacket_req *req = &req_u->req;
+ lock_sock(sk);
/* Opening a Tx-ring is NOT supported in TPACKET_V3 */
if (!closing && tx_ring && (po->tp_version > TPACKET_V2)) {
WARN(1, "Tx-ring is not supported.\n");
@@ -4291,6 +4292,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
if (pg_vec)
free_pg_vec(pg_vec, order, req->tp_block_nr);
out:
+ release_sock(sk);
return err;
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f08ed375bb91..4490dec28f50 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -13807,7 +13807,8 @@ void cfg80211_ft_event(struct net_device *netdev,
if (!ft_event->target_ap)
return;
- msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
+ msg = nlmsg_new(100 + ft_event->ies_len + ft_event->ric_ies_len,
+ GFP_KERNEL);
if (!msg)
return;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 514380104944..180261da33c9 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -976,9 +976,9 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_runtime *runtime = substream->runtime;
unsigned long appl_ptr;
- spin_lock_irqsave(&runtime->lock, flags);
if (userbuf)
mutex_lock(&runtime->realloc_mutex);
+ spin_lock_irqsave(&runtime->lock, flags);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
index 43f00dcff7af..f4546f4fdcc2 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -86,6 +86,8 @@ static int msm_digcdc_clock_control(bool flag)
if (flag) {
mutex_lock(&pdata->cdc_int_mclk0_mutex);
if (atomic_read(&pdata->int_mclk0_enabled) == false) {
+ if (msm_dig_cdc->regmap->cache_only == true)
+ return ret;
if (pdata->native_clk_set)
pdata->digital_cdc_core_clk.clk_freq_in_hz =
NATIVE_MCLK_RATE;
@@ -103,8 +105,7 @@ static int msm_digcdc_clock_control(bool flag)
* Avoid access to lpass register
* as clock enable failed during SSR.
*/
- if (ret == -ENODEV)
- msm_dig_cdc->regmap->cache_only = true;
+ msm_dig_cdc->regmap->cache_only = true;
return ret;
}
pr_debug("enabled digital codec core clk\n");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
index 9af5de2952d4..9420534d3c5f 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
@@ -570,16 +570,19 @@ static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_RX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -610,9 +613,12 @@ static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+
done:
return ret;
}
@@ -623,16 +629,19 @@ static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_TX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -663,9 +672,11 @@ static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
done:
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 9ab17d87b268..1e69ddcc3464 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -660,6 +660,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream)
if (!prtd->audio_client) {
pr_info("%s: Could not allocate memory\n", __func__);
kfree(prtd);
+ prtd = NULL;
return -ENOMEM;
}
@@ -1135,6 +1136,12 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
}
prtd = substream->runtime->private_data;
+ if (prtd == NULL) {
+ pr_err("%s prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
if (prtd->audio_client == NULL) {
pr_err("%s prtd is null.\n", __func__);
ret = -EINVAL;
@@ -1601,16 +1608,19 @@ static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_RX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -1641,9 +1651,11 @@ static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
done:
return ret;
}
@@ -2027,16 +2039,19 @@ static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_TX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -2067,9 +2082,11 @@ static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
done:
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 4e4970b7be33..532b73e7a127 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -116,6 +116,7 @@ enum {
#define QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX"
#define ADM_LSM_TX_TEXT "ADM_LSM_TX"
#define INT3_MI2S_TX_TEXT "INT3_MI2S_TX"
+#define QUAT_TDM_TX_0_TEXT "QUAT_TDM_TX_0"
#define LSM_FUNCTION_TEXT "LSM Function"
static const char * const lsm_port_text[] = {
@@ -123,7 +124,7 @@ static const char * const lsm_port_text[] = {
SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT,
SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT,
TERT_MI2S_TX_TEXT, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT,
- INT3_MI2S_TX_TEXT
+ INT3_MI2S_TX_TEXT, QUAT_TDM_TX_0_TEXT
};
struct msm_pcm_route_bdai_pp_params {
@@ -834,10 +835,10 @@ int msm_pcm_routing_reg_stream_app_type_cfg(
goto done;
}
- pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d copp_token %d\n",
__func__, fedai_id, session_type, be_id,
cfg_data->app_type, cfg_data->acdb_dev_id,
- cfg_data->sample_rate);
+ cfg_data->sample_rate, cfg_data->copp_token);
if (!is_mm_lsm_fe_id(fedai_id)) {
pr_err("%s: Invalid machine driver ID %d\n",
@@ -920,10 +921,10 @@ int msm_pcm_routing_get_stream_app_type_cfg(
*bedai_id = be_id;
*cfg_data = fe_dai_app_type_cfg[fedai_id][session_type][be_id];
- pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d copp_token %d\n",
__func__, fedai_id, session_type, *bedai_id,
cfg_data->app_type, cfg_data->acdb_dev_id,
- cfg_data->sample_rate);
+ cfg_data->sample_rate, cfg_data->copp_token);
done:
return ret;
}
@@ -1141,6 +1142,7 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
u32 channels, sample_rate;
u16 bit_width = 16;
bool is_lsm;
+ u32 copp_token = 0;
pr_debug("%s:fe_id[%d] perf_mode[%d] id[%d] stream_type[%d] passt[%d]",
__func__, fe_id, perf_mode, dspst_id,
@@ -1224,6 +1226,8 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
fe_dai_app_type_cfg[fe_id][session_type][i].sample_rate;
bit_width =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[fe_id][session_type][i].copp_token;
} else {
sample_rate = msm_bedais[i].sample_rate;
}
@@ -1243,7 +1247,7 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
adm_open(msm_bedais[i].port_id,
path_type, sample_rate, channels,
topology, perf_mode, bit_width,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s:adm open failed coppid:%d\n",
@@ -1476,6 +1480,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
uint16_t bits_per_sample = 16;
uint32_t passthr_mode = LEGACY_PCM;
int ret = 0;
+ u32 copp_token = 0;
if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
/* bad ID assigned in machine driver */
@@ -1531,6 +1536,9 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
.sample_rate;
bits_per_sample =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[fedai_id][session_type][i]
+ .copp_token;
} else
sample_rate = msm_bedais[i].sample_rate;
@@ -1543,7 +1551,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
copp_idx = adm_open(msm_bedais[i].port_id, path_type,
sample_rate, channels, topology,
perf_mode, bits_per_sample,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s: adm open failed copp_idx:%d\n",
@@ -1716,6 +1724,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
struct msm_pcm_routing_fdai_data *fdai;
uint32_t passthr_mode;
bool is_lsm;
+ u32 copp_token = 0;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -1803,6 +1812,9 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
.sample_rate;
bits_per_sample =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[val][session_type][reg]
+ .copp_token;
} else
sample_rate = msm_bedais[reg].sample_rate;
@@ -1814,7 +1826,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
copp_idx = adm_open(msm_bedais[reg].port_id, path_type,
sample_rate, channels, topology,
fdai->perf_mode, bits_per_sample,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s: adm open failed\n", __func__);
@@ -2547,6 +2559,9 @@ static int msm_routing_lsm_port_put(struct snd_kcontrol *kcontrol,
case 10:
lsm_port = AFE_PORT_ID_INT3_MI2S_TX;
break;
+ case 11:
+ lsm_port = AFE_PORT_ID_QUATERNARY_TDM_TX;
+ break;
default:
pr_err("Default lsm port");
break;
@@ -2564,7 +2579,7 @@ static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
u16 port_id;
enum afe_mad_type mad_type;
- pr_debug("%s: enter\n", __func__);
+ pr_debug("%s: id.name=%s\n", __func__, kcontrol->id.name);
for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++)
if (!strnstr(kcontrol->id.name, lsm_port_text[i],
strlen(lsm_port_text[i])))
@@ -2590,6 +2605,10 @@ static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
strlen(lsm_port_text[10])))
port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ if (strnstr(kcontrol->id.name, lsm_port_text[11],
+ strlen(lsm_port_text[11])))
+ port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
mad_type = afe_port_get_mad_type(port_id);
pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
mad_type);
@@ -2623,7 +2642,7 @@ static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
u16 port_id;
enum afe_mad_type mad_type;
- pr_debug("%s: enter\n", __func__);
+ pr_debug("%s: id.name=%s\n", __func__, kcontrol->id.name);
for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++)
if (strnstr(kcontrol->id.name, lsm_port_text[i],
strlen(lsm_port_text[i])))
@@ -2669,6 +2688,10 @@ static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
strlen(lsm_port_text[10])))
port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ if (strnstr(kcontrol->id.name, lsm_port_text[11],
+ strlen(lsm_port_text[11])))
+ port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
mad_type);
return afe_port_set_mad_type(port_id, mad_type);
@@ -11049,6 +11072,9 @@ static const struct snd_kcontrol_new lsm1_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm2_mixer_controls[] = {
@@ -11076,6 +11102,9 @@ static const struct snd_kcontrol_new lsm2_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm3_mixer_controls[] = {
@@ -11103,6 +11132,9 @@ static const struct snd_kcontrol_new lsm3_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm4_mixer_controls[] = {
@@ -11130,6 +11162,9 @@ static const struct snd_kcontrol_new lsm4_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm5_mixer_controls[] = {
@@ -11157,6 +11192,9 @@ static const struct snd_kcontrol_new lsm5_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm6_mixer_controls[] = {
@@ -11184,6 +11222,9 @@ static const struct snd_kcontrol_new lsm6_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm7_mixer_controls[] = {
@@ -11211,6 +11252,9 @@ static const struct snd_kcontrol_new lsm7_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm8_mixer_controls[] = {
@@ -11238,6 +11282,9 @@ static const struct snd_kcontrol_new lsm8_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new slim_fm_switch_mixer_controls =
@@ -11354,6 +11401,8 @@ static const struct snd_kcontrol_new lsm_controls[] = {
msm_routing_lsm_func_get, msm_routing_lsm_func_put),
SOC_ENUM_EXT(INT3_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(QUAT_TDM_TX_0_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
/* kcontrol of lsm_port */
SOC_ENUM_EXT("LSM1 Port", lsm_port_enum,
msm_routing_lsm_port_get,
@@ -11379,6 +11428,9 @@ static const struct snd_kcontrol_new lsm_controls[] = {
SOC_ENUM_EXT("LSM8 Port", lsm_port_enum,
msm_routing_lsm_port_get,
msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM9 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
};
static const char * const aanc_slim_0_rx_text[] = {
@@ -15200,6 +15252,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM1_UL_HL", NULL, "LSM1 Mixer"},
{"LSM2 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15210,6 +15263,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM2_UL_HL", NULL, "LSM2 Mixer"},
@@ -15221,6 +15275,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM3_UL_HL", NULL, "LSM3 Mixer"},
@@ -15232,6 +15287,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM4 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM4 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM4 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM4_UL_HL", NULL, "LSM4 Mixer"},
{"LSM5 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15242,6 +15298,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM5_UL_HL", NULL, "LSM5 Mixer"},
{"LSM6 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15250,6 +15307,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM6 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
{"LSM6 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
{"LSM6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM6_UL_HL", NULL, "LSM6 Mixer"},
{"LSM7 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15258,6 +15316,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM7 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
{"LSM7 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
{"LSM7 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM7 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM7_UL_HL", NULL, "LSM7 Mixer"},
{"LSM8 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15266,6 +15325,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM8 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
{"LSM8 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
{"LSM8 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM8_UL_HL", NULL, "LSM8 Mixer"},
@@ -16127,6 +16187,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
u32 session_id;
struct media_format_info voc_be_media_format;
bool is_lsm;
+ u32 copp_token = 0;
pr_debug("%s: substream->pcm->id:%s\n",
__func__, substream->pcm->id);
@@ -16204,6 +16265,9 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
[be_id].sample_rate;
bits_per_sample =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[i][session_type]
+ [be_id].copp_token;
} else
sample_rate = bedai->sample_rate;
/*
@@ -16221,7 +16285,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
copp_idx = adm_open(bedai->port_id, path_type,
sample_rate, channels, topology,
fdai->perf_mode, bits_per_sample,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s: adm open failed\n", __func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 63c90dc35c90..531f83d752b0 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -465,6 +465,7 @@ struct msm_pcm_stream_app_type_cfg {
int app_type;
int acdb_dev_id;
int sample_rate;
+ u32 copp_token;
};
/* dai_id: front-end ID,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 76d8f8d9e33c..1b8150e5a30f 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,8 +28,15 @@
#include <linux/of_device.h>
#include "msm-pcm-voice-v2.h"
+#include "msm-qti-pp-config.h"
#include "q6voice.h"
+struct msm_dtmf_detected_event_data {
+ uint32_t event_type;
+ uint32_t payload_len;
+ struct vss_istream_evt_rx_dtmf_detected dtmf_payload;
+};
+
static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX];
static struct snd_pcm_hardware msm_pcm_hardware = {
@@ -52,6 +59,44 @@ static struct snd_pcm_hardware msm_pcm_hardware = {
.fifo_size = 0,
};
+
+static int get_idx_for_session(uint32_t session_id)
+{
+ int idx = 0;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ idx = VOICE_SESSION_INDEX;
+ break;
+ case VOICE2_SESSION_VSID:
+ idx = VOICE2_SESSION_INDEX;
+ break;
+ case VOLTE_SESSION_VSID:
+ idx = VOLTE_SESSION_INDEX;
+ break;
+ case QCHAT_SESSION_VSID:
+ idx = QCHAT_SESSION_INDEX;
+ break;
+ case VOWLAN_SESSION_VSID:
+ idx = VOWLAN_SESSION_INDEX;
+ break;
+ case VOICEMMODE1_VSID:
+ idx = VOICEMMODE1_INDEX;
+ break;
+ case VOICEMMODE2_VSID:
+ idx = VOICEMMODE2_INDEX;
+ break;
+ case ALL_SESSION_VSID:
+ idx = VOICE_SESSION_INDEX_MAX - 1;
+ break;
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+ break;
+ }
+
+ return idx;
+}
+
static bool is_volte(struct msm_voice *pvolte)
{
if (pvolte == &voice_info[VOLTE_SESSION_INDEX])
@@ -122,6 +167,38 @@ static uint32_t get_session_id(struct msm_voice *pvoc)
return session_id;
}
+static void dtmf_rx_detected_evt_hdlr(uint8_t *pkt,
+ char *session,
+ void *private_data)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *soc_prtd = private_data;
+ struct msm_dtmf_detected_event_data event_data = {ADSP_STREAM_PP_EVENT,
+ sizeof(struct vss_istream_evt_rx_dtmf_detected),
+ {0} };
+
+ if (!pkt) {
+ pr_err("%s: packet is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (!private_data) {
+ pr_err("%s: private_data is NULL\n",
+ __func__);
+ return;
+ }
+
+ memcpy(&event_data.dtmf_payload, pkt,
+ sizeof(struct vss_istream_evt_rx_dtmf_detected));
+
+ ret = msm_adsp_inform_mixer_ctl(soc_prtd, (uint32_t *)&event_data);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+}
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
@@ -151,6 +228,7 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
static int msm_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_voice *voice;
if (!strncmp("VoLTE", substream->pcm->id, 5)) {
@@ -197,7 +275,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream)
runtime->private_data = voice;
mutex_unlock(&voice->lock);
-
+ msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd);
return 0;
}
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
@@ -231,6 +309,7 @@ static int msm_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_voice *prtd = runtime->private_data;
uint32_t session_id = 0;
int ret = 0;
@@ -250,6 +329,7 @@ static int msm_pcm_close(struct snd_pcm_substream *substream)
voc_end_voice_call(session_id);
}
mutex_unlock(&prtd->lock);
+ msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
return ret;
}
@@ -583,6 +663,41 @@ static int msm_voice_hd_voice_put(struct snd_kcontrol *kcontrol,
return ret;
}
+static int msm_dtmf_detect_rx_vsid_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t session_id = ucontrol->value.integer.value[0];
+ uint32_t enable = ucontrol->value.integer.value[1];
+
+ if (enable)
+ enable = 1;
+
+ pr_debug("%s: sess_id=%d enable=%d\n", __func__, session_id, enable);
+
+ return voc_enable_dtmf_rx_detection(session_id, enable);
+}
+
+static int msm_dtmf_detect_rx_vsid_cb_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_voice *dtmf_voice_info = NULL;
+ uint32_t session_id = ucontrol->value.integer.value[0];
+ uint32_t enable = ucontrol->value.integer.value[1];
+
+ if (enable)
+ enable = 1;
+
+ pr_debug("%s: enable dtmf detect cb =%d for session_id=%d\n",
+ __func__, enable, session_id);
+
+ dtmf_voice_info = &voice_info[get_idx_for_session(session_id)];
+ voc_register_dtmf_rx_detection_cb
+ ((dtmf_rx_det_cb_fn) dtmf_rx_detected_evt_hdlr,
+ (void *) dtmf_voice_info->capture_substream->private_data);
+
+ return 0;
+}
+
static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -636,6 +751,7 @@ static int msm_voice_cvd_version_get(struct snd_kcontrol *kcontrol,
return 0;
}
+
static struct snd_kcontrol_new msm_voice_controls[] = {
SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
0, 3, NULL, msm_voice_rx_device_mute_put),
@@ -654,6 +770,12 @@ static struct snd_kcontrol_new msm_voice_controls[] = {
msm_voice_topology_disable_put),
SOC_SINGLE_MULTI_EXT("HD Voice Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
NULL, msm_voice_hd_voice_put),
+ SOC_SINGLE_MULTI_EXT("DTMF_Detect Rx VSID enable",
+ SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_dtmf_detect_rx_vsid_put),
+ SOC_SINGLE_MULTI_EXT("DTMF_Detect Rx Callback VSID enable",
+ SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_dtmf_detect_rx_vsid_cb_put),
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -666,6 +788,118 @@ static struct snd_kcontrol_new msm_voice_controls[] = {
};
+static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_pcm_add_voice_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_voice_adsp_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_pcm_adsp_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s rtd is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_voice_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_voice_adsp_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s\n", mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_voice_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_voice_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+ return ret;
+}
+
+static int msm_pcm_add_voice_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_voice_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_voice_adsp_callback_config_control[0].name = mixer_str;
+ fe_voice_adsp_callback_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_voice_adsp_callback_config_control,
+ ARRAY_SIZE(fe_voice_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+
+free_mixer_str:
+ kfree(mixer_str);
+ return ret;
+}
+
static struct snd_pcm_ops msm_pcm_ops = {
.open = msm_pcm_open,
.hw_params = msm_pcm_hw_params,
@@ -684,6 +918,17 @@ static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_voice_adsp_stream_cmd_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n",
+ __func__);
+
+ ret = msm_pcm_add_voice_adsp_stream_callback_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Callback Control\n",
+ __func__);
+
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 24d4199ac30b..c6f3b5ba96ba 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -67,6 +67,7 @@ struct adm_copp {
atomic_t adm_delay_stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t token[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
};
struct source_tracking_data {
@@ -102,6 +103,7 @@ struct adm_ctl {
int ec_ref_rx_sampling_rate;
int native_mode;
+ u32 copp_token;
};
static struct adm_ctl this_adm;
@@ -222,14 +224,45 @@ static int adm_get_copp_id(int port_idx, int copp_idx)
return atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
}
+static int adm_get_idx_if_single_copp_exists(int port_idx,
+ int topology, int mode,
+ int rate, int bit_width,
+ u32 copp_token)
+{
+ int idx;
+
+ pr_debug("%s: copp_token %d\n", __func__, copp_token);
+
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if ((topology ==
+ atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
+ (mode ==
+ atomic_read(&this_adm.copp.mode[port_idx][idx])) &&
+ (rate ==
+ atomic_read(&this_adm.copp.rate[port_idx][idx])) &&
+ (bit_width ==
+ atomic_read(&this_adm.copp.bit_width[port_idx][idx])) &&
+ (copp_token ==
+ atomic_read(&this_adm.copp.token[port_idx][idx])))
+ return idx;
+ return -EINVAL;
+}
+
static int adm_get_idx_if_copp_exists(int port_idx, int topology, int mode,
- int rate, int bit_width, int app_type)
+ int rate, int bit_width, int app_type,
+ u32 copp_token)
{
int idx;
pr_debug("%s: port_idx-%d, topology-0x%x, mode-%d, rate-%d, bit_width-%d\n",
__func__, port_idx, topology, mode, rate, bit_width);
+ if (copp_token)
+ return adm_get_idx_if_single_copp_exists(port_idx,
+ topology, mode,
+ rate, bit_width,
+ copp_token);
+
for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
if ((topology ==
atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
@@ -949,7 +982,7 @@ int adm_get_pp_params(int port_id, int copp_idx, uint32_t client_id,
copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
atomic_set(copp_stat, -1);
ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_params);
- if (ret) {
+ if (ret < 0) {
pr_err("%s: Get params APR send failed port = 0x%x ret %d\n",
__func__, port_id, ret);
ret = -EINVAL;
@@ -1064,7 +1097,7 @@ int adm_get_pp_topo_module_list_v2(int port_id, int copp_idx,
copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
atomic_set(copp_stat, -1);
ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_module_list);
- if (ret) {
+ if (ret < 0) {
pr_err("%s: APR send pkt failed for port_id: 0x%x failed ret %d\n",
__func__, port_id, ret);
ret = -EINVAL;
@@ -2804,7 +2837,8 @@ static int adm_open_v5_v6(int tmp_port, int port_idx, int copp_idx,
}
int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
- int perf_mode, uint16_t bit_width, int app_type, int acdb_id)
+ int perf_mode, uint16_t bit_width, int app_type, int acdb_id,
+ u32 copp_token)
{
int ret = 0;
int port_idx, flags;
@@ -2871,7 +2905,7 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
copp_idx = adm_get_idx_if_copp_exists(port_idx, topology,
perf_mode,
rate, bit_width,
- app_type);
+ app_type, copp_token);
if (copp_idx < 0) {
copp_idx = adm_get_next_available_copp(port_idx);
@@ -2895,6 +2929,8 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
app_type);
atomic_set(&this_adm.copp.acdb_id[port_idx][copp_idx],
acdb_id);
+ atomic_set(&this_adm.copp.token[port_idx][copp_idx],
+ copp_token);
set_bit(ADM_STATUS_CALIBRATION_REQUIRED,
(void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
if ((path != ADM_PATH_COMPRESSED_RX) &&
@@ -3432,6 +3468,7 @@ int adm_close(int port_id, int perf_mode, int copp_idx)
atomic_set(&this_adm.copp.channels[port_idx][copp_idx], 0);
atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], 0);
atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.token[port_idx][copp_idx], 0);
clear_bit(ADM_STATUS_CALIBRATION_REQUIRED,
(void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 38dc3639a682..0524ca21feba 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -2374,7 +2374,8 @@ int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type)
int i;
if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX ||
- port_id == AFE_PORT_ID_INT3_MI2S_TX) {
+ port_id == AFE_PORT_ID_INT3_MI2S_TX ||
+ port_id == AFE_PORT_ID_QUATERNARY_TDM_TX) {
mad_type = MAD_SW_AUDIO;
return 0;
}
@@ -2393,7 +2394,8 @@ enum afe_mad_type afe_port_get_mad_type(u16 port_id)
int i;
if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX ||
- port_id == AFE_PORT_ID_INT3_MI2S_TX)
+ port_id == AFE_PORT_ID_INT3_MI2S_TX ||
+ port_id == AFE_PORT_ID_QUATERNARY_TDM_TX)
return MAD_SW_AUDIO;
i = port_id - SLIMBUS_0_RX;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 9d3fa1afeb6d..0ad15e90bfc6 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1086,7 +1086,7 @@ int q6asm_unmap_rtac_block(uint32_t *mem_map_handle)
__func__, result2);
result = result2;
} else {
- mem_map_handle = 0;
+ *mem_map_handle = 0;
}
result2 = q6asm_mmap_apr_dereg();
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 17d78704709f..69189140c936 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -6861,8 +6861,8 @@ static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n");
} else if (data->opcode == VSS_ISTREAM_EVT_READY) {
pr_debug("Recd VSS_ISTREAM_EVT_READY\n");
- } else if (data->opcode == VSS_ICOMMON_RSP_GET_PARAM ||
- VSS_ICOMMON_RSP_GET_PARAM_V3) {
+ } else if ((data->opcode == VSS_ICOMMON_RSP_GET_PARAM) ||
+ (data->opcode == VSS_ICOMMON_RSP_GET_PARAM_V3)) {
pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__);
ptr = data->payload;
if (ptr[0] != 0) {
@@ -6871,7 +6871,7 @@ static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
}
rtac_make_voice_callback(RTAC_CVS, data->payload,
data->payload_size);
- } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
+ } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected;
uint32_t *voc_pkt = data->payload;
uint32_t pkt_len = data->payload_size;
diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c
index 5e33fb508455..82d954c646dc 100644
--- a/sound/soc/msm/qdsp6v2/rtac.c
+++ b/sound/soc/msm/qdsp6v2/rtac.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -1349,7 +1349,7 @@ static int send_rtac_afe_apr(void __user *buf, uint32_t opcode)
if (copy_from_user(rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
(void __user *) buf +
offsetof(struct rtac_afe_user_data,
- v3_get.param_hdr),
+ v3_set.param_hdr),
payload_size)) {
pr_err("%s: Could not copy payload from user buffer\n",
__func__);
diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c
index 259763449456..4e2a40319b02 100644
--- a/sound/soc/msm/sdm660-internal.c
+++ b/sound/soc/msm/sdm660-internal.c
@@ -1379,6 +1379,8 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
snd_soc_dapm_ignore_suspend(dapm, "EAR");
snd_soc_dapm_ignore_suspend(dapm, "HEADPHONE");