diff options
574 files changed, 16700 insertions, 3063 deletions
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index b8d0a30f1644..f82da9bbb1fd 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -101,6 +101,7 @@ Date: February 2015 Contact: "Jaegeuk Kim" <jaegeuk@kernel.org> Description: Controls the trimming rate in batch mode. + <deprecated> What: /sys/fs/f2fs/<disk>/cp_interval Date: October 2015 diff --git a/Documentation/device-mapper/thin-provisioning.txt b/Documentation/device-mapper/thin-provisioning.txt index 1699a55b7b70..ef639960b272 100644 --- a/Documentation/device-mapper/thin-provisioning.txt +++ b/Documentation/device-mapper/thin-provisioning.txt @@ -112,9 +112,11 @@ $low_water_mark is expressed in blocks of size $data_block_size. If free space on the data device drops below this level then a dm event will be triggered which a userspace daemon should catch allowing it to extend the pool device. Only one such event will be sent. -Resuming a device with a new table itself triggers an event so the -userspace daemon can use this to detect a situation where a new table -already exceeds the threshold. + +No special event is triggered if a just resumed device's free space is below +the low water mark. However, resuming a device always triggers an +event; a userspace daemon should verify that free space exceeds the low +water mark when handling this event. A low water mark for the metadata device is maintained in the kernel and will trigger a dm event if free space on the metadata device drops below diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt index 1583da81c090..9472af5de4e7 100644 --- a/Documentation/devicetree/bindings/display/msm/sde.txt +++ b/Documentation/devicetree/bindings/display/msm/sde.txt @@ -247,7 +247,19 @@ Optional properties: applied in scenarios where panel interface can be more tolerant to memory latency such as command mode panels. +- qcom,sde-mixer-display-pref: A string array indicating the preferred display type + for the mixer block. Possible values: + "primary" - preferred for primary display + "secondary" - preferred for secondary display + "tertiary" - preferred for tertiary display + "none" - no preference for display +- qcom,sde-ctl-display-pref: A string array indicating the preferred display type + for the ctl block. Possible values: + "primary" - preferred for primary display + "secondary" - preferred for secondary display + "tertiary" - preferred for tertiary display + "none" - no preference for display Bus Scaling Subnodes: - qcom,sde-reg-bus: Property to provide Bus scaling for register access for mdss blocks. diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-diag.txt b/Documentation/devicetree/bindings/media/video/msm-cam-diag.txt new file mode 100644 index 000000000000..6dfe8ffac828 --- /dev/null +++ b/Documentation/devicetree/bindings/media/video/msm-cam-diag.txt @@ -0,0 +1,164 @@ +* Qualcomm Technologies, Inc. MSM Camera diag + +[Root level node] +================== +Required properties: +- compatible: Must be "qcom,diag-cam". + +[Subnode] +========== +Required properties: +- mmagic-supply: should contain mmagic regulator used for mmagic clocks. +- gdscr-supply: should contain gdsr regulator used for cci clocks. +- vfe0-vdd-supply: phandle to vfe0 regulator. +- qcom,cam-vreg-name: name of the voltage regulators required for the device. +- clocks: List of clock handles. The parent clocks of the input clocks to the + devices in this power domain are set to oscclk before power gating + and restored back after powering on a domain. This is required for + all domains which are powered on and off and not required for unused + domains. +- clock-names: name of the clock used by the driver. +- qcom,clock-rates: clock rate in Hz. +- qcom,clock-control: The valid fields are "NO_SET_RATE", "INIT_RATE" and + "SET_RATE". "NO_SET_RATE" the corresponding clock is enabled without setting + the rate assuming some other driver has already set it to appropriate rate. + "INIT_RATE" clock rate is not queried assuming some other driver has set + the clock rate and ispif will set the the clock to this rate. + "SET_RATE" clock is enabled and the rate is set to the value specified + in the property qcom,clock-rates. + +Example: + + qcom,diag-cam { + cell-index = <0>; + compatible = "qcom,diag-cam"; + status = "ok"; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + vfe0-vdd-supply = <&gdsc_vfe0>; + vfe1-vdd-supply = <&gdsc_vfe1>; + qcom,cam-vreg-name = "mmagic", "gdscr", + "vfe0-vdd", "vfe1-vdd"; + clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>, + <&clock_mmss clk_camss_top_ahb_clk>, + <&clock_mmss clk_camss_ispif_ahb_clk>, + <&clock_mmss clk_csi0phytimer_clk_src>, + <&clock_mmss clk_camss_csi0phytimer_clk>, + <&clock_mmss clk_camss_ahb_clk>, + <&clock_mmss clk_csi1phytimer_clk_src>, + <&clock_mmss clk_camss_csi1phytimer_clk>, + <&clock_mmss clk_csi2phytimer_clk_src>, + <&clock_mmss clk_camss_csi2phytimer_clk>, + <&clock_mmss clk_csi0_clk_src>, + <&clock_mmss clk_camss_csi0_clk>, + <&clock_mmss clk_camss_csi0phy_clk>, + <&clock_mmss clk_camss_csi0_ahb_clk>, + <&clock_mmss clk_camss_csi0rdi_clk>, + <&clock_mmss clk_camss_csi0pix_clk>, + <&clock_mmss clk_csi1_clk_src>, + <&clock_mmss clk_camss_csi1_clk>, + <&clock_mmss clk_camss_csi1phy_clk>, + <&clock_mmss clk_camss_csi1_ahb_clk>, + <&clock_mmss clk_camss_csi1rdi_clk>, + <&clock_mmss clk_camss_csi1pix_clk>, + <&clock_mmss clk_csi2_clk_src>, + <&clock_mmss clk_camss_csi2_clk>, + <&clock_mmss clk_camss_csi2phy_clk>, + <&clock_mmss clk_camss_csi2_ahb_clk>, + <&clock_mmss clk_camss_csi2rdi_clk>, + <&clock_mmss clk_camss_csi2pix_clk>, + <&clock_mmss clk_csi3_clk_src>, + <&clock_mmss clk_camss_csi3_clk>, + <&clock_mmss clk_camss_csi3phy_clk>, + <&clock_mmss clk_camss_csi3_ahb_clk>, + <&clock_mmss clk_camss_csi3rdi_clk>, + <&clock_mmss clk_camss_csi3pix_clk>, + <&clock_mmss clk_vfe0_clk_src>, + <&clock_mmss clk_camss_vfe0_clk>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_vfe1_clk_src>, + <&clock_mmss clk_camss_vfe1_clk>, + <&clock_mmss clk_camss_csi_vfe1_clk>, + <&clock_mmss clk_mmagic_camss_axi_clk>, + <&clock_mmss clk_camss_vfe_ahb_clk>, + <&clock_mmss clk_camss_vfe0_ahb_clk>, + <&clock_mmss clk_camss_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe0_stream_clk>, + <&clock_mmss clk_smmu_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe1_ahb_clk>, + <&clock_mmss clk_camss_vfe1_stream_clk>; + clock-names = + "clk_mmss_mmagic_ahb_clk", + "clk_camss_top_ahb_clk", + "clk_camss_ispif_ahb_clk", + "clk_csi0phytimer_clk_src", + "clk_camss_csi0phytimer_clk", + "clk_camss_ahb_clk", + "clk_csi1phytimer_clk_src", + "clk_camss_csi1phytimer_clk", + "clk_csi2phytimer_clk_src", + "clk_camss_csi2phytimer_clk", + "clk_csi0_clk_src", + "clk_camss_csi0_clk", + "clk_camss_csi0phy_clk", + "clk_camss_csi0_ahb_clk", + "clk_camss_csi0rdi_clk", + "clk_camss_csi0pix_clk", + "clk_csi1_clk_src", + "clk_camss_csi1_clk", + "clk_camss_csi1phy_clk", + "clk_camss_csi1_ahb_clk", + "clk_camss_csi1rdi_clk", + "clk_camss_csi1pix_clk", + "clk_csi2_clk_src", + "clk_camss_csi2_clk", + "clk_camss_csi2phy_clk", + "clk_camss_csi2_ahb_clk", + "clk_camss_csi2rdi_clk", + "clk_camss_csi2pix_clk", + "clk_csi3_clk_src", + "clk_camss_csi3_clk", + "clk_camss_csi3phy_clk", + "clk_camss_csi3_ahb_clk", + "clk_camss_csi3rdi_clk", + "clk_camss_csi3pix_clk", + "clk_vfe0_clk_src", + "clk_camss_vfe0_clk", + "clk_camss_csi_vfe0_clk", + "clk_vfe1_clk_src", + "clk_camss_vfe1_clk", + "clk_camss_csi_vfe1_clk", + "clk_mmagic_camss_axi_clk", + "clk_camss_vfe_ahb_clk", + "clk_camss_vfe0_ahb_clk", + "clk_camss_vfe_axi_clk", + "clk_camss_vfe0_stream_clk", + "clk_smmu_vfe_axi_clk", + "clk_camss_vfe1_ahb_clk", + "clk_camss_vfe1_stream_clk"; + qcom,clock-rates = <0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + >; + qcom,clock-cntl-support; + qcom,clock-control = "NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE", + "INIT_RATE","NO_SET_RATE","NO_SET_RATE", + "INIT_RATE","NO_SET_RATE","INIT_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE"; + }; + diff --git a/Documentation/devicetree/bindings/platform/msm/ssm.txt b/Documentation/devicetree/bindings/platform/msm/ssm.txt deleted file mode 100644 index 7df3efd66577..000000000000 --- a/Documentation/devicetree/bindings/platform/msm/ssm.txt +++ /dev/null @@ -1,13 +0,0 @@ -* Qualcomm Technologies, Inc. Secure Service Module driver - -This module enables framework to which a client can register itself -specifying different attributes and defining various permission levels -associated with different combination of attribute values and mode of the system. - -Required properties: -- compatible: Must be "qcom,ssm" - -Example: - qcom,ssm { - compatible = "qcom,ssm"; - };
\ No newline at end of file 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/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 1f52baea2f69..ecccb51c7279 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -180,13 +180,15 @@ whint_mode=%s Control which write hints are passed down to block passes down hints with its policy. alloc_mode=%s Adjust block allocation policy, which supports "reuse" and "default". -fsync_mode=%s Control the policy of fsync. Currently supports "posix" - and "strict". In "posix" mode, which is default, fsync - will follow POSIX semantics and does a light operation - to improve the filesystem performance. In "strict" mode, - fsync will be heavy and behaves in line with xfs, ext4 - and btrfs, where xfstest generic/342 will pass, but the - performance will regress. +fsync_mode=%s Control the policy of fsync. Currently supports "posix", + "strict", and "nobarrier". In "posix" mode, which is + default, fsync will follow POSIX semantics and does a + light operation to improve the filesystem performance. + In "strict" mode, fsync will be heavy and behaves in line + with xfs, ext4 and btrfs, where xfstest generic/342 will + pass, but the performance will regress. "nobarrier" is + based on "posix", but doesn't issue flush command for + non-atomic files likewise "nobarrier" mount option. test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt context. The fake fscrypt context is used by xfstests. diff --git a/Documentation/networking/netdev-FAQ.txt b/Documentation/networking/netdev-FAQ.txt index 0fe1c6e0dbcd..bfc6b3e68cc4 100644 --- a/Documentation/networking/netdev-FAQ.txt +++ b/Documentation/networking/netdev-FAQ.txt @@ -168,6 +168,15 @@ A: No. See above answer. In short, if you think it really belongs in dash marker line as described in Documentation/SubmittingPatches to temporarily embed that information into the patch that you send. +Q: Are all networking bug fixes backported to all stable releases? + +A: Due to capacity, Dave could only take care of the backports for the last + 2 stable releases. For earlier stable releases, each stable branch maintainer + is supposed to take care of them. If you find any patch is missing from an + earlier stable branch, please notify stable@vger.kernel.org with either a + commit ID or a formal patch backported, and CC Dave and other relevant + networking developers. + Q: Someone said that the comment style and coding convention is different for the networking content. Is this true? @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 133 +SUBLEVEL = 138 EXTRAVERSION = NAME = Blurry Fish Butt @@ -404,7 +404,9 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -Wno-format-security \ -std=gnu89 $(call cc-option,-fno-PIE) - +ifeq ($(TARGET_BOARD_TYPE),auto) +KBUILD_CFLAGS += -DCONFIG_PLATFORM_AUTO +endif KBUILD_AFLAGS_KERNEL := KBUILD_CFLAGS_KERNEL := KBUILD_AFLAGS := -D__ASSEMBLY__ $(call cc-option,-fno-PIE) diff --git a/arch/alpha/include/asm/xchg.h b/arch/alpha/include/asm/xchg.h index 0ca9724597c1..7081e52291d0 100644 --- a/arch/alpha/include/asm/xchg.h +++ b/arch/alpha/include/asm/xchg.h @@ -11,6 +11,10 @@ * Atomic exchange. * Since it can be used to implement critical sections * it must clobber "memory" (also for interrupts in UP). + * + * The leading and the trailing memory barriers guarantee that these + * operations are fully ordered. + * */ static inline unsigned long @@ -18,6 +22,7 @@ ____xchg(_u8, volatile char *m, unsigned long val) { unsigned long ret, tmp, addr64; + smp_mb(); __asm__ __volatile__( " andnot %4,7,%3\n" " insbl %1,%4,%1\n" @@ -42,6 +47,7 @@ ____xchg(_u16, volatile short *m, unsigned long val) { unsigned long ret, tmp, addr64; + smp_mb(); __asm__ __volatile__( " andnot %4,7,%3\n" " inswl %1,%4,%1\n" @@ -66,6 +72,7 @@ ____xchg(_u32, volatile int *m, unsigned long val) { unsigned long dummy; + smp_mb(); __asm__ __volatile__( "1: ldl_l %0,%4\n" " bis $31,%3,%1\n" @@ -86,6 +93,7 @@ ____xchg(_u64, volatile long *m, unsigned long val) { unsigned long dummy; + smp_mb(); __asm__ __volatile__( "1: ldq_l %0,%4\n" " bis $31,%3,%1\n" @@ -127,10 +135,12 @@ ____xchg(, volatile void *ptr, unsigned long x, int size) * store NEW in MEM. Return the initial value in MEM. Success is * indicated by comparing RETURN with OLD. * - * The memory barrier should be placed in SMP only when we actually - * make the change. If we don't change anything (so if the returned - * prev is equal to old) then we aren't acquiring anything new and - * we don't need any memory barrier as far I can tell. + * The leading and the trailing memory barriers guarantee that these + * operations are fully ordered. + * + * The trailing memory barrier is placed in SMP unconditionally, in + * order to guarantee that dependency ordering is preserved when a + * dependency is headed by an unsuccessful operation. */ static inline unsigned long @@ -138,6 +148,7 @@ ____cmpxchg(_u8, volatile char *m, unsigned char old, unsigned char new) { unsigned long prev, tmp, cmp, addr64; + smp_mb(); __asm__ __volatile__( " andnot %5,7,%4\n" " insbl %1,%5,%1\n" @@ -149,8 +160,8 @@ ____cmpxchg(_u8, volatile char *m, unsigned char old, unsigned char new) " or %1,%2,%2\n" " stq_c %2,0(%4)\n" " beq %2,3f\n" - __ASM__MB "2:\n" + __ASM__MB ".subsection 2\n" "3: br 1b\n" ".previous" @@ -165,6 +176,7 @@ ____cmpxchg(_u16, volatile short *m, unsigned short old, unsigned short new) { unsigned long prev, tmp, cmp, addr64; + smp_mb(); __asm__ __volatile__( " andnot %5,7,%4\n" " inswl %1,%5,%1\n" @@ -176,8 +188,8 @@ ____cmpxchg(_u16, volatile short *m, unsigned short old, unsigned short new) " or %1,%2,%2\n" " stq_c %2,0(%4)\n" " beq %2,3f\n" - __ASM__MB "2:\n" + __ASM__MB ".subsection 2\n" "3: br 1b\n" ".previous" @@ -192,6 +204,7 @@ ____cmpxchg(_u32, volatile int *m, int old, int new) { unsigned long prev, cmp; + smp_mb(); __asm__ __volatile__( "1: ldl_l %0,%5\n" " cmpeq %0,%3,%1\n" @@ -199,8 +212,8 @@ ____cmpxchg(_u32, volatile int *m, int old, int new) " mov %4,%1\n" " stl_c %1,%2\n" " beq %1,3f\n" - __ASM__MB "2:\n" + __ASM__MB ".subsection 2\n" "3: br 1b\n" ".previous" @@ -215,6 +228,7 @@ ____cmpxchg(_u64, volatile long *m, unsigned long old, unsigned long new) { unsigned long prev, cmp; + smp_mb(); __asm__ __volatile__( "1: ldq_l %0,%5\n" " cmpeq %0,%3,%1\n" @@ -222,8 +236,8 @@ ____cmpxchg(_u64, volatile long *m, unsigned long old, unsigned long new) " mov %4,%1\n" " stq_c %1,%2\n" " beq %1,3f\n" - __ASM__MB "2:\n" + __ASM__MB ".subsection 2\n" "3: br 1b\n" ".previous" diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 2d785f5a3041..c4ee25e88a7b 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -479,7 +479,6 @@ config ARC_CURR_IN_REG config ARC_EMUL_UNALIGNED bool "Emulate unaligned memory access (userspace only)" - default N select SYSCTL_ARCH_UNALIGN_NO_WARN select SYSCTL_ARCH_UNALIGN_ALLOW depends on ISA_ARCOMPACT 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/msm8996-auto-mizar.dts b/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts index 8c750dab8707..006f1a28bf55 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts +++ b/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts @@ -336,7 +336,6 @@ }; &pcie2 { - qcom,boot-option = <0x0>; perst-gpio = <&tlmm 90 0>; wake-gpio = <&tlmm 54 0>; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi index f3838785b38c..4f632cff79be 100644 --- a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi @@ -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 @@ -787,6 +787,139 @@ status = "disabled"; }; }; + + qcom,diag-cam { + cell-index = <0>; + compatible = "qcom,diag-cam"; + status = "ok"; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + vfe0-vdd-supply = <&gdsc_vfe0>; + vfe1-vdd-supply = <&gdsc_vfe1>; + qcom,cam-vreg-name = "mmagic", "gdscr", + "vfe0-vdd", "vfe1-vdd"; + clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>, + <&clock_mmss clk_camss_top_ahb_clk>, + <&clock_mmss clk_camss_ispif_ahb_clk>, + <&clock_mmss clk_csi0phytimer_clk_src>, + <&clock_mmss clk_camss_csi0phytimer_clk>, + <&clock_mmss clk_camss_ahb_clk>, + <&clock_mmss clk_csi1phytimer_clk_src>, + <&clock_mmss clk_camss_csi1phytimer_clk>, + <&clock_mmss clk_csi2phytimer_clk_src>, + <&clock_mmss clk_camss_csi2phytimer_clk>, + <&clock_mmss clk_csi0_clk_src>, + <&clock_mmss clk_camss_csi0_clk>, + <&clock_mmss clk_camss_csi0phy_clk>, + <&clock_mmss clk_camss_csi0_ahb_clk>, + <&clock_mmss clk_camss_csi0rdi_clk>, + <&clock_mmss clk_camss_csi0pix_clk>, + <&clock_mmss clk_csi1_clk_src>, + <&clock_mmss clk_camss_csi1_clk>, + <&clock_mmss clk_camss_csi1phy_clk>, + <&clock_mmss clk_camss_csi1_ahb_clk>, + <&clock_mmss clk_camss_csi1rdi_clk>, + <&clock_mmss clk_camss_csi1pix_clk>, + <&clock_mmss clk_csi2_clk_src>, + <&clock_mmss clk_camss_csi2_clk>, + <&clock_mmss clk_camss_csi2phy_clk>, + <&clock_mmss clk_camss_csi2_ahb_clk>, + <&clock_mmss clk_camss_csi2rdi_clk>, + <&clock_mmss clk_camss_csi2pix_clk>, + <&clock_mmss clk_csi3_clk_src>, + <&clock_mmss clk_camss_csi3_clk>, + <&clock_mmss clk_camss_csi3phy_clk>, + <&clock_mmss clk_camss_csi3_ahb_clk>, + <&clock_mmss clk_camss_csi3rdi_clk>, + <&clock_mmss clk_camss_csi3pix_clk>, + <&clock_mmss clk_vfe0_clk_src>, + <&clock_mmss clk_camss_vfe0_clk>, + <&clock_mmss clk_camss_csi_vfe0_clk>, + <&clock_mmss clk_vfe1_clk_src>, + <&clock_mmss clk_camss_vfe1_clk>, + <&clock_mmss clk_camss_csi_vfe1_clk>, + <&clock_mmss clk_mmagic_camss_axi_clk>, + <&clock_mmss clk_camss_vfe_ahb_clk>, + <&clock_mmss clk_camss_vfe0_ahb_clk>, + <&clock_mmss clk_camss_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe0_stream_clk>, + <&clock_mmss clk_smmu_vfe_axi_clk>, + <&clock_mmss clk_camss_vfe1_ahb_clk>, + <&clock_mmss clk_camss_vfe1_stream_clk>; + clock-names = + "clk_mmss_mmagic_ahb_clk", + "clk_camss_top_ahb_clk", + "clk_camss_ispif_ahb_clk", + "clk_csi0phytimer_clk_src", + "clk_camss_csi0phytimer_clk", + "clk_camss_ahb_clk", + "clk_csi1phytimer_clk_src", + "clk_camss_csi1phytimer_clk", + "clk_csi2phytimer_clk_src", + "clk_camss_csi2phytimer_clk", + "clk_csi0_clk_src", + "clk_camss_csi0_clk", + "clk_camss_csi0phy_clk", + "clk_camss_csi0_ahb_clk", + "clk_camss_csi0rdi_clk", + "clk_camss_csi0pix_clk", + "clk_csi1_clk_src", + "clk_camss_csi1_clk", + "clk_camss_csi1phy_clk", + "clk_camss_csi1_ahb_clk", + "clk_camss_csi1rdi_clk", + "clk_camss_csi1pix_clk", + "clk_csi2_clk_src", + "clk_camss_csi2_clk", + "clk_camss_csi2phy_clk", + "clk_camss_csi2_ahb_clk", + "clk_camss_csi2rdi_clk", + "clk_camss_csi2pix_clk", + "clk_csi3_clk_src", + "clk_camss_csi3_clk", + "clk_camss_csi3phy_clk", + "clk_camss_csi3_ahb_clk", + "clk_camss_csi3rdi_clk", + "clk_camss_csi3pix_clk", + "clk_vfe0_clk_src", + "clk_camss_vfe0_clk", + "clk_camss_csi_vfe0_clk", + "clk_vfe1_clk_src", + "clk_camss_vfe1_clk", + "clk_camss_csi_vfe1_clk", + "clk_mmagic_camss_axi_clk", + "clk_camss_vfe_ahb_clk", + "clk_camss_vfe0_ahb_clk", + "clk_camss_vfe_axi_clk", + "clk_camss_vfe0_stream_clk", + "clk_smmu_vfe_axi_clk", + "clk_camss_vfe1_ahb_clk", + "clk_camss_vfe1_stream_clk"; + qcom,clock-rates = <0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 + >; + qcom,clock-cntl-support; + qcom,clock-control = "NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE", + "INIT_RATE","NO_SET_RATE","NO_SET_RATE", + "INIT_RATE","NO_SET_RATE","INIT_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE", + "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE"; + }; }; &i2c_freq_100Khz { diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi index 661f0ebfd8fa..ea7c77e4b0a2 100644 --- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi @@ -59,8 +59,12 @@ qcom,sde-off = <0x1000>; qcom,sde-ctl-off = <0x2000 0x2200 0x2400 0x2600 0x2800>; + qcom,sde-ctl-display-pref = "primary", "secondary", "none", + "none", "none"; qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0x48000 0x49000 0x4a000>; + qcom,sde-mixer-display-pref = "primary", "secondary", "none", + "none", "none"; qcom,sde-dspp-off = <0x55000 0x57000>; qcom,sde-dspp-ad-off = <0x24000 0x22800>; qcom,sde-dspp-ad-version = <0x00030000>; 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/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts index 92f52a6de6e8..304a2ad4268f 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts @@ -154,6 +154,16 @@ /* Up to 800 Mbps */ <45 512 207108 14432000>; }; + + subsys_notif_virt: qcom,subsys_notif_virt@2d000000 { + compatible = "qcom,subsys-notif-virt"; + reg = <0x2d000000 0x18>; + reg-names = "vdev_base"; + wlan { + subsys-name = "AR6320"; + offset = <16>; + }; + }; }; &reserved_memory { diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts index d063a883a9e1..3902731dc5bd 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts @@ -132,6 +132,7 @@ pinctrl-names = "bootstrap_active", "bootstrap_sleep"; pinctrl-0 = <&cnss_bootstrap_active>; pinctrl-1 = <&cnss_bootstrap_sleep>; + qcom,wlan-ramdump-dynamic = <0x200000>; qcom,msm-bus,name = "msm-cnss"; qcom,msm-bus,num-cases = <4>; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 39c470e291f9..69381deeb703 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -738,7 +738,7 @@ timer@fffec600 { compatible = "arm,cortex-a9-twd-timer"; reg = <0xfffec600 0x100>; - interrupts = <1 13 0xf04>; + interrupts = <1 13 0xf01>; clocks = <&mpu_periph_clk>; }; diff --git a/arch/arm/include/asm/vdso.h b/arch/arm/include/asm/vdso.h index d0295f1dd1a3..ff65b6d96c7e 100644 --- a/arch/arm/include/asm/vdso.h +++ b/arch/arm/include/asm/vdso.h @@ -11,8 +11,6 @@ struct mm_struct; void arm_install_vdso(struct mm_struct *mm, unsigned long addr); -extern char vdso_start, vdso_end; - extern unsigned int vdso_total_pages; #else /* CONFIG_VDSO */ diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c index bbbffe946122..9d500067a25a 100644 --- a/arch/arm/kernel/vdso.c +++ b/arch/arm/kernel/vdso.c @@ -39,6 +39,8 @@ static struct page **vdso_text_pagelist; +extern char vdso_start[], vdso_end[]; + /* Total number of pages needed for the data and text portions of the VDSO. */ unsigned int vdso_total_pages __ro_after_init; @@ -179,13 +181,13 @@ static int __init vdso_init(void) unsigned int text_pages; int i; - if (memcmp(&vdso_start, "\177ELF", 4)) { + if (memcmp(vdso_start, "\177ELF", 4)) { pr_err("VDSO is not a valid ELF object!\n"); return -ENOEXEC; } - text_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT; - pr_debug("vdso: %i text pages at base %p\n", text_pages, &vdso_start); + text_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; + pr_debug("vdso: %i text pages at base %pK\n", text_pages, vdso_start); /* Allocate the VDSO text pagelist */ vdso_text_pagelist = kcalloc(text_pages, sizeof(struct page *), @@ -200,7 +202,7 @@ static int __init vdso_init(void) for (i = 0; i < text_pages; i++) { struct page *page; - page = virt_to_page(&vdso_start + i * PAGE_SIZE); + page = virt_to_page(vdso_start + i * PAGE_SIZE); vdso_text_pagelist[i] = page; } @@ -211,7 +213,7 @@ static int __init vdso_init(void) cntvct_ok = cntvct_functional(); - patch_vdso(&vdso_start); + patch_vdso(vdso_start); return 0; } diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c index 4f5fd4a084c0..034b89499bd7 100644 --- a/arch/arm/mach-omap1/clock.c +++ b/arch/arm/mach-omap1/clock.c @@ -1031,17 +1031,17 @@ static int clk_debugfs_register_one(struct clk *c) return -ENOMEM; c->dent = d; - d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount); + d = debugfs_create_u8("usecount", S_IRUGO, c->dent, &c->usecount); if (!d) { err = -ENOMEM; goto err_out; } - d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); + d = debugfs_create_ulong("rate", S_IRUGO, c->dent, &c->rate); if (!d) { err = -ENOMEM; goto err_out; } - d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); + d = debugfs_create_x8("flags", S_IRUGO, c->dent, &c->flags); if (!d) { err = -ENOMEM; goto err_out; diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 58920bc8807b..3d876bde8c85 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -231,7 +231,7 @@ static void omap_pm_end(void) cpu_idle_poll_ctrl(false); } -static void omap_pm_finish(void) +static void omap_pm_wake(void) { if (cpu_is_omap34xx()) omap_prcm_irq_complete(); @@ -241,7 +241,7 @@ static const struct platform_suspend_ops omap_pm_ops = { .begin = omap_pm_begin, .end = omap_pm_end, .enter = omap_pm_enter, - .finish = omap_pm_finish, + .wake = omap_pm_wake, .valid = suspend_valid_only_mem, }; diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index 83fc403aec3c..1f774ec4ab27 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c @@ -136,12 +136,6 @@ static struct clock_event_device clockevent_gpt = { .tick_resume = omap2_gp_timer_shutdown, }; -static struct property device_disabled = { - .name = "status", - .length = sizeof("disabled"), - .value = "disabled", -}; - static const struct of_device_id omap_timer_match[] __initconst = { { .compatible = "ti,omap2420-timer", }, { .compatible = "ti,omap3430-timer", }, @@ -183,8 +177,17 @@ static struct device_node * __init omap_get_timer_dt(const struct of_device_id * of_get_property(np, "ti,timer-secure", NULL))) continue; - if (!of_device_is_compatible(np, "ti,omap-counter32k")) - of_add_property(np, &device_disabled); + if (!of_device_is_compatible(np, "ti,omap-counter32k")) { + struct property *prop; + + prop = kzalloc(sizeof(*prop), GFP_KERNEL); + if (!prop) + return NULL; + prop->name = "status"; + prop->value = "disabled"; + prop->length = strlen(prop->value); + of_add_property(np, prop); + } return np; } diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 8ca94d379bc3..6f75c32dc3bf 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -854,11 +854,8 @@ static int omap_dm_timer_probe(struct platform_device *pdev) timer->irq = irq->start; timer->pdev = pdev; - /* Skip pm_runtime_enable for OMAP1 */ - if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { - pm_runtime_enable(dev); - pm_runtime_irq_safe(dev); - } + pm_runtime_enable(dev); + pm_runtime_irq_safe(dev); if (!timer->reserved) { ret = pm_runtime_get_sync(dev); diff --git a/arch/arm64/configs/msm-auto-gvm-perf_defconfig b/arch/arm64/configs/msm-auto-gvm-perf_defconfig index 63f492ee9786..cb83980e12c9 100644 --- a/arch/arm64/configs/msm-auto-gvm-perf_defconfig +++ b/arch/arm64/configs/msm-auto-gvm-perf_defconfig @@ -235,8 +235,11 @@ CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=m CONFIG_SERIO_AMBAKMI=y +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set CONFIG_SERIAL_MSM_HS=y CONFIG_DIAG_CHAR=y +# CONFIG_DEVPORT is not set CONFIG_MSM_SMD_PKT=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MSM_V2=y diff --git a/arch/arm64/configs/msm-auto-gvm_defconfig b/arch/arm64/configs/msm-auto-gvm_defconfig index 7e6d0f152306..f4934ba7bab2 100644 --- a/arch/arm64/configs/msm-auto-gvm_defconfig +++ b/arch/arm64/configs/msm-auto-gvm_defconfig @@ -224,10 +224,13 @@ CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=m CONFIG_SERIO_AMBAKMI=y CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_SERIAL_MSM_HS=y CONFIG_DIAG_CHAR=y +# CONFIG_DEVPORT is not set CONFIG_MSM_SMD_PKT=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MSM_V2=y diff --git a/arch/arm64/configs/msm-auto-perf_defconfig b/arch/arm64/configs/msm-auto-perf_defconfig index 77f186f793b2..acf8acd6375c 100644 --- a/arch/arm64/configs/msm-auto-perf_defconfig +++ b/arch/arm64/configs/msm-auto-perf_defconfig @@ -316,11 +316,14 @@ CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set CONFIG_SERIAL_MSM_HS=y CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set CONFIG_MSM_SMD_PKT=y CONFIG_MSM_ADSPRPC=y CONFIG_MSM_RDBG=m diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig index 926992c19853..15b059a621ac 100644 --- a/arch/arm64/configs/msm-auto_defconfig +++ b/arch/arm64/configs/msm-auto_defconfig @@ -317,6 +317,8 @@ CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set CONFIG_SERIAL_MSM=y CONFIG_SERIAL_MSM_CONSOLE=y CONFIG_SERIAL_MSM_HS=y @@ -324,6 +326,7 @@ CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set CONFIG_MSM_SMD_PKT=y CONFIG_MSM_ADSPRPC=y CONFIG_MSM_RDBG=m 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/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig index fc55008d8c4c..7f847fc40d14 100644 --- a/arch/arm64/configs/ranchu64_defconfig +++ b/arch/arm64/configs/ranchu64_defconfig @@ -1,6 +1,7 @@ # CONFIG_LOCALVERSION_AUTO is not set # CONFIG_SWAP is not set CONFIG_POSIX_MQUEUE=y +# CONFIG_USELIB is not set CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -17,14 +18,19 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_BITS=24 CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_DEADLINE is not set CONFIG_ARCH_VEXPRESS=y @@ -36,6 +42,8 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_CP15_BARRIER_EMULATION=y CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_SW_TTBR0_PAN=y +CONFIG_RANDOMIZE_BASE=y CONFIG_CMDLINE="console=ttyAMA0" # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y @@ -50,15 +58,16 @@ CONFIG_UNIX=y CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y -CONFIG_INET_DIAG_DESTROY=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y +CONFIG_NET_IPVTI=y CONFIG_INET_ESP=y # CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IPV6_OPTIMISTIC_DAD=y @@ -66,6 +75,7 @@ CONFIG_INET6_AH=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y @@ -124,6 +134,10 @@ CONFIG_IP_NF_MATCH_RPFILTER=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_TARGET_ECN=y CONFIG_IP_NF_TARGET_TTL=y @@ -141,6 +155,7 @@ CONFIG_IP6_NF_MATCH_OPTS=y CONFIG_IP6_NF_MATCH_HL=y CONFIG_IP6_NF_MATCH_IPV6HEADER=y CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_MATCH_RT=y CONFIG_IP6_NF_TARGET_HL=y CONFIG_IP6_NF_FILTER=y @@ -154,12 +169,14 @@ CONFIG_NET_CLS_U32=y CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_CLS_ACT=y -# CONFIG_WIRELESS is not set +CONFIG_MAC80211=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_VIRTIO_BLK=y +CONFIG_UID_SYS_STATS=y +CONFIG_MEMORY_STATE_TIME=y CONFIG_SCSI=y # CONFIG_SCSI_PROC_FS is not set CONFIG_BLK_DEV_SD=y @@ -180,13 +197,22 @@ CONFIG_PPP_DEFLATE=y CONFIG_PPP_MPPE=y CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y -# CONFIG_WLAN is not set +CONFIG_USB_USBNET=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_KEYRESET=y CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +CONFIG_KEYBOARD_GOLDFISH_ROTARY=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y @@ -199,7 +225,8 @@ CONFIG_INPUT_GPIO=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_VIRTIO_CONSOLE=y -# CONFIG_HW_RANDOM is not set +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_VIRTIO=y CONFIG_BATTERY_GOLDFISH=y # CONFIG_HWMON is not set CONFIG_MEDIA_SUPPORT=y @@ -228,8 +255,10 @@ CONFIG_DRAGONRISE_FF=y CONFIG_HID_EMS_FF=y CONFIG_HID_ELECOM=y CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y CONFIG_HID_KEYTOUCH=y CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y CONFIG_HID_WALTOP=y CONFIG_HID_GYRATION=y CONFIG_HID_TWINHAN=y @@ -244,14 +273,17 @@ CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MONTEREY=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y CONFIG_HID_ORTEK=y CONFIG_HID_PANTHERLORD=y CONFIG_PANTHERLORD_FF=y CONFIG_HID_PETALYNX=y CONFIG_HID_PICOLCD=y CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y CONFIG_HID_SAITEK=y CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y CONFIG_HID_SPEEDLINK=y CONFIG_HID_SUNPLUS=y CONFIG_HID_GREENASIA=y @@ -265,7 +297,18 @@ CONFIG_HID_WACOM=y CONFIG_HID_WIIMOTE=y CONFIG_HID_ZEROPLUS=y CONFIG_HID_ZYDACRON=y -# CONFIG_USB_SUPPORT is not set +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_GADGET=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_RTC_CLASS=y CONFIG_VIRTIO_MMIO=y CONFIG_STAGING=y @@ -286,27 +329,31 @@ CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_CUSE=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y -# CONFIG_MISC_FILESYSTEMS is not set -CONFIG_NFS_FS=y -CONFIG_ROOT_NFS=y +CONFIG_SDCARD_FS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_DEBUG_INFO=y -CONFIG_DEBUG_FS=y CONFIG_MAGIC_SYSRQ=y CONFIG_PANIC_TIMEOUT=5 -# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y -# CONFIG_FTRACE is not set +CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_ATOMIC64_SELFTEST=y -CONFIG_DEBUG_RODATA=y +CONFIG_KEYS=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_SHA512=y diff --git a/arch/arm64/include/asm/atomic_lse.h b/arch/arm64/include/asm/atomic_lse.h index 39c1d340fec5..a000e47d5016 100644 --- a/arch/arm64/include/asm/atomic_lse.h +++ b/arch/arm64/include/asm/atomic_lse.h @@ -114,7 +114,7 @@ static inline void atomic_and(int i, atomic_t *v) /* LSE atomics */ " mvn %w[i], %w[i]\n" " stclr %w[i], %[v]") - : [i] "+r" (w0), [v] "+Q" (v->counter) + : [i] "+&r" (w0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -131,7 +131,7 @@ static inline void atomic_sub(int i, atomic_t *v) /* LSE atomics */ " neg %w[i], %w[i]\n" " stadd %w[i], %[v]") - : [i] "+r" (w0), [v] "+Q" (v->counter) + : [i] "+&r" (w0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -151,7 +151,7 @@ static inline int atomic_sub_return##name(int i, atomic_t *v) \ " neg %w[i], %w[i]\n" \ " ldadd" #mb " %w[i], w30, %[v]\n" \ " add %w[i], %w[i], w30") \ - : [i] "+r" (w0), [v] "+Q" (v->counter) \ + : [i] "+&r" (w0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS , ##cl); \ \ @@ -255,7 +255,7 @@ static inline void atomic64_and(long i, atomic64_t *v) /* LSE atomics */ " mvn %[i], %[i]\n" " stclr %[i], %[v]") - : [i] "+r" (x0), [v] "+Q" (v->counter) + : [i] "+&r" (x0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -272,7 +272,7 @@ static inline void atomic64_sub(long i, atomic64_t *v) /* LSE atomics */ " neg %[i], %[i]\n" " stadd %[i], %[v]") - : [i] "+r" (x0), [v] "+Q" (v->counter) + : [i] "+&r" (x0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -292,7 +292,7 @@ static inline long atomic64_sub_return##name(long i, atomic64_t *v) \ " neg %[i], %[i]\n" \ " ldadd" #mb " %[i], x30, %[v]\n" \ " add %[i], %[i], x30") \ - : [i] "+r" (x0), [v] "+Q" (v->counter) \ + : [i] "+&r" (x0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS, ##cl); \ \ @@ -412,7 +412,7 @@ static inline long __cmpxchg_double##name(unsigned long old1, \ " eor %[old1], %[old1], %[oldval1]\n" \ " eor %[old2], %[old2], %[oldval2]\n" \ " orr %[old1], %[old1], %[old2]") \ - : [old1] "+r" (x0), [old2] "+r" (x1), \ + : [old1] "+&r" (x0), [old2] "+&r" (x1), \ [v] "+Q" (*(unsigned long *)ptr) \ : [new1] "r" (x2), [new2] "r" (x3), [ptr] "r" (x4), \ [oldval1] "r" (oldval1), [oldval2] "r" (oldval2) \ diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h index 19afa01911dd..da7a921d88d5 100644 --- a/arch/arm64/include/asm/spinlock.h +++ b/arch/arm64/include/asm/spinlock.h @@ -116,8 +116,8 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock) " cbnz %w1, 1f\n" " add %w1, %w0, %3\n" " casa %w0, %w1, %2\n" - " and %w1, %w1, #0xffff\n" - " eor %w1, %w1, %w0, lsr #16\n" + " sub %w1, %w1, %3\n" + " eor %w1, %w1, %w0\n" "1:") : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock) : "I" (1 << TICKET_SHIFT) diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c index f8df75d740f4..6dd18140ebb8 100644 --- a/arch/arm64/kernel/hibernate.c +++ b/arch/arm64/kernel/hibernate.c @@ -34,6 +34,7 @@ #include <asm/pgtable-hwdef.h> #include <asm/sections.h> #include <asm/suspend.h> +#include <asm/sysreg.h> #include <asm/virt.h> /* @@ -216,12 +217,22 @@ static int create_safe_exec_page(void *src_start, size_t length, set_pte(pte, __pte(virt_to_phys((void *)dst) | pgprot_val(PAGE_KERNEL_EXEC))); - /* Load our new page tables */ - asm volatile("msr ttbr0_el1, %0;" - "isb;" - "tlbi vmalle1is;" - "dsb ish;" - "isb" : : "r"(virt_to_phys(pgd))); + /* + * Load our new page tables. A strict BBM approach requires that we + * ensure that TLBs are free of any entries that may overlap with the + * global mappings we are about to install. + * + * For a real hibernate/resume cycle TTBR0 currently points to a zero + * page, but TLBs may contain stale ASID-tagged entries (e.g. for EFI + * runtime services), while for a userspace-driven test_resume cycle it + * points to userspace page tables (and we must point it at a zero page + * ourselves). Elsewhere we only (un)install the idmap with preemption + * disabled, so T0SZ should be as required regardless. + */ + cpu_set_reserved_ttbr0(); + local_flush_tlb_all(); + write_sysreg(virt_to_phys(pgd), ttbr0_el1); + isb(); *phys_dst_addr = virt_to_phys((void *)dst); @@ -388,6 +399,38 @@ int swsusp_arch_resume(void) void *, phys_addr_t, phys_addr_t); /* + * Restoring the memory image will overwrite the ttbr1 page tables. + * Create a second copy of just the linear map, and use this when + * restoring. + */ + tmp_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); + if (!tmp_pg_dir) { + pr_err("Failed to allocate memory for temporary page tables."); + rc = -ENOMEM; + goto out; + } + rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, 0); + if (rc) + goto out; + + /* + * Since we only copied the linear map, we need to find restore_pblist's + * linear map address. + */ + lm_restore_pblist = LMADDR(restore_pblist); + + /* + * We need a zero page that is zero before & after resume in order to + * to break before make on the ttbr1 page tables. + */ + zero_page = (void *)get_safe_page(GFP_ATOMIC); + if (!zero_page) { + pr_err("Failed to allocate zero page."); + rc = -ENOMEM; + goto out; + } + + /* * Locate the exit code in the bottom-but-one page, so that *NULL * still has disastrous affects. */ @@ -413,27 +456,6 @@ int swsusp_arch_resume(void) __flush_dcache_area(hibernate_exit, exit_size); /* - * Restoring the memory image will overwrite the ttbr1 page tables. - * Create a second copy of just the linear map, and use this when - * restoring. - */ - tmp_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); - if (!tmp_pg_dir) { - pr_err("Failed to allocate memory for temporary page tables."); - rc = -ENOMEM; - goto out; - } - rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, 0); - if (rc) - goto out; - - /* - * Since we only copied the linear map, we need to find restore_pblist's - * linear map address. - */ - lm_restore_pblist = LMADDR(restore_pblist); - - /* * KASLR will cause the el2 vectors to be in a different location in * the resumed kernel. Load hibernate's temporary copy into el2. * @@ -447,12 +469,6 @@ int swsusp_arch_resume(void) __hyp_set_vectors(el2_vectors); } - /* - * We need a zero page that is zero before & after resume in order to - * to break before make on the ttbr1 page tables. - */ - zero_page = (void *)get_safe_page(GFP_ATOMIC); - hibernate_exit(virt_to_phys(tmp_pg_dir), resume_hdr.ttbr1_el1, resume_hdr.reenter_kernel, lm_restore_pblist, resume_hdr.__hyp_stub_vectors, virt_to_phys(zero_page)); 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/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c index 71ea4c02795d..8a2dc0af4cad 100644 --- a/arch/m68k/coldfire/device.c +++ b/arch/m68k/coldfire/device.c @@ -135,7 +135,11 @@ static struct platform_device mcf_fec0 = { .id = 0, .num_resources = ARRAY_SIZE(mcf_fec0_resources), .resource = mcf_fec0_resources, - .dev.platform_data = FEC_PDATA, + .dev = { + .dma_mask = &mcf_fec0.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = FEC_PDATA, + } }; #ifdef MCFFEC_BASE1 @@ -167,7 +171,11 @@ static struct platform_device mcf_fec1 = { .id = 1, .num_resources = ARRAY_SIZE(mcf_fec1_resources), .resource = mcf_fec1_resources, - .dev.platform_data = FEC_PDATA, + .dev = { + .dma_mask = &mcf_fec1.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = FEC_PDATA, + } }; #endif /* MCFFEC_BASE1 */ #endif /* CONFIG_FEC */ diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c index 10d0b2140375..63d35076722d 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c @@ -2240,7 +2240,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, parent_irq = irq_of_parse_and_map(ciu_node, 0); if (!parent_irq) { - pr_err("ERROR: Couldn't acquire parent_irq for %s\n.", + pr_err("ERROR: Couldn't acquire parent_irq for %s\n", ciu_node->name); return -EINVAL; } @@ -2252,7 +2252,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, addr = of_get_address(ciu_node, 0, NULL, NULL); if (!addr) { - pr_err("ERROR: Couldn't acquire reg(0) %s\n.", ciu_node->name); + pr_err("ERROR: Couldn't acquire reg(0) %s\n", ciu_node->name); return -EINVAL; } host_data->raw_reg = (u64)phys_to_virt( @@ -2260,7 +2260,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, addr = of_get_address(ciu_node, 1, NULL, NULL); if (!addr) { - pr_err("ERROR: Couldn't acquire reg(1) %s\n.", ciu_node->name); + pr_err("ERROR: Couldn't acquire reg(1) %s\n", ciu_node->name); return -EINVAL; } host_data->en_reg = (u64)phys_to_virt( @@ -2268,7 +2268,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, r = of_property_read_u32(ciu_node, "cavium,max-bits", &val); if (r) { - pr_err("ERROR: Couldn't read cavium,max-bits from %s\n.", + pr_err("ERROR: Couldn't read cavium,max-bits from %s\n", ciu_node->name); return r; } @@ -2278,7 +2278,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, &octeon_irq_domain_cib_ops, host_data); if (!cib_domain) { - pr_err("ERROR: Couldn't irq_domain_add_linear()\n."); + pr_err("ERROR: Couldn't irq_domain_add_linear()\n"); return -ENOMEM; } diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index aa3800c82332..d99ca862dae3 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -167,7 +167,7 @@ #define AR71XX_AHB_DIV_MASK 0x7 #define AR724X_PLL_REG_CPU_CONFIG 0x00 -#define AR724X_PLL_REG_PCIE_CONFIG 0x18 +#define AR724X_PLL_REG_PCIE_CONFIG 0x10 #define AR724X_PLL_FB_SHIFT 0 #define AR724X_PLL_FB_MASK 0x3ff diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index fe61ce7ad9b1..054a22c0873b 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -684,6 +684,10 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value) if (value & ~known_bits) return -EOPNOTSUPP; + /* Setting FRE without FR is not supported. */ + if ((value & (PR_FP_MODE_FR | PR_FP_MODE_FRE)) == PR_FP_MODE_FRE) + return -EOPNOTSUPP; + /* Avoid inadvertently triggering emulation */ if ((value & PR_FP_MODE_FR) && raw_cpu_has_fpu && !(raw_current_cpu_data.fpu_id & MIPS_FPIR_F64)) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 0f0030e7f6d9..0b70d62e2231 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -482,7 +482,7 @@ static int fpr_get_msa(struct task_struct *target, /* * Copy the floating-point context to the supplied NT_PRFPREG buffer. * Choose the appropriate helper for general registers, and then copy - * the FCSR register separately. + * the FCSR and FIR registers separately. */ static int fpr_get(struct task_struct *target, const struct user_regset *regset, @@ -490,6 +490,7 @@ static int fpr_get(struct task_struct *target, void *kbuf, void __user *ubuf) { const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); + const int fir_pos = fcr31_pos + sizeof(u32); int err; if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) @@ -502,6 +503,12 @@ static int fpr_get(struct task_struct *target, err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.fpu.fcr31, fcr31_pos, fcr31_pos + sizeof(u32)); + if (err) + return err; + + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &boot_cpu_data.fpu_id, + fir_pos, fir_pos + sizeof(u32)); return err; } @@ -550,7 +557,8 @@ static int fpr_set_msa(struct task_struct *target, /* * Copy the supplied NT_PRFPREG buffer to the floating-point context. * Choose the appropriate helper for general registers, and then copy - * the FCSR register separately. + * the FCSR register separately. Ignore the incoming FIR register + * contents though, as the register is read-only. * * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', * which is supposed to have been guaranteed by the kernel before @@ -564,6 +572,7 @@ static int fpr_set(struct task_struct *target, const void *kbuf, const void __user *ubuf) { const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); + const int fir_pos = fcr31_pos + sizeof(u32); u32 fcr31; int err; @@ -591,6 +600,11 @@ static int fpr_set(struct task_struct *target, ptrace_setfcr31(target, fcr31); } + if (count > 0) + err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + fir_pos, + fir_pos + sizeof(u32)); + return err; } @@ -815,7 +829,7 @@ long arch_ptrace(struct task_struct *child, long request, fregs = get_fpu_regs(child); #ifdef CONFIG_32BIT - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even @@ -826,7 +840,7 @@ long arch_ptrace(struct task_struct *child, long request, break; } #endif - tmp = get_fpr32(&fregs[addr - FPR_BASE], 0); + tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; @@ -904,7 +918,7 @@ long arch_ptrace(struct task_struct *child, long request, init_fp_ctx(child); #ifdef CONFIG_32BIT - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 283b5a1967d1..d95117e71f69 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -97,7 +97,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; } fregs = get_fpu_regs(child); - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even @@ -107,7 +107,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, addr & 1); break; } - tmp = get_fpr32(&fregs[addr - FPR_BASE], 0); + tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; @@ -203,7 +203,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, sizeof(child->thread.fpu)); child->thread.fpu.fcr31 = 0; } - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index a017b23ee4aa..8a95c3d76a9a 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -40,7 +40,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "cache", VCPU_STAT(cache_exits), KVM_STAT_VCPU }, { "signal", VCPU_STAT(signal_exits), KVM_STAT_VCPU }, { "interrupt", VCPU_STAT(int_exits), KVM_STAT_VCPU }, - { "cop_unsuable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU }, + { "cop_unusable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU }, { "tlbmod", VCPU_STAT(tlbmod_exits), KVM_STAT_VCPU }, { "tlbmiss_ld", VCPU_STAT(tlbmiss_ld_exits), KVM_STAT_VCPU }, { "tlbmiss_st", VCPU_STAT(tlbmiss_st_exits), KVM_STAT_VCPU }, diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c index 37030409745c..586ca7ea3e7c 100644 --- a/arch/mips/txx9/rbtx4939/setup.c +++ b/arch/mips/txx9/rbtx4939/setup.c @@ -186,7 +186,7 @@ static void __init rbtx4939_update_ioc_pen(void) #define RBTX4939_MAX_7SEGLEDS 8 -#if IS_ENABLED(CONFIG_LEDS_CLASS) +#if IS_BUILTIN(CONFIG_LEDS_CLASS) static u8 led_val[RBTX4939_MAX_7SEGLEDS]; struct rbtx4939_led_data { struct led_classdev cdev; @@ -261,7 +261,7 @@ static inline void rbtx4939_led_setup(void) static void __rbtx4939_7segled_putc(unsigned int pos, unsigned char val) { -#if IS_ENABLED(CONFIG_LEDS_CLASS) +#if IS_BUILTIN(CONFIG_LEDS_CLASS) unsigned long flags; local_irq_save(flags); /* bit7: reserved for LED class */ diff --git a/arch/powerpc/include/asm/irq_work.h b/arch/powerpc/include/asm/irq_work.h index 744fd54de374..1bcc84903930 100644 --- a/arch/powerpc/include/asm/irq_work.h +++ b/arch/powerpc/include/asm/irq_work.h @@ -5,5 +5,6 @@ static inline bool arch_irq_work_has_interrupt(void) { return true; } +extern void arch_irq_work_raise(void); #endif /* _ASM_POWERPC_IRQ_WORK_H */ diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S index 9c9b7411b28b..55eb3b752ca0 100644 --- a/arch/powerpc/kernel/cpu_setup_power.S +++ b/arch/powerpc/kernel/cpu_setup_power.S @@ -27,6 +27,7 @@ _GLOBAL(__setup_cpu_power7) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR bl __init_LPCR bl __init_tlb_power7 @@ -40,6 +41,7 @@ _GLOBAL(__restore_cpu_power7) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR bl __init_LPCR bl __init_tlb_power7 @@ -55,6 +57,7 @@ _GLOBAL(__setup_cpu_power8) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR ori r3, r3, LPCR_PECEDH bl __init_LPCR @@ -74,6 +77,7 @@ _GLOBAL(__restore_cpu_power8) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR ori r3, r3, LPCR_PECEDH bl __init_LPCR diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 428563b195c3..767ac1572c02 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -3002,15 +3002,17 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu) goto up_out; psize = vma_kernel_pagesize(vma); - porder = __ilog2(psize); up_read(¤t->mm->mmap_sem); /* We can handle 4k, 64k or 16M pages in the VRMA */ - err = -EINVAL; - if (!(psize == 0x1000 || psize == 0x10000 || - psize == 0x1000000)) - goto out_srcu; + if (psize >= 0x1000000) + psize = 0x1000000; + else if (psize >= 0x10000) + psize = 0x10000; + else + psize = 0x1000; + porder = __ilog2(psize); /* Update VRMASD field in the LPCR */ senc = slb_pgsize_encoding(psize); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 669a15e7fa76..3c4faa4c2742 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -551,7 +551,7 @@ static int numa_setup_cpu(unsigned long lcpu) nid = of_node_to_nid_single(cpu); out_present: - if (nid < 0 || !node_online(nid)) + if (nid < 0 || !node_possible(nid)) nid = first_online_node; map_cpu_to_node(lcpu, nid); @@ -951,6 +951,32 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn) NODE_DATA(nid)->node_spanned_pages = spanned_pages; } +static void __init find_possible_nodes(void) +{ + struct device_node *rtas; + u32 numnodes, i; + + if (min_common_depth <= 0) + return; + + rtas = of_find_node_by_path("/rtas"); + if (!rtas) + return; + + if (of_property_read_u32_index(rtas, + "ibm,max-associativity-domains", + min_common_depth, &numnodes)) + goto out; + + for (i = 0; i < numnodes; i++) { + if (!node_possible(i)) + node_set(i, node_possible_map); + } + +out: + of_node_put(rtas); +} + void __init initmem_init(void) { int nid, cpu; @@ -966,12 +992,15 @@ void __init initmem_init(void) memblock_dump_all(); /* - * Reduce the possible NUMA nodes to the online NUMA nodes, - * since we do not support node hotplug. This ensures that we - * lower the maximum NUMA node ID to what is actually present. + * Modify the set of possible NUMA nodes to reflect information + * available about the set of online nodes, and the set of nodes + * that we expect to make use of for this platform's affinity + * calculations. */ nodes_and(node_possible_map, node_possible_map, node_online_map); + find_possible_nodes(); + for_each_online_node(nid) { unsigned long start_pfn, end_pfn; @@ -1304,6 +1333,40 @@ static long vphn_get_associativity(unsigned long cpu, return rc; } +static inline int find_and_online_cpu_nid(int cpu) +{ + __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0}; + int new_nid; + + /* Use associativity from first thread for all siblings */ + vphn_get_associativity(cpu, associativity); + new_nid = associativity_to_nid(associativity); + if (new_nid < 0 || !node_possible(new_nid)) + new_nid = first_online_node; + + if (NODE_DATA(new_nid) == NULL) { +#ifdef CONFIG_MEMORY_HOTPLUG + /* + * Need to ensure that NODE_DATA is initialized for a node from + * available memory (see memblock_alloc_try_nid). If unable to + * init the node, then default to nearest node that has memory + * installed. + */ + if (try_online_node(new_nid)) + new_nid = first_online_node; +#else + /* + * Default to using the nearest node that has memory installed. + * Otherwise, it would be necessary to patch the kernel MM code + * to deal with more memoryless-node error conditions. + */ + new_nid = first_online_node; +#endif + } + + return new_nid; +} + /* * Update the CPU maps and sysfs entries for a single CPU when its NUMA * characteristics change. This function doesn't perform any locking and is @@ -1369,7 +1432,6 @@ int arch_update_cpu_topology(void) { unsigned int cpu, sibling, changed = 0; struct topology_update_data *updates, *ud; - __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0}; cpumask_t updated_cpus; struct device *dev; int weight, new_nid, i = 0; @@ -1404,11 +1466,7 @@ int arch_update_cpu_topology(void) continue; } - /* Use associativity from first thread for all siblings */ - vphn_get_associativity(cpu, associativity); - new_nid = associativity_to_nid(associativity); - if (new_nid < 0 || !node_online(new_nid)) - new_nid = first_online_node; + new_nid = find_and_online_cpu_nid(cpu); if (new_nid == numa_cpu_lookup_table[cpu]) { cpumask_andnot(&cpu_associativity_changes_mask, diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 2d66a8446198..345e255c06a2 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -329,6 +329,9 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len)); break; + case BPF_LDX | BPF_W | BPF_ABS: /* A = *((u32 *)(seccomp_data + K)); */ + PPC_LWZ_OFFS(r_A, r_skb, K); + break; case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */ PPC_LWZ_OFFS(r_X, r_skb, offsetof(struct sk_buff, len)); break; diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 4eba7c00ea1f..30e2e8efbe6b 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -448,6 +448,16 @@ static void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) /* invalid entry */ continue; + /* + * BHRB rolling buffer could very much contain the kernel + * addresses at this point. Check the privileges before + * exporting it to userspace (avoid exposure of regions + * where we could have speculative execution) + */ + if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN) && + is_kernel_addr(addr)) + continue; + /* Branches are read most recent first (ie. mfbhrb 0 is * the most recent branch). * There are two types of valid entries: @@ -1188,6 +1198,7 @@ static void power_pmu_disable(struct pmu *pmu) */ write_mmcr0(cpuhw, val); mb(); + isync(); /* * Disable instruction sampling if it was enabled @@ -1196,12 +1207,26 @@ static void power_pmu_disable(struct pmu *pmu) mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); mb(); + isync(); } cpuhw->disabled = 1; cpuhw->n_added = 0; ebb_switch_out(mmcr0); + +#ifdef CONFIG_PPC64 + /* + * These are readable by userspace, may contain kernel + * addresses and are not switched by context switch, so clear + * them now to avoid leaking anything to userspace in general + * including to another process. + */ + if (ppmu->flags & PPMU_ARCH_207S) { + mtspr(SPRN_SDAR, 0); + mtspr(SPRN_SIAR, 0); + } +#endif } local_irq_restore(flags); diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 2a0452e364ba..d11f931cac69 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -626,7 +626,7 @@ static inline u32 mpic_physmask(u32 cpumask) int i; u32 mask = 0; - for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1) + for (i = 0; i < min(32, NR_CPUS) && cpu_possible(i); ++i, cpumask >>= 1) mask |= (cpumask & 1) << get_hard_smp_processor_id(i); return mask; } diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h index 087fc9b972c5..9a56e738d645 100644 --- a/arch/s390/include/asm/nospec-insn.h +++ b/arch/s390/include/asm/nospec-insn.h @@ -2,10 +2,15 @@ #ifndef _ASM_S390_NOSPEC_ASM_H #define _ASM_S390_NOSPEC_ASM_H +#include <asm/alternative-asm.h> +#include <asm/asm-offsets.h> + #ifdef __ASSEMBLY__ #ifdef CONFIG_EXPOLINE +_LC_BR_R1 = __LC_BR_R1 + /* * The expoline macros are used to create thunks in the same format * as gcc generates them. The 'comdat' section flag makes sure that @@ -101,13 +106,21 @@ .endm .macro __THUNK_EX_BR reg,ruse + # Be very careful when adding instructions to this macro! + # The ALTERNATIVE replacement code has a .+10 which targets + # the "br \reg" after the code has been patched. #ifdef CONFIG_HAVE_MARCH_Z10_FEATURES exrl 0,555f j . #else + .ifc \reg,%r1 + ALTERNATIVE "ex %r0,_LC_BR_R1", ".insn ril,0xc60000000000,0,.+10", 35 + j . + .else larl \ruse,555f ex 0,0(\ruse) j . + .endif #endif 555: br \reg .endm diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index dc6c9c604543..39572281e213 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -170,6 +170,7 @@ int main(void) OFFSET(__LC_MACHINE_FLAGS, _lowcore, machine_flags); OFFSET(__LC_GMAP, _lowcore, gmap); OFFSET(__LC_PASTE, _lowcore, paste); + OFFSET(__LC_BR_R1, _lowcore, br_r1_trampoline); /* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */ OFFSET(__LC_DUMP_REIPL, _lowcore, ipib); /* hardware defined lowcore locations 0x1000 - 0x18ff */ diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index e499370fbccb..6c1c7d399bf9 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -8,12 +8,16 @@ #include <linux/linkage.h> #include <asm/asm-offsets.h> #include <asm/ftrace.h> +#include <asm/nospec-insn.h> #include <asm/ptrace.h> + GEN_BR_THUNK %r1 + GEN_BR_THUNK %r14 + .section .kprobes.text, "ax" ENTRY(ftrace_stub) - br %r14 + BR_EX %r14 #define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE) #define STACK_PTREGS (STACK_FRAME_OVERHEAD) @@ -21,7 +25,7 @@ ENTRY(ftrace_stub) #define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW) ENTRY(_mcount) - br %r14 + BR_EX %r14 ENTRY(ftrace_caller) .globl ftrace_regs_caller @@ -49,7 +53,7 @@ ENTRY(ftrace_caller) #endif lgr %r3,%r14 la %r5,STACK_PTREGS(%r15) - basr %r14,%r1 + BASR_EX %r14,%r1 #ifdef CONFIG_FUNCTION_GRAPH_TRACER # The j instruction gets runtime patched to a nop instruction. # See ftrace_enable_ftrace_graph_caller. @@ -64,7 +68,7 @@ ftrace_graph_caller_end: #endif lg %r1,(STACK_PTREGS_PSW+8)(%r15) lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15) - br %r1 + BR_EX %r1 #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -77,6 +81,6 @@ ENTRY(return_to_handler) aghi %r15,STACK_FRAME_OVERHEAD lgr %r14,%r2 lmg %r2,%r5,32(%r15) - br %r14 + BR_EX %r14 #endif diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S index 13047a4facd2..5a9017ba26ab 100644 --- a/arch/sh/kernel/entry-common.S +++ b/arch/sh/kernel/entry-common.S @@ -255,7 +255,7 @@ debug_trap: mov.l @r8, r8 jsr @r8 nop - bra __restore_all + bra ret_from_exception nop CFI_ENDPROC diff --git a/arch/sh/kernel/sh_ksyms_32.c b/arch/sh/kernel/sh_ksyms_32.c index d77f2f6c7ff0..0b30b9dfc87f 100644 --- a/arch/sh/kernel/sh_ksyms_32.c +++ b/arch/sh/kernel/sh_ksyms_32.c @@ -34,6 +34,9 @@ DECLARE_EXPORT(__sdivsi3); DECLARE_EXPORT(__lshrsi3); DECLARE_EXPORT(__ashrsi3); DECLARE_EXPORT(__ashlsi3); +DECLARE_EXPORT(__lshrsi3_r0); +DECLARE_EXPORT(__ashrsi3_r0); +DECLARE_EXPORT(__ashlsi3_r0); DECLARE_EXPORT(__ashiftrt_r4_6); DECLARE_EXPORT(__ashiftrt_r4_7); DECLARE_EXPORT(__ashiftrt_r4_8); diff --git a/arch/sh/lib/ashlsi3.S b/arch/sh/lib/ashlsi3.S index bd47e9b403a5..70a6434945ab 100644 --- a/arch/sh/lib/ashlsi3.S +++ b/arch/sh/lib/ashlsi3.S @@ -54,21 +54,38 @@ Boston, MA 02110-1301, USA. */ ! ! (none) ! +! __ashlsi3_r0 +! +! Entry: +! +! r4: Value to shift +! r0: Shifts +! +! Exit: +! +! r0: Result +! +! Destroys: +! +! (none) + + .global __ashlsi3 + .global __ashlsi3_r0 .align 2 __ashlsi3: - mov #31,r0 - and r0,r5 + mov r5,r0 + .align 2 +__ashlsi3_r0: + and #31,r0 + mov.l r4,@-r15 + mov r0,r4 mova ashlsi3_table,r0 - mov.b @(r0,r5),r5 -#ifdef __sh1__ - add r5,r0 + mov.b @(r0,r4),r4 + add r4,r0 jmp @r0 -#else - braf r5 -#endif - mov r4,r0 + mov.l @r15+,r0 .align 2 ashlsi3_table: diff --git a/arch/sh/lib/ashrsi3.S b/arch/sh/lib/ashrsi3.S index 6f3cf46b77c2..602599d80209 100644 --- a/arch/sh/lib/ashrsi3.S +++ b/arch/sh/lib/ashrsi3.S @@ -54,22 +54,37 @@ Boston, MA 02110-1301, USA. */ ! ! (none) ! +! __ashrsi3_r0 +! +! Entry: +! +! r4: Value to shift +! r0: Shifts +! +! Exit: +! +! r0: Result +! +! Destroys: +! +! (none) .global __ashrsi3 + .global __ashrsi3_r0 .align 2 __ashrsi3: - mov #31,r0 - and r0,r5 + mov r5,r0 + .align 2 +__ashrsi3_r0: + and #31,r0 + mov.l r4,@-r15 + mov r0,r4 mova ashrsi3_table,r0 - mov.b @(r0,r5),r5 -#ifdef __sh1__ - add r5,r0 + mov.b @(r0,r4),r4 + add r4,r0 jmp @r0 -#else - braf r5 -#endif - mov r4,r0 + mov.l @r15+,r0 .align 2 ashrsi3_table: diff --git a/arch/sh/lib/lshrsi3.S b/arch/sh/lib/lshrsi3.S index 1e7aaa557130..f2a6959f526d 100644 --- a/arch/sh/lib/lshrsi3.S +++ b/arch/sh/lib/lshrsi3.S @@ -54,21 +54,37 @@ Boston, MA 02110-1301, USA. */ ! ! (none) ! +! __lshrsi3_r0 +! +! Entry: +! +! r0: Value to shift +! r5: Shifts +! +! Exit: +! +! r0: Result +! +! Destroys: +! +! (none) +! .global __lshrsi3 + .global __lshrsi3_r0 .align 2 __lshrsi3: - mov #31,r0 - and r0,r5 + mov r5,r0 + .align 2 +__lshrsi3_r0: + and #31,r0 + mov.l r4,@-r15 + mov r0,r4 mova lshrsi3_table,r0 - mov.b @(r0,r5),r5 -#ifdef __sh1__ - add r5,r0 + mov.b @(r0,r4),r4 + add r4,r0 jmp @r0 -#else - braf r5 -#endif - mov r4,r0 + mov.l @r15+,r0 .align 2 lshrsi3_table: diff --git a/arch/sparc/include/asm/atomic_64.h b/arch/sparc/include/asm/atomic_64.h index f2fbf9e16faf..29070c9a70f9 100644 --- a/arch/sparc/include/asm/atomic_64.h +++ b/arch/sparc/include/asm/atomic_64.h @@ -74,7 +74,11 @@ ATOMIC_OP(xor) #define atomic64_add_negative(i, v) (atomic64_add_return(i, v) < 0) #define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +static inline int atomic_xchg(atomic_t *v, int new) +{ + return xchg(&v->counter, new); +} static inline int __atomic_add_unless(atomic_t *v, int a, int u) { diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c index f87a55d77094..9b3f2e212b37 100644 --- a/arch/sparc/kernel/ds.c +++ b/arch/sparc/kernel/ds.c @@ -908,7 +908,7 @@ static int register_services(struct ds_info *dp) pbuf.req.handle = cp->handle; pbuf.req.major = 1; pbuf.req.minor = 0; - strcpy(pbuf.req.svc_id, cp->service_id); + strcpy(pbuf.id_buf, cp->service_id); err = __ds_send(lp, &pbuf, msg_len); if (err > 0) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 9f0107157b8f..71026930c04c 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -11,6 +11,7 @@ CONFIG_TASK_IO_ACCOUNTING=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_SCHED=y @@ -56,6 +57,7 @@ CONFIG_RANDOMIZE_BASE=y CONFIG_PHYSICAL_ALIGN=0x1000000 CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=ttyS0 reboot=p nopti" +CONFIG_PM_AUTOSLEEP=y CONFIG_PM_WAKELOCKS=y CONFIG_PM_WAKELOCKS_LIMIT=0 # CONFIG_PM_WAKELOCKS_GC is not set @@ -88,8 +90,8 @@ CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=y CONFIG_INET_ESP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_INET_DIAG_DESTROY=y @@ -105,6 +107,7 @@ CONFIG_INET6_AH=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETLABEL=y CONFIG_NETFILTER=y @@ -298,7 +301,6 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_HIDRAW=y CONFIG_UHID=y -# CONFIG_HID_GENERIC is not set CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y CONFIG_HID_ACRUX_FF=y @@ -362,6 +364,8 @@ CONFIG_USB_GADGET=y CONFIG_USB_DUMMY_HCD=y CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y CONFIG_USB_CONFIGFS_F_ACC=y CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_UEVENT=y @@ -390,6 +394,9 @@ CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_FS_ENCRYPTION=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y # CONFIG_PRINT_QUOTA_WARNING is not set @@ -423,7 +430,6 @@ CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_LOCKUP_DETECTOR=y CONFIG_PANIC_TIMEOUT=5 -# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 @@ -440,3 +446,4 @@ CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_SHA512=y diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig index e8ed8eef62ec..5cf2450842ab 100644 --- a/arch/x86/configs/x86_64_ranchu_defconfig +++ b/arch/x86/configs/x86_64_ranchu_defconfig @@ -1,5 +1,6 @@ # CONFIG_LOCALVERSION_AUTO is not set CONFIG_POSIX_MQUEUE=y +# CONFIG_USELIB is not set CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -8,20 +9,29 @@ CONFIG_TASKSTATS=y CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS_ALL=y +CONFIG_BPF_SYSCALL=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_ARCH_MMAP_RND_BITS=32 CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y CONFIG_PARTITION_ADVANCED=y CONFIG_OSF_PARTITION=y CONFIG_AMIGA_PARTITION=y @@ -34,6 +44,9 @@ CONFIG_SGI_PARTITION=y CONFIG_SUN_PARTITION=y CONFIG_KARMA_PARTITION=y CONFIG_SMP=y +CONFIG_GOLDFISH=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y CONFIG_MCORE2=y CONFIG_MAXSMP=y CONFIG_PREEMPT=y @@ -47,6 +60,9 @@ CONFIG_EFI=y CONFIG_EFI_STUB=y CONFIG_HZ_100=y CONFIG_PHYSICAL_START=0x100000 +CONFIG_RANDOMIZE_BASE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="nopti" CONFIG_PM_AUTOSLEEP=y CONFIG_PM_WAKELOCKS=y CONFIG_PM_WAKELOCKS_LIMIT=0 @@ -69,6 +85,7 @@ CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -84,6 +101,8 @@ CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=y +CONFIG_INET_AH=y CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set @@ -95,6 +114,7 @@ CONFIG_INET6_AH=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETLABEL=y CONFIG_NETFILTER=y @@ -153,6 +173,10 @@ CONFIG_IP_NF_MATCH_ECN=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_SECURITY=y @@ -161,6 +185,7 @@ CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y @@ -177,10 +202,13 @@ CONFIG_MAC80211_LEDS=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DMA_CMA=y CONFIG_CONNECTOR=y +CONFIG_OF=y +CONFIG_OF_UNITTEST=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_RAM_SIZE=16384 CONFIG_VIRTIO_BLK=y +CONFIG_UID_SYS_STATS=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y @@ -210,6 +238,7 @@ CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y CONFIG_NETCONSOLE=y CONFIG_TUN=y +CONFIG_VETH=y CONFIG_VIRTIO_NET=y CONFIG_BNX2=y CONFIG_TIGON3=y @@ -231,6 +260,7 @@ CONFIG_PPP_MPPE=y CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y CONFIG_USB_USBNET=y +CONFIG_MAC80211_HWSIM=y CONFIG_INPUT_POLLDEV=y # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_EVDEV=y @@ -263,7 +293,10 @@ CONFIG_SERIAL_NONSTANDARD=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_VIRTIO=y CONFIG_NVRAM=y +# CONFIG_DEVPORT is not set CONFIG_I2C_I801=y CONFIG_BATTERY_GOLDFISH=y CONFIG_WATCHDOG=y @@ -280,6 +313,18 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_SOUND=y CONFIG_SND=y +CONFIG_SND_HDA_INTEL=y +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_ANALOG=y +CONFIG_SND_HDA_CODEC_SIGMATEL=y +CONFIG_SND_HDA_CODEC_VIA=y +CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_CODEC_CIRRUS=y +CONFIG_SND_HDA_CODEC_CONEXANT=y +CONFIG_SND_HDA_CODEC_CA0110=y +CONFIG_SND_HDA_CODEC_CA0132=y +CONFIG_SND_HDA_CODEC_CMEDIA=y +CONFIG_SND_HDA_CODEC_SI3054=y CONFIG_HIDRAW=y CONFIG_UHID=y CONFIG_HID_A4TECH=y @@ -349,6 +394,15 @@ CONFIG_USB_UHCI_HCD=y CONFIG_USB_PRINTER=y CONFIG_USB_STORAGE=y CONFIG_USB_OTG_WAKELOCK=y +CONFIG_USB_GADGET=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_EDAC=y CONFIG_RTC_CLASS=y # CONFIG_RTC_HCTOSYS is not set @@ -362,8 +416,6 @@ CONFIG_SW_SYNC=y CONFIG_ION=y CONFIG_GOLDFISH_AUDIO=y CONFIG_GOLDFISH_SYNC=y -CONFIG_SND_HDA_INTEL=y -CONFIG_GOLDFISH=y CONFIG_GOLDFISH_PIPE=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y @@ -373,6 +425,7 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y # CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_ISO9660_FS=y CONFIG_JOLIET=y @@ -383,8 +436,10 @@ CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_HUGETLBFS=y +CONFIG_SDCARD_FS=y CONFIG_PSTORE=y CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y CONFIG_PSTORE_RAM=y # CONFIG_NETWORK_FILESYSTEMS is not set CONFIG_NLS_DEFAULT="utf8" @@ -399,16 +454,24 @@ CONFIG_DEBUG_INFO=y # CONFIG_UNUSED_SYMBOLS is not set CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DETECT_HUNG_TASK=y CONFIG_PANIC_TIMEOUT=5 CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_PROVIDE_OHCI1394_DMA_INIT=y +CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_KEYS=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_AES_X86_64=y CONFIG_CRYPTO_TWOFISH=y CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y @@ -417,3 +480,4 @@ CONFIG_PKCS7_MESSAGE_PARSER=y CONFIG_PKCS7_TEST_KEY=y # CONFIG_VIRTUALIZATION is not set CONFIG_CRC_T10DIF=y +CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder" diff --git a/arch/x86/crypto/chacha20_glue.c b/arch/x86/crypto/chacha20_glue.c index 722bacea040e..8baaff5af0b5 100644 --- a/arch/x86/crypto/chacha20_glue.c +++ b/arch/x86/crypto/chacha20_glue.c @@ -125,7 +125,7 @@ static struct crypto_alg alg = { static int __init chacha20_simd_mod_init(void) { - if (!cpu_has_ssse3) + if (!boot_cpu_has(X86_FEATURE_SSSE3)) return -ENODEV; #ifdef CONFIG_AS_AVX2 diff --git a/arch/x86/crypto/crc32c-intel_glue.c b/arch/x86/crypto/crc32c-intel_glue.c index 81a595d75cf5..15f5c7675d42 100644 --- a/arch/x86/crypto/crc32c-intel_glue.c +++ b/arch/x86/crypto/crc32c-intel_glue.c @@ -58,16 +58,11 @@ asmlinkage unsigned int crc_pcl(const u8 *buffer, int len, unsigned int crc_init); static int crc32c_pcl_breakeven = CRC32C_PCL_BREAKEVEN_EAGERFPU; -#if defined(X86_FEATURE_EAGER_FPU) #define set_pcl_breakeven_point() \ do { \ if (!use_eager_fpu()) \ crc32c_pcl_breakeven = CRC32C_PCL_BREAKEVEN_NOEAGERFPU; \ } while (0) -#else -#define set_pcl_breakeven_point() \ - (crc32c_pcl_breakeven = CRC32C_PCL_BREAKEVEN_NOEAGERFPU) -#endif #endif /* CONFIG_X86_64 */ static u32 crc32c_intel_le_hw_byte(u32 crc, unsigned char const *data, size_t length) @@ -257,7 +252,7 @@ static int __init crc32c_intel_mod_init(void) if (!x86_match_cpu(crc32c_cpu_id)) return -ENODEV; #ifdef CONFIG_X86_64 - if (cpu_has_pclmulqdq) { + if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) { alg.update = crc32c_pcl_intel_update; alg.finup = crc32c_pcl_intel_finup; alg.digest = crc32c_pcl_intel_digest; diff --git a/arch/x86/include/asm/cmpxchg_32.h b/arch/x86/include/asm/cmpxchg_32.h index f7e142926481..e4959d023af8 100644 --- a/arch/x86/include/asm/cmpxchg_32.h +++ b/arch/x86/include/asm/cmpxchg_32.h @@ -109,6 +109,6 @@ static inline u64 __cmpxchg64_local(volatile u64 *ptr, u64 old, u64 new) #endif -#define system_has_cmpxchg_double() cpu_has_cx8 +#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX8) #endif /* _ASM_X86_CMPXCHG_32_H */ diff --git a/arch/x86/include/asm/cmpxchg_64.h b/arch/x86/include/asm/cmpxchg_64.h index 1af94697aae5..caa23a34c963 100644 --- a/arch/x86/include/asm/cmpxchg_64.h +++ b/arch/x86/include/asm/cmpxchg_64.h @@ -18,6 +18,6 @@ static inline void set_64bit(volatile u64 *ptr, u64 val) cmpxchg_local((ptr), (o), (n)); \ }) -#define system_has_cmpxchg_double() cpu_has_cx16 +#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX16) #endif /* _ASM_X86_CMPXCHG_64_H */ diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 641f0f2c2982..232621c5e859 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -104,7 +104,7 @@ #define X86_FEATURE_EXTD_APICID ( 3*32+26) /* has extended APICID (8 bits) */ #define X86_FEATURE_AMD_DCM ( 3*32+27) /* multi-node processor */ #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ -#define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */ +/* free, was #define X86_FEATURE_EAGER_FPU ( 3*32+29) * "eagerfpu" Non lazy FPU restore */ #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ @@ -368,58 +368,29 @@ extern const char * const x86_bug_flags[NBUGINTS*32]; #define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit) #define cpu_has_fpu boot_cpu_has(X86_FEATURE_FPU) -#define cpu_has_de boot_cpu_has(X86_FEATURE_DE) #define cpu_has_pse boot_cpu_has(X86_FEATURE_PSE) #define cpu_has_tsc boot_cpu_has(X86_FEATURE_TSC) #define cpu_has_pge boot_cpu_has(X86_FEATURE_PGE) #define cpu_has_apic boot_cpu_has(X86_FEATURE_APIC) -#define cpu_has_sep boot_cpu_has(X86_FEATURE_SEP) -#define cpu_has_mtrr boot_cpu_has(X86_FEATURE_MTRR) -#define cpu_has_mmx boot_cpu_has(X86_FEATURE_MMX) #define cpu_has_fxsr boot_cpu_has(X86_FEATURE_FXSR) #define cpu_has_xmm boot_cpu_has(X86_FEATURE_XMM) #define cpu_has_xmm2 boot_cpu_has(X86_FEATURE_XMM2) -#define cpu_has_xmm3 boot_cpu_has(X86_FEATURE_XMM3) -#define cpu_has_ssse3 boot_cpu_has(X86_FEATURE_SSSE3) #define cpu_has_aes boot_cpu_has(X86_FEATURE_AES) #define cpu_has_avx boot_cpu_has(X86_FEATURE_AVX) #define cpu_has_avx2 boot_cpu_has(X86_FEATURE_AVX2) -#define cpu_has_ht boot_cpu_has(X86_FEATURE_HT) -#define cpu_has_nx boot_cpu_has(X86_FEATURE_NX) -#define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE) -#define cpu_has_xstore_enabled boot_cpu_has(X86_FEATURE_XSTORE_EN) -#define cpu_has_xcrypt boot_cpu_has(X86_FEATURE_XCRYPT) -#define cpu_has_xcrypt_enabled boot_cpu_has(X86_FEATURE_XCRYPT_EN) -#define cpu_has_ace2 boot_cpu_has(X86_FEATURE_ACE2) -#define cpu_has_ace2_enabled boot_cpu_has(X86_FEATURE_ACE2_EN) -#define cpu_has_phe boot_cpu_has(X86_FEATURE_PHE) -#define cpu_has_phe_enabled boot_cpu_has(X86_FEATURE_PHE_EN) -#define cpu_has_pmm boot_cpu_has(X86_FEATURE_PMM) -#define cpu_has_pmm_enabled boot_cpu_has(X86_FEATURE_PMM_EN) -#define cpu_has_ds boot_cpu_has(X86_FEATURE_DS) -#define cpu_has_pebs boot_cpu_has(X86_FEATURE_PEBS) #define cpu_has_clflush boot_cpu_has(X86_FEATURE_CLFLUSH) -#define cpu_has_bts boot_cpu_has(X86_FEATURE_BTS) #define cpu_has_gbpages boot_cpu_has(X86_FEATURE_GBPAGES) #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) #define cpu_has_pat boot_cpu_has(X86_FEATURE_PAT) -#define cpu_has_xmm4_1 boot_cpu_has(X86_FEATURE_XMM4_1) -#define cpu_has_xmm4_2 boot_cpu_has(X86_FEATURE_XMM4_2) #define cpu_has_x2apic boot_cpu_has(X86_FEATURE_X2APIC) #define cpu_has_xsave boot_cpu_has(X86_FEATURE_XSAVE) -#define cpu_has_xsaveopt boot_cpu_has(X86_FEATURE_XSAVEOPT) #define cpu_has_xsaves boot_cpu_has(X86_FEATURE_XSAVES) #define cpu_has_osxsave boot_cpu_has(X86_FEATURE_OSXSAVE) #define cpu_has_hypervisor boot_cpu_has(X86_FEATURE_HYPERVISOR) -#define cpu_has_pclmulqdq boot_cpu_has(X86_FEATURE_PCLMULQDQ) -#define cpu_has_perfctr_core boot_cpu_has(X86_FEATURE_PERFCTR_CORE) -#define cpu_has_perfctr_nb boot_cpu_has(X86_FEATURE_PERFCTR_NB) -#define cpu_has_perfctr_l2 boot_cpu_has(X86_FEATURE_PERFCTR_L2) -#define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8) -#define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) -#define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) -#define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) -#define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT) +/* + * Do not add any more of those clumsy macros - use static_cpu_has_safe() for + * fast paths and boot_cpu_has() otherwise! + */ #if __GNUC__ >= 4 extern void warn_pre_alternatives(void); diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h index 3c3550c3a4a3..146d838e6ee7 100644 --- a/arch/x86/include/asm/fpu/internal.h +++ b/arch/x86/include/asm/fpu/internal.h @@ -42,6 +42,7 @@ extern void fpu__init_cpu_xstate(void); extern void fpu__init_system(struct cpuinfo_x86 *c); extern void fpu__init_check_bugs(void); extern void fpu__resume_cpu(void); +extern u64 fpu__get_supported_xfeatures_mask(void); /* * Debugging facility: @@ -57,7 +58,7 @@ extern void fpu__resume_cpu(void); */ static __always_inline __pure bool use_eager_fpu(void) { - return static_cpu_has_safe(X86_FEATURE_EAGER_FPU); + return true; } static __always_inline __pure bool use_xsaveopt(void) @@ -595,7 +596,8 @@ switch_fpu_prepare(struct fpu *old_fpu, struct fpu *new_fpu, int cpu) * If the task has used the math, pre-load the FPU on xsave processors * or if the past 5 consecutive context-switches used math. */ - fpu.preload = new_fpu->fpstate_active && + fpu.preload = static_cpu_has(X86_FEATURE_FPU) && + new_fpu->fpstate_active && (use_eager_fpu() || new_fpu->counter > 5); if (old_fpu->fpregs_active) { diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h index 3a6c89b70307..f23cd8c80b1c 100644 --- a/arch/x86/include/asm/fpu/xstate.h +++ b/arch/x86/include/asm/fpu/xstate.h @@ -22,7 +22,7 @@ #define XFEATURE_MASK_LAZY (XFEATURE_MASK_FP | \ XFEATURE_MASK_SSE | \ XFEATURE_MASK_YMM | \ - XFEATURE_MASK_OPMASK | \ + XFEATURE_MASK_OPMASK | \ XFEATURE_MASK_ZMM_Hi256 | \ XFEATURE_MASK_Hi16_ZMM) diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index fc3c7e49c8e4..ae357d0afc91 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h @@ -105,11 +105,12 @@ struct x86_emulate_ops { * @addr: [IN ] Linear address from which to read. * @val: [OUT] Value read from memory, zero-extended to 'u_long'. * @bytes: [IN ] Number of bytes to read from memory. + * @system:[IN ] Whether the access is forced to be at CPL0. */ int (*read_std)(struct x86_emulate_ctxt *ctxt, unsigned long addr, void *val, unsigned int bytes, - struct x86_exception *fault); + struct x86_exception *fault, bool system); /* * read_phys: Read bytes of standard (non-emulated/special) memory. @@ -127,10 +128,11 @@ struct x86_emulate_ops { * @addr: [IN ] Linear address to which to write. * @val: [OUT] Value write to memory, zero-extended to 'u_long'. * @bytes: [IN ] Number of bytes to write to memory. + * @system:[IN ] Whether the access is forced to be at CPL0. */ int (*write_std)(struct x86_emulate_ctxt *ctxt, unsigned long addr, void *val, unsigned int bytes, - struct x86_exception *fault); + struct x86_exception *fault, bool system); /* * fetch: Read bytes of standard (non-emulated/special) memory. * Used for instruction fetch. diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index 222a6a3ca2b5..a438c5598a90 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h @@ -21,15 +21,6 @@ extern int smp_num_siblings; extern unsigned int num_processors; -static inline bool cpu_has_ht_siblings(void) -{ - bool has_siblings = false; -#ifdef CONFIG_SMP - has_siblings = cpu_has_ht && smp_num_siblings > 1; -#endif - return has_siblings; -} - DECLARE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map); DECLARE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_core_map); /* cpus sharing the last level cache: */ diff --git a/arch/x86/include/asm/xor_32.h b/arch/x86/include/asm/xor_32.h index 5a08bc8bff33..c54beb44c4c1 100644 --- a/arch/x86/include/asm/xor_32.h +++ b/arch/x86/include/asm/xor_32.h @@ -553,7 +553,7 @@ do { \ if (cpu_has_xmm) { \ xor_speed(&xor_block_pIII_sse); \ xor_speed(&xor_block_sse_pf64); \ - } else if (cpu_has_mmx) { \ + } else if (boot_cpu_has(X86_FEATURE_MMX)) { \ xor_speed(&xor_block_pII_mmx); \ xor_speed(&xor_block_p5_mmx); \ } else { \ diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index a3e1f8497f8c..deddc9b93299 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -1368,7 +1368,7 @@ void setup_local_APIC(void) * TODO: set up through-local-APIC from through-I/O-APIC? --macro */ value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; - if (!cpu && (pic_mode || !value)) { + if (!cpu && (pic_mode || !value || skip_ioapic_setup)) { value = APIC_DM_EXTINT; apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", cpu); } else { diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 4bf9e77f3e05..f4fb8f5b0be4 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -304,7 +304,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c) int cpu = smp_processor_id(); /* get information required for multi-node processors */ - if (cpu_has_topoext) { + if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { u32 eax, ebx, ecx, edx; cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); @@ -954,7 +954,7 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) void set_dr_addr_mask(unsigned long mask, int dr) { - if (!cpu_has_bpext) + if (!boot_cpu_has(X86_FEATURE_BPEXT)) return; switch (dr) { diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 8eabbafff213..0498ad3702f5 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1539,7 +1539,9 @@ void cpu_init(void) printk(KERN_INFO "Initializing CPU#%d\n", cpu); - if (cpu_feature_enabled(X86_FEATURE_VME) || cpu_has_tsc || cpu_has_de) + if (cpu_feature_enabled(X86_FEATURE_VME) || + cpu_has_tsc || + boot_cpu_has(X86_FEATURE_DE)) cr4_clear_bits(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE); load_current_idt(); diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 209ac1e7d1f0..565648bc1a0a 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -445,7 +445,8 @@ static void init_intel(struct cpuinfo_x86 *c) if (cpu_has_xmm2) set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC); - if (cpu_has_ds) { + + if (boot_cpu_has(X86_FEATURE_DS)) { unsigned int l1; rdmsr(MSR_IA32_MISC_ENABLE, l1, l2); if (!(l1 & (1<<11))) diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index b4ca91cf55b0..3fa72317ad78 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -591,7 +591,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf) unsigned edx; if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { - if (cpu_has_topoext) + if (boot_cpu_has(X86_FEATURE_TOPOEXT)) cpuid_count(0x8000001d, index, &eax.full, &ebx.full, &ecx.full, &edx); else @@ -637,7 +637,7 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c) void init_amd_cacheinfo(struct cpuinfo_x86 *c) { - if (cpu_has_topoext) { + if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { num_cache_leaves = find_num_cache_leaves(c); } else if (c->extended_cpuid_level >= 0x80000006) { if (cpuid_edx(0x80000006) & 0xf000) @@ -809,7 +809,7 @@ static int __cache_amd_cpumap_setup(unsigned int cpu, int index, struct cacheinfo *this_leaf; int i, sibling; - if (cpu_has_topoext) { + if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { unsigned int apicid, nshared, first, last; this_leaf = this_cpu_ci->info_list + index; diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c index b5624fafa44a..136ae86f4f5f 100644 --- a/arch/x86/kernel/cpu/mtrr/generic.c +++ b/arch/x86/kernel/cpu/mtrr/generic.c @@ -349,7 +349,7 @@ static void get_fixed_ranges(mtrr_type *frs) void mtrr_save_fixed_ranges(void *info) { - if (cpu_has_mtrr) + if (boot_cpu_has(X86_FEATURE_MTRR)) get_fixed_ranges(mtrr_state.fixed_ranges); } diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index fa77ac8291f0..f924f41af89a 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c @@ -682,7 +682,7 @@ void __init mtrr_bp_init(void) phys_addr = 32; - if (cpu_has_mtrr) { + if (boot_cpu_has(X86_FEATURE_MTRR)) { mtrr_if = &generic_mtrr_ops; size_or_mask = SIZE_OR_MASK_BITS(36); size_and_mask = 0x00f00000; diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 1cee5d2d7ece..3ea177cb7366 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -160,7 +160,7 @@ static inline int amd_pmu_addr_offset(int index, bool eventsel) if (offset) return offset; - if (!cpu_has_perfctr_core) + if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) offset = index; else offset = index << 1; @@ -652,7 +652,7 @@ static __initconst const struct x86_pmu amd_pmu = { static int __init amd_core_pmu_init(void) { - if (!cpu_has_perfctr_core) + if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) return 0; switch (boot_cpu_data.x86) { diff --git a/arch/x86/kernel/cpu/perf_event_amd_uncore.c b/arch/x86/kernel/cpu/perf_event_amd_uncore.c index cc6cedb8f25d..49742746a6c9 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_amd_uncore.c @@ -523,10 +523,10 @@ static int __init amd_uncore_init(void) if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) goto fail_nodev; - if (!cpu_has_topoext) + if (!boot_cpu_has(X86_FEATURE_TOPOEXT)) goto fail_nodev; - if (cpu_has_perfctr_nb) { + if (boot_cpu_has(X86_FEATURE_PERFCTR_NB)) { amd_uncore_nb = alloc_percpu(struct amd_uncore *); if (!amd_uncore_nb) { ret = -ENOMEM; @@ -540,7 +540,7 @@ static int __init amd_uncore_init(void) ret = 0; } - if (cpu_has_perfctr_l2) { + if (boot_cpu_has(X86_FEATURE_PERFCTR_L2)) { amd_uncore_l2 = alloc_percpu(struct amd_uncore *); if (!amd_uncore_l2) { ret = -ENOMEM; @@ -583,10 +583,11 @@ fail_online: /* amd_uncore_nb/l2 should have been freed by cleanup_cpu_online */ amd_uncore_nb = amd_uncore_l2 = NULL; - if (cpu_has_perfctr_l2) + + if (boot_cpu_has(X86_FEATURE_PERFCTR_L2)) perf_pmu_unregister(&amd_l2_pmu); fail_l2: - if (cpu_has_perfctr_nb) + if (boot_cpu_has(X86_FEATURE_PERFCTR_NB)) perf_pmu_unregister(&amd_nb_pmu); if (amd_uncore_l2) free_percpu(amd_uncore_l2); diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 1f4acd68b98b..74b8dcd1bbdc 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -11,6 +11,7 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of_irq.h> +#include <linux/libfdt.h> #include <linux/slab.h> #include <linux/pci.h> #include <linux/of_pci.h> @@ -199,19 +200,22 @@ static struct of_ioapic_type of_ioapic_type[] = static int dt_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { - struct of_phandle_args *irq_data = (void *)arg; + struct irq_fwspec *fwspec = (struct irq_fwspec *)arg; struct of_ioapic_type *it; struct irq_alloc_info tmp; + int type_index; - if (WARN_ON(irq_data->args_count < 2)) + if (WARN_ON(fwspec->param_count < 2)) return -EINVAL; - if (irq_data->args[1] >= ARRAY_SIZE(of_ioapic_type)) + + type_index = fwspec->param[1]; + if (type_index >= ARRAY_SIZE(of_ioapic_type)) return -EINVAL; - it = &of_ioapic_type[irq_data->args[1]]; + it = &of_ioapic_type[type_index]; ioapic_set_alloc_attr(&tmp, NUMA_NO_NODE, it->trigger, it->polarity); tmp.ioapic_id = mpc_ioapic_id(mp_irqdomain_ioapic_idx(domain)); - tmp.ioapic_pin = irq_data->args[0]; + tmp.ioapic_pin = fwspec->param[0]; return mp_irqdomain_alloc(domain, virq, nr_irqs, &tmp); } @@ -276,14 +280,15 @@ static void __init x86_flattree_get_config(void) map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK), (u64)128); - initial_boot_params = dt = early_memremap(initial_dtb, map_len); - size = of_get_flat_dt_size(); + dt = early_memremap(initial_dtb, map_len); + size = fdt_totalsize(dt); if (map_len < size) { early_memunmap(dt, map_len); - initial_boot_params = dt = early_memremap(initial_dtb, size); + dt = early_memremap(initial_dtb, size); map_len = size; } + early_init_dt_verify(dt); unflatten_and_copy_device_tree(); early_memunmap(dt, map_len); } diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c index d25097c3fc1d..6aa0b519c851 100644 --- a/arch/x86/kernel/fpu/core.c +++ b/arch/x86/kernel/fpu/core.c @@ -114,6 +114,10 @@ void __kernel_fpu_begin(void) kernel_fpu_disable(); if (fpu->fpregs_active) { + /* + * Ignore return value -- we don't care if reg state + * is clobbered. + */ copy_fpregs_to_fpstate(fpu); } else { this_cpu_write(fpu_fpregs_owner_ctx, NULL); @@ -189,8 +193,12 @@ void fpu__save(struct fpu *fpu) preempt_disable(); if (fpu->fpregs_active) { - if (!copy_fpregs_to_fpstate(fpu)) - fpregs_deactivate(fpu); + if (!copy_fpregs_to_fpstate(fpu)) { + if (use_eager_fpu()) + copy_kernel_to_fpregs(&fpu->state); + else + fpregs_deactivate(fpu); + } } preempt_enable(); } @@ -259,7 +267,11 @@ static void fpu_copy(struct fpu *dst_fpu, struct fpu *src_fpu) preempt_disable(); if (!copy_fpregs_to_fpstate(dst_fpu)) { memcpy(&src_fpu->state, &dst_fpu->state, xstate_size); - fpregs_deactivate(src_fpu); + + if (use_eager_fpu()) + copy_kernel_to_fpregs(&src_fpu->state); + else + fpregs_deactivate(src_fpu); } preempt_enable(); } @@ -409,8 +421,10 @@ static inline void copy_init_fpstate_to_fpregs(void) { if (use_xsave()) copy_kernel_to_xregs(&init_fpstate.xsave, -1); - else + else if (static_cpu_has(X86_FEATURE_FXSR)) copy_kernel_to_fxregs(&init_fpstate.fxsave); + else + copy_kernel_to_fregs(&init_fpstate.fsave); } /* @@ -423,7 +437,7 @@ void fpu__clear(struct fpu *fpu) { WARN_ON_FPU(fpu != ¤t->thread.fpu); /* Almost certainly an anomaly */ - if (!use_eager_fpu()) { + if (!use_eager_fpu() || !static_cpu_has(X86_FEATURE_FPU)) { /* FPU state will be reallocated lazily at the first use. */ fpu__drop(fpu); } else { diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c index 1011c05b1bd5..954517285fa2 100644 --- a/arch/x86/kernel/fpu/init.c +++ b/arch/x86/kernel/fpu/init.c @@ -3,8 +3,11 @@ */ #include <asm/fpu/internal.h> #include <asm/tlbflush.h> +#include <asm/setup.h> +#include <asm/cmdline.h> #include <linux/sched.h> +#include <linux/init.h> /* * Initialize the TS bit in CR0 according to the style of context-switches @@ -12,10 +15,7 @@ */ static void fpu__init_cpu_ctx_switch(void) { - if (!cpu_has_eager_fpu) - stts(); - else - clts(); + clts(); } /* @@ -75,13 +75,15 @@ static void fpu__init_system_early_generic(struct cpuinfo_x86 *c) cr0 &= ~(X86_CR0_TS | X86_CR0_EM); write_cr0(cr0); - asm volatile("fninit ; fnstsw %0 ; fnstcw %1" - : "+m" (fsw), "+m" (fcw)); + if (!test_bit(X86_FEATURE_FPU, (unsigned long *)cpu_caps_cleared)) { + asm volatile("fninit ; fnstsw %0 ; fnstcw %1" + : "+m" (fsw), "+m" (fcw)); - if (fsw == 0 && (fcw & 0x103f) == 0x003f) - set_cpu_cap(c, X86_FEATURE_FPU); - else - clear_cpu_cap(c, X86_FEATURE_FPU); + if (fsw == 0 && (fcw & 0x103f) == 0x003f) + set_cpu_cap(c, X86_FEATURE_FPU); + else + clear_cpu_cap(c, X86_FEATURE_FPU); + } #ifndef CONFIG_MATH_EMULATION if (!cpu_has_fpu) { @@ -130,7 +132,7 @@ static void __init fpu__init_system_generic(void) * Set up the legacy init FPU context. (xstate init might overwrite this * with a more modern format, if the CPU supports it.) */ - fpstate_init_fxstate(&init_fpstate.fxsave); + fpstate_init(&init_fpstate); fpu__init_system_mxcsr(); } @@ -230,53 +232,16 @@ static void __init fpu__init_system_xstate_size_legacy(void) } /* - * FPU context switching strategies: - * - * Against popular belief, we don't do lazy FPU saves, due to the - * task migration complications it brings on SMP - we only do - * lazy FPU restores. - * - * 'lazy' is the traditional strategy, which is based on setting - * CR0::TS to 1 during context-switch (instead of doing a full - * restore of the FPU state), which causes the first FPU instruction - * after the context switch (whenever it is executed) to fault - at - * which point we lazily restore the FPU state into FPU registers. - * - * Tasks are of course under no obligation to execute FPU instructions, - * so it can easily happen that another context-switch occurs without - * a single FPU instruction being executed. If we eventually switch - * back to the original task (that still owns the FPU) then we have - * not only saved the restores along the way, but we also have the - * FPU ready to be used for the original task. - * - * 'eager' switching is used on modern CPUs, there we switch the FPU - * state during every context switch, regardless of whether the task - * has used FPU instructions in that time slice or not. This is done - * because modern FPU context saving instructions are able to optimize - * state saving and restoration in hardware: they can detect both - * unused and untouched FPU state and optimize accordingly. - * - * [ Note that even in 'lazy' mode we might optimize context switches - * to use 'eager' restores, if we detect that a task is using the FPU - * frequently. See the fpu->counter logic in fpu/internal.h for that. ] + * Find supported xfeatures based on cpu features and command-line input. + * This must be called after fpu__init_parse_early_param() is called and + * xfeatures_mask is enumerated. */ -static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO; - -static int __init eager_fpu_setup(char *s) +u64 __init fpu__get_supported_xfeatures_mask(void) { - if (!strcmp(s, "on")) - eagerfpu = ENABLE; - else if (!strcmp(s, "off")) - eagerfpu = DISABLE; - else if (!strcmp(s, "auto")) - eagerfpu = AUTO; - return 1; + return XCNTXT_MASK; } -__setup("eagerfpu=", eager_fpu_setup); -/* - * Pick the FPU context switching strategy: - */ +/* Legacy code to initialize eager fpu mode. */ static void __init fpu__init_system_ctx_switch(void) { static bool on_boot_cpu = 1; @@ -286,25 +251,31 @@ static void __init fpu__init_system_ctx_switch(void) WARN_ON_FPU(current->thread.fpu.fpstate_active); current_thread_info()->status = 0; +} - /* Auto enable eagerfpu for xsaveopt */ - if (cpu_has_xsaveopt && eagerfpu != DISABLE) - eagerfpu = ENABLE; - - if (xfeatures_mask & XFEATURE_MASK_EAGER) { - if (eagerfpu == DISABLE) { - pr_err("x86/fpu: eagerfpu switching disabled, disabling the following xstate features: 0x%llx.\n", - xfeatures_mask & XFEATURE_MASK_EAGER); - xfeatures_mask &= ~XFEATURE_MASK_EAGER; - } else { - eagerfpu = ENABLE; - } +/* + * We parse fpu parameters early because fpu__init_system() is executed + * before parse_early_param(). + */ +static void __init fpu__init_parse_early_param(void) +{ + if (cmdline_find_option_bool(boot_command_line, "no387")) + setup_clear_cpu_cap(X86_FEATURE_FPU); + + if (cmdline_find_option_bool(boot_command_line, "nofxsr")) { + setup_clear_cpu_cap(X86_FEATURE_FXSR); + setup_clear_cpu_cap(X86_FEATURE_FXSR_OPT); + setup_clear_cpu_cap(X86_FEATURE_XMM); } - if (eagerfpu == ENABLE) - setup_force_cpu_cap(X86_FEATURE_EAGER_FPU); + if (cmdline_find_option_bool(boot_command_line, "noxsave")) + fpu__xstate_clear_all_cpu_caps(); + + if (cmdline_find_option_bool(boot_command_line, "noxsaveopt")) + setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT); - printk(KERN_INFO "x86/fpu: Using '%s' FPU context switches.\n", eagerfpu == ENABLE ? "eager" : "lazy"); + if (cmdline_find_option_bool(boot_command_line, "noxsaves")) + setup_clear_cpu_cap(X86_FEATURE_XSAVES); } /* @@ -313,6 +284,7 @@ static void __init fpu__init_system_ctx_switch(void) */ void __init fpu__init_system(struct cpuinfo_x86 *c) { + fpu__init_parse_early_param(); fpu__init_system_early_generic(c); /* @@ -336,62 +308,3 @@ void __init fpu__init_system(struct cpuinfo_x86 *c) fpu__init_system_ctx_switch(); } - -/* - * Boot parameter to turn off FPU support and fall back to math-emu: - */ -static int __init no_387(char *s) -{ - setup_clear_cpu_cap(X86_FEATURE_FPU); - return 1; -} -__setup("no387", no_387); - -/* - * Disable all xstate CPU features: - */ -static int __init x86_noxsave_setup(char *s) -{ - if (strlen(s)) - return 0; - - fpu__xstate_clear_all_cpu_caps(); - - return 1; -} -__setup("noxsave", x86_noxsave_setup); - -/* - * Disable the XSAVEOPT instruction specifically: - */ -static int __init x86_noxsaveopt_setup(char *s) -{ - setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT); - - return 1; -} -__setup("noxsaveopt", x86_noxsaveopt_setup); - -/* - * Disable the XSAVES instruction: - */ -static int __init x86_noxsaves_setup(char *s) -{ - setup_clear_cpu_cap(X86_FEATURE_XSAVES); - - return 1; -} -__setup("noxsaves", x86_noxsaves_setup); - -/* - * Disable FX save/restore and SSE support: - */ -static int __init x86_nofxsr_setup(char *s) -{ - setup_clear_cpu_cap(X86_FEATURE_FXSR); - setup_clear_cpu_cap(X86_FEATURE_FXSR_OPT); - setup_clear_cpu_cap(X86_FEATURE_XMM); - - return 1; -} -__setup("nofxsr", x86_nofxsr_setup); diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 70fc312221fc..3fa200ecca62 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -632,8 +632,7 @@ void __init fpu__init_system_xstate(void) BUG(); } - /* Support only the state known to the OS: */ - xfeatures_mask = xfeatures_mask & XCNTXT_MASK; + xfeatures_mask &= fpu__get_supported_xfeatures_mask(); /* Enable xstate instructions to be able to continue with initialization: */ fpu__init_cpu_xstate(); diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index 50a3fad5b89f..2bcfb5f2bc44 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -300,6 +300,10 @@ static int arch_build_bp_info(struct perf_event *bp) return -EINVAL; if (bp->attr.bp_addr & (bp->attr.bp_len - 1)) return -EINVAL; + + if (!boot_cpu_has(X86_FEATURE_BPEXT)) + return -EOPNOTSUPP; + /* * It's impossible to use a range breakpoint to fake out * user vs kernel detection because bp_len - 1 can't @@ -307,8 +311,6 @@ static int arch_build_bp_info(struct perf_event *bp) * breakpoints, then we'll have to check for kprobe-blacklisted * addresses anywhere in the range. */ - if (!cpu_has_bpext) - return -EOPNOTSUPP; info->mask = bp->attr.bp_len - 1; info->len = X86_BREAKPOINT_LEN_1; } diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 00c7878043ef..1f7aefc7b0b4 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -295,7 +295,7 @@ do { \ static bool match_smt(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o) { - if (cpu_has_topoext) { + if (boot_cpu_has(X86_FEATURE_TOPOEXT)) { int cpu1 = c->cpu_index, cpu2 = o->cpu_index; if (c->phys_proc_id == o->phys_proc_id && @@ -1344,6 +1344,7 @@ static void remove_siblinginfo(int cpu) cpumask_clear(topology_core_cpumask(cpu)); c->phys_proc_id = 0; c->cpu_core_id = 0; + c->booted_cores = 0; cpumask_clear_cpu(cpu, cpu_sibling_setup_mask); } diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 1fbd2631be60..8c73bf1492b8 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -751,7 +751,6 @@ dotraplinkage void do_device_not_available(struct pt_regs *regs, long error_code) { RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); - BUG_ON(use_eager_fpu()); #ifdef CONFIG_MATH_EMULATION if (read_cr0() & X86_CR0_EM) { diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c index af57736a0309..d6d64a519559 100644 --- a/arch/x86/kernel/vm86_32.c +++ b/arch/x86/kernel/vm86_32.c @@ -357,8 +357,10 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus) tss = &per_cpu(cpu_tss, get_cpu()); /* make room for real-mode segments */ tsk->thread.sp0 += 16; - if (cpu_has_sep) + + if (static_cpu_has_safe(X86_FEATURE_SEP)) tsk->thread.sysenter_cs = 0; + load_sp0(tss, &tsk->thread); put_cpu(); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 8864fec63a20..f1507626ed36 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -790,6 +790,19 @@ static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel) return assign_eip_near(ctxt, ctxt->_eip + rel); } +static int linear_read_system(struct x86_emulate_ctxt *ctxt, ulong linear, + void *data, unsigned size) +{ + return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, true); +} + +static int linear_write_system(struct x86_emulate_ctxt *ctxt, + ulong linear, void *data, + unsigned int size) +{ + return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, true); +} + static int segmented_read_std(struct x86_emulate_ctxt *ctxt, struct segmented_address addr, void *data, @@ -801,7 +814,7 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt, rc = linearize(ctxt, addr, size, false, &linear); if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception); + return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, false); } static int segmented_write_std(struct x86_emulate_ctxt *ctxt, @@ -815,7 +828,7 @@ static int segmented_write_std(struct x86_emulate_ctxt *ctxt, rc = linearize(ctxt, addr, size, true, &linear); if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception); + return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, false); } /* @@ -1488,8 +1501,7 @@ static int read_interrupt_descriptor(struct x86_emulate_ctxt *ctxt, return emulate_gp(ctxt, index << 3 | 0x2); addr = dt.address + index * 8; - return ctxt->ops->read_std(ctxt, addr, desc, sizeof *desc, - &ctxt->exception); + return linear_read_system(ctxt, addr, desc, sizeof *desc); } static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt, @@ -1552,8 +1564,7 @@ static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt, if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->read_std(ctxt, *desc_addr_p, desc, sizeof(*desc), - &ctxt->exception); + return linear_read_system(ctxt, *desc_addr_p, desc, sizeof(*desc)); } /* allowed just for 8 bytes segments */ @@ -1567,8 +1578,7 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt, if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->write_std(ctxt, addr, desc, sizeof *desc, - &ctxt->exception); + return linear_write_system(ctxt, addr, desc, sizeof *desc); } static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, @@ -1729,8 +1739,7 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, return ret; } } else if (ctxt->mode == X86EMUL_MODE_PROT64) { - ret = ctxt->ops->read_std(ctxt, desc_addr+8, &base3, - sizeof(base3), &ctxt->exception); + ret = linear_read_system(ctxt, desc_addr+8, &base3, sizeof(base3)); if (ret != X86EMUL_CONTINUE) return ret; if (is_noncanonical_address(get_desc_base(&seg_desc) | @@ -2043,11 +2052,11 @@ static int __emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq) eip_addr = dt.address + (irq << 2); cs_addr = dt.address + (irq << 2) + 2; - rc = ops->read_std(ctxt, cs_addr, &cs, 2, &ctxt->exception); + rc = linear_read_system(ctxt, cs_addr, &cs, 2); if (rc != X86EMUL_CONTINUE) return rc; - rc = ops->read_std(ctxt, eip_addr, &eip, 2, &ctxt->exception); + rc = linear_read_system(ctxt, eip_addr, &eip, 2); if (rc != X86EMUL_CONTINUE) return rc; @@ -2891,12 +2900,12 @@ static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt, #ifdef CONFIG_X86_64 base |= ((u64)base3) << 32; #endif - r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL); + r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL, true); if (r != X86EMUL_CONTINUE) return false; if (io_bitmap_ptr + port/8 > desc_limit_scaled(&tr_seg)) return false; - r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL); + r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL, true); if (r != X86EMUL_CONTINUE) return false; if ((perm >> bit_idx) & mask) @@ -3025,35 +3034,30 @@ static int task_switch_16(struct x86_emulate_ctxt *ctxt, u16 tss_selector, u16 old_tss_sel, ulong old_tss_base, struct desc_struct *new_desc) { - const struct x86_emulate_ops *ops = ctxt->ops; struct tss_segment_16 tss_seg; int ret; u32 new_tss_base = get_desc_base(new_desc); - ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; save_state_to_tss16(ctxt, &tss_seg); - ret = ops->write_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_write_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; - ret = ops->read_std(ctxt, new_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, new_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; if (old_tss_sel != 0xffff) { tss_seg.prev_task_link = old_tss_sel; - ret = ops->write_std(ctxt, new_tss_base, - &tss_seg.prev_task_link, - sizeof tss_seg.prev_task_link, - &ctxt->exception); + ret = linear_write_system(ctxt, new_tss_base, + &tss_seg.prev_task_link, + sizeof tss_seg.prev_task_link); if (ret != X86EMUL_CONTINUE) return ret; } @@ -3169,38 +3173,34 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt, u16 tss_selector, u16 old_tss_sel, ulong old_tss_base, struct desc_struct *new_desc) { - const struct x86_emulate_ops *ops = ctxt->ops; struct tss_segment_32 tss_seg; int ret; u32 new_tss_base = get_desc_base(new_desc); u32 eip_offset = offsetof(struct tss_segment_32, eip); u32 ldt_sel_offset = offsetof(struct tss_segment_32, ldt_selector); - ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; save_state_to_tss32(ctxt, &tss_seg); /* Only GP registers and segment selectors are saved */ - ret = ops->write_std(ctxt, old_tss_base + eip_offset, &tss_seg.eip, - ldt_sel_offset - eip_offset, &ctxt->exception); + ret = linear_write_system(ctxt, old_tss_base + eip_offset, &tss_seg.eip, + ldt_sel_offset - eip_offset); if (ret != X86EMUL_CONTINUE) return ret; - ret = ops->read_std(ctxt, new_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, new_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; if (old_tss_sel != 0xffff) { tss_seg.prev_task_link = old_tss_sel; - ret = ops->write_std(ctxt, new_tss_base, - &tss_seg.prev_task_link, - sizeof tss_seg.prev_task_link, - &ctxt->exception); + ret = linear_write_system(ctxt, new_tss_base, + &tss_seg.prev_task_link, + sizeof tss_seg.prev_task_link); if (ret != X86EMUL_CONTINUE) return ret; } diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 1c96f09367ae..a1afd80a68aa 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -288,8 +288,16 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu) if (!kvm_vcpu_has_lapic(vcpu)) return; + /* + * KVM emulates 82093AA datasheet (with in-kernel IOAPIC implementation) + * which doesn't have EOI register; Some buggy OSes (e.g. Windows with + * Hyper-V role) disable EOI broadcast in lapic not checking for IOAPIC + * version first and level-triggered interrupts never get EOIed in + * IOAPIC. + */ feat = kvm_find_cpuid_entry(apic->vcpu, 0x1, 0); - if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31)))) + if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31))) && + !ioapic_in_kernel(vcpu->kvm)) v |= APIC_LVR_DIRECTED_EOI; apic_set_reg(apic, APIC_LVR, v); } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 528b4352fa99..63c44a9bf6bb 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2319,6 +2319,8 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr, return; } + WARN_ON_ONCE(vmx->emulation_required); + if (kvm_exception_is_soft(nr)) { vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, vmx->vcpu.arch.event_exit_inst_len); @@ -6037,12 +6039,12 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu) goto out; } - if (err != EMULATE_DONE) { - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; - vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; - vcpu->run->internal.ndata = 0; - return 0; - } + if (err != EMULATE_DONE) + goto emulation_error; + + if (vmx->emulation_required && !vmx->rmode.vm86_active && + vcpu->arch.exception.pending) + goto emulation_error; if (vcpu->arch.halt_request) { vcpu->arch.halt_request = 0; @@ -6058,6 +6060,12 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu) out: return ret; + +emulation_error: + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; + vcpu->run->internal.ndata = 0; + return 0; } static int __grow_ple_window(int val) @@ -6684,8 +6692,7 @@ static int nested_vmx_check_vmptr(struct kvm_vcpu *vcpu, int exit_reason, vmcs_read32(VMX_INSTRUCTION_INFO), false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr, - sizeof(vmptr), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &vmptr, sizeof(vmptr), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -7203,8 +7210,8 @@ static int handle_vmread(struct kvm_vcpu *vcpu) vmx_instruction_info, true, &gva)) return 1; /* _system ok, as nested_vmx_check_permission verified cpl=0 */ - kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, gva, - &field_value, (is_long_mode(vcpu) ? 8 : 4), NULL); + kvm_write_guest_virt_system(vcpu, gva, &field_value, + (is_long_mode(vcpu) ? 8 : 4), NULL); } nested_vmx_succeed(vcpu); @@ -7239,8 +7246,8 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, exit_qualification, vmx_instruction_info, false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, - &field_value, (is_64_bit_mode(vcpu) ? 8 : 4), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &field_value, + (is_64_bit_mode(vcpu) ? 8 : 4), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -7330,9 +7337,9 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu) vmx_instruction_info, true, &vmcs_gva)) return 1; /* ok to use *_system, as nested_vmx_check_permission verified cpl=0 */ - if (kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, vmcs_gva, - (void *)&to_vmx(vcpu)->nested.current_vmptr, - sizeof(u64), &e)) { + if (kvm_write_guest_virt_system(vcpu, vmcs_gva, + (void *)&to_vmx(vcpu)->nested.current_vmptr, + sizeof(u64), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -7386,8 +7393,7 @@ static int handle_invept(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION), vmx_instruction_info, false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &operand, - sizeof(operand), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &operand, sizeof(operand), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -7446,8 +7452,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION), vmx_instruction_info, false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vpid, - sizeof(u32), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &vpid, sizeof(u32), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f37f0c72b22a..53d43d22a84b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3973,13 +3973,14 @@ long kvm_arch_vm_ioctl(struct file *filp, mutex_unlock(&kvm->lock); break; case KVM_XEN_HVM_CONFIG: { + struct kvm_xen_hvm_config xhc; r = -EFAULT; - if (copy_from_user(&kvm->arch.xen_hvm_config, argp, - sizeof(struct kvm_xen_hvm_config))) + if (copy_from_user(&xhc, argp, sizeof(xhc))) goto out; r = -EINVAL; - if (kvm->arch.xen_hvm_config.flags) + if (xhc.flags) goto out; + memcpy(&kvm->arch.xen_hvm_config, &xhc, sizeof(xhc)); r = 0; break; } @@ -4244,11 +4245,10 @@ static int kvm_fetch_guest_virt(struct x86_emulate_ctxt *ctxt, return X86EMUL_CONTINUE; } -int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt, +int kvm_read_guest_virt(struct kvm_vcpu *vcpu, gva_t addr, void *val, unsigned int bytes, struct x86_exception *exception) { - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0; return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access, @@ -4256,12 +4256,17 @@ int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt, } EXPORT_SYMBOL_GPL(kvm_read_guest_virt); -static int kvm_read_guest_virt_system(struct x86_emulate_ctxt *ctxt, - gva_t addr, void *val, unsigned int bytes, - struct x86_exception *exception) +static int emulator_read_std(struct x86_emulate_ctxt *ctxt, + gva_t addr, void *val, unsigned int bytes, + struct x86_exception *exception, bool system) { struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, exception); + u32 access = 0; + + if (!system && kvm_x86_ops->get_cpl(vcpu) == 3) + access |= PFERR_USER_MASK; + + return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access, exception); } static int kvm_read_guest_phys_system(struct x86_emulate_ctxt *ctxt, @@ -4273,18 +4278,16 @@ static int kvm_read_guest_phys_system(struct x86_emulate_ctxt *ctxt, return r < 0 ? X86EMUL_IO_NEEDED : X86EMUL_CONTINUE; } -int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt, - gva_t addr, void *val, - unsigned int bytes, - struct x86_exception *exception) +static int kvm_write_guest_virt_helper(gva_t addr, void *val, unsigned int bytes, + struct kvm_vcpu *vcpu, u32 access, + struct x86_exception *exception) { - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); void *data = val; int r = X86EMUL_CONTINUE; while (bytes) { gpa_t gpa = vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr, - PFERR_WRITE_MASK, + access, exception); unsigned offset = addr & (PAGE_SIZE-1); unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset); @@ -4305,6 +4308,27 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt, out: return r; } + +static int emulator_write_std(struct x86_emulate_ctxt *ctxt, gva_t addr, void *val, + unsigned int bytes, struct x86_exception *exception, + bool system) +{ + struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); + u32 access = PFERR_WRITE_MASK; + + if (!system && kvm_x86_ops->get_cpl(vcpu) == 3) + access |= PFERR_USER_MASK; + + return kvm_write_guest_virt_helper(addr, val, bytes, vcpu, + access, exception); +} + +int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, gva_t addr, void *val, + unsigned int bytes, struct x86_exception *exception) +{ + return kvm_write_guest_virt_helper(addr, val, bytes, vcpu, + PFERR_WRITE_MASK, exception); +} EXPORT_SYMBOL_GPL(kvm_write_guest_virt_system); static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva, @@ -5024,8 +5048,8 @@ static void emulator_set_hflags(struct x86_emulate_ctxt *ctxt, unsigned emul_fla static const struct x86_emulate_ops emulate_ops = { .read_gpr = emulator_read_gpr, .write_gpr = emulator_write_gpr, - .read_std = kvm_read_guest_virt_system, - .write_std = kvm_write_guest_virt_system, + .read_std = emulator_read_std, + .write_std = emulator_write_std, .read_phys = kvm_read_guest_phys_system, .fetch = kvm_fetch_guest_virt, .read_emulated = emulator_read_emulated, diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index f2afa5fe48a6..53a750a10598 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -164,11 +164,11 @@ int kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip); void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr); -int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt, +int kvm_read_guest_virt(struct kvm_vcpu *vcpu, gva_t addr, void *val, unsigned int bytes, struct x86_exception *exception); -int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt, +int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, gva_t addr, void *val, unsigned int bytes, struct x86_exception *exception); diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index c013326a0d7a..08e94b6139ab 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -1,5 +1,6 @@ #include <linux/mm.h> #include <linux/gfp.h> +#include <linux/hugetlb.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> #include <asm/tlb.h> @@ -600,6 +601,10 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot) (mtrr != MTRR_TYPE_WRBACK)) return 0; + /* Bail out if we are we on a populated non-leaf entry: */ + if (pud_present(*pud) && !pud_huge(*pud)) + return 0; + prot = pgprot_4k_2_large(prot); set_pte((pte_t *)pud, pfn_pte( @@ -628,6 +633,10 @@ int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot) return 0; } + /* Bail out if we are we on a populated non-leaf entry: */ + if (pmd_present(*pmd) && !pmd_huge(*pmd)) + return 0; + prot = pgprot_4k_2_large(prot); set_pte((pte_t *)pmd, pfn_pte( diff --git a/arch/x86/mm/setup_nx.c b/arch/x86/mm/setup_nx.c index 90555bf60aa4..92e2eacb3321 100644 --- a/arch/x86/mm/setup_nx.c +++ b/arch/x86/mm/setup_nx.c @@ -31,7 +31,7 @@ early_param("noexec", noexec_setup); void x86_configure_nx(void) { - if (cpu_has_nx && !disable_nx) + if (boot_cpu_has(X86_FEATURE_NX) && !disable_nx) __supported_pte_mask |= _PAGE_NX; else __supported_pte_mask &= ~_PAGE_NX; @@ -39,7 +39,7 @@ void x86_configure_nx(void) void __init x86_report_nx(void) { - if (!cpu_has_nx) { + if (!boot_cpu_has(X86_FEATURE_NX)) { printk(KERN_NOTICE "Notice: NX (Execute Disable) protection " "missing in CPU!\n"); } else { diff --git a/arch/x86/power/hibernate_32.c b/arch/x86/power/hibernate_32.c index 291226b952a9..77ac4e4deb16 100644 --- a/arch/x86/power/hibernate_32.c +++ b/arch/x86/power/hibernate_32.c @@ -142,7 +142,7 @@ static inline void resume_init_first_level_page_table(pgd_t *pg_dir) #endif } -int swsusp_arch_resume(void) +asmlinkage int swsusp_arch_resume(void) { int error; diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index 009947d419a6..0e0c773edffc 100644 --- a/arch/x86/power/hibernate_64.c +++ b/arch/x86/power/hibernate_64.c @@ -78,7 +78,7 @@ static int set_up_temporary_mappings(void) return 0; } -int swsusp_arch_resume(void) +asmlinkage int swsusp_arch_resume(void) { int error; diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index 8ea8211b2d58..f8bb0e4d035a 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -108,6 +108,7 @@ static void round_robin_cpu(unsigned int tsk_index) cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus); if (cpumask_empty(tmp)) { mutex_unlock(&round_robin_lock); + free_cpumask_var(tmp); return; } for_each_cpu(cpu, tmp) { @@ -125,6 +126,8 @@ static void round_robin_cpu(unsigned int tsk_index) mutex_unlock(&round_robin_lock); set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu)); + + free_cpumask_var(tmp); } static void exit_round_robin(unsigned int tsk_index) diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index bf6873f95e72..0b5eedb60d04 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -204,6 +204,7 @@ u32 acpi_ev_fixed_event_detect(void) u32 fixed_status; u32 fixed_enable; u32 i; + acpi_status status; ACPI_FUNCTION_NAME(ev_fixed_event_detect); @@ -211,8 +212,12 @@ u32 acpi_ev_fixed_event_detect(void) * Read the fixed feature status and enable registers, as all the cases * depend on their values. Ignore errors here. */ - (void)acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status); - (void)acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable); + status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status); + status |= + acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable); + if (ACPI_FAILURE(status)) { + return (int_status); + } ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, "Fixed Event Block: Enable %08X Status %08X\n", diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index 7eba578d36f3..10262cae8a19 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -308,6 +308,14 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) /* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */ status = AE_OK; + } else if (ACPI_FAILURE(status)) { + + /* If return_object exists, delete it */ + + if (info->return_object) { + acpi_ut_remove_reference(info->return_object); + info->return_object = NULL; + } } ACPI_DEBUG_PRINT((ACPI_DB_NAMES, diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index bb01dea39fdc..9825780a1cd2 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -161,7 +161,7 @@ int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) { int ret; - if (ignore_ppc) { + if (ignore_ppc || !pr->performance) { /* * Only when it is notification event, the _OST object * will be evaluated. Otherwise it is skipped. diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 60d6db82ce5a..f9b86a1d922d 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4187,6 +4187,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* https://bugzilla.kernel.org/show_bug.cgi?id=15573 */ { "C300-CTFDDAC128MAG", "0001", ATA_HORKAGE_NONCQ, }, + /* Some Sandisk SSDs lock up hard with NCQ enabled. Reported on + SD7SN6S256G and SD8SN8U256G */ + { "SanDisk SD[78]SN*G", NULL, ATA_HORKAGE_NONCQ, }, + /* devices which puke on READ_NATIVE_MAX */ { "HDS724040KLSA80", "KFAOA20N", ATA_HORKAGE_BROKEN_HPA, }, { "WDC WD3200JD-00KLB0", "WD-WCAMR1130137", ATA_HORKAGE_BROKEN_HPA }, @@ -4247,6 +4251,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "SanDisk SD7UB3Q*G1001", NULL, ATA_HORKAGE_NOLPM, }, /* devices that don't properly handle queued TRIM commands */ + { "Micron_M500IT_*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 0dd6379ac215..7e00d10d5a94 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -25,6 +25,7 @@ #include <linux/async.h> #include <linux/pm_runtime.h> #include <linux/pinctrl/devinfo.h> +#include <linux/platform_device.h> #include "base.h" #include "power/power.h" @@ -642,6 +643,20 @@ void device_initial_probe(struct device *dev) { __device_attach(dev, true); } +#ifdef CONFIG_PLATFORM_AUTO +static inline int lock_parent(struct device *dev) +{ + if (!dev->parent || dev->bus == &platform_bus_type) + return 0; + + return 1; +} +#else +static inline int lock_parent(struct device *dev) +{ + return dev->parent ? 1 : 0; +} +#endif static int __driver_attach(struct device *dev, void *data) { @@ -659,14 +674,13 @@ static int __driver_attach(struct device *dev, void *data) if (!driver_match_device(drv, dev)) return 0; - - if (dev->parent) /* Needed for USB */ + if (lock_parent(dev)) device_lock(dev->parent); device_lock(dev); if (!dev->driver) driver_probe_device(drv, dev); device_unlock(dev); - if (dev->parent) + if (lock_parent(dev)) device_unlock(dev->parent); return 0; diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index 93362362aa55..8474a1b0740f 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -230,6 +230,8 @@ static int pcd_block_open(struct block_device *bdev, fmode_t mode) struct pcd_unit *cd = bdev->bd_disk->private_data; int ret; + check_disk_change(bdev); + mutex_lock(&pcd_mutex); ret = cdrom_open(&cd->info, bdev, mode); mutex_unlock(&pcd_mutex); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 54cef3dc0beb..91676535a1a3 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -336,6 +336,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK }, + /* Additional Realtek 8723BU Bluetooth devices */ + { USB_DEVICE(0x7392, 0xa611), .driver_info = BTUSB_REALTEK }, + /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK }, @@ -343,6 +346,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK }, + /* Additional Realtek 8822BE Bluetooth devices */ + { USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK }, + /* Silicon Wave based devices */ { USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE }, diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index b5f245d2875c..0151039bff05 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -1154,9 +1154,6 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, cd_dbg(CD_OPEN, "entering cdrom_open\n"); - /* open is event synchronization point, check events first */ - check_disk_change(bdev); - /* if this was a O_NONBLOCK open and we should honor the flags, * do a quick open without drive/disc integrity checks. */ cdi->use_count++; diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index 584bc3126403..e2808fefbb78 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -497,6 +497,9 @@ static struct cdrom_device_ops gdrom_ops = { static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode) { int ret; + + check_disk_change(bdev); + mutex_lock(&gdrom_mutex); ret = cdrom_open(gd.cd_info, bdev, mode); mutex_unlock(&gdrom_mutex); 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/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index 001a1b367dc6..aa45c2e7ec7b 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -37,6 +37,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = { .ctx = 0, .mempool = POOL_TYPE_MUX_APPS, .num_tbl_entries = 0, + .md_info_inited = 0, .tbl = NULL, .ops = NULL, }, @@ -46,6 +47,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = { .ctx = 0, .mempool = POOL_TYPE_MDM_MUX, .num_tbl_entries = 0, + .md_info_inited = 0, .tbl = NULL, .ops = NULL, }, @@ -54,6 +56,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = { .ctx = 0, .mempool = POOL_TYPE_MDM2_MUX, .num_tbl_entries = 0, + .md_info_inited = 0, .tbl = NULL, .ops = NULL, }, @@ -62,6 +65,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = { .ctx = 0, .mempool = POOL_TYPE_QSC_MUX, .num_tbl_entries = 0, + .md_info_inited = 0, .tbl = NULL, .ops = NULL, } @@ -85,6 +89,8 @@ void diag_md_open_all() for (i = 0; i < NUM_DIAG_MD_DEV; i++) { ch = &diag_md[i]; + if (!ch->md_info_inited) + continue; if (ch->ops && ch->ops->open) ch->ops->open(ch->ctx, DIAG_MEMORY_DEVICE_MODE); } @@ -101,6 +107,8 @@ void diag_md_close_all() for (i = 0; i < NUM_DIAG_MD_DEV; i++) { ch = &diag_md[i]; + if (!ch->md_info_inited) + continue; if (ch->ops && ch->ops->close) ch->ops->close(ch->ctx, DIAG_MEMORY_DEVICE_MODE); @@ -159,7 +167,7 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) mutex_unlock(&driver->md_session_lock); ch = &diag_md[id]; - if (!ch) + if (!ch || !ch->md_info_inited) return -EINVAL; spin_lock_irqsave(&ch->lock, flags); @@ -236,6 +244,8 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) { ch = &diag_md[i]; + if (!ch->md_info_inited) + continue; for (j = 0; j < ch->num_tbl_entries && !err; j++) { entry = &ch->tbl[j]; if (entry->len <= 0 || entry->buf == NULL) @@ -358,6 +368,8 @@ int diag_md_close_peripheral(int id, uint8_t peripheral) return -EINVAL; ch = &diag_md[id]; + if (!ch || !ch->md_info_inited) + return -EINVAL; spin_lock_irqsave(&ch->lock, flags); for (i = 0; i < ch->num_tbl_entries && !found; i++) { @@ -405,6 +417,7 @@ int diag_md_init(void) ch->tbl[j].ctx = 0; } spin_lock_init(&(ch->lock)); + ch->md_info_inited = 1; } return 0; @@ -433,6 +446,7 @@ int diag_md_mdm_init(void) ch->tbl[j].ctx = 0; } spin_lock_init(&(ch->lock)); + ch->md_info_inited = 1; } return 0; diff --git a/drivers/char/diag/diag_memorydevice.h b/drivers/char/diag/diag_memorydevice.h index 9b4aa392233d..4d65dedfdb58 100644 --- a/drivers/char/diag/diag_memorydevice.h +++ b/drivers/char/diag/diag_memorydevice.h @@ -38,6 +38,7 @@ struct diag_md_info { int ctx; int mempool; int num_tbl_entries; + int md_info_inited; spinlock_t lock; struct diag_buf_tbl_t *tbl; struct diag_mux_ops *ops; diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index c7b46304dc84..ff024c2200c0 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -48,6 +48,7 @@ #define STM_RSP_SUPPORTED_INDEX 7 #define STM_RSP_STATUS_INDEX 8 #define STM_RSP_NUM_BYTES 9 +#define RETRY_MAX_COUNT 1000 struct diag_md_hdlc_reset_work { int pid; @@ -272,28 +273,22 @@ static void pack_rsp_and_send(unsigned char *buf, int len, diag_md_session_get_peripheral(APPS_DATA); if (info && info->peripheral_mask) { - if (info->peripheral_mask == DIAG_CON_ALL || - (info->peripheral_mask & (1 << APPS_DATA)) || - (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) { - rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1); - } else { - for (i = 0; i <= NUM_PERIPHERALS; i++) { - if (info->peripheral_mask & (1 << i)) - break; - } - rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1); + for (i = 0; i < NUM_MD_SESSIONS; i++) { + if (info->peripheral_mask & (1 << i)) + break; } + rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, TYPE_CMD); } else rsp_ctxt = driver->rsp_buf_ctxt; mutex_unlock(&driver->md_session_lock); /* * Keep trying till we get the buffer back. It should probably - * take one or two iterations. When this loops till UINT_MAX, it + * take one or two iterations. When this loops till RETRY_MAX_COUNT, it * means we did not get a write complete for the previous * response. */ - while (retry_count < UINT_MAX) { + while (retry_count < RETRY_MAX_COUNT) { if (!driver->rsp_buf_busy) break; /* @@ -366,27 +361,21 @@ static void encode_rsp_and_send(unsigned char *buf, int len, diag_md_session_get_peripheral(APPS_DATA); if (info && info->peripheral_mask) { - if (info->peripheral_mask == DIAG_CON_ALL || - (info->peripheral_mask & (1 << APPS_DATA)) || - (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) { - rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1); - } else { - for (i = 0; i <= NUM_PERIPHERALS; i++) { - if (info->peripheral_mask & (1 << i)) - break; - } - rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1); + for (i = 0; i < NUM_MD_SESSIONS; i++) { + if (info->peripheral_mask & (1 << i)) + break; } + rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, TYPE_CMD); } else rsp_ctxt = driver->rsp_buf_ctxt; mutex_unlock(&driver->md_session_lock); /* * Keep trying till we get the buffer back. It should probably - * take one or two iterations. When this loops till UINT_MAX, it + * take one or two iterations. When this loops till RETRY_MAX_COUNT, it * means we did not get a write complete for the previous * response. */ - while (retry_count < UINT_MAX) { + while (retry_count < RETRY_MAX_COUNT) { if (!driver->rsp_buf_busy) break; /* @@ -1754,11 +1743,18 @@ static int diagfwd_mux_write_done(unsigned char *buf, int len, int buf_ctxt, } break; case TYPE_CMD: - if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) { + if (peripheral >= 0 && peripheral < NUM_PERIPHERALS && + num != TYPE_CMD) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "Marking buffer as free after write done p: %d, t: %d, buf_num: %d\n", + peripheral, type, num); diagfwd_write_done(peripheral, type, num); - } - if (peripheral == APPS_DATA || - ctxt == DIAG_MEMORY_DEVICE_MODE) { + } else if (peripheral == APPS_DATA || + (peripheral >= 0 && peripheral < NUM_PERIPHERALS && + num == TYPE_CMD)) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "Marking APPS response buffer free after write done for p: %d, t: %d, buf_num: %d\n", + peripheral, type, num); spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags); driver->rsp_buf_busy = 0; driver->encoded_rsp_len = 0; 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/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index 92a810648bd0..530aacca3eb8 100644 --- a/drivers/char/hw_random/stm32-rng.c +++ b/drivers/char/hw_random/stm32-rng.c @@ -21,6 +21,7 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/slab.h> #define RNG_CR 0x00 @@ -46,6 +47,7 @@ struct stm32_rng_private { struct hwrng rng; void __iomem *base; struct clk *clk; + struct reset_control *rst; }; static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) @@ -140,6 +142,13 @@ static int stm32_rng_probe(struct platform_device *ofdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); + priv->rst = devm_reset_control_get(&ofdev->dev, NULL); + if (!IS_ERR(priv->rst)) { + reset_control_assert(priv->rst); + udelay(2); + reset_control_deassert(priv->rst); + } + dev_set_drvdata(dev, priv); priv->rng.name = dev_driver_string(dev), diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c index 0c98a9d51a24..44ce80606944 100644 --- a/drivers/char/hw_random/via-rng.c +++ b/drivers/char/hw_random/via-rng.c @@ -140,7 +140,7 @@ static int via_rng_init(struct hwrng *rng) * RNG configuration like it used to be the case in this * register */ if ((c->x86 == 6) && (c->x86_model >= 0x0f)) { - if (!cpu_has_xstore_enabled) { + if (!boot_cpu_has(X86_FEATURE_XSTORE_EN)) { pr_err(PFX "can't enable hardware RNG " "if XSTORE is not enabled\n"); return -ENODEV; @@ -200,8 +200,9 @@ static int __init mod_init(void) { int err; - if (!cpu_has_xstore) + if (!boot_cpu_has(X86_FEATURE_XSTORE)) return -ENODEV; + pr_info("VIA RNG detected\n"); err = hwrng_register(&via_rng); if (err) { diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index 6e658aa114f1..a70518a4fcec 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -251,8 +251,9 @@ static int ipmi_powernv_probe(struct platform_device *pdev) ipmi->irq = opal_event_request(prop); } - if (request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH, - "opal-ipmi", ipmi)) { + rc = request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH, + "opal-ipmi", ipmi); + if (rc) { dev_warn(dev, "Unable to request irq\n"); goto err_dispose; } diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 83c206f0fc98..d6d166fe49a3 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -757,7 +757,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->ssif_state = SSIF_NORMAL; ipmi_ssif_unlock_cond(ssif_info, flags); pr_warn(PFX "Error getting flags: %d %d, %x\n", - result, len, data[2]); + result, len, (len >= 3) ? data[2] : 0); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_GET_MSG_FLAGS_CMD) { /* @@ -779,7 +779,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, if ((result < 0) || (len < 3) || (data[2] != 0)) { /* Error clearing flags */ pr_warn(PFX "Error clearing flags: %d %d, %x\n", - result, len, data[2]); + result, len, (len >= 3) ? data[2] : 0); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) { pr_warn(PFX "Invalid response clearing flags: %x %x\n", diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index a0d9ac6b6cc9..e759100e41a7 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -26,6 +26,7 @@ #include <linux/spinlock.h> #include <linux/freezer.h> #include <linux/major.h> +#include <linux/of.h> #include "tpm.h" #include "tpm_eventlog.h" @@ -324,8 +325,20 @@ static void tpm1_chip_unregister(struct tpm_chip *chip) */ int tpm_chip_register(struct tpm_chip *chip) { +#ifdef CONFIG_OF + struct device_node *np; +#endif int rc; +#ifdef CONFIG_OF + np = of_find_node_by_name(NULL, "vtpm"); + if (np) { + if (of_property_read_bool(np, "powered-while-suspended")) + chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED; + } + of_node_put(np); +#endif + rc = tpm1_chip_register(chip); if (rc) return rc; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 36afc1a21699..95a40ec854ad 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -787,6 +787,10 @@ int tpm_do_selftest(struct tpm_chip *chip) loops = jiffies_to_msecs(duration) / delay_msec; rc = tpm_continue_selftest(chip); + if (rc == TPM_ERR_INVALID_POSTINIT) { + chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED; + dev_info(&chip->dev, "TPM not ready (%d)\n", rc); + } /* This may fail if there was no TPM driver during a suspend/resume * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST) */ @@ -931,6 +935,9 @@ int tpm_pm_suspend(struct device *dev) if (chip == NULL) return -ENODEV; + if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) + return 0; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { tpm2_shutdown(chip, TPM2_SU_STATE); return 0; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 772d99b3a8e4..36e1abda00f9 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -168,6 +168,7 @@ struct tpm_vendor_specific { enum tpm_chip_flags { TPM_CHIP_FLAG_REGISTERED = BIT(0), TPM_CHIP_FLAG_TPM2 = BIT(1), + TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), }; struct tpm_chip { diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 73d65813de8b..d859b34d2b60 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2244,6 +2244,9 @@ static int clk_core_get_phase(struct clk_core *core) int ret; clk_prepare_lock(); + /* Always try to update cached phase if possible */ + if (core->ops->get_phase) + core->phase = core->ops->get_phase(core->hw); ret = core->phase; clk_prepare_unlock(); diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index 33c20c6b45af..b840e4ace623 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -60,6 +60,12 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw) u16 degrees; u32 delay_num = 0; + /* See the comment for rockchip_mmc_set_phase below */ + if (!rate) { + pr_err("%s: invalid clk rate\n", __func__); + return -EINVAL; + } + raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift); degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; @@ -86,6 +92,23 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) u32 raw_value; u32 delay; + /* + * The below calculation is based on the output clock from + * MMC host to the card, which expects the phase clock inherits + * the clock rate from its parent, namely the output clock + * provider of MMC host. However, things may go wrong if + * (1) It is orphan. + * (2) It is assigned to the wrong parent. + * + * This check help debug the case (1), which seems to be the + * most likely problem we often face and which makes it difficult + * for people to debug unstable mmc tuning results. + */ + if (!rate) { + pr_err("%s: invalid clk rate\n", __func__); + return -EINVAL; + } + nineties = degrees / 90; remainder = (degrees % 90); diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c index fdd41b17a24f..294efaef5b82 100644 --- a/drivers/clk/samsung/clk-exynos3250.c +++ b/drivers/clk/samsung/clk-exynos3250.c @@ -683,7 +683,7 @@ static struct samsung_pll_rate_table exynos3250_epll_rates[] = { PLL_36XX_RATE(144000000, 96, 2, 3, 0), PLL_36XX_RATE( 96000000, 128, 2, 4, 0), PLL_36XX_RATE( 84000000, 112, 2, 4, 0), - PLL_36XX_RATE( 80000004, 106, 2, 4, 43691), + PLL_36XX_RATE( 80000003, 106, 2, 4, 43691), PLL_36XX_RATE( 73728000, 98, 2, 4, 19923), PLL_36XX_RATE( 67737598, 270, 3, 5, 62285), PLL_36XX_RATE( 65535999, 174, 2, 5, 49982), @@ -719,7 +719,7 @@ static struct samsung_pll_rate_table exynos3250_vpll_rates[] = { PLL_36XX_RATE(148352005, 98, 2, 3, 59070), PLL_36XX_RATE(108000000, 144, 2, 4, 0), PLL_36XX_RATE( 74250000, 99, 2, 4, 0), - PLL_36XX_RATE( 74176002, 98, 3, 4, 59070), + PLL_36XX_RATE( 74176002, 98, 2, 4, 59070), PLL_36XX_RATE( 54054000, 216, 3, 5, 14156), PLL_36XX_RATE( 54000000, 144, 2, 5, 0), { /* sentinel */ } diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 5bebf8cb0d70..f0b564c7c9c1 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -711,13 +711,13 @@ static struct samsung_pll_rate_table epll_24mhz_tbl[] __initdata = { /* sorted in descending order */ /* PLL_36XX_RATE(rate, m, p, s, k) */ PLL_36XX_RATE(192000000, 64, 2, 2, 0), - PLL_36XX_RATE(180633600, 90, 3, 2, 20762), + PLL_36XX_RATE(180633605, 90, 3, 2, 20762), PLL_36XX_RATE(180000000, 90, 3, 2, 0), PLL_36XX_RATE(73728000, 98, 2, 4, 19923), - PLL_36XX_RATE(67737600, 90, 2, 4, 20762), + PLL_36XX_RATE(67737602, 90, 2, 4, 20762), PLL_36XX_RATE(49152000, 98, 3, 4, 19923), - PLL_36XX_RATE(45158400, 90, 3, 4, 20762), - PLL_36XX_RATE(32768000, 131, 3, 5, 4719), + PLL_36XX_RATE(45158401, 90, 3, 4, 20762), + PLL_36XX_RATE(32768001, 131, 3, 5, 4719), { }, }; diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index d1a29f6c1084..7027e77bf859 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -65,7 +65,7 @@ static struct samsung_pll_rate_table pll2650_24mhz_tbl[] __initdata = { PLL_36XX_RATE(480000000, 160, 2, 2, 0), PLL_36XX_RATE(432000000, 144, 2, 2, 0), PLL_36XX_RATE(400000000, 200, 3, 2, 0), - PLL_36XX_RATE(394073130, 459, 7, 2, 49282), + PLL_36XX_RATE(394073128, 459, 7, 2, 49282), PLL_36XX_RATE(333000000, 111, 2, 2, 0), PLL_36XX_RATE(300000000, 100, 2, 2, 0), PLL_36XX_RATE(266000000, 266, 3, 3, 0), diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index cee062c588de..91c89ac193b9 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -747,7 +747,7 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = { PLL_35XX_RATE(800000000U, 400, 6, 1), PLL_35XX_RATE(733000000U, 733, 12, 1), PLL_35XX_RATE(700000000U, 175, 3, 1), - PLL_35XX_RATE(667000000U, 222, 4, 1), + PLL_35XX_RATE(666000000U, 222, 4, 1), PLL_35XX_RATE(633000000U, 211, 4, 1), PLL_35XX_RATE(600000000U, 500, 5, 2), PLL_35XX_RATE(552000000U, 460, 5, 2), @@ -773,12 +773,12 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = { /* AUD_PLL */ static struct samsung_pll_rate_table exynos5443_aud_pll_rates[] = { PLL_36XX_RATE(400000000U, 200, 3, 2, 0), - PLL_36XX_RATE(393216000U, 197, 3, 2, -25690), + PLL_36XX_RATE(393216003U, 197, 3, 2, -25690), PLL_36XX_RATE(384000000U, 128, 2, 2, 0), - PLL_36XX_RATE(368640000U, 246, 4, 2, -15729), - PLL_36XX_RATE(361507200U, 181, 3, 2, -16148), - PLL_36XX_RATE(338688000U, 113, 2, 2, -6816), - PLL_36XX_RATE(294912000U, 98, 1, 3, 19923), + PLL_36XX_RATE(368639991U, 246, 4, 2, -15729), + PLL_36XX_RATE(361507202U, 181, 3, 2, -16148), + PLL_36XX_RATE(338687988U, 113, 2, 2, -6816), + PLL_36XX_RATE(294912002U, 98, 1, 3, 19923), PLL_36XX_RATE(288000000U, 96, 1, 3, 0), PLL_36XX_RATE(252000000U, 84, 1, 3, 0), { /* sentinel */ } diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c index 0945a8852299..69e3e848716a 100644 --- a/drivers/clk/samsung/clk-s3c2410.c +++ b/drivers/clk/samsung/clk-s3c2410.c @@ -168,7 +168,7 @@ static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = { PLL_35XX_RATE(226000000, 105, 1, 1), PLL_35XX_RATE(210000000, 132, 2, 1), /* 2410 common */ - PLL_35XX_RATE(203000000, 161, 3, 1), + PLL_35XX_RATE(202800000, 161, 3, 1), PLL_35XX_RATE(192000000, 88, 1, 1), PLL_35XX_RATE(186000000, 85, 1, 1), PLL_35XX_RATE(180000000, 82, 1, 1), @@ -178,18 +178,18 @@ static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = { PLL_35XX_RATE(147000000, 90, 2, 1), PLL_35XX_RATE(135000000, 82, 2, 1), PLL_35XX_RATE(124000000, 116, 1, 2), - PLL_35XX_RATE(118000000, 150, 2, 2), + PLL_35XX_RATE(118500000, 150, 2, 2), PLL_35XX_RATE(113000000, 105, 1, 2), - PLL_35XX_RATE(101000000, 127, 2, 2), + PLL_35XX_RATE(101250000, 127, 2, 2), PLL_35XX_RATE(90000000, 112, 2, 2), - PLL_35XX_RATE(85000000, 105, 2, 2), + PLL_35XX_RATE(84750000, 105, 2, 2), PLL_35XX_RATE(79000000, 71, 1, 2), - PLL_35XX_RATE(68000000, 82, 2, 2), - PLL_35XX_RATE(56000000, 142, 2, 3), + PLL_35XX_RATE(67500000, 82, 2, 2), + PLL_35XX_RATE(56250000, 142, 2, 3), PLL_35XX_RATE(48000000, 120, 2, 3), - PLL_35XX_RATE(51000000, 161, 3, 3), + PLL_35XX_RATE(50700000, 161, 3, 3), PLL_35XX_RATE(45000000, 82, 1, 3), - PLL_35XX_RATE(34000000, 82, 2, 3), + PLL_35XX_RATE(33750000, 82, 2, 3), { /* sentinel */ }, }; diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c index 517e1c7624d4..a00209702f39 100644 --- a/drivers/clocksource/fsl_ftm_timer.c +++ b/drivers/clocksource/fsl_ftm_timer.c @@ -281,7 +281,7 @@ static int __init __ftm_clk_init(struct device_node *np, char *cnt_name, static unsigned long __init ftm_clk_init(struct device_node *np) { - unsigned long freq; + long freq; freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt"); if (freq <= 0) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 7c0bdfb1a2ca..0dcbf951ad1b 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -100,9 +100,19 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq = policy->max; policy->shared_type = cpu->shared_type; - if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) + if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { + int i; + cpumask_copy(policy->cpus, cpu->shared_cpu_map); - else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) { + + for_each_cpu(i, policy->cpus) { + if (unlikely(i == policy->cpu)) + continue; + + memcpy(&all_cpu_data[i]->perf_caps, &cpu->perf_caps, + sizeof(cpu->perf_caps)); + } + } else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) { /* Support only SW_ANY for now. */ pr_debug("Unsupported CPU co-ord type\n"); return -EFAULT; @@ -166,8 +176,13 @@ static int __init cppc_cpufreq_init(void) return ret; out: - for_each_possible_cpu(i) - kfree(all_cpu_data[i]); + for_each_possible_cpu(i) { + cpu = all_cpu_data[i]; + if (!cpu) + break; + free_cpumask_var(cpu->shared_cpu_map); + kfree(cpu); + } kfree(all_cpu_data); return -ENODEV; diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c index da2d6777bd09..97a364694bfc 100644 --- a/drivers/crypto/padlock-aes.c +++ b/drivers/crypto/padlock-aes.c @@ -515,7 +515,7 @@ static int __init padlock_init(void) if (!x86_match_cpu(padlock_cpu_id)) return -ENODEV; - if (!cpu_has_xcrypt_enabled) { + if (!boot_cpu_has(X86_FEATURE_XCRYPT_EN)) { printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n"); return -ENODEV; } diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index 4e154c9b9206..8c5f90647b7a 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -540,7 +540,7 @@ static int __init padlock_init(void) struct shash_alg *sha1; struct shash_alg *sha256; - if (!x86_match_cpu(padlock_sha_ids) || !cpu_has_phe_enabled) + if (!x86_match_cpu(padlock_sha_ids) || !boot_cpu_has(X86_FEATURE_PHE_EN)) return -ENODEV; /* Register the newly added algorithm module if on * diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-core.c b/drivers/crypto/sunxi-ss/sun4i-ss-core.c index 107cd2a41cae..24651d3217cd 100644 --- a/drivers/crypto/sunxi-ss/sun4i-ss-core.c +++ b/drivers/crypto/sunxi-ss/sun4i-ss-core.c @@ -422,6 +422,7 @@ static struct platform_driver sun4i_ss_driver = { module_platform_driver(sun4i_ss_driver); +MODULE_ALIAS("platform:sun4i-ss"); MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>"); diff --git a/drivers/crypto/vmx/aes.c b/drivers/crypto/vmx/aes.c index 263af709e536..b907e4b1bbe2 100644 --- a/drivers/crypto/vmx/aes.c +++ b/drivers/crypto/vmx/aes.c @@ -53,8 +53,6 @@ static int p8_aes_init(struct crypto_tfm *tfm) alg, PTR_ERR(fallback)); return PTR_ERR(fallback); } - printk(KERN_INFO "Using '%s' as fallback implementation.\n", - crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); crypto_cipher_set_flags(fallback, crypto_cipher_get_flags((struct diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c index 3f8bb9a40df1..9506e8693c81 100644 --- a/drivers/crypto/vmx/aes_cbc.c +++ b/drivers/crypto/vmx/aes_cbc.c @@ -55,8 +55,6 @@ static int p8_aes_cbc_init(struct crypto_tfm *tfm) alg, PTR_ERR(fallback)); return PTR_ERR(fallback); } - printk(KERN_INFO "Using '%s' as fallback implementation.\n", - crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); crypto_blkcipher_set_flags( fallback, diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c index d83ab4bac8b1..7d070201b3d3 100644 --- a/drivers/crypto/vmx/aes_ctr.c +++ b/drivers/crypto/vmx/aes_ctr.c @@ -53,8 +53,6 @@ static int p8_aes_ctr_init(struct crypto_tfm *tfm) alg, PTR_ERR(fallback)); return PTR_ERR(fallback); } - printk(KERN_INFO "Using '%s' as fallback implementation.\n", - crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); crypto_blkcipher_set_flags( fallback, diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c index 9cb3a0b715e2..84b9389bf1ed 100644 --- a/drivers/crypto/vmx/ghash.c +++ b/drivers/crypto/vmx/ghash.c @@ -64,8 +64,6 @@ static int p8_ghash_init_tfm(struct crypto_tfm *tfm) alg, PTR_ERR(fallback)); return PTR_ERR(fallback); } - printk(KERN_INFO "Using '%s' as fallback implementation.\n", - crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback))); crypto_shash_set_flags(fallback, crypto_shash_get_flags((struct crypto_shash 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/dma/pl330.c b/drivers/dma/pl330.c index 66d84bcf9bbf..8db791ef2027 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1533,7 +1533,7 @@ static void pl330_dotask(unsigned long data) /* Returns 1 if state was updated, 0 otherwise */ static int pl330_update(struct pl330_dmac *pl330) { - struct dma_pl330_desc *descdone, *tmp; + struct dma_pl330_desc *descdone; unsigned long flags; void __iomem *regs; u32 val; @@ -1611,7 +1611,9 @@ static int pl330_update(struct pl330_dmac *pl330) } /* Now that we are in no hurry, do the callbacks */ - list_for_each_entry_safe(descdone, tmp, &pl330->req_done, rqd) { + while (!list_empty(&pl330->req_done)) { + descdone = list_first_entry(&pl330->req_done, + struct dma_pl330_desc, rqd); list_del(&descdone->rqd); spin_unlock_irqrestore(&pl330->lock, flags); dma_pl330_rqcb(descdone, PL330_ERR_NONE); diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 7820d07e7bee..2b36d1c63aa5 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -851,7 +851,7 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, rcar_dmac_chan_configure_desc(chan, desc); - max_chunk_size = (RCAR_DMATCR_MASK + 1) << desc->xfer_shift; + max_chunk_size = RCAR_DMATCR_MASK << desc->xfer_shift; /* * Allocate and fill the transfer chunk descriptors. We own the only diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index 56410ea75ac5..6682b3eec2b6 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -448,7 +448,7 @@ usb_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, static int usb_dmac_chan_terminate_all(struct dma_chan *chan) { struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); - struct usb_dmac_desc *desc; + struct usb_dmac_desc *desc, *_desc; unsigned long flags; LIST_HEAD(head); LIST_HEAD(list); @@ -459,7 +459,7 @@ static int usb_dmac_chan_terminate_all(struct dma_chan *chan) if (uchan->desc) uchan->desc = NULL; list_splice_init(&uchan->desc_got, &list); - list_for_each_entry(desc, &list, node) + list_for_each_entry_safe(desc, _desc, &list, node) list_move_tail(&desc->node, &uchan->desc_freed); spin_unlock_irqrestore(&uchan->vc.lock, flags); vchan_dma_desc_free_list(&uchan->vc, &head); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index c2f5117fd8cb..5545a7f3a98f 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1130,7 +1130,13 @@ static int context_add_buffer(struct context *ctx) return -ENOMEM; offset = (void *)&desc->buffer - (void *)desc; - desc->buffer_size = PAGE_SIZE - offset; + /* + * Some controllers, like JMicron ones, always issue 0x20-byte DMA reads + * for descriptors, even 0x10-byte ones. This can cause page faults when + * an IOMMU is in use and the oversized read crosses a page boundary. + * Work around this by always leaving at least 0x10 bytes of padding. + */ + desc->buffer_size = PAGE_SIZE - offset - 0x10; desc->buffer_bus = bus_addr + offset; desc->used = 0; diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 0e08e665f715..053a23a7be94 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -18,7 +18,7 @@ EXPORT_SYMBOL_GPL(dmi_kobj); * of and an antecedent to, SMBIOS, which stands for System * Management BIOS. See further: http://www.dmtf.org/standards */ -static const char dmi_empty_string[] = " "; +static const char dmi_empty_string[] = ""; static u32 dmi_ver __initdata; static u32 dmi_len; @@ -44,25 +44,21 @@ static int dmi_memdev_nr; static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s) { const u8 *bp = ((u8 *) dm) + dm->length; + const u8 *nsp; if (s) { - s--; - while (s > 0 && *bp) { + while (--s > 0 && *bp) bp += strlen(bp) + 1; - s--; - } - - if (*bp != 0) { - size_t len = strlen(bp)+1; - size_t cmp_len = len > 8 ? 8 : len; - if (!memcmp(bp, dmi_empty_string, cmp_len)) - return dmi_empty_string; + /* Strings containing only spaces are considered empty */ + nsp = bp; + while (*nsp == ' ') + nsp++; + if (*nsp != '\0') return bp; - } } - return ""; + return dmi_empty_string; } static const char * __init dmi_string(const struct dmi_header *dm, u8 s) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 6b5625e66119..88ceac091454 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -209,6 +209,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) return -ENOMEM; filp->private_data = priv; + filp->f_mode |= FMODE_UNSIGNED_OFFSET; priv->filp = filp; priv->uid = current_euid(); priv->pid = get_pid(task_pid(current)); diff --git a/drivers/gpu/drm/exynos/regs-fimc.h b/drivers/gpu/drm/exynos/regs-fimc.h index 30496134a3d0..d7cbe53c4c01 100644 --- a/drivers/gpu/drm/exynos/regs-fimc.h +++ b/drivers/gpu/drm/exynos/regs-fimc.h @@ -569,7 +569,7 @@ #define EXYNOS_CIIMGEFF_FIN_EMBOSSING (4 << 26) #define EXYNOS_CIIMGEFF_FIN_SILHOUETTE (5 << 26) #define EXYNOS_CIIMGEFF_FIN_MASK (7 << 26) -#define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) +#define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | (0xff << 0)) /* Real input DMA size register */ #define EXYNOS_CIREAL_ISIZE_AUTOLOAD_ENABLE (1 << 31) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 7f39b8ad88ae..de6710fe3ff4 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -768,6 +768,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Radiant P845", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Radiant Systems Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "P845"), + }, + }, { } /* terminating entry */ }; diff --git a/drivers/gpu/drm/msm/dba_bridge.c b/drivers/gpu/drm/msm/dba_bridge.c index 7887bda23df0..62294ddf8034 100644 --- a/drivers/gpu/drm/msm/dba_bridge.c +++ b/drivers/gpu/drm/msm/dba_bridge.c @@ -132,7 +132,9 @@ static void _dba_bridge_pre_enable(struct drm_bridge *bridge) } d_bridge = to_dba_bridge(bridge); - if (d_bridge->ops.power_on) + + /* Skip power_on calling when splash is enabled in bootloader. */ + if ((d_bridge->ops.power_on) && (!d_bridge->cont_splash_enabled)) d_bridge->ops.power_on(d_bridge->dba_ctx, true, 0); } @@ -193,7 +195,8 @@ static void _dba_bridge_enable(struct drm_bridge *bridge) video_cfg.scaninfo, video_cfg.ar, video_cfg.vic); } - if (d_bridge->ops.video_on) { + /* Skip video_on calling if splash is enabled in bootloader. */ + if ((d_bridge->ops.video_on) && (!d_bridge->cont_splash_enabled)) { rc = d_bridge->ops.video_on(d_bridge->dba_ctx, true, &video_cfg, 0); if (rc) diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index 7a90c7be4e5c..b1cd666f8be4 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -1608,14 +1608,15 @@ int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool cont_splash_enabled) } mutex_lock(&dsi_ctrl->ctrl_lock); - rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, 0x1); - if (rc) { - pr_err("[DSI_%d] Controller state check failed, rc=%d\n", - dsi_ctrl->index, rc); - goto error; - } - if (!cont_splash_enabled) { + rc = dsi_ctrl_check_state( + dsi_ctrl, DSI_CTRL_OP_HOST_INIT, 0x1); + if (rc) { + pr_err("[DSI_%d] Ctrl state check failed, rc=%d\n", + dsi_ctrl->index, rc); + goto error; + } + dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw, &dsi_ctrl->host_config.lane_map); @@ -1970,12 +1971,6 @@ error: return rc; } -void dsi_ctrl_update_power_state(struct dsi_ctrl *dsi_ctrl, - enum dsi_power_state state) -{ - dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_POWER_STATE_CHANGE, state); -} - /** * dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller * @dsi_ctrl: DSI controller handle. diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h index c0ba532011b5..c343c41eb8e1 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h @@ -405,16 +405,6 @@ int dsi_ctrl_set_power_state(struct dsi_ctrl *dsi_ctrl, enum dsi_power_state state); /** - * dsi_ctrl_update_power_state() - update power state for dsi controller - * @dsi_ctrl: DSI controller handle. - * @state: Power state. - * - * Update power state for DSI controller. - * - */ -void dsi_ctrl_update_power_state(struct dsi_ctrl *dsi_ctrl, - enum dsi_power_state state); -/** * dsi_ctrl_set_cmd_engine_state() - set command engine state * @dsi_ctrl: DSI Controller handle. * @state: Engine state. diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c index c468a6f5caa2..7c9c3c7b4cca 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c @@ -236,6 +236,12 @@ static int dsi_display_phy_power_on(struct dsi_display *display) int i; struct dsi_display_ctrl *ctrl; + /* early return for splash enabled case */ + if (display->cont_splash_enabled) { + pr_debug("skip phy power on\n"); + return rc; + } + /* Sequence does not matter for split dsi usecases */ for (i = 0; i < display->ctrl_count; i++) { @@ -292,6 +298,12 @@ static int dsi_display_ctrl_core_clk_on(struct dsi_display *display) int i; struct dsi_display_ctrl *m_ctrl, *ctrl; + /* early return for splash enabled case */ + if (display->cont_splash_enabled) { + pr_debug("skip core clk on calling\n"); + return rc; + } + /* * In case of split DSI usecases, the clock for master controller should * be enabled before the other controller. Master controller in the @@ -334,6 +346,12 @@ static int dsi_display_ctrl_link_clk_on(struct dsi_display *display) int i; struct dsi_display_ctrl *m_ctrl, *ctrl; + /* early return for splash enabled case */ + if (display->cont_splash_enabled) { + pr_debug("skip ctrl link clk on calling\n"); + return rc; + } + /* * In case of split DSI usecases, the clock for master controller should * be enabled before the other controller. Master controller in the @@ -1166,6 +1184,13 @@ static int dsi_display_parse_dt(struct dsi_display *display) goto error; } + /* Only read swap property in split case */ + if (display->ctrl_count > 1) { + display->dsi_split_swap = + of_property_read_bool(display->pdev->dev.of_node, + "qcom,dsi-split-swap"); + } + if (of_get_property(display->pdev->dev.of_node, "qcom,dsi-panel", &size)) { display->panel_count = size / sizeof(int); @@ -2281,6 +2306,14 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) for (i = 0; i < info->num_of_h_tiles; i++) info->h_tile_instance[i] = display->ctrl[i].ctrl->index; + /* + * h_tile_instance[2] = {0, 1} means DSI0 left(master), DSI1 right + * h_tile_instance[2] = {1, 0} means DSI1 left(master), DSI0 right + * So in case of split case and swap property is set, swap two DSIs. + */ + if (info->num_of_h_tiles > 1 && display->dsi_split_swap) + swap(info->h_tile_instance[0], info->h_tile_instance[1]); + info->is_connected = true; info->width_mm = phy_props.panel_width_mm; info->height_mm = phy_props.panel_height_mm; @@ -2827,28 +2860,12 @@ int dsi_dsiplay_setup_splash_resource(struct dsi_display *display) if (!ctrl) return -EINVAL; - dsi_pwr_enable_regulator(&ctrl->ctrl->pwr_info.host_pwr, true); - dsi_pwr_enable_regulator(&ctrl->ctrl->pwr_info.digital, true); - dsi_pwr_enable_regulator(&ctrl->phy->pwr_info.phy_pwr, true); - - ret = dsi_clk_enable_core_clks(&ctrl->ctrl->clk_info.core_clks, - true); - if (ret) { - SDE_ERROR("failed to set core clk for dsi, ret = %d\n", - ret); - return -EINVAL; - } - - ret = dsi_clk_enable_link_clks(&ctrl->ctrl->clk_info.link_clks, - true); + ret = dsi_ctrl_set_power_state(ctrl->ctrl, + DSI_CTRL_POWER_LINK_CLK_ON); if (ret) { - SDE_ERROR("failed to set link clk for dsi, ret = %d\n", - ret); - return -EINVAL; + SDE_ERROR("calling dsi_ctrl_set_power_state failed\n"); + return ret; } - - dsi_ctrl_update_power_state(ctrl->ctrl, - DSI_CTRL_POWER_LINK_CLK_ON); } return ret; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index 3723f19fd0e7..25b2d0c1ec53 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h @@ -121,6 +121,8 @@ struct dsi_display_clk_info { * @bridge: Pointer to DRM bridge object. * @cmd_engine_refcount: Reference count enforcing single instance of cmd eng * @root: Debugfs root directory + * @cont_splash_enabled: Early splash status. + * @dsi_split_swap: Swap dsi output in split mode. */ struct dsi_display { struct platform_device *pdev; @@ -160,6 +162,7 @@ struct dsi_display { struct dentry *root; bool cont_splash_enabled; + bool dsi_split_swap; }; int dsi_display_dev_probe(struct platform_device *pdev); diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c index a0f6b5c6a732..8a086dc68328 100644 --- a/drivers/gpu/drm/msm/sde/sde_color_processing.c +++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 as published by @@ -78,7 +78,7 @@ enum { SDE_CP_CRTC_DSPP_IGC, SDE_CP_CRTC_DSPP_PCC, SDE_CP_CRTC_DSPP_GC, - SDE_CP_CRTC_DSPP_HUE, + SDE_CP_CRTC_DSPP_HSIC, SDE_CP_CRTC_DSPP_SAT, SDE_CP_CRTC_DSPP_VAL, SDE_CP_CRTC_DSPP_CONT, @@ -444,6 +444,7 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, struct sde_hw_cp_cfg hw_cfg; struct sde_hw_mixer *hw_lm; struct sde_hw_dspp *hw_dspp; + struct drm_msm_pa_hsic *hsic_cfg; u32 num_mixers = sde_crtc->num_mixers; int i = 0; bool feature_enabled = false; @@ -484,33 +485,28 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, } hw_dspp->ops.setup_gc(hw_dspp, &hw_cfg); break; - case SDE_CP_CRTC_DSPP_HUE: - if (!hw_dspp || !hw_dspp->ops.setup_hue) { + case SDE_CP_CRTC_DSPP_HSIC: + if (!hw_dspp || !hw_dspp->ops.setup_pa_hsic) { ret = -EINVAL; continue; } - hw_dspp->ops.setup_hue(hw_dspp, &hw_cfg); - break; - case SDE_CP_CRTC_DSPP_SAT: - if (!hw_dspp || !hw_dspp->ops.setup_sat) { - ret = -EINVAL; - continue; - } - hw_dspp->ops.setup_sat(hw_dspp, &hw_cfg); - break; - case SDE_CP_CRTC_DSPP_VAL: - if (!hw_dspp || !hw_dspp->ops.setup_val) { - ret = -EINVAL; - continue; + if (hw_cfg.payload && (hw_cfg.len == + sizeof(struct drm_msm_pa_hsic))) { + /* hw_cfg is valid, check for feature flag */ + hsic_cfg = (struct drm_msm_pa_hsic *) + hw_cfg.payload; + if ((hsic_cfg->flags & + PA_HSIC_LEFT_DISPLAY_ONLY) && (i > 0)) { + /* skip right side programming */ + continue; + } else if ((hsic_cfg->flags & + PA_HSIC_RIGHT_DISPLAY_ONLY) + && (i == 0)) { + /* skip left side programming */ + continue; + } } - hw_dspp->ops.setup_val(hw_dspp, &hw_cfg); - break; - case SDE_CP_CRTC_DSPP_CONT: - if (!hw_dspp || !hw_dspp->ops.setup_cont) { - ret = -EINVAL; - continue; - } - hw_dspp->ops.setup_cont(hw_dspp, &hw_cfg); + hw_dspp->ops.setup_pa_hsic(hw_dspp, &hw_cfg); break; case SDE_CP_CRTC_DSPP_MEMCOLOR: if (!hw_dspp || !hw_dspp->ops.setup_pa_memcolor) @@ -907,9 +903,9 @@ static void dspp_hsic_install_property(struct drm_crtc *crtc) switch (version) { case 1: snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", - "SDE_DSPP_HUE_V", version); - sde_cp_crtc_install_range_property(crtc, feature_name, - SDE_CP_CRTC_DSPP_HUE, 0, U32_MAX, 0); + "SDE_DSPP_PA_HSIC_V", version); + sde_cp_crtc_create_blob_property(crtc, feature_name, + SDE_CP_CRTC_DSPP_HSIC); break; default: DRM_ERROR("version %d not supported\n", version); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index 7db98afad713..6433d3f3bca4 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -233,7 +233,7 @@ struct sde_connector { * Returns: Pointer to associated private display structure */ #define sde_connector_get_display(C) \ - ((C) ? to_sde_connector((C))->display : 0) + ((C) ? to_sde_connector((C))->display : NULL) /** * sde_connector_get_panel - get sde connector's private panel pointer @@ -241,7 +241,7 @@ struct sde_connector { * Returns: Pointer to associated private display structure */ #define sde_connector_get_panel(C) \ - ((C) ? to_sde_connector((C))->panel : 0) + ((C) ? to_sde_connector((C))->panel : NULL) /** * sde_connector_get_encoder - get sde connector's private encoder pointer @@ -249,7 +249,7 @@ struct sde_connector { * Returns: Pointer to associated private encoder structure */ #define sde_connector_get_encoder(C) \ - ((C) ? to_sde_connector((C))->encoder : 0) + ((C) ? to_sde_connector((C))->encoder : NULL) /** * sde_connector_get_propinfo - get sde connector's property info pointer @@ -257,7 +257,7 @@ struct sde_connector { * Returns: Pointer to associated private property info structure */ #define sde_connector_get_propinfo(C) \ - ((C) ? &to_sde_connector((C))->property_info : 0) + ((C) ? &to_sde_connector((C))->property_info : NULL) /** * struct sde_connector_state - private connector status structure @@ -300,7 +300,7 @@ struct sde_connector_state { * Returns: Integer value of requested property */ #define sde_connector_get_property_values(S) \ - ((S) ? (to_sde_connector_state((S))->property_values) : 0) + ((S) ? (to_sde_connector_state((S))->property_values) : NULL) /** * sde_connector_get_out_fb - query out_fb value from sde connector state @@ -308,7 +308,7 @@ struct sde_connector_state { * Returns: Output fb associated with specified connector state */ #define sde_connector_get_out_fb(S) \ - ((S) ? to_sde_connector_state((S))->out_fb : 0) + ((S) ? to_sde_connector_state((S))->out_fb : NULL) /** * sde_connector_get_topology_name - helper accessor to retrieve topology_name diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 9e0bf09bff0a..95a25462cadc 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -91,6 +91,7 @@ enum { HW_OFF, HW_LEN, + HW_DISP, HW_PROP_MAX, }; @@ -201,6 +202,7 @@ enum { MIXER_OFF, MIXER_LEN, MIXER_BLOCKS, + MIXER_DISP, MIXER_PROP_MAX, }; @@ -320,12 +322,15 @@ static struct sde_prop_type rgb_prop[] = { static struct sde_prop_type ctl_prop[] = { {HW_OFF, "qcom,sde-ctl-off", true, PROP_TYPE_U32_ARRAY}, {HW_LEN, "qcom,sde-ctl-size", false, PROP_TYPE_U32}, + {HW_DISP, "qcom,sde-ctl-display-pref", false, PROP_TYPE_STRING_ARRAY}, }; static struct sde_prop_type mixer_prop[] = { {MIXER_OFF, "qcom,sde-mixer-off", true, PROP_TYPE_U32_ARRAY}, {MIXER_LEN, "qcom,sde-mixer-size", false, PROP_TYPE_U32}, {MIXER_BLOCKS, "qcom,sde-mixer-blocks", false, PROP_TYPE_NODE}, + {MIXER_DISP, "qcom,sde-mixer-display-pref", false, + PROP_TYPE_STRING_ARRAY}, }; static struct sde_prop_type mixer_blocks_prop[] = { @@ -1102,6 +1107,7 @@ static int sde_ctl_parse_dt(struct device_node *np, goto end; for (i = 0; i < off_count; i++) { + const char *disp_pref = NULL; ctl = sde_cfg->ctl + i; ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i); ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0); @@ -1109,6 +1115,16 @@ static int sde_ctl_parse_dt(struct device_node *np, snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u", ctl->id - CTL_0); + of_property_read_string_index(np, + ctl_prop[HW_DISP].prop_name, i, &disp_pref); + if (disp_pref) { + if (!strcmp(disp_pref, "primary")) + set_bit(SDE_CTL_PRIMARY_PREF, &ctl->features); + else if (!strcmp(disp_pref, "secondary")) + set_bit(SDE_CTL_SECONDARY_PREF, &ctl->features); + else if (!strcmp(disp_pref, "tertiary")) + set_bit(SDE_CTL_TERTIARY_PREF, &ctl->features); + } if (i < MAX_SPLIT_DISPLAY_CTL) set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features); if (i < MAX_PP_SPLIT_DISPLAY_CTL) @@ -1187,6 +1203,7 @@ static int sde_mixer_parse_dt(struct device_node *np, } for (i = 0, pp_idx = 0, dspp_idx = 0; i < off_count; i++) { + const char *disp_pref = NULL; mixer = sde_cfg->mixer + i; sblk = kzalloc(sizeof(*sblk), GFP_KERNEL); if (!sblk) { @@ -1216,6 +1233,21 @@ static int sde_mixer_parse_dt(struct device_node *np, if (sde_cfg->has_src_split) set_bit(SDE_MIXER_SOURCESPLIT, &mixer->features); + of_property_read_string_index(np, + mixer_prop[MIXER_DISP].prop_name, i, &disp_pref); + + if (disp_pref) { + if (!strcmp(disp_pref, "primary")) + set_bit(SDE_DISP_PRIMARY_PREF, + &mixer->features); + else if (!strcmp(disp_pref, "secondary")) + set_bit(SDE_DISP_SECONDARY_PREF, + &mixer->features); + else if (!strcmp(disp_pref, "tertiary")) + set_bit(SDE_DISP_TERTIARY_PREF, + &mixer->features); + } + if ((i < ROT_LM_OFFSET) || (i >= LINE_LM_OFFSET)) { mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0 : PINGPONG_MAX; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index 81e6bfe6defe..0d09f05bb195 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -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 @@ -119,12 +119,18 @@ enum { * @SDE_MIXER_LAYER Layer mixer layer blend configuration, * @SDE_MIXER_SOURCESPLIT Layer mixer supports source-split configuration * @SDE_MIXER_GC Gamma correction block + * @SDE_DISP_PRIMARY_PREF Primary display prefers this mixer + * @SDE_DISP_SECONDARY_PREF Secondary display prefers this mixer + * @SDE_DISP_TERTIARY_PREF Tertiary display prefers this mixer * @SDE_MIXER_MAX maximum value */ enum { SDE_MIXER_LAYER = 0x1, SDE_MIXER_SOURCESPLIT, SDE_MIXER_GC, + SDE_DISP_PRIMARY_PREF, + SDE_DISP_SECONDARY_PREF, + SDE_DISP_TERTIARY_PREF, SDE_MIXER_MAX }; @@ -180,11 +186,17 @@ enum { * CTL sub-blocks * @SDE_CTL_SPLIT_DISPLAY CTL supports video mode split display * @SDE_CTL_PINGPONG_SPLIT CTL supports pingpong split + * @SDE_CTL_PRIMARY_PREF Primary display perfers this CTL + * @SDE_CTL_SECONDARY_PREF Secondary display perfers this CTL + * @SDE_CTL_TERTIARY_PREF Tertiary display perfers this CTL * @SDE_CTL_MAX */ enum { SDE_CTL_SPLIT_DISPLAY = 0x1, SDE_CTL_PINGPONG_SPLIT, + SDE_CTL_PRIMARY_PREF, + SDE_CTL_SECONDARY_PREF, + SDE_CTL_TERTIARY_PREF, SDE_CTL_MAX }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c index f1f66f37ba6a..6a8d9e0cf2e3 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 @@ -256,6 +256,47 @@ void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg) __setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic_blk, contrast, SSPP); } +void sde_setup_dspp_pa_hsic_v1_7(struct sde_hw_dspp *ctx, void *cfg) +{ + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_pa_hsic *hsic_cfg; + u32 hue = 0; + u32 sat = 0; + u32 val = 0; + u32 cont = 0; + + if (!ctx || !cfg) { + DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg); + return; + } + + if (hw_cfg->payload && + (hw_cfg->len != sizeof(struct drm_msm_pa_hsic))) { + DRM_ERROR("invalid size of payload len %d exp %zd\n", + hw_cfg->len, sizeof(struct drm_msm_pa_hsic)); + return; + } + + if (!hw_cfg->payload) { + DRM_DEBUG_DRIVER("disable pa hsic feature\n"); + } else { + hsic_cfg = hw_cfg->payload; + if (hsic_cfg->flags & PA_HSIC_HUE_ENABLE) + hue = hsic_cfg->hue; + if (hsic_cfg->flags & PA_HSIC_SAT_ENABLE) + sat = hsic_cfg->saturation; + if (hsic_cfg->flags & PA_HSIC_VAL_ENABLE) + val = hsic_cfg->value; + if (hsic_cfg->flags & PA_HSIC_CONT_ENABLE) + cont = hsic_cfg->contrast; + } + + __setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic, hue, DSPP); + __setup_pa_sat(&ctx->hw, &ctx->cap->sblk->hsic, sat, DSPP); + __setup_pa_val(&ctx->hw, &ctx->cap->sblk->hsic, val, DSPP); + __setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic, cont, DSPP); +} + void sde_setup_pipe_pa_memcol_v1_7(struct sde_hw_pipe *ctx, enum sde_memcolor_type type, void *cfg) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h index 0f9bc0e38322..185f6b548b65 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 @@ -45,6 +45,13 @@ void sde_setup_pipe_pa_val_v1_7(struct sde_hw_pipe *ctx, void *cfg); void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg); /** + * sde_setup_dspp_pa_hsic_v1_7 - setup DSPP hsic feature in v1.7 hardware + * @ctx: Pointer to DSPP context + * @cfg: Pointer to hsic data + */ +void sde_setup_dspp_pa_hsic_v1_7(struct sde_hw_dspp *ctx, void *cfg); + +/** * sde_setup_pipe_pa_memcol_v1_7 - setup SSPP memory color in v1.7 hardware * @ctx: Pointer to pipe context * @type: Memory color type (Skin, sky, or foliage) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c index 2fd879a0030d..4c5af0666d88 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.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 @@ -78,7 +78,8 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) case SDE_DSPP_HSIC: if (c->cap->sblk->hsic.version == (SDE_COLOR_PROCESS_VER(0x1, 0x7))) - c->ops.setup_hue = sde_setup_dspp_pa_hue_v1_7; + c->ops.setup_pa_hsic = + sde_setup_dspp_pa_hsic_v1_7; break; case SDE_DSPP_VLUT: if (c->cap->sblk->vlut.version == diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h index 6e6ad2f8d0e5..e1e8622dd11f 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-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 @@ -92,32 +92,11 @@ struct sde_hw_dspp_ops { void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg); /** - * setup_hue - setup dspp PA hue + * setup_cont - setup dspp PA hsic * @ctx: Pointer to dspp context * @cfg: Pointer to configuration */ - void (*setup_hue)(struct sde_hw_dspp *ctx, void *cfg); - - /** - * setup_sat - setup dspp PA saturation - * @ctx: Pointer to dspp context - * @cfg: Pointer to configuration - */ - void (*setup_sat)(struct sde_hw_dspp *ctx, void *cfg); - - /** - * setup_val - setup dspp PA value - * @ctx: Pointer to dspp context - * @cfg: Pointer to configuration - */ - void (*setup_val)(struct sde_hw_dspp *ctx, void *cfg); - - /** - * setup_cont - setup dspp PA contrast - * @ctx: Pointer to dspp context - * @cfg: Pointer to configuration - */ - void (*setup_cont)(struct sde_hw_dspp *ctx, void *cfg); + void (*setup_pa_hsic)(struct sde_hw_dspp *dspp, void *cfg); /** * setup_vlut - setup dspp PA VLUT diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 86a5c23b5258..95ab14ffc3ac 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -1128,6 +1128,7 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) { struct msm_mmu *mmu; int i, ret; + int data = 0; for (i = 0; i < MSM_SMMU_DOMAIN_MAX; i++) { struct msm_gem_address_space *aspace; @@ -1188,6 +1189,20 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) msm_gem_address_space_put(aspace); goto fail; } + + /* + * Enable stage 1 smmu after user has finished early + * mapping of splash memory. + */ + ret = mmu->funcs->set_property(mmu, + DOMAIN_ATTR_EARLY_MAP, + &data); + if (ret) { + SDE_ERROR("failed to set map att(%d): %d\n", + data, ret); + msm_gem_address_space_put(aspace); + goto fail; + } } } diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index 6055dc861c72..384b6f50979c 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -25,6 +25,8 @@ #include "sde_connector.h" #include "sde_hw_sspp.h" #include "sde_splash.h" +#include "dsi_display.h" +#include "sde_hdmi.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) @@ -41,6 +43,7 @@ * @dspp: Whether the user requires a DSPP * @num_lm: Number of layer mixers needed in the use case * @hw_res: Hardware resources required as reported by the encoders + * @disp_id: Current display ID, lm/ctl may have prefer display */ struct sde_rm_requirements { enum sde_rm_topology_name top_name; @@ -49,6 +52,7 @@ struct sde_rm_requirements { int num_ctl; bool needs_split_display; struct sde_encoder_hw_resources hw_res; + uint32_t disp_id; }; /** @@ -565,7 +569,9 @@ static bool _sde_rm_check_lm_and_get_connected_blks( struct sde_lm_cfg *lm_cfg = (struct sde_lm_cfg *)lm->catalog; struct sde_pingpong_cfg *pp_cfg; struct sde_rm_hw_iter iter; - + unsigned long caps = ((struct sde_lm_cfg *)lm->catalog)->features; + unsigned int preferred_disp_id = 0; + bool preferred_disp_match = false; *dspp = NULL; *pp = NULL; @@ -584,9 +590,21 @@ static bool _sde_rm_check_lm_and_get_connected_blks( } } + /* bypass rest of the checks if preferred display is found */ + if (BIT(SDE_DISP_PRIMARY_PREF) & caps) + preferred_disp_id = 1; + else if (BIT(SDE_DISP_SECONDARY_PREF) & caps) + preferred_disp_id = 2; + else if (BIT(SDE_DISP_TERTIARY_PREF) & caps) + preferred_disp_id = 3; + + if (reqs->disp_id == preferred_disp_id) + preferred_disp_match = true; + /* Matches user requirements? */ - if ((RM_RQ_DSPP(reqs) && lm_cfg->dspp == DSPP_MAX) || - (!RM_RQ_DSPP(reqs) && lm_cfg->dspp != DSPP_MAX)) { + if (!preferred_disp_match && + ((RM_RQ_DSPP(reqs) && lm_cfg->dspp == DSPP_MAX) || + (!RM_RQ_DSPP(reqs) && lm_cfg->dspp != DSPP_MAX))) { SDE_DEBUG("dspp req mismatch lm %d reqdspp %d, lm->dspp %d\n", lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)), lm_cfg->dspp); @@ -769,6 +787,7 @@ static int _sde_rm_reserve_ctls( while (_sde_rm_get_hw_locked(rm, &iter)) { unsigned long caps; bool has_split_display, has_ppsplit; + bool ctl_found = false; if (RESERVED_BY_OTHER(iter.blk, rsvp)) continue; @@ -780,11 +799,33 @@ static int _sde_rm_reserve_ctls( SDE_DEBUG("ctl %d caps 0x%lX\n", iter.blk->id, caps); /* early return when finding the matched ctl id */ - if ((prefer_ctl_id > 0) && (iter.blk->id == prefer_ctl_id)) { - ctls[i] = iter.blk; + if ((prefer_ctl_id > 0) && (iter.blk->id == prefer_ctl_id)) + ctl_found = true; + switch (reqs->disp_id) { + case 1: + if (BIT(SDE_CTL_PRIMARY_PREF) & caps) + ctl_found = true; + break; + case 2: + if (BIT(SDE_CTL_SECONDARY_PREF) & caps) + ctl_found = true; + break; + case 3: + if (BIT(SDE_CTL_TERTIARY_PREF) & caps) + ctl_found = true; + break; + default: + break; + } + + if (ctl_found) { + ctls[i] = iter.blk; + prefer_ctl_id = 0; if (++i == reqs->num_ctl) break; + else + continue; } if (reqs->needs_split_display != has_split_display) @@ -933,6 +974,30 @@ static int _sde_rm_make_next_rsvp( struct sde_rm_requirements *reqs) { int ret; + struct sde_connector *sde_conn = + to_sde_connector(conn_state->connector); + struct dsi_display *dsi; + struct sde_hdmi *hdmi; + const char *display_type; + + if (sde_conn->connector_type == DRM_MODE_CONNECTOR_DSI) { + dsi = (struct dsi_display *)sde_conn->display; + display_type = dsi->display_type; + } else if (sde_conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) { + hdmi = (struct sde_hdmi *)sde_conn->display; + display_type = hdmi->display_type; + } else { + /* virtual display does not have display type */ + display_type = "none"; + } + if (!strcmp("primary", display_type)) + reqs->disp_id = 1; + else if (!strcmp("secondary", display_type)) + reqs->disp_id = 2; + else if (!strcmp("tertiary", display_type)) + reqs->disp_id = 3; + else /* No display type set in dtsi */ + reqs->disp_id = 0; /* Create reservation info, tag reserved blocks with it as we go */ rsvp->seq = ++rm->rsvp_next_seq; diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c index 9c3964e99c1f..e5231c4b1aae 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.c +++ b/drivers/gpu/drm/msm/sde/sde_splash.c @@ -275,6 +275,25 @@ static void _sde_splash_destroy_splash_node(struct sde_splash_info *sinfo) sinfo->splash_mem_size = NULL; } +static void _sde_splash_update_display_splash_status(struct sde_kms *sde_kms) +{ + struct dsi_display *dsi_display; + struct sde_hdmi *sde_hdmi; + int i = 0; + + for (i = 0; i < sde_kms->dsi_display_count; i++) { + dsi_display = (struct dsi_display *)sde_kms->dsi_displays[i]; + + dsi_display->cont_splash_enabled = false; + } + + for (i = 0; i < sde_kms->hdmi_display_count; i++) { + sde_hdmi = (struct sde_hdmi *)sde_kms->hdmi_displays[i]; + + sde_hdmi->cont_splash_enabled = false; + } +} + static void _sde_splash_sent_pipe_update_uevent(struct sde_kms *sde_kms) { char *event_string; @@ -737,6 +756,7 @@ bool sde_splash_get_lk_complete_status(struct msm_kms *kms) intr = sde_kms->hw_intr; if (sde_kms->splash_info.handoff && + !sde_kms->splash_info.display_splash_enabled && SDE_LK_EXIT_VALUE == SDE_REG_READ(&intr->hw, SCRATCH_REGISTER_1)) { SDE_DEBUG("LK totoally exits\n"); @@ -816,6 +836,9 @@ int sde_splash_free_resource(struct msm_kms *kms, /* send uevent to notify user to recycle resource */ _sde_splash_sent_pipe_update_uevent(sde_kms); + /* set display's splash status to false after handoff is done */ + _sde_splash_update_display_splash_status(sde_kms); + /* Finally mark handoff flag to false to say * handoff is complete. */ @@ -856,17 +879,13 @@ int sde_splash_free_resource(struct msm_kms *kms, } /* - * In below function, it will - * 1. Notify LK to stop display splash. - * 2. Set DOMAIN_ATTR_EARLY_MAP to 1 to enable stage 1 translation in iommu. + * Below function will notify LK to stop display splash. */ int sde_splash_lk_stop_splash(struct msm_kms *kms, struct drm_atomic_state *state) { struct sde_splash_info *sinfo; - struct msm_mmu *mmu; struct sde_kms *sde_kms = to_sde_kms(kms); - int ret; sinfo = &sde_kms->splash_info; @@ -886,26 +905,5 @@ int sde_splash_lk_stop_splash(struct msm_kms *kms, } mutex_unlock(&sde_splash_lock); - if (!sde_kms->aspace[0] || !sde_kms->aspace[0]->mmu) { - /* We do not return fault value here, to ensure - * flag "lk_is_exited" is set. - */ - SDE_ERROR("invalid mmu\n"); - WARN_ON(1); - } else { - mmu = sde_kms->aspace[0]->mmu; - /* After LK has exited, set early domain map attribute - * to 1 to enable stage 1 translation in iommu driver. - */ - if (mmu->funcs && mmu->funcs->set_property) { - ret = mmu->funcs->set_property(mmu, - DOMAIN_ATTR_EARLY_MAP, - &sinfo->display_splash_enabled); - - if (ret) - SDE_ERROR("set_property failed\n"); - } - } - return 0; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index d908321b94ce..e6d07680eb05 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -67,7 +67,6 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). */ vma->vm_flags &= ~VM_PFNMAP; - vma->vm_pgoff = 0; ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, obj->size, &rk_obj->dma_attrs); @@ -99,6 +98,12 @@ int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) if (ret) return ret; + /* + * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the + * whole buffer from the start. + */ + vma->vm_pgoff = 0; + obj = vma->vm_private_data; return rockchip_drm_gem_object_mmap(obj, vma); diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index b4de18e65db8..6296e9f270ca 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -208,6 +208,9 @@ static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data, case VIRTGPU_PARAM_3D_FEATURES: value = vgdev->has_virgl_3d == true ? 1 : 0; break; + case VIRTGPU_PARAM_CAPSET_QUERY_FIX: + value = 1; + break; default: return -EINVAL; } @@ -483,7 +486,7 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev, { struct virtio_gpu_device *vgdev = dev->dev_private; struct drm_virtgpu_get_caps *args = data; - int size; + unsigned size, host_caps_size; int i; int found_valid = -1; int ret; @@ -492,6 +495,10 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev, if (vgdev->num_capsets == 0) return -ENOSYS; + /* don't allow userspace to pass 0 */ + if (args->size == 0) + return -EINVAL; + spin_lock(&vgdev->display_info_lock); for (i = 0; i < vgdev->num_capsets; i++) { if (vgdev->capsets[i].id == args->cap_set_id) { @@ -507,11 +514,9 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev, return -EINVAL; } - size = vgdev->capsets[found_valid].max_size; - if (args->size > size) { - spin_unlock(&vgdev->display_info_lock); - return -EINVAL; - } + host_caps_size = vgdev->capsets[found_valid].max_size; + /* only copy to user the minimum of the host caps size or the guest caps size */ + size = min(args->size, host_caps_size); list_for_each_entry(cache_ent, &vgdev->cap_cache, head) { if (cache_ent->id == args->cap_set_id && diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 966047711fbf..1073c0d1fae5 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -37,6 +37,8 @@ static uint kovaplus_convert_event_cpi(uint value) static void kovaplus_profile_activated(struct kovaplus_device *kovaplus, uint new_profile_index) { + if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings)) + return; kovaplus->actual_profile = new_profile_index; kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level; kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x; diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index d7ebdf8651f5..d3c6115f16b9 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -1390,7 +1390,7 @@ static void nct6775_update_pwm(struct device *dev) duty_is_dc = data->REG_PWM_MODE[i] && (nct6775_read_value(data, data->REG_PWM_MODE[i]) & data->PWM_MODE_MASK[i]); - data->pwm_mode[i] = duty_is_dc; + data->pwm_mode[i] = !duty_is_dc; fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]); for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) { @@ -2267,7 +2267,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); - return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]); + return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]); } static ssize_t @@ -2288,9 +2288,9 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, if (val > 1) return -EINVAL; - /* Setting DC mode is not supported for all chips/channels */ + /* Setting DC mode (0) is not supported for all chips/channels */ if (data->REG_PWM_MODE[nr] == 0) { - if (val) + if (!val) return -EINVAL; return count; } @@ -2299,7 +2299,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, data->pwm_mode[nr] = val; reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]); reg &= ~data->PWM_MODE_MASK[nr]; - if (val) + if (!val) reg |= data->PWM_MODE_MASK[nr]; nct6775_write_value(data, data->REG_PWM_MODE[nr], reg); mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 18477dd1e243..c3f4c9ef6705 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -141,7 +141,7 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) const struct adm1275_data *data = to_adm1275_data(info); int ret = 0; - if (page) + if (page > 0) return -ENXIO; switch (reg) { @@ -218,7 +218,7 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, const struct adm1275_data *data = to_adm1275_data(info); int ret; - if (page) + if (page > 0) return -ENXIO; switch (reg) { diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index dd4883a19045..e951f9b87abb 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -45,7 +45,7 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg) { int ret; - if (page) + if (page > 0) return -ENXIO; switch (reg) { diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index cb07713aceda..129fcf1c06d9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -26,6 +26,7 @@ #include <linux/stm.h> #include <linux/fs.h> #include <linux/mm.h> +#include <linux/vmalloc.h> #include "stm.h" #include <uapi/linux/stm.h> @@ -650,7 +651,7 @@ static void stm_device_release(struct device *dev) { struct stm_device *stm = to_stm_device(dev); - kfree(stm); + vfree(stm); } int stm_register_device(struct device *parent, struct stm_data *stm_data, @@ -667,7 +668,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return -EINVAL; nmasters = stm_data->sw_end - stm_data->sw_start; - stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL); + stm = vzalloc(sizeof(*stm) + nmasters * sizeof(void *)); if (!stm) return -ENOMEM; @@ -709,7 +710,7 @@ err_device: /* matches device_initialize() above */ put_device(&stm->dev); err_free: - kfree(stm); + vfree(stm); return err; } 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/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 43207f52e5a3..332d32c53c41 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -856,12 +856,16 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, */ if (of_device_is_compatible(np, "marvell,mv78230-i2c")) { drv_data->offload_enabled = true; - drv_data->errata_delay = true; + /* The delay is only needed in standard mode (100kHz) */ + if (bus_freq <= 100000) + drv_data->errata_delay = true; } if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) { drv_data->offload_enabled = false; - drv_data->errata_delay = true; + /* The delay is only needed in standard mode (100kHz) */ + if (bus_freq <= 100000) + drv_data->errata_delay = true; } if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c")) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 599c0d7bd906..6f89484765e3 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -33,7 +33,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> -#include <linux/spinlock.h> /* register offsets */ #define ICSCR 0x00 /* slave ctrl */ @@ -84,6 +83,7 @@ #define RCAR_BUS_PHASE_START (MDBS | MIE | ESG) #define RCAR_BUS_PHASE_DATA (MDBS | MIE) +#define RCAR_BUS_MASK_DATA (~(ESG | FSB) & 0xFF) #define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB) #define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE) @@ -94,7 +94,6 @@ #define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF) #define ID_LAST_MSG (1 << 0) -#define ID_IOERROR (1 << 1) #define ID_DONE (1 << 2) #define ID_ARBLOST (1 << 3) #define ID_NACK (1 << 4) @@ -108,10 +107,10 @@ enum rcar_i2c_type { struct rcar_i2c_priv { void __iomem *io; struct i2c_adapter adap; - struct i2c_msg *msg; + struct i2c_msg *msg; + int msgs_left; struct clk *clk; - spinlock_t lock; wait_queue_head_t wait; int pos; @@ -144,9 +143,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) { /* reset master mode */ rcar_i2c_write(priv, ICMIER, 0); - rcar_i2c_write(priv, ICMCR, 0); + rcar_i2c_write(priv, ICMCR, MDBS); rcar_i2c_write(priv, ICMSR, 0); - rcar_i2c_write(priv, ICMAR, 0); + /* start clock */ + rcar_i2c_write(priv, ICCCR, priv->icccr); } static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) @@ -257,16 +257,28 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv) { int read = !!rcar_i2c_is_recv(priv); + priv->pos = 0; + priv->flags = 0; + if (priv->msgs_left == 1) + rcar_i2c_flags_set(priv, ID_LAST_MSG); + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read); rcar_i2c_write(priv, ICMSR, 0); rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND); } +static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) +{ + priv->msg++; + priv->msgs_left--; + rcar_i2c_prepare_msg(priv); +} + /* * interrupt functions */ -static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) +static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) { struct i2c_msg *msg = priv->msg; @@ -276,14 +288,7 @@ static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) * Do nothing */ if (!(msr & MDE)) - return 0; - - /* - * If address transfer phase finished, - * goto data phase. - */ - if (msr & MAT) - rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA); + return; if (priv->pos < msg->len) { /* @@ -305,29 +310,23 @@ static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) * [ICRXTX] -> [SHIFT] -> [I2C bus] */ - if (priv->flags & ID_LAST_MSG) + if (priv->flags & ID_LAST_MSG) { /* * If current msg is the _LAST_ msg, * prepare stop condition here. * ID_DONE will be set on STOP irq. */ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP); - else - /* - * If current msg is _NOT_ last msg, - * it doesn't call stop phase. - * thus, there is no STOP irq. - * return ID_DONE here. - */ - return ID_DONE; + } else { + rcar_i2c_next_msg(priv); + return; + } } rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND); - - return 0; } -static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) +static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) { struct i2c_msg *msg = priv->msg; @@ -337,14 +336,10 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) * Do nothing */ if (!(msr & MDR)) - return 0; + return; if (msr & MAT) { - /* - * Address transfer phase finished, - * but, there is no data at this point. - * Do nothing. - */ + /* Address transfer phase finished, but no data at this point. */ } else if (priv->pos < msg->len) { /* * get received data @@ -360,12 +355,11 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) */ if (priv->pos + 1 >= msg->len) rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP); - else - rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA); - rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV); - - return 0; + if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG)) + rcar_i2c_next_msg(priv); + else + rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV); } static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) @@ -426,22 +420,21 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) static irqreturn_t rcar_i2c_irq(int irq, void *ptr) { struct rcar_i2c_priv *priv = ptr; - irqreturn_t result = IRQ_HANDLED; - u32 msr; - - /*-------------- spin lock -----------------*/ - spin_lock(&priv->lock); + u32 msr, val; - if (rcar_i2c_slave_irq(priv)) - goto exit; + /* Clear START or STOP as soon as we can */ + val = rcar_i2c_read(priv, ICMCR); + rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA); msr = rcar_i2c_read(priv, ICMSR); /* Only handle interrupts that are currently enabled */ msr &= rcar_i2c_read(priv, ICMIER); if (!msr) { - result = IRQ_NONE; - goto exit; + if (rcar_i2c_slave_irq(priv)) + return IRQ_HANDLED; + + return IRQ_NONE; } /* Arbitration lost */ @@ -452,8 +445,7 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr) /* Nack */ if (msr & MNR) { - /* go to stop phase */ - rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP); + /* HW automatically sends STOP after received NACK */ rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); rcar_i2c_flags_set(priv, ID_NACK); goto out; @@ -461,14 +453,15 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr) /* Stop */ if (msr & MST) { + priv->msgs_left--; /* The last message also made it */ rcar_i2c_flags_set(priv, ID_DONE); goto out; } if (rcar_i2c_is_recv(priv)) - rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr)); + rcar_i2c_irq_recv(priv, msr); else - rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr)); + rcar_i2c_irq_send(priv, msr); out: if (rcar_i2c_flags_has(priv, ID_DONE)) { @@ -477,11 +470,7 @@ out: wake_up(&priv->wait); } -exit: - spin_unlock(&priv->lock); - /*-------------- spin unlock -----------------*/ - - return result; + return IRQ_HANDLED; } static int rcar_i2c_master_xfer(struct i2c_adapter *adap, @@ -490,22 +479,11 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, { struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); struct device *dev = rcar_i2c_priv_to_dev(priv); - unsigned long flags; int i, ret; - long timeout; + long time_left; pm_runtime_get_sync(dev); - /*-------------- spin lock -----------------*/ - spin_lock_irqsave(&priv->lock, flags); - - rcar_i2c_init(priv); - /* start clock */ - rcar_i2c_write(priv, ICCCR, priv->icccr); - - spin_unlock_irqrestore(&priv->lock, flags); - /*-------------- spin unlock -----------------*/ - ret = rcar_i2c_bus_barrier(priv); if (ret < 0) goto out; @@ -514,48 +492,28 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, /* This HW can't send STOP after address phase */ if (msgs[i].len == 0) { ret = -EOPNOTSUPP; - break; - } - - /*-------------- spin lock -----------------*/ - spin_lock_irqsave(&priv->lock, flags); - - /* init each data */ - priv->msg = &msgs[i]; - priv->pos = 0; - priv->flags = 0; - if (i == num - 1) - rcar_i2c_flags_set(priv, ID_LAST_MSG); - - rcar_i2c_prepare_msg(priv); - - spin_unlock_irqrestore(&priv->lock, flags); - /*-------------- spin unlock -----------------*/ - - timeout = wait_event_timeout(priv->wait, - rcar_i2c_flags_has(priv, ID_DONE), - adap->timeout); - if (!timeout) { - ret = -ETIMEDOUT; - break; - } - - if (rcar_i2c_flags_has(priv, ID_NACK)) { - ret = -ENXIO; - break; - } - - if (rcar_i2c_flags_has(priv, ID_ARBLOST)) { - ret = -EAGAIN; - break; - } - - if (rcar_i2c_flags_has(priv, ID_IOERROR)) { - ret = -EIO; - break; + goto out; } + } - ret = i + 1; /* The number of transfer */ + /* init data */ + priv->msg = msgs; + priv->msgs_left = num; + + rcar_i2c_prepare_msg(priv); + + time_left = wait_event_timeout(priv->wait, + rcar_i2c_flags_has(priv, ID_DONE), + num * adap->timeout); + if (!time_left) { + rcar_i2c_init(priv); + ret = -ETIMEDOUT; + } else if (rcar_i2c_flags_has(priv, ID_NACK)) { + ret = -ENXIO; + } else if (rcar_i2c_flags_has(priv, ID_ARBLOST)) { + ret = -EAGAIN; + } else { + ret = num - priv->msgs_left; /* The number of transfer */ } out: pm_runtime_put(dev); @@ -650,23 +608,27 @@ static int rcar_i2c_probe(struct platform_device *pdev) return PTR_ERR(priv->clk); } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->io = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->io)) + return PTR_ERR(priv->io); + bus_speed = 100000; /* default 100 kHz */ of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed); priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data; + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); ret = rcar_i2c_clock_calculate(priv, bus_speed, dev); if (ret < 0) - return ret; + goto out_pm_put; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->io = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->io)) - return PTR_ERR(priv->io); + rcar_i2c_init(priv); + pm_runtime_put(dev); irq = platform_get_irq(pdev, 0); init_waitqueue_head(&priv->wait); - spin_lock_init(&priv->lock); adap = &priv->adap; adap->nr = pdev->id; @@ -682,22 +644,26 @@ static int rcar_i2c_probe(struct platform_device *pdev) dev_name(dev), priv); if (ret < 0) { dev_err(dev, "cannot get irq %d\n", irq); - return ret; + goto out_pm_disable; } - pm_runtime_enable(dev); platform_set_drvdata(pdev, priv); ret = i2c_add_numbered_adapter(adap); if (ret < 0) { dev_err(dev, "reg adap failed: %d\n", ret); - pm_runtime_disable(dev); - return ret; + goto out_pm_disable; } dev_info(dev, "probed\n"); return 0; + + out_pm_put: + pm_runtime_put(dev); + out_pm_disable: + pm_runtime_disable(dev); + return ret; } static int rcar_i2c_remove(struct platform_device *pdev) diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index ef907fd5ba98..08a21d635d0d 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1593,6 +1593,8 @@ static int idecd_open(struct block_device *bdev, fmode_t mode) struct cdrom_info *info; int rc = -ENXIO; + check_disk_change(bdev); + mutex_lock(&ide_cd_mutex); info = ide_cd_get(bdev->bd_disk); if (!info) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index c5b999f0c519..7ef9b13262a8 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -24,6 +24,13 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf, if ((length == 0) || (bytes_per_datum == 0)) return -EINVAL; + /* + * Make sure we don't overflow an unsigned int after kfifo rounds up to + * the next power of 2. + */ + if (roundup_pow_of_two(length) > UINT_MAX / bytes_per_datum) + return -EINVAL; + return __kfifo_alloc((struct __kfifo *)&buf->kf, length, bytes_per_datum, GFP_KERNEL); } 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 = ® + 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/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 2b9c00faca7d..795938edce3f 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1295,7 +1295,7 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf, if (IS_ERR(ctx)) return PTR_ERR(ctx); - if (unlikely(cmd.optval > KMALLOC_MAX_SIZE)) + if (unlikely(cmd.optlen > KMALLOC_MAX_SIZE)) return -EINVAL; optval = memdup_user((void __user *) (unsigned long) cmd.optval, diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index c5390f6f94c5..43d277a931c2 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -3161,12 +3161,9 @@ int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd) int err; err = mlx5_core_xrcd_dealloc(dev->mdev, xrcdn); - if (err) { + if (err) mlx5_ib_warn(dev, "failed to dealloc xrcdn 0x%x\n", xrcdn); - return err; - } kfree(xrcd); - return 0; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 37b42447045d..fcb18b11db75 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1953,6 +1953,9 @@ static struct net_device *ipoib_add_port(const char *format, goto event_failed; } + /* call event handler to ensure pkey in sync */ + queue_work(ipoib_workqueue, &priv->flush_heavy); + result = register_netdev(priv->dev); if (result) { printk(KERN_WARNING "%s: couldn't register ipoib port %d; error %d\n", diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 3851d5715772..aeb8250ab079 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1249,6 +1249,7 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN060B", 0 }, { "ELAN060C", 0 }, { "ELAN0611", 0 }, + { "ELAN0612", 0 }, { "ELAN1000", 0 }, { } }; diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index cb6aecbc1dc2..25dba1d7aa57 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -130,7 +130,7 @@ static int elan_smbus_get_baseline_data(struct i2c_client *client, bool max_baseline, u8 *value) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, max_baseline ? @@ -149,7 +149,7 @@ static int elan_smbus_get_version(struct i2c_client *client, bool iap, u8 *version) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, iap ? ETP_SMBUS_IAP_VERSION_CMD : @@ -169,7 +169,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *ic_type, u8 *version) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, ETP_SMBUS_SM_VERSION_CMD, val); @@ -186,7 +186,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client, static int elan_smbus_get_product_id(struct i2c_client *client, u16 *id) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, ETP_SMBUS_UNIQUEID_CMD, val); @@ -203,7 +203,7 @@ static int elan_smbus_get_checksum(struct i2c_client *client, bool iap, u16 *csum) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, iap ? ETP_SMBUS_FW_CHECKSUM_CMD : @@ -223,7 +223,7 @@ static int elan_smbus_get_max(struct i2c_client *client, unsigned int *max_x, unsigned int *max_y) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val); if (error) { @@ -241,7 +241,7 @@ static int elan_smbus_get_resolution(struct i2c_client *client, u8 *hw_res_x, u8 *hw_res_y) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val); @@ -261,7 +261,7 @@ static int elan_smbus_get_num_traces(struct i2c_client *client, unsigned int *y_traces) { int error; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val); @@ -288,7 +288,7 @@ static int elan_smbus_iap_get_mode(struct i2c_client *client, { int error; u16 constant; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; error = i2c_smbus_read_block_data(client, ETP_SMBUS_IAP_CTRL_CMD, val); if (error < 0) { @@ -339,7 +339,7 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client) int len; int error; enum tp_mode mode; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06}; u16 password; @@ -413,7 +413,7 @@ static int elan_smbus_write_fw_block(struct i2c_client *client, struct device *dev = &client->dev; int error; u16 result; - u8 val[3]; + u8 val[I2C_SMBUS_BLOCK_MAX] = {0}; /* * Due to the limitation of smbus protocol limiting diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 4d113c9e4b77..7bf2597ce44c 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -425,6 +425,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id); #ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, + { "GDIX1002", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 6317478916ef..56f2980adc28 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -2256,8 +2256,17 @@ static int arm_smmu_attach_dynamic(struct iommu_domain *domain, smmu_domain->pgtbl_ops = pgtbl_ops; ret = 0; out: - if (ret) + if (ret) { free_io_pgtable_ops(pgtbl_ops); + /* unassign any freed page table memory */ + if (arm_smmu_is_master_side_secure(smmu_domain)) { + arm_smmu_secure_domain_lock(smmu_domain); + arm_smmu_secure_pool_destroy(smmu_domain); + arm_smmu_unassign_table(smmu_domain); + arm_smmu_secure_domain_unlock(smmu_domain); + } + smmu_domain->pgtbl_ops = NULL; + } mutex_unlock(&smmu->attach_lock); return ret; diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index e9b241b1c9dd..ac596928f6b4 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -753,7 +753,7 @@ static inline void set_irq_posting_cap(void) * should have X86_FEATURE_CX16 support, this has been confirmed * with Intel hardware guys. */ - if ( cpu_has_cx16 ) + if (boot_cpu_has(X86_FEATURE_CX16)) intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP; for_each_iommu(iommu, drhd) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 9a22494a2371..2b8b0e4036b5 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -750,7 +750,7 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | tlist << ICC_SGI1R_TARGET_LIST_SHIFT); - pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); + pr_devel("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val); gic_write_sgi1r(val); } diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c index d91dd580e978..37aaea88a6ad 100644 --- a/drivers/isdn/hardware/eicon/diva.c +++ b/drivers/isdn/hardware/eicon/diva.c @@ -387,10 +387,10 @@ void divasa_xdi_driver_unload(void) ** Receive and process command from user mode utility */ void *diva_xdi_open_adapter(void *os_handle, const void __user *src, - int length, + int length, void *mptr, divas_xdi_copy_from_user_fn_t cp_fn) { - diva_xdi_um_cfg_cmd_t msg; + diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr; diva_os_xdi_adapter_t *a = NULL; diva_os_spin_lock_magic_t old_irql; struct list_head *tmp; @@ -400,21 +400,21 @@ void *diva_xdi_open_adapter(void *os_handle, const void __user *src, length, sizeof(diva_xdi_um_cfg_cmd_t))) return NULL; } - if ((*cp_fn) (os_handle, &msg, src, sizeof(msg)) <= 0) { + if ((*cp_fn) (os_handle, msg, src, sizeof(*msg)) <= 0) { DBG_ERR(("A: A(?) open, write error")) return NULL; } diva_os_enter_spin_lock(&adapter_lock, &old_irql, "open_adapter"); list_for_each(tmp, &adapter_queue) { a = list_entry(tmp, diva_os_xdi_adapter_t, link); - if (a->controller == (int)msg.adapter) + if (a->controller == (int)msg->adapter) break; a = NULL; } diva_os_leave_spin_lock(&adapter_lock, &old_irql, "open_adapter"); if (!a) { - DBG_ERR(("A: A(%d) open, adapter not found", msg.adapter)) + DBG_ERR(("A: A(%d) open, adapter not found", msg->adapter)) } return (a); @@ -436,8 +436,10 @@ void diva_xdi_close_adapter(void *adapter, void *os_handle) int diva_xdi_write(void *adapter, void *os_handle, const void __user *src, - int length, divas_xdi_copy_from_user_fn_t cp_fn) + int length, void *mptr, + divas_xdi_copy_from_user_fn_t cp_fn) { + diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr; diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter; void *data; @@ -458,7 +460,13 @@ diva_xdi_write(void *adapter, void *os_handle, const void __user *src, return (-2); } - length = (*cp_fn) (os_handle, data, src, length); + if (msg) { + *(diva_xdi_um_cfg_cmd_t *)data = *msg; + length = (*cp_fn) (os_handle, (char *)data + sizeof(*msg), + src + sizeof(*msg), length - sizeof(*msg)); + } else { + length = (*cp_fn) (os_handle, data, src, length); + } if (length > 0) { if ((*(a->interface.cmd_proc)) (a, (diva_xdi_um_cfg_cmd_t *) data, length)) { diff --git a/drivers/isdn/hardware/eicon/diva.h b/drivers/isdn/hardware/eicon/diva.h index e979085d1b89..a0a607c0c32e 100644 --- a/drivers/isdn/hardware/eicon/diva.h +++ b/drivers/isdn/hardware/eicon/diva.h @@ -19,10 +19,11 @@ int diva_xdi_read(void *adapter, void *os_handle, void __user *dst, int max_length, divas_xdi_copy_to_user_fn_t cp_fn); int diva_xdi_write(void *adapter, void *os_handle, const void __user *src, - int length, divas_xdi_copy_from_user_fn_t cp_fn); + int length, void *msg, + divas_xdi_copy_from_user_fn_t cp_fn); void *diva_xdi_open_adapter(void *os_handle, const void __user *src, - int length, + int length, void *msg, divas_xdi_copy_from_user_fn_t cp_fn); void diva_xdi_close_adapter(void *adapter, void *os_handle); diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c index a2e0ed6c9a4d..91bd2ba0bdd8 100644 --- a/drivers/isdn/hardware/eicon/divasmain.c +++ b/drivers/isdn/hardware/eicon/divasmain.c @@ -591,19 +591,22 @@ static int divas_release(struct inode *inode, struct file *file) static ssize_t divas_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + diva_xdi_um_cfg_cmd_t msg; int ret = -EINVAL; if (!file->private_data) { file->private_data = diva_xdi_open_adapter(file, buf, - count, + count, &msg, xdi_copy_from_user); - } - if (!file->private_data) { - return (-ENODEV); + if (!file->private_data) + return (-ENODEV); + ret = diva_xdi_write(file->private_data, file, + buf, count, &msg, xdi_copy_from_user); + } else { + ret = diva_xdi_write(file->private_data, file, + buf, count, NULL, xdi_copy_from_user); } - ret = diva_xdi_write(file->private_data, file, - buf, count, xdi_copy_from_user); switch (ret) { case -1: /* Message should be removed from rx mailbox first */ ret = -EBUSY; @@ -622,11 +625,12 @@ static ssize_t divas_write(struct file *file, const char __user *buf, static ssize_t divas_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { + diva_xdi_um_cfg_cmd_t msg; int ret = -EINVAL; if (!file->private_data) { file->private_data = diva_xdi_open_adapter(file, buf, - count, + count, &msg, xdi_copy_from_user); } if (!file->private_data) { diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index aa84fcfd59fc..16c3390e5d9f 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -285,8 +285,10 @@ do { \ break; \ \ mutex_unlock(&(ca)->set->bucket_lock); \ - if (kthread_should_stop()) \ + if (kthread_should_stop()) { \ + set_current_state(TASK_RUNNING); \ return 0; \ + } \ \ try_to_freeze(); \ schedule(); \ diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 02619cabda8b..7fe7df56fa33 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -904,7 +904,7 @@ void bcache_write_super(struct cache_set *); int bch_flash_dev_create(struct cache_set *c, uint64_t size); -int bch_cached_dev_attach(struct cached_dev *, struct cache_set *); +int bch_cached_dev_attach(struct cached_dev *, struct cache_set *, uint8_t *); void bch_cached_dev_detach(struct cached_dev *); void bch_cached_dev_run(struct cached_dev *); void bcache_device_stop(struct bcache_device *); diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index a5a6909280fe..4ed621ad27e4 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1869,14 +1869,17 @@ void bch_initial_gc_finish(struct cache_set *c) */ for_each_cache(ca, c, i) { for_each_bucket(b, ca) { - if (fifo_full(&ca->free[RESERVE_PRIO])) + if (fifo_full(&ca->free[RESERVE_PRIO]) && + fifo_full(&ca->free[RESERVE_BTREE])) break; if (bch_can_invalidate_bucket(ca, b) && !GC_MARK(b)) { __bch_invalidate_one_bucket(ca, b); - fifo_push(&ca->free[RESERVE_PRIO], - b - ca->buckets); + if (!fifo_push(&ca->free[RESERVE_PRIO], + b - ca->buckets)) + fifo_push(&ca->free[RESERVE_BTREE], + b - ca->buckets); } } } diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 53c0fa005821..c772fc27b332 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -633,11 +633,11 @@ static void do_bio_hook(struct search *s, struct bio *orig_bio) static void search_free(struct closure *cl) { struct search *s = container_of(cl, struct search, cl); - bio_complete(s); if (s->iop.bio) bio_put(s->iop.bio); + bio_complete(s); closure_debug_destroy(cl); mempool_free(s, s->d->c->search); } diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index b9a526271f02..69356e9a3be5 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -936,7 +936,8 @@ void bch_cached_dev_detach(struct cached_dev *dc) cached_dev_put(dc); } -int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) +int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c, + uint8_t *set_uuid) { uint32_t rtime = cpu_to_le32(get_seconds()); struct uuid_entry *u; @@ -945,7 +946,8 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) bdevname(dc->bdev, buf); - if (memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16)) + if ((set_uuid && memcmp(set_uuid, c->sb.set_uuid, 16)) || + (!set_uuid && memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16))) return -ENOENT; if (dc->disk.c) { @@ -1189,7 +1191,7 @@ static void register_bdev(struct cache_sb *sb, struct page *sb_page, list_add(&dc->list, &uncached_devices); list_for_each_entry(c, &bch_cache_sets, list) - bch_cached_dev_attach(dc, c); + bch_cached_dev_attach(dc, c, NULL); if (BDEV_STATE(&dc->sb) == BDEV_STATE_NONE || BDEV_STATE(&dc->sb) == BDEV_STATE_STALE) @@ -1711,7 +1713,7 @@ static void run_cache_set(struct cache_set *c) bcache_write_super(c); list_for_each_entry_safe(dc, t, &uncached_devices, list) - bch_cached_dev_attach(dc, c); + bch_cached_dev_attach(dc, c, NULL); flash_devs_run(c); @@ -1828,6 +1830,7 @@ void bch_cache_release(struct kobject *kobj) static int cache_alloc(struct cache_sb *sb, struct cache *ca) { size_t free; + size_t btree_buckets; struct bucket *b; __module_get(THIS_MODULE); @@ -1837,9 +1840,19 @@ static int cache_alloc(struct cache_sb *sb, struct cache *ca) ca->journal.bio.bi_max_vecs = 8; ca->journal.bio.bi_io_vec = ca->journal.bio.bi_inline_vecs; + /* + * when ca->sb.njournal_buckets is not zero, journal exists, + * and in bch_journal_replay(), tree node may split, + * so bucket of RESERVE_BTREE type is needed, + * the worst situation is all journal buckets are valid journal, + * and all the keys need to replay, + * so the number of RESERVE_BTREE type buckets should be as much + * as journal buckets + */ + btree_buckets = ca->sb.njournal_buckets ?: 8; free = roundup_pow_of_two(ca->sb.nbuckets) >> 10; - if (!init_fifo(&ca->free[RESERVE_BTREE], 8, GFP_KERNEL) || + if (!init_fifo(&ca->free[RESERVE_BTREE], btree_buckets, GFP_KERNEL) || !init_fifo_exact(&ca->free[RESERVE_PRIO], prio_buckets(ca), GFP_KERNEL) || !init_fifo(&ca->free[RESERVE_MOVINGGC], free, GFP_KERNEL) || !init_fifo(&ca->free[RESERVE_NONE], free, GFP_KERNEL) || diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 4fbb5532f24c..5a5c1f1bd8a5 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -191,7 +191,7 @@ STORE(__cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); - ssize_t v = size; + ssize_t v; struct cache_set *c; struct kobj_uevent_env *env; @@ -263,17 +263,20 @@ STORE(__cached_dev) } if (attr == &sysfs_attach) { - if (bch_parse_uuid(buf, dc->sb.set_uuid) < 16) + uint8_t set_uuid[16]; + + if (bch_parse_uuid(buf, set_uuid) < 16) return -EINVAL; + v = -ENOENT; list_for_each_entry(c, &bch_cache_sets, list) { - v = bch_cached_dev_attach(dc, c); + v = bch_cached_dev_attach(dc, c, set_uuid); if (!v) return size; } pr_err("Can't attach %s: cache set not found", buf); - size = v; + return v; } if (attr == &sysfs_detach && dc->disk.c) diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index bbb1dc9e1639..f2c0000de613 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -425,19 +425,28 @@ static int bch_writeback_thread(void *arg) while (!kthread_should_stop()) { down_write(&dc->writeback_lock); - if (!atomic_read(&dc->has_dirty) || - (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) && - !dc->writeback_running)) { + set_current_state(TASK_INTERRUPTIBLE); + /* + * If the bache device is detaching, skip here and continue + * to perform writeback. Otherwise, if no dirty data on cache, + * or there is dirty data on cache but writeback is disabled, + * the writeback thread should sleep here and wait for others + * to wake up it. + */ + if (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) && + (!atomic_read(&dc->has_dirty) || !dc->writeback_running)) { up_write(&dc->writeback_lock); - set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); return 0; + } try_to_freeze(); schedule(); continue; } + set_current_state(TASK_RUNNING); searched_full_index = refill_dirty(dc); @@ -447,6 +456,14 @@ static int bch_writeback_thread(void *arg) cached_dev_put(dc); SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN); bch_write_bdev_super(dc, NULL); + /* + * If bcache device is detaching via sysfs interface, + * writeback thread should stop after there is no dirty + * data on cache. BCACHE_DEV_DETACHING flag is set in + * bch_cached_dev_detach(). + */ + if (test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags)) + break; } up_write(&dc->writeback_lock); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a3ec3c5a8ee9..b5a5081ac736 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1686,6 +1686,17 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev) struct md_rdev *repl = conf->mirrors[conf->raid_disks + number].rdev; freeze_array(conf, 0); + if (atomic_read(&repl->nr_pending)) { + /* It means that some queued IO of retry_list + * hold repl. Thus, we cannot set replacement + * as NULL, avoiding rdev NULL pointer + * dereference in sync_request_write and + * handle_write_finished. + */ + err = -EBUSY; + unfreeze_array(conf); + goto abort; + } clear_bit(Replacement, &repl->flags); p->rdev = repl; conf->mirrors[conf->raid_disks + number].rdev = NULL; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 45e7a47e5f7b..2ebd180b0071 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2630,7 +2630,8 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio) for (m = 0; m < conf->copies; m++) { int dev = r10_bio->devs[m].devnum; rdev = conf->mirrors[dev].rdev; - if (r10_bio->devs[m].bio == NULL) + if (r10_bio->devs[m].bio == NULL || + r10_bio->devs[m].bio->bi_end_io == NULL) continue; if (!r10_bio->devs[m].bio->bi_error) { rdev_clear_badblocks( @@ -2645,7 +2646,8 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio) md_error(conf->mddev, rdev); } rdev = conf->mirrors[dev].replacement; - if (r10_bio->devs[m].repl_bio == NULL) + if (r10_bio->devs[m].repl_bio == NULL || + r10_bio->devs[m].repl_bio->bi_end_io == NULL) continue; if (!r10_bio->devs[m].repl_bio->bi_error) { diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 9284acea4f7b..bb77d20f2ef2 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2028,15 +2028,16 @@ static int grow_one_stripe(struct r5conf *conf, gfp_t gfp) static int grow_stripes(struct r5conf *conf, int num) { struct kmem_cache *sc; + size_t namelen = sizeof(conf->cache_name[0]); int devs = max(conf->raid_disks, conf->previous_raid_disks); if (conf->mddev->gendisk) - sprintf(conf->cache_name[0], + snprintf(conf->cache_name[0], namelen, "raid%d-%s", conf->level, mdname(conf->mddev)); else - sprintf(conf->cache_name[0], + snprintf(conf->cache_name[0], namelen, "raid%d-%p", conf->level, conf->mddev); - sprintf(conf->cache_name[1], "%s-alt", conf->cache_name[0]); + snprintf(conf->cache_name[1], namelen, "%.27s-alt", conf->cache_name[0]); conf->active_name = 0; sc = kmem_cache_create(conf->cache_name[conf->active_name], diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c index 19ec11174632..713a47cfea7f 100644 --- a/drivers/media/i2c/adv7481.c +++ b/drivers/media/i2c/adv7481.c @@ -49,12 +49,12 @@ #define I2C_SW_RST_DELAY 5000 #define GPIO_HW_RST_DELAY_HI 10000 #define GPIO_HW_RST_DELAY_LOW 10000 -#define SDP_MIN_SLEEP 5000 -#define SDP_MAX_SLEEP 6000 -#define SDP_NUM_TRIES 30 -#define LOCK_MIN_SLEEP 5000 -#define LOCK_MAX_SLEEP 6000 -#define LOCK_NUM_TRIES 200 +#define SDP_MIN_SLEEP 5000 +#define SDP_MAX_SLEEP 6000 +#define SDP_NUM_TRIES 50 +#define LOCK_MIN_SLEEP 5000 +#define LOCK_MAX_SLEEP 6000 +#define LOCK_NUM_TRIES 200 #define MAX_DEFAULT_WIDTH 1280 #define MAX_DEFAULT_HEIGHT 720 @@ -193,19 +193,20 @@ const uint8_t adv7481_default_edid_data[] = { /* Display Parameters */ 0x80, 0x10, 0x09, 0x78, 0x0A, /* Color characteristics */ -0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4C, +0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, 0x12, +0x48, 0x4C, /* Established Timings */ 0x21, 0x08, 0x00, /* Standard Timings */ -0x81, 0xC0, 0x81, 0x40, 0x3B, 0xC0, 0x3B, 0x40, -0x31, 0xC0, 0x31, 0x40, 0x01, 0x01, 0x01, 0x01, +0xD1, 0xC0, 0xD1, 0x40, 0x81, 0xC0, 0x81, 0x40, +0x3B, 0xC0, 0x3B, 0x40, 0x31, 0xC0, 0x31, 0x40, /* Detailed Timings Block */ -0x01, 0x1D, 0x00, 0xBC, 0x52, 0xD0, 0x1E, 0x20, -0xB8, 0x28, 0x55, 0x40, 0xA0, 0x5A, 0x00, 0x00, +0x1A, 0x36, 0x80, 0xA0, 0x70, 0x38, 0x1F, 0x40, +0x30, 0x20, 0x35, 0x00, 0x40, 0x44, 0x21, 0x00, 0x00, 0x1E, /* Monitor Descriptor Block 2 */ -0x8C, 0x0A, 0xD0, 0xB4, 0x20, 0xE0, 0x14, 0x10, -0x12, 0x48, 0x3A, 0x00, 0xD8, 0xA2, 0x00, 0x00, +0x00, 0x19, 0x00, 0xA0, 0x50, 0xD0, 0x15, 0x20, +0x30, 0x20, 0x35, 0x00, 0x80, 0xD8, 0x10, 0x00, 0x00, 0x1E, /* Monitor Descriptor Block 3 */ 0x00, 0x00, 0x00, 0xFD, 0x00, 0x17, 0x4B, 0x0F, @@ -218,16 +219,16 @@ const uint8_t adv7481_default_edid_data[] = { /* Extension Flag CEA */ 0x01, /* Checksum */ -0x5B, +0x16, /* Block 1 (Extension Block) */ /* Extension Header */ -0x02, 0x03, 0x1E, +0x02, 0x03, 0x22, /* Display supports */ 0x71, -/* Video Data Bock */ -0x48, 0x84, 0x13, 0x3C, 0x03, 0x02, 0x11, 0x12, -0x01, +/* Video Data Block */ +0x4C, 0x84, 0x13, 0x3C, 0x03, 0x02, 0x11, 0x12, +0x01, 0x90, 0x1F, 0x20, 0x22, /* HDMI VSDB */ /* Deep color All, Max_TMDS_Clock = 150 MHz */ 0x68, 0x03, 0x0C, 0x00, 0x10, 0x00, 0x80, @@ -239,17 +240,17 @@ const uint8_t adv7481_default_edid_data[] = { /* Speaker Allocation Data Block */ 0x83, 0x01, 0x00, 0x00, /* Detailed Timing Descriptor */ -0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, -0x6E, 0x28, 0x55, 0x00, 0xA0, 0x2A, 0x53, 0x00, +0x1A, 0x36, 0x80, 0xA0, 0x70, 0x38, 0x1F, 0x40, +0x30, 0x20, 0x35, 0x00, 0x40, 0x44, 0x21, 0x00, 0x00, 0x1E, /* Detailed Timing Descriptor */ -0x8C, 0x0A, 0xD0, 0xB4, 0x20, 0xE0, 0x14, 0x10, -0x12, 0x48, 0x3A, 0x00, 0xD8, 0xA2, 0x00, 0x00, +0x00, 0x19, 0x00, 0xA0, 0x50, 0xD0, 0x15, 0x20, +0x30, 0x20, 0x35, 0x00, 0x80, 0xD8, 0x10, 0x00, 0x00, 0x1E, /* Detailed Timing Descriptor */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, +0x41, 0x0A, 0xD0, 0xA0, 0x20, 0xE0, 0x13, 0x10, +0x30, 0x20, 0x3A, 0x00, 0xD8, 0x90, 0x00, 0x00, +0x00, 0x18, /* Detailed Timing Descriptor */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -259,9 +260,9 @@ const uint8_t adv7481_default_edid_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* DTD padding */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, /* Checksum */ -0xC6 +0x8C }; #define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data) @@ -410,7 +411,7 @@ static int adv7481_set_irq(struct adv7481_state *state) ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD)); ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_INT1_CONF_ADDR, - ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR, + ADV_REG_SETFIELD(AD_4_XTAL_PER, IO_INTRQ_DUR_SEL) | ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL)); ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, @@ -612,7 +613,8 @@ static void adv7481_irq_delay_work(struct work_struct *work) pr_debug("%s: dev: %d got datapath raw status: 0x%x\n", __func__, state->device_num, raw_status); - if (ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) && + if ((state->mode == ADV7481_IP_CVBS_1) && + ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) && ADV_REG_GETFIELD(raw_status, IO_INT_SD_RAW)) { uint8_t sdp_sts = 0; @@ -648,7 +650,7 @@ static void adv7481_irq_delay_work(struct work_struct *work) adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, SDP_RW_MAP_REG, 0x00); - } else { + } else if (state->mode == ADV7481_IP_HDMI) { if (ADV_REG_GETFIELD(int_status, IO_CP_LOCK_CP_ST) && ADV_REG_GETFIELD(raw_status, @@ -1211,10 +1213,12 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return ret; } -static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) +static int adv7481_get_sd_timings(struct adv7481_state *state, + int *sd_standard, struct adv7481_vid_params *vid_params) { int ret = 0; int sdp_stat, sdp_stat2; + int interlace_reg = 0; int timeout = 0; if (sd_standard == NULL) @@ -1231,6 +1235,25 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) sdp_stat2 = adv7481_rd_byte(&state->i2c_client, state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); } while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES)); + + interlace_reg = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_INTERLACE_STATE_ADDR); + + if (ADV_REG_GETFIELD(interlace_reg, SDP_RO_MAIN_INTERLACE_STATE)) + pr_debug("%s: Interlaced video detected\n", __func__); + else + pr_debug("%s: Interlaced video not detected\n", __func__); + + if (ADV_REG_GETFIELD(interlace_reg, SDP_RO_MAIN_FIELD_LEN)) + pr_debug("%s: Field length is correct\n", __func__); + else + pr_debug("%s: Field length is not correct\n", __func__); + + if (ADV_REG_GETFIELD(interlace_reg, SDP_RO_MAIN_SD_FIELD_RATE)) + pr_debug("%s: SD 50 Hz detected\n", __func__); + else + pr_debug("%s: SD 60 Hz detected\n", __func__); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, SDP_RW_MAP_REG, 0x00); @@ -1244,36 +1267,58 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) __func__, __LINE__, sdp_stat); return -EBUSY; } + vid_params->act_pix = 720; + vid_params->intrlcd = 1; switch (ADV_REG_GETFIELD(sdp_stat, SDP_RO_MAIN_AD_RESULT)) { case AD_NTSM_M_J: *sd_standard = V4L2_STD_NTSC; + pr_debug("%s, V4L2_STD_NTSC\n", __func__); + vid_params->act_lines = 507; break; case AD_NTSC_4_43: *sd_standard = V4L2_STD_NTSC_443; + pr_debug("%s, V4L2_STD_NTSC_443\n", __func__); + vid_params->act_lines = 507; break; case AD_PAL_M: *sd_standard = V4L2_STD_PAL_M; + pr_debug("%s, V4L2_STD_PAL_M\n", __func__); + vid_params->act_lines = 576; break; case AD_PAL_60: *sd_standard = V4L2_STD_PAL_60; + pr_debug("%s, V4L2_STD_PAL_60\n", __func__); + vid_params->act_lines = 576; break; case AD_PAL_B_G: *sd_standard = V4L2_STD_PAL; + pr_debug("%s, V4L2_STD_PAL\n", __func__); + vid_params->act_lines = 576; break; case AD_SECAM: *sd_standard = V4L2_STD_SECAM; + pr_debug("%s, V4L2_STD_SECAM\n", __func__); + vid_params->act_lines = 576; break; case AD_PAL_COMB_N: *sd_standard = V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + pr_debug("%s, V4L2_STD_PAL_Nc | V4L2_STD_PAL_N\n", __func__); + vid_params->act_lines = 576; break; case AD_SECAM_525: *sd_standard = V4L2_STD_SECAM; + pr_debug("%s, V4L2_STD_SECAM (AD_SECAM_525)\n", __func__); + vid_params->act_lines = 576; break; default: *sd_standard = V4L2_STD_UNKNOWN; + pr_debug("%s, V4L2_STD_UNKNOWN\n", __func__); + vid_params->act_lines = 507; break; } + pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d\n", + __func__, __LINE__, vid_params->act_pix, vid_params->act_lines); return ret; } @@ -1892,6 +1937,8 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, vid_params->pix_clk = hdmi_params->tmds_freq; + vid_params->act_lines = vid_params->act_lines * fieldfactor; + switch (hdmi_params->color_depth) { case CD_10BIT: vid_params->pix_clk = ((vid_params->pix_clk*4)/5); @@ -1992,6 +2039,9 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std) struct adv7481_state *state = to_state(sd); uint8_t tStatus = 0x0; uint32_t count = 0; + struct adv7481_vid_params vid_params; + + memset(&vid_params, 0, sizeof(vid_params)); pr_debug("Enter %s\n", __func__); /* Select SDP read-only main Map */ @@ -2032,7 +2082,7 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std) case ADV7481_IP_CVBS_6_HDMI_SIM: case ADV7481_IP_CVBS_7_HDMI_SIM: case ADV7481_IP_CVBS_8_HDMI_SIM: - ret = adv7481_get_sd_timings(state, &temp); + ret = adv7481_get_sd_timings(state, &temp, &vid_params); break; default: return -EINVAL; @@ -2062,6 +2112,7 @@ static int adv7481_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { int ret; + int sd_standard; struct adv7481_vid_params vid_params; struct adv7481_hdmi_params hdmi_params; struct adv7481_state *state = to_state(sd); @@ -2086,8 +2137,9 @@ static int adv7481_get_fmt(struct v4l2_subdev *sd, if (!ret) { fmt->width = vid_params.act_pix; fmt->height = vid_params.act_lines; + fmt->field = V4L2_FIELD_NONE; if (vid_params.intrlcd) - fmt->height /= 2; + fmt->field = V4L2_FIELD_INTERLACED; } else { pr_err("%s: Error %d in adv7481_get_hdmi_timings\n", __func__, ret); @@ -2096,8 +2148,14 @@ static int adv7481_get_fmt(struct v4l2_subdev *sd, case ADV7481_IP_CVBS_1: fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->width = 720; - fmt->height = 507; + ret = adv7481_get_sd_timings(state, &sd_standard, &vid_params); + if (!ret) { + fmt->width = vid_params.act_pix; + fmt->height = vid_params.act_lines; + fmt->field = V4L2_FIELD_INTERLACED; + } else { + pr_err("%s: Unable to get sd_timings\n", __func__); + } break; default: return -EINVAL; diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h index 3d484c6d335a..403e538b6127 100644 --- a/drivers/media/i2c/adv7481_reg.h +++ b/drivers/media/i2c/adv7481_reg.h @@ -416,6 +416,13 @@ #define SDP_RO_MAIN_LOST_LOCK_SHFT 1 #define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 #define SDP_RO_MAIN_IN_LOCK_SHFT 0 +#define SDP_RO_MAIN_INTERLACE_STATE_ADDR 0x13 +#define SDP_RO_MAIN_INTERLACE_STATE_BMSK 0x0040 +#define SDP_RO_MAIN_INTERLACE_STATE_SHFT 6 +#define SDP_RO_MAIN_FIELD_LEN_BMSK 0x0020 +#define SDP_RO_MAIN_FIELD_LEN_SHFT 5 +#define SDP_RO_MAIN_SD_FIELD_RATE_BMSK 0x0004 +#define SDP_RO_MAIN_SD_FIELD_RATE_SHFT 2 /* SDP R/O Map 1 Registers */ #define SDP_RO_MAP_1_FIELD_ADDR 0x45 diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index f384f295676e..679d122af63c 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -2124,6 +2124,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); if (dev->sd_cx25840) { + /* set host data for clk_freq configuration */ + v4l2_set_subdev_hostdata(dev->sd_cx25840, + &dev->clk_freq); + dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE; v4l2_subdev_call(dev->sd_cx25840, core, load_fw); } diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index e8f847226a19..6eb3be13b430 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -872,6 +872,16 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) if (cx23885_boards[dev->board].clk_freq > 0) dev->clk_freq = cx23885_boards[dev->board].clk_freq; + if (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE && + dev->pci->subsystem_device == 0x7137) { + /* Hauppauge ImpactVCBe device ID 0x7137 is populated + * with an 888, and a 25Mhz crystal, instead of the + * usual third overtone 50Mhz. The default clock rate must + * be overridden so the cx25840 is properly configured + */ + dev->clk_freq = 25000000; + } + dev->pci_bus = dev->pci->bus->number; dev->pci_slot = PCI_SLOT(dev->pci->devfn); cx23885_irq_add(dev, 0x001f00); diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 0042803a9de7..54398d8a4696 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -871,6 +871,10 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->nr = ++cx25821_devcount; sprintf(dev->name, "cx25821[%d]", dev->nr); + if (dev->nr >= ARRAY_SIZE(card)) { + CX25821_INFO("dev->nr >= %zd", ARRAY_SIZE(card)); + return -ENODEV; + } if (dev->pci->device != 0x8210) { pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", __func__, dev->pci->device); @@ -886,9 +890,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->channels[i].sram_channels = &cx25821_sram_channels[i]; } - if (dev->nr > 1) - CX25821_INFO("dev->nr > 1!"); - /* board config */ dev->board = 1; /* card[dev->nr]; */ dev->_max_num_decoders = MAX_DECODERS; diff --git a/drivers/media/platform/msm/ais/Makefile b/drivers/media/platform/msm/ais/Makefile index 4387b96f01d0..8c596dfcdcd8 100644 --- a/drivers/media/platform/msm/ais/Makefile +++ b/drivers/media/platform/msm/ais/Makefile @@ -22,4 +22,5 @@ obj-$(CONFIG_MSM_AIS_JPEG) += jpeg_10/ obj-$(CONFIG_MSM_AIS_JPEGDMA) += jpeg_dma/ obj-$(CONFIG_MSM_AIS) += msm_buf_mgr/ obj-$(CONFIG_MSM_AIS) += msm_ais_mgr/ +obj-$(CONFIG_MSM_AIS) += msm_ais_diag/ obj-$(CONFIG_MSM_AIS_FD) += fd/ diff --git a/drivers/media/platform/msm/ais/common/Makefile b/drivers/media/platform/msm/ais/common/Makefile index e1fa3f2ea848..1849d9c9af4c 100644 --- a/drivers/media/platform/msm/ais/common/Makefile +++ b/drivers/media/platform/msm/ais/common/Makefile @@ -1,2 +1,2 @@ ccflags-y += -Idrivers/media/platform/msm/ais/ -obj-$(CONFIG_MSM_AIS) += msm_camera_io_util.o cam_smmu_api.o cam_hw_ops.o cam_soc_api.o +obj-$(CONFIG_MSM_AIS) += msm_camera_io_util.o cam_smmu_api.o cam_hw_ops.o cam_soc_api.o msm_camera_diag_util.o diff --git a/drivers/media/platform/msm/ais/common/cam_hw_ops.c b/drivers/media/platform/msm/ais/common/cam_hw_ops.c index cf28e0ca6536..9110c88f9d8a 100644 --- a/drivers/media/platform/msm/ais/common/cam_hw_ops.c +++ b/drivers/media/platform/msm/ais/common/cam_hw_ops.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 @@ -19,6 +19,7 @@ #include <linux/pm_opp.h> #include <linux/regulator/rpm-smd-regulator.h> #include "cam_hw_ops.h" +#include "msm_camera_diag_util.h" #ifdef CONFIG_CAM_AHB_DBG #define CDBG(fmt, args...) pr_err(fmt, ##args) @@ -242,6 +243,8 @@ static int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id, data.ahb_clk_state = max; CDBG("dbg: state : %u, vector : %d\n", data.ahb_clk_state, max); + + msm_camera_diag_update_ahb_state(data.ahb_clk_state); } } else { pr_err("err: no bus vector found\n"); diff --git a/drivers/media/platform/msm/ais/common/cam_hw_ops.h b/drivers/media/platform/msm/ais/common/cam_hw_ops.h index 32f93f7b6e0e..e3e9f1381ad8 100644 --- a/drivers/media/platform/msm/ais/common/cam_hw_ops.h +++ b/drivers/media/platform/msm/ais/common/cam_hw_ops.h @@ -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 @@ -12,16 +12,7 @@ #ifndef _CAM_HW_OPS_H_ #define _CAM_HW_OPS_H_ -enum cam_ahb_clk_vote { - /* need to update the voting requests - * according to dtsi entries. - */ - CAM_AHB_SUSPEND_VOTE = 0x0, - CAM_AHB_SVS_VOTE = 0x01, - CAM_AHB_NOMINAL_VOTE = 0x02, - CAM_AHB_TURBO_VOTE = 0x03, - CAM_AHB_DYNAMIC_VOTE = 0xFF, -}; +#include <media/ais/msm_ais_mgr.h> enum cam_ahb_clk_client { CAM_AHB_CLIENT_CSIPHY, diff --git a/drivers/media/platform/msm/ais/common/cam_soc_api.c b/drivers/media/platform/msm/ais/common/cam_soc_api.c index 92f3e4007390..46738a1ca381 100644 --- a/drivers/media/platform/msm/ais/common/cam_soc_api.c +++ b/drivers/media/platform/msm/ais/common/cam_soc_api.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 @@ -25,6 +25,8 @@ #include <linux/of_platform.h> #include <linux/msm-bus.h> #include "cam_soc_api.h" +#include "msm_camera_diag_util.h" +#include <linux/clk/msm-clock-generic.h> struct msm_cam_bus_pscale_data { struct msm_bus_scale_pdata *pdata; @@ -361,6 +363,7 @@ int msm_camera_clk_enable(struct device *dev, clk_info[i].clk_name); goto cam_clk_set_err; } + clk_ptr[i]->flags |= CLKFLAG_NO_RATE_CACHE; rc = clk_set_rate(clk_ptr[i], clk_rate); if (rc < 0) { @@ -374,12 +377,15 @@ int msm_camera_clk_enable(struct device *dev, if (clk_rate == 0) { clk_rate = clk_round_rate(clk_ptr[i], 0); + + if (clk_rate < 0) { pr_err("%s round rate failed\n", clk_info[i].clk_name); goto cam_clk_set_err; } } + clk_ptr[i]->flags |= CLKFLAG_NO_RATE_CACHE; rc = clk_set_rate(clk_ptr[i], clk_rate); if (rc < 0) { @@ -410,6 +416,8 @@ int msm_camera_clk_enable(struct device *dev, } } } + + msm_camera_diag_update_clklist(clk_info, clk_ptr, num_clk, enable); return rc; cam_clk_enable_err: diff --git a/drivers/media/platform/msm/ais/common/msm_camera_diag_util.c b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.c new file mode 100644 index 000000000000..d4d417090210 --- /dev/null +++ b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.c @@ -0,0 +1,364 @@ +/* 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 "msm_camera_diag_util.h" +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/irqreturn.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <media/v4l2-subdev.h> +#include <linux/ratelimit.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include "msm_camera_io_util.h" +#include "msm.h" + + +#define MAX_CLK_NUM 100 +struct camera_diag_clk_list { + struct msm_ais_diag_clk_info_t *clk_infolist; + struct clk **ppclk; + uint32_t clk_num; + uint32_t clk_capacity; + struct mutex lock; +}; + +struct camera_diag_ddrbw { + struct msm_ais_diag_bus_info_t bus_info; + struct mutex lock; +}; + +static struct camera_diag_clk_list s_diag_clk_list; +static struct camera_diag_ddrbw s_ddrbw; + +int msm_camera_get_reg_list(void __iomem *base, + struct msm_camera_reg_list_cmd *reg_list) +{ + int rc = 0; + uint32_t i; + uint32_t *reg_values = NULL; + uint32_t addrs_size = sizeof(uint32_t) * reg_list->reg_num; + uint32_t *reg_addrs = kzalloc(addrs_size, GFP_KERNEL); + + if (!reg_addrs) { + rc = -ENOMEM; + goto alloc_addr_failed; + } + + if (copy_from_user(reg_addrs, + (void __user *)(reg_list->regaddr_list), + sizeof(uint32_t) * reg_list->reg_num)) { + rc = -EFAULT; + pr_err("%s copy_from_user fail\n", __func__); + goto copy_addr_failed; + } + + reg_values = kzalloc(addrs_size, GFP_KERNEL); + if (!reg_values) { + rc = -ENOMEM; + goto copy_addr_failed; + } + + for (i = 0 ; i < reg_list->reg_num; ++i) { + reg_values[i] = msm_camera_io_r(base + reg_addrs[i]); + pr_debug("reg 0x%x 0x%x\n", + reg_addrs[i], + reg_values[i]); + } + + if (copy_to_user(reg_list->value_list, reg_values, + sizeof(uint32_t) * reg_list->reg_num)) { + rc = -EFAULT; + pr_err("%s copy_to_user fail %u\n", + __func__, + reg_list->reg_num); + goto copy_value_failed; + } + +copy_value_failed: + kfree(reg_values); + +copy_addr_failed: + kfree(reg_addrs); + +alloc_addr_failed: + + return rc; +} + +int msm_camera_diag_init(void) +{ + s_diag_clk_list.clk_num = 0; + s_diag_clk_list.clk_capacity = MAX_CLK_NUM; + s_diag_clk_list.clk_infolist = kzalloc( + sizeof(struct msm_ais_diag_clk_info_t) * + s_diag_clk_list.clk_capacity, + GFP_KERNEL); + + if (!s_diag_clk_list.clk_infolist) + return -ENOMEM; + + s_diag_clk_list.ppclk = kzalloc(sizeof(struct clk *) * + s_diag_clk_list.clk_capacity, + GFP_KERNEL); + if (!s_diag_clk_list.ppclk) { + kfree(s_diag_clk_list.clk_infolist); + return -ENOMEM; + } + + mutex_init(&s_diag_clk_list.lock); + mutex_init(&s_ddrbw.lock); + return 0; +} + +int msm_camera_diag_uninit(void) +{ + mutex_destroy(&s_ddrbw.lock); + mutex_destroy(&s_diag_clk_list.lock); + + kfree(s_diag_clk_list.clk_infolist); + s_diag_clk_list.clk_infolist = NULL; + + kfree(s_diag_clk_list.ppclk); + s_diag_clk_list.ppclk = NULL; + + return 0; +} + +static uint32_t msm_camera_diag_find_clk_idx( + struct msm_cam_clk_info *clk_info, + struct clk *clk_ptr) +{ + uint32_t i = 0; + + for (; i < s_diag_clk_list.clk_num; ++i) { + if (clk_ptr == s_diag_clk_list.ppclk[i]) + return i; + } + + return s_diag_clk_list.clk_capacity; +} + +int msm_camera_diag_update_clklist( + struct msm_cam_clk_info *clk_info, + struct clk **clk_ptr, int num_clk, int enable) +{ + uint32_t i = 0; + uint32_t idx = 0; + uint32_t actual_idx = 0; + struct msm_ais_diag_clk_info_t *pclk_info = NULL; + + mutex_lock(&s_diag_clk_list.lock); + for (; i < num_clk; ++i) { + idx = msm_camera_diag_find_clk_idx(&clk_info[i], clk_ptr[i]); + if (idx < s_diag_clk_list.clk_num) { + actual_idx = idx; + pclk_info = + &s_diag_clk_list.clk_infolist[actual_idx]; + } else if (s_diag_clk_list.clk_num < + s_diag_clk_list.clk_capacity) { + actual_idx = s_diag_clk_list.clk_num++; + memset(&s_diag_clk_list.clk_infolist[actual_idx], + 0, + sizeof(struct msm_ais_diag_clk_info_t)); + pclk_info = + &s_diag_clk_list.clk_infolist[actual_idx]; + memcpy(pclk_info->clk_name, + clk_info[i].clk_name, + sizeof(pclk_info->clk_name)); + s_diag_clk_list.ppclk[actual_idx] = clk_ptr[i]; + pr_debug("%s new clk %s clk_num %u\n", + __func__, + clk_info[i].clk_name, + s_diag_clk_list.clk_num); + } else { + pr_err("%s too many clks\n", __func__); + continue; + } + + pclk_info->clk_rate = clk_get_rate(clk_ptr[i]); + if (enable) { + ++pclk_info->enable; + } else { + int cnt = pclk_info->enable; + + if (cnt > 0) + --pclk_info->enable; + } + } + + mutex_unlock(&s_diag_clk_list.lock); + return 0; +} + +int msm_camera_diag_get_clk_list( + struct msm_ais_diag_clk_list_t *clk_infolist) +{ + int rc = 0; + + mutex_lock(&s_diag_clk_list.lock); + clk_infolist->clk_num = s_diag_clk_list.clk_num; + if (copy_to_user(clk_infolist->clk_info, + s_diag_clk_list.clk_infolist, + sizeof(struct msm_ais_diag_clk_info_t) * + s_diag_clk_list.clk_num)) { + rc = -EFAULT; + } + mutex_unlock(&s_diag_clk_list.lock); + return rc; +} + +int msm_camera_diag_get_gpio_list( + struct msm_ais_diag_gpio_list_t *gpio_list) +{ + int rc = 0; + uint32_t gpio_num = gpio_list->gpio_num; + uint32_t i = 0; + int32_t *vals = NULL; + uint32_t idxs_size = sizeof(uint32_t) * gpio_num; + uint32_t vals_size = sizeof(int32_t) * gpio_num; + uint32_t *idxs = kzalloc(idxs_size, GFP_KERNEL); + + if (!idxs) { + rc = -ENOMEM; + goto alloc_idxs_failed; + } + + if (copy_from_user(idxs, + (void __user *)(gpio_list->gpio_idx_list), + idxs_size)) { + rc = -EFAULT; + pr_err("%s copy_from_user fail\n", __func__); + goto copy_idxs_failed; + } + + vals = kzalloc(vals_size, GFP_KERNEL); + if (!vals) { + rc = -ENOMEM; + goto copy_idxs_failed; + } + + for (; i < gpio_num; ++i) + vals[i] = + gpio_get_value(idxs[i]); + + if (copy_to_user(gpio_list->gpio_val_list, vals, + vals_size)) { + rc = -EFAULT; + pr_err("%s copy_to_user fail %u\n", + __func__, + gpio_num); + } + + kfree(vals); + +copy_idxs_failed: + kfree(idxs); + +alloc_idxs_failed: + return rc; +} + +int msm_camera_diag_set_gpio_list( + struct msm_ais_diag_gpio_list_t *gpio_list) +{ + int rc = 0; + uint32_t gpio_num = gpio_list->gpio_num; + uint32_t i = 0; + int32_t val; + int32_t *vals = NULL; + uint32_t idxs_size = sizeof(uint32_t) * gpio_num; + uint32_t vals_size = sizeof(int32_t) * gpio_num; + uint32_t *idxs = kzalloc(idxs_size, GFP_KERNEL); + + if (!idxs) { + rc = -ENOMEM; + goto alloc_idxs_failed; + } + + if (copy_from_user(idxs, + (void __user *)(gpio_list->gpio_idx_list), + idxs_size)) { + rc = -EFAULT; + pr_err("%s copy_from_user fail\n", __func__); + goto copy_idxs_failed; + } + + vals = kzalloc(vals_size, GFP_KERNEL); + if (!vals) { + rc = -ENOMEM; + goto copy_idxs_failed; + } + + if (copy_from_user(vals, + (void __user *)(gpio_list->gpio_val_list), + vals_size)) { + rc = -EFAULT; + pr_err("%s copy_from_user fail\n", __func__); + goto copy_vals_failed; + } + + for (; i < gpio_num; ++i) { + gpio_set_value(idxs[i], vals[i]); + val = gpio_get_value(idxs[i]); + pr_debug("val set %d after %d\n", vals[i], val); + } + +copy_vals_failed: + kfree(vals); + +copy_idxs_failed: + kfree(idxs); + +alloc_idxs_failed: + return rc; +} + +int msm_camera_diag_update_ahb_state(enum cam_ahb_clk_vote vote) +{ + mutex_lock(&s_ddrbw.lock); + s_ddrbw.bus_info.ahb_clk_vote_state = vote; + mutex_unlock(&s_ddrbw.lock); + return 0; +} + +int msm_camera_diag_update_isp_state( + uint32_t isp_bus_vector_idx, + uint64_t isp_ab, uint64_t isp_ib) +{ + mutex_lock(&s_ddrbw.lock); + s_ddrbw.bus_info.isp_bus_vector_idx = isp_bus_vector_idx; + s_ddrbw.bus_info.isp_ab = isp_ab; + s_ddrbw.bus_info.isp_ib = isp_ib; + mutex_unlock(&s_ddrbw.lock); + return 0; +} + +int msm_camera_diag_get_ddrbw(struct msm_ais_diag_bus_info_t *info) +{ + int rc = 0; + + mutex_lock(&s_ddrbw.lock); + info->ahb_clk_vote_state = s_ddrbw.bus_info.ahb_clk_vote_state; + info->isp_bus_vector_idx = s_ddrbw.bus_info.isp_bus_vector_idx; + info->isp_ab = s_ddrbw.bus_info.isp_ab; + info->isp_ib = s_ddrbw.bus_info.isp_ib; + + mutex_unlock(&s_ddrbw.lock); + return rc; +} + + diff --git a/drivers/media/platform/msm/ais/common/msm_camera_diag_util.h b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.h new file mode 100644 index 000000000000..1d4b09d726e6 --- /dev/null +++ b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.h @@ -0,0 +1,47 @@ +/* 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 __MSM_CAMERA_DIAG_UTIL_H +#define __MSM_CAMERA_DIAG_UTIL_H + +#include <media/ais/msm_ais_mgr.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/msm-bus.h> +#include <linux/mutex.h> +#include <linux/clk.h> +#include <linux/regulator/consumer.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <soc/qcom/ais.h> + +int msm_camera_get_reg_list(void __iomem *base, + struct msm_camera_reg_list_cmd *reg_list); +int msm_camera_diag_init(void); +int msm_camera_diag_uninit(void); + +int msm_camera_diag_update_clklist(struct msm_cam_clk_info *clk_info, + struct clk **clk_ptr, int num_clk, int enable); +int msm_camera_diag_get_clk_list( + struct msm_ais_diag_clk_list_t *clk_infolist); + +int msm_camera_diag_update_ahb_state(enum cam_ahb_clk_vote vote); +int msm_camera_diag_update_isp_state(uint32_t isp_bus_vector_idx, + uint64_t isp_ab, uint64_t isp_ib); +int msm_camera_diag_get_ddrbw(struct msm_ais_diag_bus_info_t *info); +int msm_camera_diag_get_gpio_list( + struct msm_ais_diag_gpio_list_t *gpio_list); +int msm_camera_diag_set_gpio_list( + struct msm_ais_diag_gpio_list_t *gpio_list); + +#endif diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c index a09237f3d5ef..c6c2e0d02b65 100644 --- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c +++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.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 @@ -19,6 +19,7 @@ #include <soc/qcom/ais.h> #include <linux/msm-bus.h> #include "msm_camera_io_util.h" +#include "msm_camera_diag_util.h" #define BUFF_SIZE_128 128 @@ -365,6 +366,8 @@ int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info, } } } + + msm_camera_diag_update_clklist(clk_info, clk_ptr, num_clk, enable); return rc; diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c index 585865b12387..c23fddf6e52f 100644 --- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, 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 @@ -1144,6 +1144,43 @@ static void msm_isp_release_all_bufq( } } +static int msm_isp_get_bufq_state(struct msm_isp_buf_mgr *buf_mgr, + struct msm_vfe_bufq_state *bufq_state) +{ + int rc = 0; + struct msm_isp_bufq *bufq = NULL; + uint32_t i = 0; + int32_t *k_bufq_states = NULL; + uint32_t size = 0; + + bufq = msm_isp_get_bufq(buf_mgr, bufq_state->handle); + if (bufq) { + bufq_state->nbufs = bufq->num_bufs; + size = bufq->num_bufs*sizeof(int32_t); + k_bufq_states = kzalloc(size, GFP_KERNEL); + if (!k_bufq_states) { + rc = -ENOMEM; + goto alloc_states_failed; + } + + for (i = 0; i < bufq_state->nbufs; ++i) + k_bufq_states[i] = bufq->bufs[i].state; + + if (copy_to_user(bufq_state->buf_state, + k_bufq_states, + sizeof(int32_t) * bufq->num_bufs)) { + rc = -EFAULT; + pr_err("%s copy_to_user fail\n", __func__); + goto copy_failed; + } + +copy_failed: + kfree(k_bufq_states); + } + +alloc_states_failed: + return rc; +} /** * msm_isp_buf_put_scratch() - Release scratch buffers @@ -1357,6 +1394,14 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr, rc = buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd); break; } + case VIDIOC_MSM_ISP_CMD_EXT: { + struct msm_vfe_cmd_ext *cmd_ext = (struct msm_vfe_cmd_ext *)arg; + + if (cmd_ext->type == VFE_GET_BUFQ_STATE) + rc = buf_mgr->ops->get_bufq_state(buf_mgr, + &cmd_ext->data.bufq_state); + break; + } } return rc; } @@ -1507,6 +1552,7 @@ static struct msm_isp_buf_ops isp_buf_ops = { .buf_mgr_debug = msm_isp_buf_mgr_debug, .get_bufq = msm_isp_get_bufq, .update_put_buf_cnt = msm_isp_update_put_buf_cnt, + .get_bufq_state = msm_isp_get_bufq_state, }; int msm_isp_create_isp_buf_mgr( diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h index 4794771d3213..d9a3661306e3 100644 --- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h +++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, 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 @@ -44,16 +44,6 @@ enum msm_isp_buffer_src_t { MSM_ISP_BUFFER_SRC_MAX, }; -enum msm_isp_buffer_state { - MSM_ISP_BUFFER_STATE_UNUSED, /* not used */ - MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */ - MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */ - MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */ - MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */ - MSM_ISP_BUFFER_STATE_DIVERTED, /* Sent to other hardware*/ - MSM_ISP_BUFFER_STATE_DISPATCHED, /* Sent to HAL*/ -}; - enum msm_isp_buffer_put_state { MSM_ISP_BUFFER_STATE_PUT_PREPARED, /* on init */ MSM_ISP_BUFFER_STATE_PUT_BUF, /* on rotation */ @@ -182,6 +172,9 @@ struct msm_isp_buf_ops { int (*update_put_buf_cnt)(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, uint32_t bufq_handle, int32_t buf_index, struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit); + + int (*get_bufq_state)(struct msm_isp_buf_mgr *buf_mgr, + struct msm_vfe_bufq_state *bufq_state); }; struct msm_isp_buf_mgr { diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c index 52bf8121f32b..1ddcab3ed331 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp47.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp47.c @@ -22,6 +22,7 @@ #include "cam_hw_ops.h" #include "msm_isp47.h" #include "cam_soc_api.h" +#include "msm_camera_diag_util.h" #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) @@ -2396,6 +2397,8 @@ int msm_vfe47_update_bandwidth( ab, ib, isp_bandwidth_mgr->client_info, sched_clock()); + msm_camera_diag_update_isp_state( + isp_bandwidth_mgr->bus_vector_active_idx, ab, ib); return 0; } 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 77f2ab5e7c3d..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 @@ -1956,7 +1956,8 @@ static void msm_isp_handle_done_buf_frame_id_mismatch( static int msm_isp_process_done_buf(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf, - struct timeval *time_stamp, uint32_t frame_id) + struct timeval *time_stamp, struct timeval *time_stamp_system, + uint32_t frame_id) { int rc; unsigned long flags; @@ -2037,7 +2038,13 @@ static int msm_isp_process_done_buf(struct vfe_device *vfe_dev, } buf_event.frame_id = frame_id; + /* timestamp stores monotonic time */ buf_event.timestamp = *time_stamp; + /* for buf_event, mono_timestamp is unused attribute + * reuse this to store system time and propagate to + * userspace + */ + buf_event.mono_timestamp = *time_stamp_system; buf_event.u.buf_done.session_id = stream_info->session_id; buf_event.u.buf_done.stream_id = stream_info->stream_id; buf_event.u.buf_done.handle = buf->bufq_handle; @@ -2831,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__, @@ -3579,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]. @@ -3619,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) { @@ -3934,6 +3968,7 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev, struct msm_isp_buffer *done_buf = NULL; unsigned long flags; struct timeval *time_stamp; + struct timeval *time_stamp_system; uint32_t frame_id, buf_index = -1; struct msm_vfe_axi_stream *temp_stream; @@ -3947,6 +3982,8 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev, time_stamp = &ts->vt_time; } else { time_stamp = &ts->buf_time; + /* store system time */ + time_stamp_system = &ts->event_time; } frame_id = vfe_dev->axi_data. @@ -4089,7 +4126,7 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev, } msm_isp_process_done_buf(vfe_dev, stream_info, - done_buf, time_stamp, frame_id); + done_buf, time_stamp, time_stamp_system, frame_id); } void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, 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 2ba19b13535b..a9b6e5e6a861 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_util.c @@ -847,6 +847,24 @@ static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg) } #endif /* CONFIG_COMPAT */ +static int process_isp_cmd_ext(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + struct msm_vfe_cmd_ext *cmd = (struct msm_vfe_cmd_ext *)arg; + + switch (cmd->type) { + case VFE_GET_BUFQ_STATE: { + mutex_lock(&vfe_dev->buf_mgr->lock); + rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, + VIDIOC_MSM_ISP_CMD_EXT, arg); + mutex_unlock(&vfe_dev->buf_mgr->lock); + break; + } + } + + return rc; +} + static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { @@ -983,12 +1001,14 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, mutex_unlock(&vfe_dev->core_mutex); break; case VIDIOC_MSM_ISP_FETCH_ENG_START: - case VIDIOC_MSM_ISP_MAP_BUF_START_FE: mutex_lock(&vfe_dev->core_mutex); rc = msm_isp_start_fetch_engine(vfe_dev, arg); mutex_unlock(&vfe_dev->core_mutex); break; + case VIDIOC_MSM_ISP_CMD_EXT: + process_isp_cmd_ext(vfe_dev, arg); + break; case VIDIOC_MSM_ISP_FETCH_ENG_MULTI_PASS_START: case VIDIOC_MSM_ISP_MAP_BUF_START_MULTI_PASS_FE: mutex_lock(&vfe_dev->core_mutex); @@ -1053,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; @@ -2350,16 +2375,6 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&vfe_dev->realtime_mutex); mutex_lock(&vfe_dev->core_mutex); - /* Enable vfe clks to wake up from XO shutdown mode */ - if (vfe_dev->pdev->id == 0) - id = CAM_AHB_CLIENT_VFE0; - else - id = CAM_AHB_CLIENT_VFE1; - if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE) < 0) - pr_err("%s: failed to vote for AHB\n", __func__); - vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 1); - vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 1); - if (!vfe_dev->vfe_open_cnt) { pr_err("%s invalid state open cnt %d\n", __func__, vfe_dev->vfe_open_cnt); @@ -2374,6 +2389,17 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_unlock(&vfe_dev->realtime_mutex); return 0; } + + /* Enable vfe clks to wake up from XO shutdown mode */ + if (vfe_dev->pdev->id == 0) + id = CAM_AHB_CLIENT_VFE0; + else + id = CAM_AHB_CLIENT_VFE1; + if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE) < 0) + pr_err("%s: failed to vote for AHB\n", __func__); + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 1); + vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 1); + /* Unregister page fault handler */ cam_smmu_reg_client_page_fault_handler( vfe_dev->buf_mgr->iommu_hdl, diff --git a/drivers/media/platform/msm/ais/ispif/msm_ispif.c b/drivers/media/platform/msm/ais/ispif/msm_ispif.c index a72ac566bb8c..5ddf554d6ef3 100644 --- a/drivers/media/platform/msm/ais/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/ais/ispif/msm_ispif.c @@ -29,6 +29,7 @@ #include "msm_camera_io_util.h" #include "cam_hw_ops.h" #include "cam_soc_api.h" +#include "msm_camera_diag_util.h" #ifdef CONFIG_AIS_MSM_ISPIF_V1 #include "msm_ispif_hwreg_v1.h" @@ -1526,6 +1527,22 @@ static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg) case ISPIF_SET_VFE_INFO: rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info); break; + case ISPIF_READ_REG_LIST_CMD: + { + struct msm_camera_reg_list_cmd reg_list_cmd; + + if (copy_from_user(®_list_cmd, + (void __user *)pcdata->reg_list, + sizeof(struct msm_camera_reg_list_cmd))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + rc = msm_camera_get_reg_list(ispif->base, ®_list_cmd); + break; + } + case ISPIF_WRITE_REG_LIST_CMD: + break; default: pr_err("%s: invalid cfg_type\n", __func__); rc = -EINVAL; diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c index 902e05b3329b..c3f3542cc87a 100644 --- a/drivers/media/platform/msm/ais/msm.c +++ b/drivers/media/platform/msm/ais/msm.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 @@ -31,6 +31,7 @@ #include "msm_sd.h" #include "cam_hw_ops.h" #include <media/ais/msm_ais_buf_mgr.h> +#include "msm_camera_diag_util.h" static struct v4l2_device *msm_v4l2_dev; @@ -1384,6 +1385,12 @@ static int msm_probe(struct platform_device *pdev) goto v4l2_fail; } + rc = msm_camera_diag_init(); + if (rc < 0) { + pr_err("%s: failed to init diag clk list\n", __func__); + goto v4l2_fail; + } + goto probe_end; v4l2_fail: @@ -1428,6 +1435,7 @@ static int __init msm_init(void) static void __exit msm_exit(void) { + msm_camera_diag_uninit(); platform_driver_unregister(&msm_driver); } diff --git a/drivers/media/platform/msm/ais/msm_ais_diag/Makefile b/drivers/media/platform/msm/ais/msm_ais_diag/Makefile new file mode 100644 index 000000000000..7c40ea02b70a --- /dev/null +++ b/drivers/media/platform/msm/ais/msm_ais_diag/Makefile @@ -0,0 +1,4 @@ +ccflags-y += -Idrivers/media/platform/msm/ais +ccflags-y += -Idrivers/media/platform/msm/ais/common +ccflags-y += -Idrivers/media/platform/msm/ais/sensor/io +obj-$(CONFIG_MSM_AIS) += msm_diag_cam.o diff --git a/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c new file mode 100644 index 000000000000..c2933d79babc --- /dev/null +++ b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c @@ -0,0 +1,267 @@ +/* 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 <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/regulator/consumer.h> +#include "msm_sd.h" +#include "msm_diag_cam.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" +#include "cam_hw_ops.h" +#include "msm_camera_diag_util.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +#undef DIAG_CAM_DBG +#ifdef MSM_DIAG_CAM_DEBUG +#define DIAG_CAM_DBG(fmt, args...) pr_err(fmt, ##args) +#else +#define DIAG_CAM_DBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +#define MSM_DIAG_CAM_DRV_NAME "msm_diag_cam" +static struct platform_driver msm_diag_camera_driver; +static struct diag_cam_device *new_diag_cam_dev; + +int msm_ais_enable_allclocks(void) +{ + int rc = 0; + + CDBG("%s:\n", __func__); + /* Vote ON for clocks */ + if (new_diag_cam_dev == NULL) { + rc = -EINVAL; + pr_err("%s: clock structure uninitialised %d\n", __func__, + rc); + return rc; + } + + rc = msm_camera_enable_vreg(&new_diag_cam_dev->pdev->dev, + new_diag_cam_dev->diag_cam_vreg, + new_diag_cam_dev->regulator_count, + NULL, + 0, + &new_diag_cam_dev->diag_cam_reg_ptr[0], 1); + if (rc < 0) + pr_err("%s:%d diag_cam enable_vreg failed\n", __func__, + __LINE__); + + rc = msm_camera_clk_enable(&new_diag_cam_dev->pdev->dev, + new_diag_cam_dev->diag_cam_clk_info, + new_diag_cam_dev->diag_cam_clk, + new_diag_cam_dev->num_clk, true); + + if (rc < 0) { + pr_err("%s: clk enable failed %d\n", __func__, rc); + rc = 0; + return rc; + } + pr_debug("Turned ON camera clocks\n"); + return 0; + +} + +int msm_ais_disable_allclocks(void) +{ + int rc = 0; + + CDBG("%s:\n", __func__); + /* Vote OFF for clocks */ + if (new_diag_cam_dev == NULL) { + rc = -EINVAL; + pr_err("%s: clock structure uninitialised %d\n", __func__, + rc); + return rc; + } + + if ((new_diag_cam_dev->pdev == NULL) || + (new_diag_cam_dev->diag_cam_clk_info == NULL) || + (new_diag_cam_dev->diag_cam_clk == NULL) || + (new_diag_cam_dev->num_clk == 0)) { + rc = -EINVAL; + pr_err("%s: Clock details uninitialised %d\n", __func__, + rc); + return rc; + } + + rc = msm_camera_clk_enable(&new_diag_cam_dev->pdev->dev, + new_diag_cam_dev->diag_cam_clk_info, + new_diag_cam_dev->diag_cam_clk, + new_diag_cam_dev->num_clk, false); + if (rc < 0) { + pr_err("%s: clk disable failed %d\n", __func__, rc); + return rc; + } + + rc = msm_camera_enable_vreg(&new_diag_cam_dev->pdev->dev, + new_diag_cam_dev->diag_cam_vreg, + new_diag_cam_dev->regulator_count, + NULL, + 0, + &new_diag_cam_dev->diag_cam_reg_ptr[0], 0); + if (rc < 0) + pr_err("%s:%d diag_cam disable_vreg failed\n", __func__, + __LINE__); + + pr_debug("Turned OFF camera clocks\n"); + return 0; +} + +int msm_diag_camera_get_vreginfo_list( + struct msm_ais_diag_regulator_info_list_t *p_vreglist) +{ + int rc = 0; + uint32_t i = 0; + uint32_t len = 0; + uint32_t len1 = 0; + struct regulator *vreg = NULL; + char *vreg_name_inuser = NULL; + + p_vreglist->regulator_num = new_diag_cam_dev->regulator_count; + + pr_debug("ais diag regulator_count %u\n", + new_diag_cam_dev->regulator_count); + + for (; i < p_vreglist->regulator_num ; ++i) { + vreg = new_diag_cam_dev->diag_cam_reg_ptr[i]; + p_vreglist->infolist[i].enable = + regulator_is_enabled(vreg); + len = strlen(new_diag_cam_dev->diag_cam_vreg[i].reg_name); + len1 = sizeof(p_vreglist->infolist[i].regulatorname); + len = (len >= len1) ? len1 : (len+1); + vreg_name_inuser = + p_vreglist->infolist[i].regulatorname; + if (copy_to_user((void __user *)vreg_name_inuser, + (void *)new_diag_cam_dev->diag_cam_vreg[i].reg_name, + len)) { + rc = -EFAULT; + pr_err("%s copy_to_user fail\n", __func__); + break; + } + } + + pr_debug("msm_diag_camera_get_vreginfo_list exit\n"); + return rc; +} + +static int msm_diag_cam_probe(struct platform_device *pdev) +{ + int rc = 0; + + CDBG("%s: pdev %pK device id = %d\n", __func__, pdev, pdev->id); + + new_diag_cam_dev = kzalloc(sizeof(struct diag_cam_device), + GFP_KERNEL); + if (!new_diag_cam_dev) + return -ENOMEM; + + if (pdev->dev.of_node) + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + + rc = msm_camera_get_clk_info(pdev, + &new_diag_cam_dev->diag_cam_clk_info, + &new_diag_cam_dev->diag_cam_clk, + &new_diag_cam_dev->num_clk); + if (rc < 0) { + pr_err("%s: msm_diag_cam_get_clk_info() failed", __func__); + kfree(new_diag_cam_dev); + return -EFAULT; + } + + new_diag_cam_dev->ref_count = 0; + new_diag_cam_dev->pdev = pdev; + + rc = msm_camera_get_dt_vreg_data( + new_diag_cam_dev->pdev->dev.of_node, + &(new_diag_cam_dev->diag_cam_vreg), + &(new_diag_cam_dev->regulator_count)); + if (rc < 0) { + pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__); + rc = -EFAULT; + goto diag_cam_release_mem; + } + + if ((new_diag_cam_dev->regulator_count < 0) || + (new_diag_cam_dev->regulator_count > MAX_REGULATOR)) { + pr_err("%s: invalid reg count = %d, max is %d\n", __func__, + new_diag_cam_dev->regulator_count, MAX_REGULATOR); + rc = -EFAULT; + goto diag_cam_invalid_vreg_data; + } + + rc = msm_camera_config_vreg(&new_diag_cam_dev->pdev->dev, + new_diag_cam_dev->diag_cam_vreg, + new_diag_cam_dev->regulator_count, + NULL, + 0, + &new_diag_cam_dev->diag_cam_reg_ptr[0], 1); + if (rc < 0) + pr_err("%s:%d diag_cam config_vreg failed\n", __func__, + __LINE__); + + platform_set_drvdata(pdev, new_diag_cam_dev); + + return 0; + +diag_cam_invalid_vreg_data: + kfree(new_diag_cam_dev->diag_cam_vreg); +diag_cam_release_mem: + kfree(new_diag_cam_dev); + new_diag_cam_dev = NULL; + return rc; +} + +static int msm_diag_cam_exit(struct platform_device *pdev) +{ + return 0; +} + +static int __init msm_diag_cam_init_module(void) +{ + return platform_driver_register(&msm_diag_camera_driver); +} + +static void __exit msm_diag_cam_exit_module(void) +{ + kfree(new_diag_cam_dev); + platform_driver_unregister(&msm_diag_camera_driver); +} + +static const struct of_device_id msm_diag_camera_match_table[] = { + { .compatible = "qcom,diag-cam" }, + {}, +}; + +static struct platform_driver msm_diag_camera_driver = { + .probe = msm_diag_cam_probe, + .remove = msm_diag_cam_exit, + .driver = { + .name = MSM_DIAG_CAM_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_diag_camera_match_table, + }, +}; + +MODULE_DEVICE_TABLE(of, msm_diag_camera_match_table); + +module_init(msm_diag_cam_init_module); +module_exit(msm_diag_cam_exit_module); +MODULE_DESCRIPTION("MSM diag camera driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h new file mode 100644 index 000000000000..572ba8dfba3a --- /dev/null +++ b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h @@ -0,0 +1,57 @@ +/* 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 MSM_DIAG_CAM_H +#define MSM_DIAG_CAM_H + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <media/v4l2-subdev.h> +#include <linux/workqueue.h> +#include <media/ais/msm_ais_sensor.h> +#include <soc/qcom/ais.h> +#include <media/ais/msm_ais.h> +#include "msm_sd.h" +#include "cam_soc_api.h" + +#define NUM_MASTERS 2 +#define NUM_QUEUES 2 + +#define TRUE 1 +#define FALSE 0 + + +enum msm_diag_cam_state_t { + AIS_DIAG_STATE_DISABLED, + AIS_DIAG_STATE_ENABLED, +}; + +struct diag_cam_device { + struct platform_device *pdev; + uint8_t ref_count; + enum msm_diag_cam_state_t diag_cam_state; + size_t num_clk; + size_t num_clk_cases; + struct clk **diag_cam_clk; + uint32_t **diag_cam_clk_rates; + struct msm_cam_clk_info *diag_cam_clk_info; + struct camera_vreg_t *diag_cam_vreg; + struct regulator *diag_cam_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; +}; + +int msm_ais_enable_allclocks(void); +int msm_ais_disable_allclocks(void); +int msm_diag_camera_get_vreginfo_list( + struct msm_ais_diag_regulator_info_list_t *p_vreglist); +#endif diff --git a/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile b/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile index b7a078738489..bb14aec1ee29 100644 --- a/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile +++ b/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile @@ -2,4 +2,5 @@ ccflags-y += -Idrivers/media/platform/msm/ais ccflags-y += -Idrivers/media/platform/msm/ais/common ccflags-y += -Idrivers/media/platform/msm/ais/sensor/io ccflags-y += -Idrivers/media/platform/msm/ais/sensor/cci +ccflags-y += -Idrivers/media/platform/msm/ais/msm_ais_diag obj-$(CONFIG_MSM_AIS) += msm_ais_mgr.o diff --git a/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c b/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c index 9391c1d0d4ab..4ae07932f5da 100644 --- a/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c +++ b/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c @@ -15,6 +15,8 @@ #include <media/ais/msm_ais_mgr.h> #include "msm_ais_mngr.h" #include "msm_early_cam.h" +#include "msm_camera_diag_util.h" +#include "msm_diag_cam.h" #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) @@ -42,7 +44,56 @@ static long msm_ais_hndl_ioctl(struct v4l2_subdev *sd, void *arg) case AIS_CLK_DISABLE: rc = msm_ais_disable_clocks(); break; + case AIS_CLK_ENABLE_ALLCLK: + rc = msm_ais_enable_allclocks(); + break; + case AIS_CLK_DISABLE_ALLCLK: + rc = msm_ais_disable_allclocks(); + break; + default: + pr_err("invalid cfg_type\n"); + rc = -EINVAL; + } + + if (rc) + pr_err("msm_ais_hndl_ioctl failed %ld\n", rc); + + mutex_unlock(&clk_mngr_dev->cont_mutex); + return rc; +} + +static long msm_ais_hndl_ext_ioctl(struct v4l2_subdev *sd, void *arg) +{ + long rc = 0; + struct clk_mgr_cfg_data_ext *pcdata = + (struct clk_mgr_cfg_data_ext *)arg; + struct msm_ais_mngr_device *clk_mngr_dev = + (struct msm_ais_mngr_device *)v4l2_get_subdevdata(sd); + if (WARN_ON(!clk_mngr_dev) || WARN_ON(!pcdata)) { + rc = -EINVAL; + return rc; + } + + mutex_lock(&clk_mngr_dev->cont_mutex); + CDBG(pr_fmt("cfg_type = %d\n"), pcdata->cfg_type); + switch (pcdata->cfg_type) { + case AIS_DIAG_GET_REGULATOR_INFO_LIST: + rc = msm_diag_camera_get_vreginfo_list( + &pcdata->data.vreg_infolist); + break; + case AIS_DIAG_GET_BUS_INFO_STATE: + rc = msm_camera_diag_get_ddrbw(&pcdata->data.bus_info); + break; + case AIS_DIAG_GET_CLK_INFO_LIST: + rc = msm_camera_diag_get_clk_list(&pcdata->data.clk_infolist); + break; + case AIS_DIAG_GET_GPIO_LIST: + rc = msm_camera_diag_get_gpio_list(&pcdata->data.gpio_list); + break; + case AIS_DIAG_SET_GPIO_LIST: + rc = msm_camera_diag_set_gpio_list(&pcdata->data.gpio_list); + break; default: pr_err("invalid cfg_type\n"); rc = -EINVAL; @@ -63,6 +114,11 @@ static long msm_ais_mngr_subdev_ioctl(struct v4l2_subdev *sd, if (rc) pr_err("msm_ais_mngr_subdev_ioctl failed\n"); break; + case VIDIOC_MSM_AIS_CLK_CFG_EXT: + rc = msm_ais_hndl_ext_ioctl(sd, arg); + if (rc) + pr_err("msm_ais_hndl_ext_ioctl failed\n"); + break; default: rc = -ENOIOCTLCMD; } @@ -136,6 +192,7 @@ static int32_t __init msm_ais_mngr_init(void) static void __exit msm_ais_mngr_exit(void) { + msm_sd_unregister(&msm_ais_mngr_dev->subdev); mutex_destroy(&msm_ais_mngr_dev->cont_mutex); kfree(msm_ais_mngr_dev); diff --git a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c index 6d26dff7525d..b820aa45136a 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c @@ -36,6 +36,7 @@ #include "include/msm_csid_3_6_0_hwreg.h" #include "include/msm_csid_3_5_1_hwreg.h" #include "cam_hw_ops.h" +#include "msm_camera_diag_util.h" #define V4L2_IDENT_CSID 50002 #define CSID_VERSION_V20 0x02000011 @@ -870,6 +871,20 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void *arg) case CSID_STOP: rc = msm_csid_stop(csid_dev, cdata->cfg.csid_cidmask); break; + case CSID_READ_REG_LIST_CMD: + { + struct msm_camera_reg_list_cmd reg_list_cmd; + + if (copy_from_user(®_list_cmd, + (void __user *)cdata->cfg.csid_reg_list_cmd, + sizeof(struct msm_camera_reg_list_cmd))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + rc = msm_camera_get_reg_list(csid_dev->base, ®_list_cmd); + break; + } default: pr_err("%s: %d failed\n", __func__, __LINE__); rc = -ENOIOCTLCMD; diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c index c3b087f61888..ebf817149184 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.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 @@ -27,6 +27,7 @@ #include "include/msm_csiphy_3_4_2_1_hwreg.h" #include "include/msm_csiphy_3_5_hwreg.h" #include "cam_hw_ops.h" +#include "msm_camera_diag_util.h" #define DBG_CSIPHY 0 #define SOF_DEBUG_ENABLE 1 @@ -1264,6 +1265,20 @@ static int32_t msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg) } break; + case CSIPHY_READ_REG_LIST_CMD: + { + struct msm_camera_reg_list_cmd reg_list_cmd; + + if (copy_from_user(®_list_cmd, + (void __user *)cdata->cfg.csiphy_reg_list_cmd, + sizeof(struct msm_camera_reg_list_cmd))) { + pr_err("%s: %d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + rc = msm_camera_get_reg_list(csiphy_dev->base, ®_list_cmd); + break; + } default: pr_err("%s: %d failed\n", __func__, __LINE__); rc = -ENOIOCTLCMD; diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.c b/drivers/media/platform/msm/ais/sensor/msm_sensor.c index 0dda3a64b1a2..657c6ee7c0b6 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.c @@ -842,7 +842,8 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, if (s_ctrl->is_csid_tg_mode) goto DONE; - if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) { + if ((s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) && + (s_ctrl->sensor_state != MSM_SENSOR_CCI_DOWN)) { pr_err("%s:%d failed: invalid state %d\n", __func__, __LINE__, s_ctrl->sensor_state); rc = -EFAULT; @@ -1242,7 +1243,12 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp) pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); break; } - read_config_ptr->data = local_data; + if (copy_to_user((void __user *)&read_config_ptr->data, + &local_data, sizeof(local_data))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } break; } case CFG_SLAVE_WRITE_I2C_ARRAY: { @@ -1398,7 +1404,8 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp) if (s_ctrl->is_csid_tg_mode) goto DONE; - if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) { + if ((s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) && + (s_ctrl->sensor_state != MSM_SENSOR_CCI_DOWN)) { pr_err("%s:%d failed: invalid state %d\n", __func__, __LINE__, s_ctrl->sensor_state); rc = -EFAULT; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 6db3c3c527a3..925a89601636 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -236,6 +236,7 @@ static int msm_isp_validate_axi_request(struct vfe_device *vfe_dev, case V4L2_PIX_FMT_META: case V4L2_PIX_FMT_META10: case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y10: stream_info->num_planes = 1; stream_info->format_factor = ISP_Q2; break; @@ -345,6 +346,7 @@ static uint32_t msm_isp_axi_get_plane_size( case V4L2_PIX_FMT_QGRBG10: case V4L2_PIX_FMT_QRGGB10: case V4L2_PIX_FMT_META10: + case V4L2_PIX_FMT_Y10: /* TODO: fix me */ size = plane_cfg[plane_idx].output_height * plane_cfg[plane_idx].output_width; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index a2381557070d..6640e414a798 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -1150,8 +1150,10 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, goto debug_read_error; } - if (dbg->off % sizeof(u32)) - return -EFAULT; + if (dbg->off % sizeof(u32)) { + rc = -EFAULT; + goto debug_read_error; + } ptr = dbg->base + dbg->off; tot = 0; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 3af6e53b21e7..ee3cfb88855c 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -2614,6 +2614,7 @@ int msm_comm_check_core_init(struct msm_vidc_core *core) int rc = 0; struct hfi_device *hdev; struct msm_vidc_inst *inst = NULL; + int dref = 0; mutex_lock(&core->lock); if (core->state >= VIDC_CORE_INIT_DONE) { @@ -2637,11 +2638,16 @@ int msm_comm_check_core_init(struct msm_vidc_core *core) * Just grab one of the inst from instances list and * use it. */ - inst = list_first_entry(&core->instances, + inst = list_first_entry_or_null(&core->instances, struct msm_vidc_inst, list); + if (inst) + dref = kref_get_unless_zero(&inst->kref); mutex_unlock(&core->lock); - msm_comm_print_debug_info(inst); + if (dref) { + msm_comm_print_debug_info(inst); + put_inst(inst); + } mutex_lock(&core->lock); BUG_ON(msm_vidc_debug_timeout); diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 537b858cb94a..fa6af4a7dae1 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1268,16 +1268,17 @@ static void __camif_subdev_try_format(struct camif_dev *camif, { const struct s3c_camif_variant *variant = camif->variant; const struct vp_pix_limits *pix_lim; - int i = ARRAY_SIZE(camif_mbus_formats); + unsigned int i; /* FIXME: constraints against codec or preview path ? */ pix_lim = &variant->vp_pix_limits[VP_CODEC]; - while (i-- >= 0) + for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++) if (camif_mbus_formats[i] == mf->code) break; - mf->code = camif_mbus_formats[i]; + if (i == ARRAY_SIZE(camif_mbus_formats)) + mf->code = camif_mbus_formats[0]; if (pad == CAMIF_SD_PAD_SINK) { v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 76bf8ba372b3..5b53e31ce262 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -187,7 +187,7 @@ USB 2.0 spec says bulk packet size is always 512 bytes */ #define EM28XX_BULK_PACKET_MULTIPLIER 384 -#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 94 #define EM28XX_INTERLACED_DEFAULT 1 diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index 02b5f69e1a42..14cf6dfc3b14 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -2698,6 +2698,8 @@ mptctl_hp_targetinfo(unsigned long arg) __FILE__, __LINE__, iocnum); return -ENODEV; } + if (karg.hdr.id >= MPT_MAX_FC_DEVICES) + return -EINVAL; dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n", ioc->name)); 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/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 1e688bfec567..fe90b7e04427 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -576,15 +576,9 @@ static void vmballoon_pop(struct vmballoon *b) } } - if (b->batch_page) { - vunmap(b->batch_page); - b->batch_page = NULL; - } - - if (b->page) { - __free_page(b->page); - b->page = NULL; - } + /* Clearing the batch_page unconditionally has no adverse effect */ + free_page((unsigned long)b->batch_page); + b->batch_page = NULL; } /* @@ -991,16 +985,13 @@ static const struct vmballoon_ops vmballoon_batched_ops = { static bool vmballoon_init_batching(struct vmballoon *b) { - b->page = alloc_page(VMW_PAGE_ALLOC_NOSLEEP); - if (!b->page) - return false; + struct page *page; - b->batch_page = vmap(&b->page, 1, VM_MAP, PAGE_KERNEL); - if (!b->batch_page) { - __free_page(b->page); + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!page) return false; - } + b->batch_page = page_address(page); return true; } 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-iproc.c b/drivers/mmc/host/sdhci-iproc.c index f280744578e4..ffd448149796 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -32,6 +32,8 @@ struct sdhci_iproc_host { const struct sdhci_iproc_data *data; u32 shadow_cmd; u32 shadow_blk; + bool is_cmd_shadowed; + bool is_blk_shadowed; }; #define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18) @@ -47,8 +49,22 @@ static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg) static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg) { - u32 val = sdhci_iproc_readl(host, (reg & ~3)); - u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host); + u32 val; + u16 word; + + if ((reg == SDHCI_TRANSFER_MODE) && iproc_host->is_cmd_shadowed) { + /* Get the saved transfer mode */ + val = iproc_host->shadow_cmd; + } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) && + iproc_host->is_blk_shadowed) { + /* Get the saved block info */ + val = iproc_host->shadow_blk; + } else { + val = sdhci_iproc_readl(host, (reg & ~3)); + } + word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff; return word; } @@ -104,13 +120,15 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg) if (reg == SDHCI_COMMAND) { /* Write the block now as we are issuing a command */ - if (iproc_host->shadow_blk != 0) { + if (iproc_host->is_blk_shadowed) { sdhci_iproc_writel(host, iproc_host->shadow_blk, SDHCI_BLOCK_SIZE); - iproc_host->shadow_blk = 0; + iproc_host->is_blk_shadowed = false; } oldval = iproc_host->shadow_cmd; - } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { + iproc_host->is_cmd_shadowed = false; + } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) && + iproc_host->is_blk_shadowed) { /* Block size and count are stored in shadow reg */ oldval = iproc_host->shadow_blk; } else { @@ -122,9 +140,11 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg) if (reg == SDHCI_TRANSFER_MODE) { /* Save the transfer mode until the command is issued */ iproc_host->shadow_cmd = newval; + iproc_host->is_cmd_shadowed = true; } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { /* Save the block info until the command is issued */ iproc_host->shadow_blk = newval; + iproc_host->is_blk_shadowed = true; } else { /* Command or other regular 32-bit write */ sdhci_iproc_writel(host, newval, reg & ~3); diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index aea00ce708b6..81a781c1f9d6 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -39,6 +39,7 @@ #include <linux/msm-bus.h> #include <linux/pm_runtime.h> #include <trace/events/mmc.h> +#include <soc/qcom/boot_stats.h> #include "sdhci-msm.h" #include "sdhci-msm-ice.h" @@ -801,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: @@ -3166,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 @@ -4250,6 +4258,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) void __iomem *tlmm_mem; unsigned long flags; bool force_probe; + char boot_marker[40]; pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__); msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host), @@ -4274,6 +4283,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto out_host_free; } + snprintf(boot_marker, sizeof(boot_marker), + "M - DRIVER %s Init", mmc_hostname(host->mmc)); + place_marker(boot_marker); + pltfm_host = sdhci_priv(host); pltfm_host->priv = msm_host; msm_host->mmc = host->mmc; @@ -4747,6 +4760,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (sdhci_msm_is_bootdevice(&pdev->dev)) mmc_flush_detect_work(host->mmc); + snprintf(boot_marker, sizeof(boot_marker), + "M - DRIVER %s Ready", mmc_hostname(host->mmc)); + place_marker(boot_marker); + /* Successful initialization */ goto out; diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index a5e4b4b93d1b..ec3766264408 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -531,7 +531,8 @@ static void bgmac_dma_tx_ring_free(struct bgmac *bgmac, int i; for (i = 0; i < BGMAC_TX_RING_SLOTS; i++) { - int len = dma_desc[i].ctl1 & BGMAC_DESC_CTL1_LEN; + u32 ctl1 = le32_to_cpu(dma_desc[i].ctl1); + unsigned int len = ctl1 & BGMAC_DESC_CTL1_LEN; slot = &ring->slots[i]; dev_kfree_skb(slot->skb); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index d946bba43726..87534c6efd66 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -594,7 +594,7 @@ static void bnx2x_ets_e3b0_nig_disabled(const struct link_params *params, * slots for the highest priority. */ REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS : - NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100); + NIG_REG_P0_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100); /* Mapping between the CREDIT_WEIGHT registers and actual client * numbers */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a38a9cb3d544..9904d768a20a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2925,6 +2925,9 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags) struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; struct hwrm_vnic_tpa_cfg_input req = {0}; + if (vnic->fw_vnic_id == INVALID_HW_RING_ID) + return 0; + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_TPA_CFG, -1, -1); if (tpa_flags) { diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index b36643ef0593..029fa5bee520 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1726,6 +1726,8 @@ static int enic_open(struct net_device *netdev) } for (i = 0; i < enic->rq_count; i++) { + /* enable rq before updating rq desc */ + vnic_rq_enable(&enic->rq[i]); vnic_rq_fill(&enic->rq[i], enic_rq_alloc_buf); /* Need at least one buffer on ring to get going */ if (vnic_rq_desc_used(&enic->rq[i]) == 0) { @@ -1737,8 +1739,6 @@ static int enic_open(struct net_device *netdev) for (i = 0; i < enic->wq_count; i++) vnic_wq_enable(&enic->wq[i]); - for (i = 0; i < enic->rq_count; i++) - vnic_rq_enable(&enic->rq[i]); if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) enic_dev_add_station_addr(enic); @@ -1765,8 +1765,12 @@ static int enic_open(struct net_device *netdev) return 0; err_out_free_rq: - for (i = 0; i < enic->rq_count; i++) + for (i = 0; i < enic->rq_count; i++) { + err = vnic_rq_disable(&enic->rq[i]); + if (err) + return err; vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); + } enic_dev_notify_unset(enic); err_out_free_intr: enic_unset_affinity_hint(enic); @@ -2539,11 +2543,11 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); /* Query PCI controller on system for DMA addressing - * limitation for the device. Try 64-bit first, and + * limitation for the device. Try 47-bit first, and * fail to 32-bit. */ - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(47)); if (err) { err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { @@ -2557,10 +2561,10 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_release_regions; } } else { - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(47)); if (err) { dev_err(dev, "Unable to obtain %u-bit DMA " - "for consistent allocations, aborting\n", 64); + "for consistent allocations, aborting\n", 47); goto err_out_release_regions; } using_dac = 1; diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 901661149b44..2d61369f586f 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -3053,9 +3053,6 @@ static void gfar_process_frame(struct net_device *ndev, struct sk_buff *skb) if (ndev->features & NETIF_F_RXCSUM) gfar_rx_checksum(skb, fcb); - /* Tell the skb what kind of packet this is */ - skb->protocol = eth_type_trans(skb, ndev); - /* There's need to check for NETIF_F_HW_VLAN_CTAG_RX here. * Even if vlan rx accel is disabled, on some chips * RXFCB_VLN is pseudo randomly set. @@ -3126,13 +3123,15 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) continue; } + gfar_process_frame(ndev, skb); + /* Increment the number of packets */ total_pkts++; total_bytes += skb->len; skb_record_rx_queue(skb, rx_queue->qindex); - gfar_process_frame(ndev, skb); + skb->protocol = eth_type_trans(skb, ndev); /* Send the packet up the stack */ napi_gro_receive(&rx_queue->grp->napi_rx, skb); diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 1908a38e7f31..485b9cc53f8b 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1574,7 +1574,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) * we have already determined whether we have link or not. */ if (!mac->autoneg) - return -E1000_ERR_CONFIG; + return 1; /* Auto-Neg is enabled. Auto Speed Detection takes care * of MAC speed/duplex configuration. So we only need to diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c index 645ace74429e..fe133f33a6c6 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.c +++ b/drivers/net/ethernet/intel/e1000e/mac.c @@ -450,7 +450,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) * we have already determined whether we have link or not. */ if (!mac->autoneg) - return -E1000_ERR_CONFIG; + return 1; /* Auto-Neg is enabled. Auto Speed Detection takes care * of MAC speed/duplex configuration. So we only need to diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 20d8806d2bff..6369d88b81c1 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -2330,8 +2330,8 @@ static int e1000_alloc_ring_dma(struct e1000_adapter *adapter, { struct pci_dev *pdev = adapter->pdev; - ring->desc = dma_alloc_coherent(&pdev->dev, ring->size, &ring->dma, - GFP_KERNEL); + ring->desc = dma_zalloc_coherent(&pdev->dev, ring->size, &ring->dma, + GFP_KERNEL); if (!ring->desc) return -ENOMEM; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 7430dd44019e..ea693bbf56d8 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -818,6 +818,7 @@ static void mvneta_port_up(struct mvneta_port *pp) } mvreg_write(pp, MVNETA_TXQ_CMD, q_map); + q_map = 0; /* Enable all initialized RXQs. */ mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def)); } diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 62f1a3433a62..d6d87dd8a28f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -386,11 +386,11 @@ struct mlx4_qp *mlx4_qp_lookup(struct mlx4_dev *dev, u32 qpn) struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; struct mlx4_qp *qp; - spin_lock(&qp_table->lock); + spin_lock_irq(&qp_table->lock); qp = __mlx4_qp_lookup(dev, qpn); - spin_unlock(&qp_table->lock); + spin_unlock_irq(&qp_table->lock); return qp; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 6c66d2979795..16bd585365a8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1623,7 +1623,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) cmd->checksum_disabled = 1; cmd->max_reg_cmds = (1 << cmd->log_sz) - 1; - cmd->bitmask = (1 << cmd->max_reg_cmds) - 1; + cmd->bitmask = (1UL << cmd->max_reg_cmds) - 1; cmd->cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16; if (cmd->cmdif_rev > CMD_IF_REV) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 7ccdb46c6764..21e0af2620ee 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -43,7 +43,7 @@ #define ILT_CFG_REG(cli, reg) PSWRQ2_REG_ ## cli ## _ ## reg ## _RT_OFFSET /* ILT entry structure */ -#define ILT_ENTRY_PHY_ADDR_MASK 0x000FFFFFFFFFFFULL +#define ILT_ENTRY_PHY_ADDR_MASK (~0ULL >> 12) #define ILT_ENTRY_PHY_ADDR_SHIFT 0 #define ILT_ENTRY_VALID_MASK 0x1ULL #define ILT_ENTRY_VALID_SHIFT 52 diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index cc106d892e29..b15e322b8bfe 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1787,7 +1787,7 @@ static struct vnet *vnet_new(const u64 *local_mac, dev->ethtool_ops = &vnet_ethtool_ops; dev->watchdog_timeo = VNET_TX_TIMEOUT; - dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | + dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_ALL_TSO | NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 4e3d2e7c697c..e8c3a8c32534 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -518,7 +518,9 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, mtt = irda_get_mtt(skb); pr_debug("%s(%ld), mtt=%d\n", __func__ , jiffies, mtt); - if (mtt) + if (mtt > 1000) + mdelay(mtt/1000); + else if (mtt) udelay(mtt); /* Enable DMA interrupt */ diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c index 49bbc6826883..9a7dca2bb618 100644 --- a/drivers/net/phy/bcm-cygnus.c +++ b/drivers/net/phy/bcm-cygnus.c @@ -61,17 +61,17 @@ static int bcm_cygnus_afe_config(struct phy_device *phydev) return rc; /* make rcal=100, since rdb default is 000 */ - rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB1, 0x10); + rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10); if (rc < 0) return rc; /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ - rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x10); + rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10); if (rc < 0) return rc; /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ - rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x00); + rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00); return 0; } diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index b2091c88b44d..ce16b26d49ff 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -14,11 +14,18 @@ #ifndef _LINUX_BCM_PHY_LIB_H #define _LINUX_BCM_PHY_LIB_H +#include <linux/brcmphy.h> #include <linux/phy.h> int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val); int bcm_phy_read_exp(struct phy_device *phydev, u16 reg); +static inline int bcm_phy_write_exp_sel(struct phy_device *phydev, + u16 reg, u16 val) +{ + return bcm_phy_write_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER, val); +} + int bcm_phy_write_misc(struct phy_device *phydev, u16 reg, u16 chl, u16 value); int bcm_phy_read_misc(struct phy_device *phydev, diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 03d4809a9126..bffa70e46202 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -48,10 +48,10 @@ static void r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ - bcm_phy_write_exp(phydev, 0x00b0, 0x0010); + bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); /* Disable Reset R_AL/RC_CAL Engine */ - bcm_phy_write_exp(phydev, 0x00b0, 0x0000); + bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); } static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index e83acc608678..dc934347ae28 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -1203,6 +1203,23 @@ static void dp83640_remove(struct phy_device *phydev) kfree(dp83640); } +static int dp83640_soft_reset(struct phy_device *phydev) +{ + int ret; + + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + /* From DP83640 datasheet: "Software driver code must wait 3 us + * following a software reset before allowing further serial MII + * operations with the DP83640." + */ + udelay(10); /* Taking udelay inaccuracy into account */ + + return 0; +} + static int dp83640_config_init(struct phy_device *phydev) { struct dp83640_private *dp83640 = phydev->priv; @@ -1496,6 +1513,7 @@ static struct phy_driver dp83640_driver = { .flags = PHY_HAS_INTERRUPT, .probe = dp83640_probe, .remove = dp83640_remove, + .soft_reset = dp83640_soft_reset, .config_init = dp83640_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index e74709e4b5dd..49174837c2ba 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -983,7 +983,8 @@ static void team_port_disable(struct team *team, static void ___team_compute_features(struct team *team) { struct team_port *port; - u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL; + netdev_features_t vlan_features = TEAM_VLAN_FEATURES & + NETIF_F_ALL_FOR_ALL; unsigned short max_hard_header_len = ETH_HLEN; unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 96a5028621c8..8edbccf06b7b 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -593,7 +593,7 @@ static const struct driver_info cdc_mbim_info_zlp = { */ static const struct driver_info cdc_mbim_info_ndp_to_end = { .description = "CDC MBIM", - .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, + .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP, .bind = cdc_mbim_bind, .unbind = cdc_mbim_unbind, .manage_power = cdc_mbim_manage_power, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 8aaa09b3c753..d72205f06a1d 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -637,6 +637,9 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x0846, 0x68a2, 8)}, {QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */ {QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */ + {QMI_FIXED_INTF(0x1435, 0xd181, 3)}, /* Wistron NeWeb D18Q1 */ + {QMI_FIXED_INTF(0x1435, 0xd181, 4)}, /* Wistron NeWeb D18Q1 */ + {QMI_FIXED_INTF(0x1435, 0xd181, 5)}, /* Wistron NeWeb D18Q1 */ {QMI_FIXED_INTF(0x16d8, 0x6003, 0)}, /* CMOTech 6003 */ {QMI_FIXED_INTF(0x16d8, 0x6007, 0)}, /* CMOTech CHE-628S */ {QMI_FIXED_INTF(0x16d8, 0x6008, 0)}, /* CMOTech CMU-301 */ @@ -713,6 +716,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */ {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */ {QMI_FIXED_INTF(0x2001, 0x7e35, 4)}, /* D-Link DWM-222 */ + {QMI_FIXED_INTF(0x2020, 0x2033, 4)}, /* BroadMobi BM806U */ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */ @@ -762,6 +766,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ + {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index b2c1a435357f..2991d7155540 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1610,7 +1610,7 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) tx_data += len; agg->skb_len += len; - agg->skb_num++; + agg->skb_num += skb_shinfo(skb)->gso_segs ?: 1; dev_kfree_skb_any(skb); diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index c5f375befd2f..7337e6c0e126 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -945,10 +945,11 @@ static int smsc75xx_set_features(struct net_device *netdev, /* it's racing here! */ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); - if (ret < 0) + if (ret < 0) { netdev_warn(dev->net, "Error writing RFE_CTL\n"); - - return ret; + return ret; + } + return 0; } static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index d01285250204..2759d386ade7 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1912,8 +1912,8 @@ static int virtnet_probe(struct virtio_device *vdev) /* Assume link up if device can't report link status, otherwise get link status from config. */ + netif_carrier_off(dev); if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { - netif_carrier_off(dev); schedule_work(&vi->config_work); } else { vi->status = VIRTIO_NET_S_LINK_UP; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f02c1b148545..187b60c8a672 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7060,10 +7060,20 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k_vif *arvif = (void *)vif->drv_priv; + struct ath10k_peer *peer; u32 bw, smps; spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr); + if (!peer) { + spin_unlock_bh(&ar->data_lock); + ath10k_warn(ar, "mac sta rc update failed to find peer %pM on vdev %i\n", + sta->addr, arvif->vdev_id); + return; + } + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", sta->addr, changed, sta->bandwidth, sta->rx_nss, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 83e5aa6a9f28..ad35e760ed3f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -6167,7 +6167,7 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, req->alpha2[0], req->alpha2[1]); /* ignore non-ISO3166 country codes */ - for (i = 0; i < sizeof(req->alpha2); i++) + for (i = 0; i < 2; i++) if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { brcmf_err("not a ISO3166 code\n"); return; diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index 03219cf1693a..545a1515d7fe 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -2717,10 +2717,14 @@ err_pcie_link_up: cnss_configure_wlan_en_gpio(WLAN_EN_LOW); cnss_wlan_vreg_set(vreg_info, VREG_OFF); if (penv->pdev) { - pr_err("%d: Unregistering pci device\n", __LINE__); - pci_unregister_driver(&cnss_wlan_pci_driver); - penv->pdev = NULL; - penv->pci_register_again = true; + if (wdrv && wdrv->update_status) + wdrv->update_status(penv->pdev, CNSS_SSR_FAIL); + if (!penv->recovery_in_progress) { + pr_err("%d: Unregistering pci device\n", __LINE__); + pci_unregister_driver(&cnss_wlan_pci_driver); + penv->pdev = NULL; + penv->pci_register_again = true; + } } err_wlan_vreg_on: diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index fc35b0892768..5183f3de7c9b 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1890,7 +1890,7 @@ static int cnss_init_dump_entry(struct cnss_plat_data *plat_priv) return msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); } -static int cnss_qca6174_register_ramdump(struct cnss_plat_data *plat_priv) +static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv) { int ret = 0; struct device *dev; @@ -1941,7 +1941,7 @@ out: return ret; } -static void cnss_qca6174_unregister_ramdump(struct cnss_plat_data *plat_priv) +static void cnss_unregister_ramdump_v1(struct cnss_plat_data *plat_priv) { struct device *dev; struct cnss_ramdump_info *ramdump_info; @@ -1958,7 +1958,7 @@ static void cnss_qca6174_unregister_ramdump(struct cnss_plat_data *plat_priv) ramdump_info->ramdump_pa); } -static int cnss_qca6290_register_ramdump(struct cnss_plat_data *plat_priv) +static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv) { int ret = 0; struct cnss_subsys_info *subsys_info; @@ -2014,7 +2014,7 @@ free_ramdump: return ret; } -static void cnss_qca6290_unregister_ramdump(struct cnss_plat_data *plat_priv) +static void cnss_unregister_ramdump_v2(struct cnss_plat_data *plat_priv) { struct cnss_ramdump_info_v2 *info_v2; @@ -2034,11 +2034,11 @@ int cnss_register_ramdump(struct cnss_plat_data *plat_priv) switch (plat_priv->device_id) { case QCA6174_DEVICE_ID: - ret = cnss_qca6174_register_ramdump(plat_priv); + ret = cnss_register_ramdump_v1(plat_priv); break; case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - ret = cnss_qca6290_register_ramdump(plat_priv); + ret = cnss_register_ramdump_v2(plat_priv); break; default: cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); @@ -2052,11 +2052,11 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv) { switch (plat_priv->device_id) { case QCA6174_DEVICE_ID: - cnss_qca6174_unregister_ramdump(plat_priv); + cnss_unregister_ramdump_v1(plat_priv); break; case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - cnss_qca6290_unregister_ramdump(plat_priv); + cnss_unregister_ramdump_v2(plat_priv); break; default: cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 5746366ff852..2356caa3af78 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -133,7 +133,8 @@ int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv) pci_disable_device(pci_priv->pci_dev); if (pci_priv->pci_dev->device != QCA6174_DEVICE_ID) { - if (pci_set_power_state(pci_priv->pci_dev, PCI_D3hot)) + ret = pci_set_power_state(pci_priv->pci_dev, PCI_D3hot); + if (ret) cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); } @@ -404,10 +405,12 @@ static int cnss_pci_suspend(struct device *dev) SAVE_PCI_CONFIG_SPACE); pci_disable_device(pci_dev); - ret = pci_set_power_state(pci_dev, PCI_D3hot); - if (ret) - cnss_pr_err("Failed to set D3Hot, err = %d\n", - ret); + if (pci_dev->device != QCA6174_DEVICE_ID) { + ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", + ret); + } } cnss_pci_set_monitor_wake_intr(pci_priv, false); @@ -643,9 +646,12 @@ int cnss_auto_suspend(struct device *dev) cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); pci_disable_device(pci_dev); - ret = pci_set_power_state(pci_dev, PCI_D3hot); - if (ret) - cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); + if (pci_dev->device != QCA6174_DEVICE_ID) { + ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", + ret); + } cnss_pr_dbg("Suspending PCI link\n"); if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) { diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8a9164da6c50..e8b770a95f7a 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2925,8 +2925,10 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) { u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]); - if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) + if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) { + kfree(hwname); return -EINVAL; + } param.regd = hwsim_world_regdom_custom[idx]; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c index 5624ade92cc0..c2a156a8acec 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c @@ -304,9 +304,6 @@ static void _rtl92c_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw, writeVal = 0x00000000; if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1) writeVal = writeVal - 0x06060606; - else if (rtlpriv->dm.dynamic_txhighpower_lvl == - TXHIGHPWRLEVEL_BT2) - writeVal = writeVal; *(p_outwriteval + rf) = writeVal; } } diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index fee4c01fbdfd..a0de2453fa09 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -342,6 +342,9 @@ static int xennet_open(struct net_device *dev) unsigned int i = 0; struct netfront_queue *queue = NULL; + if (!np->queues) + return -ENODEV; + for (i = 0; i < num_queues; ++i) { queue = &np->queues[i]; napi_enable(&queue->napi); @@ -1363,18 +1366,8 @@ static int netfront_probe(struct xenbus_device *dev, #ifdef CONFIG_SYSFS info->netdev->sysfs_groups[0] = &xennet_dev_group; #endif - err = register_netdev(info->netdev); - if (err) { - pr_warn("%s: register_netdev err=%d\n", __func__, err); - goto fail; - } return 0; - - fail: - xennet_free_netdev(netdev); - dev_set_drvdata(&dev->dev, NULL); - return err; } static void xennet_end_access(int ref, void *page) @@ -1743,8 +1736,6 @@ static void xennet_destroy_queues(struct netfront_info *info) { unsigned int i; - rtnl_lock(); - for (i = 0; i < info->netdev->real_num_tx_queues; i++) { struct netfront_queue *queue = &info->queues[i]; @@ -1753,8 +1744,6 @@ static void xennet_destroy_queues(struct netfront_info *info) netif_napi_del(&queue->napi); } - rtnl_unlock(); - kfree(info->queues); info->queues = NULL; } @@ -1770,8 +1759,6 @@ static int xennet_create_queues(struct netfront_info *info, if (!info->queues) return -ENOMEM; - rtnl_lock(); - for (i = 0; i < *num_queues; i++) { struct netfront_queue *queue = &info->queues[i]; @@ -1780,7 +1767,7 @@ static int xennet_create_queues(struct netfront_info *info, ret = xennet_init_queue(queue); if (ret < 0) { - dev_warn(&info->netdev->dev, + dev_warn(&info->xbdev->dev, "only created %d queues\n", i); *num_queues = i; break; @@ -1794,10 +1781,8 @@ static int xennet_create_queues(struct netfront_info *info, netif_set_real_num_tx_queues(info->netdev, *num_queues); - rtnl_unlock(); - if (*num_queues == 0) { - dev_err(&info->netdev->dev, "no queues\n"); + dev_err(&info->xbdev->dev, "no queues\n"); return -EINVAL; } return 0; @@ -1839,6 +1824,7 @@ static int talk_to_netback(struct xenbus_device *dev, goto out; } + rtnl_lock(); if (info->queues) xennet_destroy_queues(info); @@ -1849,6 +1835,7 @@ static int talk_to_netback(struct xenbus_device *dev, info->queues = NULL; goto out; } + rtnl_unlock(); /* Create shared ring, alloc event channel -- for each queue */ for (i = 0; i < num_queues; ++i) { @@ -1945,8 +1932,10 @@ abort_transaction_no_dev_fatal: xenbus_transaction_end(xbt, 1); destroy_ring: xennet_disconnect_backend(info); + rtnl_lock(); xennet_destroy_queues(info); out: + rtnl_unlock(); device_unregister(&dev->dev); return err; } @@ -1982,6 +1971,15 @@ static int xennet_connect(struct net_device *dev) netdev_update_features(dev); rtnl_unlock(); + if (dev->reg_state == NETREG_UNINITIALIZED) { + err = register_netdev(dev); + if (err) { + pr_warn("%s: register_netdev err=%d\n", __func__, err); + device_unregister(&np->xbdev->dev); + return err; + } + } + /* * All public and private state should now be sane. Get * ready to start sending and receiving packets and give the driver @@ -2172,10 +2170,14 @@ static int xennet_remove(struct xenbus_device *dev) xennet_disconnect_backend(info); - unregister_netdev(info->netdev); + if (info->netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(info->netdev); - if (info->queues) + if (info->queues) { + rtnl_lock(); xennet_destroy_queues(info); + rtnl_unlock(); + } xennet_free_netdev(info->netdev); return 0; diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 3bbdf60f8908..49f3fba75f4d 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -955,6 +955,9 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, mw_base = nt->mw_vec[mw_num].phys_addr; mw_size = nt->mw_vec[mw_num].phys_size; + if (max_mw_size && mw_size > max_mw_size) + mw_size = max_mw_size; + tx_size = (unsigned int)mw_size / num_qps_mw; qp_offset = tx_size * (qp_num / mw_count); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 1c8aedf21370..e86fcc9e9852 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1583,7 +1583,7 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid) nvmeq->cq_vector = qid - 1; result = adapter_alloc_cq(dev, qid, nvmeq); if (result < 0) - return result; + goto release_vector; result = adapter_alloc_sq(dev, qid, nvmeq); if (result < 0) @@ -1597,9 +1597,12 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid) return result; release_sq: + dev->online_queues--; adapter_delete_sq(dev, qid); release_cq: adapter_delete_cq(dev, qid); + release_vector: + nvmeq->cq_vector = -1; return result; } diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 312cb5b74dec..1d288fa4f4d6 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -1365,9 +1365,27 @@ lba_hw_init(struct lba_device *d) WRITE_REG32(stat, d->hba.base_addr + LBA_ERROR_CONFIG); } - /* Set HF mode as the default (vs. -1 mode). */ + + /* + * Hard Fail vs. Soft Fail on PCI "Master Abort". + * + * "Master Abort" means the MMIO transaction timed out - usually due to + * the device not responding to an MMIO read. We would like HF to be + * enabled to find driver problems, though it means the system will + * crash with a HPMC. + * + * In SoftFail mode "~0L" is returned as a result of a timeout on the + * pci bus. This is like how PCI busses on x86 and most other + * architectures behave. In order to increase compatibility with + * existing (x86) PCI hardware and existing Linux drivers we enable + * Soft Faul mode on PA-RISC now too. + */ stat = READ_REG32(d->hba.base_addr + LBA_STAT_CTL); +#if defined(ENABLE_HARDFAIL) WRITE_REG32(stat | HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL); +#else + WRITE_REG32(stat & ~HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL); +#endif /* ** Writing a zero to STAT_CTL.rf (bit 0) will clear reset signal diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 32bd8ab79d53..dd9ebdc968c8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1140,11 +1140,14 @@ static int pci_pm_runtime_suspend(struct device *dev) int error; /* - * If pci_dev->driver is not set (unbound), the device should - * always remain in D0 regardless of the runtime PM status + * If pci_dev->driver is not set (unbound), we leave the device in D0, + * but it may go to D3cold when the bridge above it runtime suspends. + * Save its config space in case that happens. */ - if (!pci_dev->driver) + if (!pci_dev->driver) { + pci_save_state(pci_dev); return 0; + } if (!pm || !pm->runtime_suspend) return -ENOSYS; @@ -1195,16 +1198,18 @@ static int pci_pm_runtime_resume(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; /* - * If pci_dev->driver is not set (unbound), the device should - * always remain in D0 regardless of the runtime PM status + * Restoring config space is necessary even if the device is not bound + * to a driver because although we left it in D0, it may have gone to + * D3cold when the bridge above it runtime suspended. */ + pci_restore_standard_config(pci_dev); + if (!pci_dev->driver) return 0; if (!pm || !pm->runtime_resume) return -ENOSYS; - pci_restore_standard_config(pci_dev); pci_fixup_device(pci_fixup_resume_early, pci_dev); __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4eb1cf0ed00c..5697b32819cb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3614,6 +3614,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9120, quirk_dma_func1_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123, quirk_dma_func1_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9128, + quirk_dma_func1_alias); /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130, quirk_dma_func1_alias); @@ -3626,6 +3628,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a, /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0, quirk_dma_func1_alias); +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c127 */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220, + quirk_dma_func1_alias); /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230, quirk_dma_func1_alias); diff --git a/drivers/platform/goldfish/goldfish_pipe_v2.c b/drivers/platform/goldfish/goldfish_pipe_v2.c index 90bac4b055a3..f0b9b46047be 100644 --- a/drivers/platform/goldfish/goldfish_pipe_v2.c +++ b/drivers/platform/goldfish/goldfish_pipe_v2.c @@ -951,7 +951,8 @@ static int goldfish_pipe_dma_alloc_locked(struct goldfish_pipe *pipe) dma->dma_size, &dma->phys_begin, GFP_KERNEL); - return -ENOMEM; + if (!dma->dma_vaddr) + return -ENOMEM; dma->phys_end = dma->phys_begin + dma->dma_size; pipe->dev->dma_alloc_total += dma->dma_size; @@ -996,7 +997,6 @@ static int goldfish_dma_mmap_locked( dma->phys_begin >> PAGE_SHIFT, sz_requested, vma->vm_page_prot); - if (status < 0) { dev_err(pdev_dev, "Cannot remap pfn range....\n"); return -EAGAIN; @@ -1025,7 +1025,6 @@ static int goldfish_dma_mmap(struct file *filp, struct vm_area_struct *vma) status = goldfish_dma_mmap_locked(pipe, vma); mutex_unlock(&pipe->lock); return status; - } static int goldfish_pipe_dma_create_region( @@ -1154,6 +1153,15 @@ static struct miscdevice goldfish_pipe_miscdev = { .fops = &goldfish_pipe_fops, }; + +static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth) +{ + const unsigned long paddr = __pa(addr); + + writel(paddr >> 32, porth); + writel((u32)paddr, portl); +} + static int goldfish_pipe_device_init_v2(struct platform_device *pdev) { struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; @@ -1197,14 +1205,14 @@ static int goldfish_pipe_device_init_v2(struct platform_device *pdev) dev->buffers = (struct goldfish_pipe_dev_buffers *)page; /* Send the buffer addresses to the host */ - gf_write_ptr(&dev->buffers->signalled_pipe_buffers, + write_pa_addr(&dev->buffers->signalled_pipe_buffers, dev->base + PIPE_REG_SIGNAL_BUFFER, dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); writel((u32)MAX_SIGNALLED_PIPES, dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); - gf_write_ptr(&dev->buffers->open_command_params, + write_pa_addr(&dev->buffers->open_command_params, dev->base + PIPE_REG_OPEN_BUFFER, dev->base + PIPE_REG_OPEN_BUFFER_HIGH); diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 333d28e64087..92773ff33188 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -133,16 +133,6 @@ config IPA_UT The user interface to run and control the tests is debugfs file system. -config SSM - tristate "QTI Secure Service Module" - depends on QSEECOM - depends on MSM_SMD - help - Provides an interface for OEM driver to communicate with Trustzone - and modem for key exchange and mode change. - This driver uses Secure Channel Manager interface for trustzone - communication and communicates with modem over SMD channel. - config GPIO_USB_DETECT tristate "GPIO-based USB VBUS Detection" depends on POWER_SUPPLY diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index d985aa81a3bb..f2a0d7e1ba1c 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_EP_PCIE) += ep_pcie/ obj-$(CONFIG_GPIO_USB_DETECT) += gpio-usbdetect.o obj-$(CONFIG_MSM_11AD) += msm_11ad/ obj-$(CONFIG_SEEMP_CORE) += seemp_core/ -obj-$(CONFIG_SSM) += ssm.o obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/ obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o diff --git a/drivers/platform/msm/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/platform/msm/ssm.c b/drivers/platform/msm/ssm.c deleted file mode 100644 index 6a2909b8c5f4..000000000000 --- a/drivers/platform/msm/ssm.c +++ /dev/null @@ -1,516 +0,0 @@ -/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -/* - * QTI Secure Service Module(SSM) driver - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/of.h> -#include <linux/cdev.h> -#include <linux/uaccess.h> -#include <linux/mutex.h> -#include <linux/ion.h> -#include <linux/types.h> -#include <linux/elf.h> -#include <linux/platform_device.h> -#include <linux/msm_ion.h> -#include <linux/platform_data/qcom_ssm.h> -#include <soc/qcom/scm.h> -#include <soc/qcom/smd.h> - -#include "../../misc/qseecom_kernel.h" -#include "ssm.h" - -/* Macros */ -#define SSM_DEV_NAME "ssm" -#define MPSS_SUBSYS 0 -#define SSM_INFO_CMD_ID 1 -#define MAX_APP_NAME_SIZE 32 -#define SSM_MSG_LEN 200 -#define SSM_MSG_FIELD_LEN 11 -#define ATOM_MSG_LEN (SSM_MSG_FIELD_LEN + SSM_MSG_LEN + 40) - -#define TZAPP_NAME "SsmApp" -#define CHANNEL_NAME "SSM_RTR_MODEM_APPS" - -/* SSM driver structure.*/ -struct ssm_driver { - int32_t app_status; - int32_t update_status; - unsigned char *channel_name; - unsigned char *smd_buffer; - struct device *dev; - smd_channel_t *ch; - struct work_struct ipc_work; - struct mutex mutex; - struct qseecom_handle *qseecom_handle; - struct tzapp_get_mode_info_rsp *resp; - bool key_status; - bool ready; -}; - -static struct ssm_driver *ssm_drv; - -static unsigned int getint(char *buff, unsigned long *res) -{ - char value[SSM_MSG_FIELD_LEN]; - - memcpy(value, buff, SSM_MSG_FIELD_LEN); - value[SSM_MSG_FIELD_LEN - 1] = '\0'; - - return kstrtoul(skip_spaces(value), 10, res); -} - -/* - * Setup CMD/RSP pointers. - */ -static void setup_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd, - int *cmd_len, void **resp, int *resp_len) -{ - *cmd = handle->sbuf; - if (*cmd_len & QSEECOM_ALIGN_MASK) - *cmd_len = QSEECOM_ALIGN(*cmd_len); - - *resp = handle->sbuf + *cmd_len; - if (*resp_len & QSEECOM_ALIGN_MASK) - *resp_len = QSEECOM_ALIGN(*resp_len); -} - -/* - * Send packet to modem over SMD channel. - */ -static int update_modem(enum ssm_ipc_req ipc_req, struct ssm_driver *ssm, - int length, char *data) -{ - unsigned int packet_len = length + SSM_MSG_FIELD_LEN; - int rc = 0, count; - - snprintf(ssm->smd_buffer, SSM_MSG_FIELD_LEN + 1, "%10u|", ipc_req); - memcpy(ssm->smd_buffer + SSM_MSG_FIELD_LEN, data, length); - - if (smd_write_avail(ssm->ch) < packet_len) { - dev_err(ssm->dev, "Not enough space dropping request\n"); - rc = -ENOSPC; - goto out; - } - - count = smd_write(ssm->ch, ssm->smd_buffer, packet_len); - if (count < packet_len) { - dev_err(ssm->dev, "smd_write failed for %d\n", ipc_req); - rc = -EIO; - } - -out: - return rc; -} - -/* - * Header Format - * Each member of header is of 10 byte (ASCII). - * Each entry is separated by '|' delimiter. - * |<-10 bytes->|<-10 bytes->| - * |-------------------------| - * | IPC code | error code | - * |-------------------------| - * - */ -static int decode_packet(char *buffer, struct ssm_common_msg *pkt) -{ - int rc; - - rc = getint(buffer, (unsigned long *)&pkt->ipc_req); - if (rc < 0) - return -EINVAL; - - buffer += SSM_MSG_FIELD_LEN; - rc = getint(buffer, (unsigned long *)&pkt->err_code); - if (rc < 0) - return -EINVAL; - - dev_dbg(ssm_drv->dev, "req %d error code %d\n", - pkt->ipc_req, pkt->err_code); - return 0; -} - -static void process_message(struct ssm_common_msg pkt, struct ssm_driver *ssm) -{ - - switch (pkt.ipc_req) { - - case SSM_MTOA_MODE_UPDATE_STATUS: - if (pkt.err_code) { - dev_err(ssm->dev, "Modem mode update failed\n"); - ssm->update_status = FAILED; - } else - ssm->update_status = SUCCESS; - - dev_dbg(ssm->dev, "Modem mode update status %d\n", - pkt.err_code); - break; - - default: - dev_dbg(ssm->dev, "Invalid message\n"); - break; - }; -} - -/* - * Work function to handle and process packets coming from modem. - */ -static void ssm_app_modem_work_fn(struct work_struct *work) -{ - int sz, rc; - struct ssm_common_msg pkt; - struct ssm_driver *ssm; - - ssm = container_of(work, struct ssm_driver, ipc_work); - - mutex_lock(&ssm->mutex); - sz = smd_cur_packet_size(ssm->ch); - if ((sz < SSM_MSG_FIELD_LEN) || (sz > ATOM_MSG_LEN)) { - dev_dbg(ssm_drv->dev, "Garbled message size\n"); - goto unlock; - } - - if (smd_read_avail(ssm->ch) < sz) { - dev_err(ssm_drv->dev, "SMD error data in channel\n"); - goto unlock; - } - - if (smd_read(ssm->ch, ssm->smd_buffer, sz) != sz) { - dev_err(ssm_drv->dev, "Incomplete data\n"); - goto unlock; - } - - rc = decode_packet(ssm->smd_buffer, &pkt); - if (rc < 0) { - dev_err(ssm_drv->dev, "Corrupted header\n"); - goto unlock; - } - - process_message(pkt, ssm); - -unlock: - mutex_unlock(&ssm->mutex); -} - -/* - * MODEM-APPS smd channel callback function. - */ -static void modem_request(void *ctxt, unsigned event) -{ - struct ssm_driver *ssm; - - ssm = (struct ssm_driver *)ctxt; - - switch (event) { - case SMD_EVENT_OPEN: - case SMD_EVENT_CLOSE: - dev_dbg(ssm->dev, "SMD port status changed\n"); - break; - case SMD_EVENT_DATA: - if (smd_read_avail(ssm->ch) > 0) - schedule_work(&ssm->ipc_work); - break; - }; -} - -/* - * Load SSM application in TZ and start application: - */ -static int ssm_load_app(struct ssm_driver *ssm) -{ - int rc; - - /* Load the APP */ - rc = qseecom_start_app(&ssm->qseecom_handle, TZAPP_NAME, SZ_4K); - if (rc < 0) { - dev_err(ssm->dev, "Unable to load SSM app\n"); - ssm->app_status = FAILED; - return -EIO; - } - - ssm->app_status = SUCCESS; - return 0; -} - -static struct ssm_platform_data *populate_ssm_pdata(struct device *dev) -{ - struct ssm_platform_data *pdata; - - pdata = devm_kzalloc(dev, sizeof(struct ssm_platform_data), - GFP_KERNEL); - if (!pdata) - return NULL; - - pdata->need_key_exchg = - of_property_read_bool(dev->of_node, "qcom,need-keyexhg"); - - pdata->channel_name = CHANNEL_NAME; - - return pdata; -} - -static int ssm_probe(struct platform_device *pdev) -{ - int rc; - struct ssm_platform_data *pdata; - struct ssm_driver *drv; - - if (pdev->dev.of_node) - pdata = populate_ssm_pdata(&pdev->dev); - else - pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "Empty platform data\n"); - return -ENOMEM; - } - - drv = devm_kzalloc(&pdev->dev, sizeof(struct ssm_driver), - GFP_KERNEL); - if (!drv) - return -ENOMEM; - - /* Allocate response buffer */ - drv->resp = devm_kzalloc(&pdev->dev, - sizeof(struct tzapp_get_mode_info_rsp), - GFP_KERNEL); - if (!drv->resp) { - devm_kfree(&pdev->dev, drv); - rc = -ENOMEM; - goto exit; - } - - /* Initialize the driver structure */ - drv->app_status = RETRY; - drv->ready = false; - drv->update_status = FAILED; - mutex_init(&drv->mutex); - drv->key_status = !pdata->need_key_exchg; - drv->channel_name = (char *)pdata->channel_name; - INIT_WORK(&drv->ipc_work, ssm_app_modem_work_fn); - - /* Allocate memory for smd buffer */ - drv->smd_buffer = devm_kzalloc(&pdev->dev, - (sizeof(char) * ATOM_MSG_LEN), GFP_KERNEL); - if (!drv->smd_buffer) { - devm_kfree(&pdev->dev, drv->resp); - devm_kfree(&pdev->dev, drv); - rc = -ENOMEM; - goto exit; - } - - drv->dev = &pdev->dev; - ssm_drv = drv; - platform_set_drvdata(pdev, ssm_drv); - - dev_dbg(&pdev->dev, "probe success\n"); - return 0; - -exit: - mutex_destroy(&drv->mutex); - platform_set_drvdata(pdev, NULL); - return rc; - -} - -static int ssm_remove(struct platform_device *pdev) -{ - - if (!ssm_drv) - return 0; - /* - * Step to exit - * 1. set ready to 0 (oem access closed). - * 2. Close SMD modem connection closed. - * 3. cleanup ion. - */ - ssm_drv->ready = false; - smd_close(ssm_drv->ch); - flush_work(&ssm_drv->ipc_work); - - /* Shutdown tzapp */ - dev_dbg(&pdev->dev, "Shutting down TZapp\n"); - qseecom_shutdown_app(&ssm_drv->qseecom_handle); - - /* freeing the memory allocations - for the driver and the buffer */ - devm_kfree(&pdev->dev, ssm_drv->smd_buffer); - devm_kfree(&pdev->dev, ssm_drv->resp); - devm_kfree(&pdev->dev, ssm_drv); - - return 0; -} - -static struct of_device_id ssm_match_table[] = { - { - .compatible = "qcom,ssm", - }, - {} -}; - -static struct platform_driver ssm_pdriver = { - .probe = ssm_probe, - .remove = ssm_remove, - .driver = { - .name = SSM_DEV_NAME, - .owner = THIS_MODULE, - .of_match_table = ssm_match_table, - }, -}; -module_platform_driver(ssm_pdriver); - -/* - * Interface for external OEM driver. - * This interface supports following functionalities: - * 1. Set mode (encrypted mode and it's length is passed as parameter). - * 2. Set mode from TZ (read encrypted mode from TZ) - * 3. Get status of mode update. - * - */ -int ssm_oem_driver_intf(int cmd, char *mode, int len) -{ - int rc, req_len, resp_len; - struct tzapp_get_mode_info_req *get_mode_req; - struct tzapp_get_mode_info_rsp *get_mode_resp; - - /* If ssm_drv is NULL, probe failed */ - if (!ssm_drv) - return -ENODEV; - - mutex_lock(&ssm_drv->mutex); - - if (ssm_drv->app_status == RETRY) { - /* Load TZAPP */ - rc = ssm_load_app(ssm_drv); - if (rc) { - rc = -ENODEV; - goto unlock; - } - } else if (ssm_drv->app_status == FAILED) { - rc = -ENODEV; - goto unlock; - } - - /* Open modem SMD interface */ - if (!ssm_drv->ready) { - rc = smd_named_open_on_edge(ssm_drv->channel_name, - SMD_APPS_MODEM, - &ssm_drv->ch, - ssm_drv, - modem_request); - if (rc) { - rc = -EAGAIN; - goto unlock; - } else - ssm_drv->ready = true; - } - - /* Try again modem key-exchange not yet done.*/ - if (!ssm_drv->key_status) { - rc = -EAGAIN; - goto unlock; - } - - /* Set return status to success */ - rc = 0; - - switch (cmd) { - case SSM_READY: - break; - - case SSM_MODE_INFO_READY: - ssm_drv->update_status = RETRY; - /* Fill command structure */ - req_len = sizeof(struct tzapp_get_mode_info_req); - resp_len = sizeof(struct tzapp_get_mode_info_rsp); - setup_cmd_rsp_buffers(ssm_drv->qseecom_handle, - (void **)&get_mode_req, &req_len, - (void **)&get_mode_resp, &resp_len); - get_mode_req->tzapp_ssm_cmd = GET_ENC_MODE; - - rc = qseecom_set_bandwidth(ssm_drv->qseecom_handle, 1); - if (rc) { - ssm_drv->update_status = FAILED; - dev_err(ssm_drv->dev, "set bandwidth failed\n"); - rc = -EIO; - break; - } - rc = qseecom_send_command(ssm_drv->qseecom_handle, - (void *)get_mode_req, req_len, - (void *)get_mode_resp, resp_len); - if (rc || get_mode_resp->status) { - ssm_drv->update_status = FAILED; - break; - } - rc = qseecom_set_bandwidth(ssm_drv->qseecom_handle, 0); - if (rc) { - ssm_drv->update_status = FAILED; - dev_err(ssm_drv->dev, "clear bandwidth failed\n"); - rc = -EIO; - break; - } - - if (get_mode_resp->enc_mode_len > ENC_MODE_MAX_SIZE) { - ssm_drv->update_status = FAILED; - rc = -EINVAL; - break; - } - /* Send mode_info to modem */ - rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv, - get_mode_resp->enc_mode_len, - get_mode_resp->enc_mode_info); - if (rc) - ssm_drv->update_status = FAILED; - break; - - case SSM_SET_MODE: - ssm_drv->update_status = RETRY; - - if (len > ENC_MODE_MAX_SIZE) { - ssm_drv->update_status = FAILED; - rc = -EINVAL; - break; - } - memcpy(ssm_drv->resp->enc_mode_info, mode, len); - ssm_drv->resp->enc_mode_len = len; - - /* Send mode_info to modem */ - rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv, - ssm_drv->resp->enc_mode_len, - ssm_drv->resp->enc_mode_info); - if (rc) - ssm_drv->update_status = FAILED; - break; - - case SSM_GET_MODE_STATUS: - rc = ssm_drv->update_status; - break; - - default: - rc = -EINVAL; - dev_err(ssm_drv->dev, "Invalid command\n"); - break; - }; - -unlock: - mutex_unlock(&ssm_drv->mutex); - return rc; -} - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("QTI Secure Service Module"); - diff --git a/drivers/platform/msm/ssm.h b/drivers/platform/msm/ssm.h deleted file mode 100644 index ee4f1bc1d83f..000000000000 --- a/drivers/platform/msm/ssm.h +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __SSM_H_ -#define __SSM_H_ - -#define MAX_APP_NAME_SIZE 32 -#define ENC_MODE_MAX_SIZE 200 - -/* tzapp response.*/ -enum tz_response { - RESULT_SUCCESS = 0, - RESULT_FAILURE = 0xFFFFFFFF, -}; - -/* tzapp command list.*/ -enum tz_commands { - ENC_MODE, - GET_ENC_MODE, - KEY_EXCHANGE = 11, -}; - -/* MODEM/SSM command list.*/ -enum ssm_ipc_req { - SSM_IPC_MIN = 0x0000AAAB, - SSM_ATOM_MODE_UPDATE, - SSM_MTOA_MODE_UPDATE_STATUS = SSM_IPC_MIN + 4, - SSM_INVALID_REQ, -}; - -/* OEM request commands list.*/ -enum oem_req { - SSM_READY, - SSM_MODE_INFO_READY, - SSM_SET_MODE, - SSM_GET_MODE_STATUS, - SSM_INVALID, -}; - -/* Modem mode update status.*/ -enum modem_mode_status { - SUCCESS, - RETRY, - FAILED = -1, -}; - -/* tzapp encode mode request.*/ -__packed struct tzapp_mode_enc_req { - uint32_t tzapp_ssm_cmd; - uint8_t mode_info[4]; -}; - -/* tzapp encode mode response.*/ -__packed struct tzapp_mode_enc_rsp { - uint32_t tzapp_ssm_cmd; - uint8_t enc_mode_info[ENC_MODE_MAX_SIZE]; - uint32_t enc_mode_len; - uint32_t status; -}; - -/* tzapp get mode request.*/ -__packed struct tzapp_get_mode_info_req { - uint32_t tzapp_ssm_cmd; -}; - -/* tzapp get mode response.*/ -__packed struct tzapp_get_mode_info_rsp { - uint32_t tzapp_ssm_cmd; - uint8_t enc_mode_info[ENC_MODE_MAX_SIZE]; - uint32_t enc_mode_len; - uint32_t status; -}; - -/* Modem/SSM packet format.*/ -struct ssm_common_msg { - enum ssm_ipc_req ipc_req; - int err_code; - -}; - -#endif 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 2cf8eb4e7ceb..e96523a4d43e 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -2238,6 +2238,7 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, { switch (chg->real_charger_type) { case POWER_SUPPLY_TYPE_USB_HVDCP: + case POWER_SUPPLY_TYPE_USB_HVDCP_3: case POWER_SUPPLY_TYPE_USB_PD: if (chg->smb_version == PM660_SUBTYPE) val->intval = MICRO_9V; @@ -3359,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, @@ -3378,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/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index 21381d1ad7fa..1aface23668e 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -1,5 +1,5 @@ /* - * 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 @@ -1665,20 +1665,29 @@ static int cpr3_hmss_init_controller(struct cpr3_controller *ctrl) return 0; } -static int cpr3_hmss_regulator_suspend(struct platform_device *pdev, - pm_message_t state) +#if CONFIG_PM +static int cpr3_hmss_regulator_suspend(struct device *dev) { - struct cpr3_controller *ctrl = platform_get_drvdata(pdev); + struct cpr3_controller *ctrl = dev_get_drvdata(dev); return cpr3_regulator_suspend(ctrl); } -static int cpr3_hmss_regulator_resume(struct platform_device *pdev) +static int cpr3_hmss_regulator_resume(struct device *dev) { - struct cpr3_controller *ctrl = platform_get_drvdata(pdev); + struct cpr3_controller *ctrl = dev_get_drvdata(dev); return cpr3_regulator_resume(ctrl); } +#else +#define cpr3_hmss_regulator_suspend NULL +#define cpr3_hmss_regulator_resume NULL +#endif + +static const struct dev_pm_ops cpr3_hmss_regulator_pm_ops = { + .suspend = cpr3_hmss_regulator_suspend, + .resume = cpr3_hmss_regulator_resume, +}; /* Data corresponds to the SoC revision */ static const struct of_device_id cpr_regulator_match_table[] = { @@ -1811,11 +1820,10 @@ static struct platform_driver cpr3_hmss_regulator_driver = { .name = "qcom,cpr3-hmss-regulator", .of_match_table = cpr_regulator_match_table, .owner = THIS_MODULE, + .pm = &cpr3_hmss_regulator_pm_ops, }, .probe = cpr3_hmss_regulator_probe, .remove = cpr3_hmss_regulator_remove, - .suspend = cpr3_hmss_regulator_suspend, - .resume = cpr3_hmss_regulator_resume, }; static int cpr_regulator_init(void) diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c index 5a031f503b51..ec14ffe6b4c4 100644 --- a/drivers/regulator/cpr3-mmss-regulator.c +++ b/drivers/regulator/cpr3-mmss-regulator.c @@ -1,5 +1,5 @@ /* - * 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 @@ -1078,20 +1078,29 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl) return 0; } -static int cpr3_mmss_regulator_suspend(struct platform_device *pdev, - pm_message_t state) +#if CONFIG_PM +static int cpr3_mmss_regulator_suspend(struct device *dev) { - struct cpr3_controller *ctrl = platform_get_drvdata(pdev); + struct cpr3_controller *ctrl = dev_get_drvdata(dev); return cpr3_regulator_suspend(ctrl); } -static int cpr3_mmss_regulator_resume(struct platform_device *pdev) +static int cpr3_mmss_regulator_resume(struct device *dev) { - struct cpr3_controller *ctrl = platform_get_drvdata(pdev); + struct cpr3_controller *ctrl = dev_get_drvdata(dev); return cpr3_regulator_resume(ctrl); } +#else +#define cpr3_mmss_regulator_suspend NULL +#define cpr3_mmss_regulator_resume NULL +#endif + +static const struct dev_pm_ops cpr3_mmss_regulator_pm_ops = { + .suspend = cpr3_mmss_regulator_suspend, + .resume = cpr3_mmss_regulator_resume, +}; /* Data corresponds to the SoC revision */ static const struct of_device_id cpr_regulator_match_table[] = { @@ -1233,11 +1242,10 @@ static struct platform_driver cpr3_mmss_regulator_driver = { .name = "qcom,cpr3-mmss-regulator", .of_match_table = cpr_regulator_match_table, .owner = THIS_MODULE, + .pm = &cpr3_mmss_regulator_pm_ops, }, .probe = cpr3_mmss_regulator_probe, .remove = cpr3_mmss_regulator_remove, - .suspend = cpr3_mmss_regulator_suspend, - .resume = cpr3_mmss_regulator_resume, }; static int cpr_regulator_init(void) diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 499e437c7e91..f9d77b4c44ef 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -274,6 +274,7 @@ int of_regulator_match(struct device *dev, struct device_node *node, dev_err(dev, "failed to parse DT for regulator %s\n", child->name); + of_node_put(child); return -EINVAL; } match->of_node = of_node_get(child); diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c index e1cfa06810ef..e79f2a181ad2 100644 --- a/drivers/rtc/hctosys.c +++ b/drivers/rtc/hctosys.c @@ -49,6 +49,11 @@ static int __init rtc_hctosys(void) tv64.tv_sec = rtc_tm_to_time64(&tm); +#if BITS_PER_LONG == 32 + if (tv64.tv_sec > INT_MAX) + goto err_read; +#endif + err = do_settimeofday64(&tv64); dev_info(rtc->dev.parent, diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index afab89f5be48..a161fbf6f172 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -132,20 +132,23 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct snvs_rtc_data *data = dev_get_drvdata(dev); unsigned long time; + int ret; rtc_tm_to_time(tm, &time); /* Disable RTC first */ - snvs_rtc_enable(data, false); + ret = snvs_rtc_enable(data, false); + if (ret) + return ret; /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH); regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH)); /* Enable RTC again */ - snvs_rtc_enable(data, true); + ret = snvs_rtc_enable(data, true); - return 0; + return ret; } static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -287,7 +290,11 @@ static int snvs_rtc_probe(struct platform_device *pdev) regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff); /* Enable RTC */ - snvs_rtc_enable(data, true); + ret = snvs_rtc_enable(data, true); + if (ret) { + dev_err(&pdev->dev, "failed to enable rtc %d\n", ret); + goto error_rtc_device_register; + } device_init_wakeup(&pdev->dev, true); diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 560d9a5e0225..a9528083061d 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -86,7 +86,8 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm) for (i = 2; i < 6; i++) buf[i] = __raw_readl(&rtcreg->dat); spin_unlock_irq(&pdata->lock); - sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2]; + sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) | + (buf[3] << 8) | buf[2]; rtc_time_to_tm(sec, tm); return rtc_valid_tm(tm); } @@ -147,7 +148,8 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0; alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0; spin_unlock_irq(&pdata->lock); - sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2]; + sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) | + (buf[3] << 8) | buf[2]; rtc_time_to_tm(sec, &alrm->time); return rtc_valid_tm(&alrm->time); } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 92e03b42e661..3fc73b5894f0 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -822,6 +822,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_set_timeout(cdev, 0); cdev->private->iretry = 255; + cdev->private->async_kill_io_rc = -ETIMEDOUT; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); @@ -898,7 +899,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) /* OK, i/o is dead now. Call interrupt handler. */ if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); + ERR_PTR(cdev->private->async_kill_io_rc)); } static void @@ -915,14 +916,16 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); + ERR_PTR(cdev->private->async_kill_io_rc)); } void ccw_device_kill_io(struct ccw_device *cdev) { int ret; + ccw_device_set_timeout(cdev, 0); cdev->private->iretry = 255; + cdev->private->async_kill_io_rc = -EIO; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index b108f4a5c7dd..b142c7a389b7 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -155,6 +155,7 @@ struct ccw_device_private { unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; struct irb irb; /* device status */ + int async_kill_io_rc; struct senseid senseid; /* SenseID info */ struct pgid pgid[8]; /* path group IDs per chpid*/ struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 766a9176b4ad..cf531ad8b6ee 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1321,9 +1321,10 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) host = aac->scsi_host_ptr; scsi_block_requests(host); aac_adapter_disable_int(aac); - if (aac->thread->pid != current->pid) { + if (aac->thread && aac->thread->pid != current->pid) { spin_unlock_irq(host->host_lock); kthread_stop(aac->thread); + aac->thread = NULL; jafo = 1; } @@ -1392,6 +1393,7 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) aac->name); if (IS_ERR(aac->thread)) { retval = PTR_ERR(aac->thread); + aac->thread = NULL; goto out; } } diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index aa6eccb8940b..8da8b46da722 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -1085,6 +1085,7 @@ static void __aac_shutdown(struct aac_dev * aac) up(&fib->event_wait); } kthread_stop(aac->thread); + aac->thread = NULL; } aac_send_shutdown(aac); aac_adapter_disable_int(aac); @@ -1189,8 +1190,10 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) * Map in the registers from the adapter. */ aac->base_size = AAC_MIN_FOOTPRINT_SIZE; - if ((*aac_drivers[index].init)(aac)) + if ((*aac_drivers[index].init)(aac)) { + error = -ENODEV; goto out_unmap; + } if (aac->sync_mode) { if (aac_sync_mode) diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index decdc71b6b86..f6d7c4712e66 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -2009,7 +2009,7 @@ static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, * have valid data in the sense buffer that could * confuse the higher levels. */ - memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); + memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); //printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); //{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } /* diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 0002caf687dd..eb3b5c0f299f 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1858,6 +1858,7 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req, /* we will not receive ABTS response for this IO */ BNX2FC_IO_DBG(io_req, "Timer context finished processing " "this scsi cmd\n"); + return; } /* Cancel the timeout_work, as we received IO completion */ diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 4639dac64e7f..f096766150bc 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -634,7 +634,12 @@ lpfc_issue_lip(struct Scsi_Host *shost) LPFC_MBOXQ_t *pmboxq; int mbxstatus = MBXERR_ERROR; + /* + * If the link is offline, disabled or BLOCK_MGMT_IO + * it doesn't make any sense to allow issue_lip + */ if ((vport->fc_flag & FC_OFFLINE_MODE) || + (phba->hba_flag & LINK_DISABLED) || (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)) return -EPERM; diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index be901f6db6d3..4131addfb872 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -691,8 +691,9 @@ lpfc_work_done(struct lpfc_hba *phba) (phba->hba_flag & HBA_SP_QUEUE_EVT)) { if (pring->flag & LPFC_STOP_IOCB_EVENT) { pring->flag |= LPFC_DEFERRED_RING_EVENT; - /* Set the lpfc data pending flag */ - set_bit(LPFC_DATA_READY, &phba->data_flags); + /* Preserve legacy behavior. */ + if (!(phba->hba_flag & HBA_SP_QUEUE_EVT)) + set_bit(LPFC_DATA_READY, &phba->data_flags); } else { if (phba->link_state >= LPFC_LINK_UP) { pring->flag &= ~LPFC_DEFERRED_RING_EVENT; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index ef43847153ea..3406586b9201 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -115,6 +115,8 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe) /* set consumption flag every once in a while */ if (!((q->host_index + 1) % q->entry_repost)) bf_set(wqe_wqec, &wqe->generic.wqe_com, 1); + else + bf_set(wqe_wqec, &wqe->generic.wqe_com, 0); if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED) bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id); lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index b868ef3b2ca3..7d67a68bcc62 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -8637,7 +8637,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), "fw_event_%s%d", ioc->driver_name, ioc->id); ioc->firmware_event_thread = alloc_ordered_workqueue( - ioc->firmware_event_name, WQ_MEM_RECLAIM); + ioc->firmware_event_name, 0); if (!ioc->firmware_event_thread) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 1f6a3b86965f..440d79e6aea5 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -268,7 +268,8 @@ qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; /* Read all mbox registers? */ - mboxes = (1 << ha->mbx_count) - 1; + WARN_ON_ONCE(ha->mbx_count > 32); + mboxes = (1ULL << ha->mbx_count) - 1; if (!ha->mcp) ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERROR.\n"); else @@ -2495,7 +2496,8 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; /* Read all mbox registers? */ - mboxes = (1 << ha->mbx_count) - 1; + WARN_ON_ONCE(ha->mbx_count > 32); + mboxes = (1ULL << ha->mbx_count) - 1; if (!ha->mcp) ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERROR.\n"); else diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index a7cfc270bd08..ce1d063f3e83 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -168,6 +168,8 @@ #define DEV_DB_NON_PERSISTENT 0 #define DEV_DB_PERSISTENT 1 +#define QL4_ISP_REG_DISCONNECT 0xffffffffU + #define COPY_ISID(dst_isid, src_isid) { \ int i, j; \ for (i = 0, j = ISID_SIZE - 1; i < ISID_SIZE;) \ diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 01c3610a60cf..d8c03431d0aa 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -262,6 +262,24 @@ static struct iscsi_transport qla4xxx_iscsi_transport = { static struct scsi_transport_template *qla4xxx_scsi_transport; +static int qla4xxx_isp_check_reg(struct scsi_qla_host *ha) +{ + u32 reg_val = 0; + int rval = QLA_SUCCESS; + + if (is_qla8022(ha)) + reg_val = readl(&ha->qla4_82xx_reg->host_status); + else if (is_qla8032(ha) || is_qla8042(ha)) + reg_val = qla4_8xxx_rd_direct(ha, QLA8XXX_PEG_ALIVE_COUNTER); + else + reg_val = readw(&ha->reg->ctrl_status); + + if (reg_val == QL4_ISP_REG_DISCONNECT) + rval = QLA_ERROR; + + return rval; +} + static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num, uint32_t iface_type, uint32_t payload_size, uint32_t pid, struct sockaddr *dst_addr) @@ -9196,10 +9214,17 @@ static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) struct srb *srb = NULL; int ret = SUCCESS; int wait = 0; + int rval; ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%llu: Abort command issued cmd=%p, cdb=0x%x\n", ha->host_no, id, lun, cmd, cmd->cmnd[0]); + rval = qla4xxx_isp_check_reg(ha); + if (rval != QLA_SUCCESS) { + ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n"); + return FAILED; + } + spin_lock_irqsave(&ha->hardware_lock, flags); srb = (struct srb *) CMD_SP(cmd); if (!srb) { @@ -9251,6 +9276,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) struct scsi_qla_host *ha = to_qla_host(cmd->device->host); struct ddb_entry *ddb_entry = cmd->device->hostdata; int ret = FAILED, stat; + int rval; if (!ddb_entry) return ret; @@ -9270,6 +9296,12 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); + rval = qla4xxx_isp_check_reg(ha); + if (rval != QLA_SUCCESS) { + ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n"); + return FAILED; + } + /* FIXME: wait for hba to go online */ stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun); if (stat != QLA_SUCCESS) { @@ -9313,6 +9345,7 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) struct scsi_qla_host *ha = to_qla_host(cmd->device->host); struct ddb_entry *ddb_entry = cmd->device->hostdata; int stat, ret; + int rval; if (!ddb_entry) return FAILED; @@ -9330,6 +9363,12 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) ha->host_no, cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); + rval = qla4xxx_isp_check_reg(ha); + if (rval != QLA_SUCCESS) { + ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n"); + return FAILED; + } + stat = qla4xxx_reset_target(ha, ddb_entry); if (stat != QLA_SUCCESS) { starget_printk(KERN_INFO, scsi_target(cmd->device), @@ -9384,9 +9423,16 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd) { int return_status = FAILED; struct scsi_qla_host *ha; + int rval; ha = to_qla_host(cmd->device->host); + rval = qla4xxx_isp_check_reg(ha); + if (rval != QLA_SUCCESS) { + ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n"); + return FAILED; + } + if ((is_qla8032(ha) || is_qla8042(ha)) && ql4xdontresethba) qla4_83xx_set_idc_dontreset(ha); diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index e3cd3ece4412..c3d1891d2d3f 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -52,6 +52,8 @@ struct srp_internal { struct transport_container rport_attr_cont; }; +static int scsi_is_srp_rport(const struct device *dev); + #define to_srp_internal(tmpl) container_of(tmpl, struct srp_internal, t) #define dev_to_rport(d) container_of(d, struct srp_rport, dev) @@ -61,9 +63,24 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r) return dev_to_shost(r->dev.parent); } +static int find_child_rport(struct device *dev, void *data) +{ + struct device **child = data; + + if (scsi_is_srp_rport(dev)) { + WARN_ON_ONCE(*child); + *child = dev; + } + return 0; +} + static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost) { - return transport_class_to_srp_rport(&shost->shost_gendev); + struct device *child = NULL; + + WARN_ON_ONCE(device_for_each_child(&shost->shost_gendev, &child, + find_child_rport) < 0); + return child ? dev_to_rport(child) : NULL; } /** @@ -637,7 +654,8 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) struct srp_rport *rport = shost_to_rport(shost); pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev)); - return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 && + return rport && rport->fast_io_fail_tmo < 0 && + rport->dev_loss_tmo < 0 && i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 6e66e2ad9daf..c83dadfff171 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1420,6 +1420,85 @@ static int media_not_present(struct scsi_disk *sdkp, return 0; } +/** + * sd_check_events - check media events + * @disk: kernel device descriptor + * @clearing: disk events currently being cleared + * + * Returns mask of DISK_EVENT_*. + * + * Note: this function is invoked from the block subsystem. + **/ +static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) +{ + struct scsi_disk *sdkp = scsi_disk(disk); + struct scsi_device *sdp = sdkp->device; + struct scsi_sense_hdr *sshdr = NULL; + int retval; + + SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n")); + + /* Simply return for embedded storage media such as UFS */ + if (!sdp->removable) + goto out; + + /* + * If the device is offline, don't send any commands - just pretend as + * if the command failed. If the device ever comes back online, we + * can deal with it then. It is only because of unrecoverable errors + * that we would ever take a device offline in the first place. + */ + if (!scsi_device_online(sdp)) { + set_media_not_present(sdkp); + goto out; + } + + /* + * Using TEST_UNIT_READY enables differentiation between drive with + * no cartridge loaded - NOT READY, drive with changed cartridge - + * UNIT ATTENTION, or with same cartridge - GOOD STATUS. + * + * Drives that auto spin down. eg iomega jaz 1G, will be started + * by sd_spinup_disk() from sd_revalidate_disk(), which happens whenever + * sd_revalidate() is called. + */ + retval = -ENODEV; + + if (scsi_block_when_processing_errors(sdp)) { + sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL); + retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES, + sshdr); + } + + /* failed to execute TUR, assume media not present */ + if (host_byte(retval)) { + set_media_not_present(sdkp); + goto out; + } + + if (media_not_present(sdkp, sshdr)) + goto out; + + /* + * For removable scsi disk we have to recognise the presence + * of a disk in the drive. + */ + if (!sdkp->media_present) + sdp->changed = 1; + sdkp->media_present = 1; +out: + /* + * sdp->changed is set under the following conditions: + * + * Medium present state has changed in either direction. + * Device has indicated UNIT_ATTENTION. + */ + kfree(sshdr); + retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0; + sdp->changed = 0; + return retval; +} + static int sd_sync_cache(struct scsi_disk *sdkp) { int retries, res; @@ -1612,6 +1691,7 @@ static const struct block_device_operations sd_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = sd_compat_ioctl, #endif + .check_events = sd_check_events, .revalidate_disk = sd_revalidate_disk, .unlock_native_capacity = sd_unlock_native_capacity, .pr_ops = &sd_pr_ops, @@ -2334,6 +2414,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer) int res; struct scsi_device *sdp = sdkp->device; struct scsi_mode_data data; + int disk_ro = get_disk_ro(sdkp->disk); set_disk_ro(sdkp->disk, 0); if (sdp->skip_ms_page_3f) { @@ -2373,7 +2454,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer) "Test WP failed, assume Write Enabled\n"); } else { sdkp->write_prot = ((data.device_specific & 0x80) != 0); - set_disk_ro(sdkp->disk, sdkp->write_prot); + set_disk_ro(sdkp->disk, sdkp->write_prot || disk_ro); } } diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 804586aeaffe..de53c9694b68 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -522,6 +522,8 @@ static int sr_block_open(struct block_device *bdev, fmode_t mode) struct scsi_cd *cd; int ret = -ENXIO; + check_disk_change(bdev); + mutex_lock(&sr_mutex); cd = scsi_cd_get(bdev->bd_disk); if (cd) { @@ -582,18 +584,28 @@ out: static unsigned int sr_block_check_events(struct gendisk *disk, unsigned int clearing) { - struct scsi_cd *cd = scsi_cd(disk); + unsigned int ret = 0; + struct scsi_cd *cd; - if (atomic_read(&cd->device->disk_events_disable_depth)) + cd = scsi_cd_get(disk); + if (!cd) return 0; - return cdrom_check_events(&cd->cdi, clearing); + if (!atomic_read(&cd->device->disk_events_disable_depth)) + ret = cdrom_check_events(&cd->cdi, clearing); + + scsi_cd_put(cd); + return ret; } static int sr_block_revalidate_disk(struct gendisk *disk) { - struct scsi_cd *cd = scsi_cd(disk); struct scsi_sense_hdr sshdr; + struct scsi_cd *cd; + + cd = scsi_cd_get(disk); + if (!cd) + return -ENXIO; /* if the unit is not ready, nothing more to do */ if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr)) @@ -602,6 +614,7 @@ static int sr_block_revalidate_disk(struct gendisk *disk) sr_cd_check(&cd->cdi); get_sectorsize(cd); out: + scsi_cd_put(cd); return 0; } diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 351d81dc2200..44b7a69d022a 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1538,7 +1538,7 @@ static struct scsi_host_template scsi_driver = { .eh_timed_out = storvsc_eh_timed_out, .slave_alloc = storvsc_device_alloc, .slave_configure = storvsc_device_configure, - .cmd_per_lun = 255, + .cmd_per_lun = 2048, .this_id = -1, .use_clustering = ENABLE_CLUSTERING, /* Make sure we dont get a sg segment crosses a page boundary */ diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 6b349e301869..c6425e3df5a0 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -536,7 +536,7 @@ sym_getsync(struct sym_hcb *np, u_char dt, u_char sfac, u_char *divp, u_char *fa * Look for the greatest clock divisor that allows an * input speed faster than the period. */ - while (div-- > 0) + while (--div > 0) if (kpc >= (div_10M[div] << 2)) break; /* diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6d43254d84b9..6af0ca6eb7e2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5216,7 +5216,7 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) /* REPORT SUPPORTED OPERATION CODES is not supported */ sdev->no_report_opcodes = 1; - /* WRITE_SAME command is not supported*/ + /* WRITE_SAME command is not supported */ sdev->no_write_same = 1; ufshcd_set_queue_depth(sdev); 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..ef249bcba68c 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; @@ -817,7 +1148,7 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) break; case IOCTL_HAB_SEND: send_param = (struct hab_send *)data; - if (send_param->sizebytes > HAB_MAX_MSG_SIZEBYTES) { + if (send_param->sizebytes > HAB_HEADER_SIZE_MASK) { ret = -EINVAL; break; } @@ -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..e743d9b00a66 --- /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 * const *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/khab_test.c b/drivers/soc/qcom/hab/khab_test.c index 3773211aeec7..7d6df8861421 100644 --- a/drivers/soc/qcom/hab/khab_test.c +++ b/drivers/soc/qcom/hab/khab_test.c @@ -13,7 +13,9 @@ #include "hab.h" #include "khab_test.h" #include "hab_pipe.h" +#ifdef CONFIG_MSM_GVM_QUIN #include "hab_qvm.h" +#endif #include <asm/cacheflush.h> #include <linux/list.h> @@ -30,8 +32,10 @@ enum hab_perf_test_type { static int hab_shmm_throughput_test(void) { struct hab_device *habDev; +#ifdef CONFIG_MSM_GVM_QUIN struct qvm_channel *dev; - struct hab_shared_buf *sh_buf; +#endif + struct hab_shared_buf *sh_buf = NULL; struct physical_channel *pchan; struct timeval tv1, tv2; int i, counter; @@ -40,7 +44,7 @@ static int hab_shmm_throughput_test(void) register int sum; register int *pp, *lastone; - int throughput[3][2] = {0}; + int throughput[3][2] = { {0} }; int latency[6][PERF_TEST_ITERATION]; int ret = 0, tmp, size; @@ -52,6 +56,7 @@ static int hab_shmm_throughput_test(void) pchan = list_first_entry(&(habDev->pchannels), struct physical_channel, node); +#ifdef CONFIG_MSM_GVM_QUIN dev = pchan->hyp_data; if (!dev) { ret = -EPERM; @@ -59,6 +64,8 @@ static int hab_shmm_throughput_test(void) } sh_buf = dev->pipe_ep->tx_info.sh_buf; +#endif + /* pChannel is of 128k, we use 64k to test */ size = 0x10000; 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/msm_smem.c b/drivers/soc/qcom/msm_smem.c index a94f741c2056..cf3e0e084ab4 100644 --- a/drivers/soc/qcom/msm_smem.c +++ b/drivers/soc/qcom/msm_smem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, 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 @@ -26,6 +26,7 @@ #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/ramdump.h> +#include <soc/qcom/scm.h> #include <soc/qcom/smem.h> @@ -1085,12 +1086,15 @@ static __init int modem_restart_late_init(void) void *handle; struct restart_notifier_block *nb; - if (smem_dev) - smem_ramdump_dev = create_ramdump_device("smem", smem_dev); - if (IS_ERR_OR_NULL(smem_ramdump_dev)) { - LOG_ERR("%s: Unable to create smem ramdump device.\n", - __func__); - smem_ramdump_dev = NULL; + if (scm_is_secure_device()) { + if (smem_dev) + smem_ramdump_dev = create_ramdump_device("smem", + smem_dev); + if (IS_ERR_OR_NULL(smem_ramdump_dev)) { + LOG_ERR("%s: Unable to create smem ramdump device.\n", + __func__); + smem_ramdump_dev = NULL; + } } for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) { 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/scm_qcpe.c b/drivers/soc/qcom/scm_qcpe.c index f50527e50a25..614670888aac 100644 --- a/drivers/soc/qcom/scm_qcpe.c +++ b/drivers/soc/qcom/scm_qcpe.c @@ -476,7 +476,8 @@ static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc) size_bytes = sizeof(smc_params); memset(&smc_params, 0x0, sizeof(smc_params)); - ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, 0); + ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, + HABMM_SOCKET_RECV_FLAGS_UNINTERRUPTIBLE); if (ret) { pr_err("habmm_socket_recv failed, ret= 0x%x\n", ret); goto err_ret; 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/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 625030f1f256..3528cb08c78e 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_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 @@ -873,8 +873,7 @@ err: } static const struct dev_pm_ops msm_watchdog_dev_pm_ops = { - .suspend_noirq = msm_watchdog_suspend, - .resume_noirq = msm_watchdog_resume, + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(msm_watchdog_suspend, msm_watchdog_resume) }; static struct platform_driver msm_watchdog_driver = { 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/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c index e06864f64beb..0f6bc6b8e4c6 100644 --- a/drivers/staging/rtl8192u/r8192U_core.c +++ b/drivers/staging/rtl8192u/r8192U_core.c @@ -1749,6 +1749,8 @@ static short rtl8192_usb_initendpoints(struct net_device *dev) priv->rx_urb[16] = usb_alloc_urb(0, GFP_KERNEL); priv->oldaddr = kmalloc(16, GFP_KERNEL); + if (!priv->oldaddr) + return -ENOMEM; oldaddr = priv->oldaddr; align = ((long)oldaddr) & 3; if (align) { diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 3a1de5c87cb4..37959c8f42a9 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -596,6 +596,11 @@ static int arc_serial_probe(struct platform_device *pdev) if (dev_id < 0) dev_id = 0; + if (dev_id >= ARRAY_SIZE(arc_uart_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", dev_id); + return -EINVAL; + } + uart = &arc_uart_ports[dev_id]; port = &uart->port; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 3d790033744e..01e2274b23f2 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1818,6 +1818,10 @@ static int lpuart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); return ret; } + if (ret >= ARRAY_SIZE(lpuart_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", ret); + return -EINVAL; + } sport->port.line = ret; sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 98176d12b3e1..07ede982b472 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1923,6 +1923,12 @@ static int serial_imx_probe(struct platform_device *pdev) else if (ret < 0) return ret; + if (sport->port.line >= ARRAY_SIZE(imx_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", + sport->port.line); + return -EINVAL; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index cd0414bbe094..daa4a65ef6ff 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1274,6 +1274,10 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.line = pdev->id < 0 ? 0 : pdev->id; else if (ret < 0) return ret; + if (s->port.line >= ARRAY_SIZE(auart_port)) { + dev_err(&pdev->dev, "serial%d out of range\n", s->port.line); + return -EINVAL; + } if (of_id) { pdev->id_entry = of_id->data; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index df642496f989..a20b51a4f860 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -860,15 +860,12 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) dma->rx_conf.direction = DMA_DEV_TO_MEM; dma->rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma->rx_conf.src_addr = p->port.mapbase + S3C2410_URXH; - dma->rx_conf.src_maxburst = 16; + dma->rx_conf.src_maxburst = 1; dma->tx_conf.direction = DMA_MEM_TO_DEV; dma->tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma->tx_conf.dst_addr = p->port.mapbase + S3C2410_UTXH; - if (dma_get_cache_alignment() >= 16) - dma->tx_conf.dst_maxburst = 16; - else - dma->tx_conf.dst_maxburst = 1; + dma->tx_conf.dst_maxburst = 1; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -1807,6 +1804,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index); + if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", index); + return -EINVAL; + } ourport = &s3c24xx_serial_ports[index]; ourport->drv_data = s3c24xx_get_driver_data(pdev); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 009e0dbc12d2..4f2f4aca8d2e 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1026,7 +1026,7 @@ static struct uart_port *cdns_uart_get_port(int id) struct uart_port *port; /* Try the given port id if failed use default method */ - if (cdns_uart_port[id].mapbase != 0) { + if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) { /* Find the next unused port */ for (id = 0; id < CDNS_UART_NR_PORTS; id++) if (cdns_uart_port[id].mapbase == 0) 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/dwc2/core.h b/drivers/usb/dwc2/core.h index a738a68d2292..a899d47c2a7c 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -187,7 +187,7 @@ struct dwc2_hsotg_ep { unsigned char dir_in; unsigned char index; unsigned char mc; - unsigned char interval; + u16 interval; unsigned int halted:1; unsigned int periodic:1; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 0abf73c91beb..98705b83d2dc 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2424,12 +2424,6 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0); - dwc2_hsotg_enqueue_setup(hsotg); - - dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - dwc2_readl(hsotg->regs + DIEPCTL0), - dwc2_readl(hsotg->regs + DOEPCTL0)); - /* clear global NAKs */ val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; if (!is_usb_reset) @@ -2440,6 +2434,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, mdelay(3); hsotg->lx_state = DWC2_L0; + + dwc2_hsotg_enqueue_setup(hsotg); + + dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(hsotg->regs + DIEPCTL0), + dwc2_readl(hsotg->regs + DOEPCTL0)); } static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 927c84ea4921..8f36f3df55eb 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -232,6 +232,8 @@ #define DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE (1 << 0) /* Global TX Fifo Size Register */ +#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ +#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ #define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d65a10a80d56..ce6a44e136dc 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1465,7 +1465,7 @@ static int count_ext_compat(struct usb_configuration *c) return res; } -static void fill_ext_compat(struct usb_configuration *c, u8 *buf) +static int fill_ext_compat(struct usb_configuration *c, u8 *buf) { int i, count; @@ -1492,10 +1492,12 @@ static void fill_ext_compat(struct usb_configuration *c, u8 *buf) buf += 23; } count += 24; - if (count >= 4096) - return; + if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; } } + + return count; } static int count_ext_prop(struct usb_configuration *c, int interface) @@ -1540,25 +1542,20 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) struct usb_os_desc *d; struct usb_os_desc_ext_prop *ext_prop; int j, count, n, ret; - u8 *start = buf; f = c->interface[interface]; + count = 10; /* header length */ for (j = 0; j < f->os_desc_n; ++j) { if (interface != f->os_desc_table[j].if_id) continue; d = f->os_desc_table[j].os_desc; if (d) list_for_each_entry(ext_prop, &d->ext_prop, entry) { - /* 4kB minus header length */ - n = buf - start; - if (n >= 4086) - return 0; - - count = ext_prop->data_len + + n = ext_prop->data_len + ext_prop->name_len + 14; - if (count > 4086 - n) - return -EINVAL; - usb_ext_prop_put_size(buf, count); + if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + usb_ext_prop_put_size(buf, n); usb_ext_prop_put_type(buf, ext_prop->type); ret = usb_ext_prop_put_name(buf, ext_prop->name, ext_prop->name_len); @@ -1584,11 +1581,12 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) default: return -EINVAL; } - buf += count; + buf += n; + count += n; } } - return 0; + return count; } /* @@ -1877,6 +1875,7 @@ unknown: req->complete = composite_setup_complete; buf = req->buf; os_desc_cfg = cdev->os_desc_config; + w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ); memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { @@ -1900,8 +1899,8 @@ unknown: count += 16; /* header */ put_unaligned_le32(count, buf); buf += 16; - fill_ext_compat(os_desc_cfg, buf); - value = w_length; + value = fill_ext_compat(os_desc_cfg, buf); + value = min_t(u16, w_length, value); } break; case USB_RECIP_INTERFACE: @@ -1930,8 +1929,7 @@ unknown: interface, buf); if (value < 0) return value; - - value = w_length; + value = min_t(u16, w_length, value); } break; } @@ -2228,8 +2226,8 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, goto end; } - /* OS feature descriptor length <= 4kB */ - cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL); + cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ, + GFP_KERNEL); if (!cdev->os_desc_req->buf) { ret = PTR_ERR(cdev->os_desc_req->buf); kfree(cdev->os_desc_req); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 1a2af68ca93d..a31322ed9447 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1259,9 +1259,9 @@ static void purge_configs_funcs(struct gadget_info *gi) cfg = container_of(c, struct config_usb_cfg, c); - list_for_each_entry_safe(f, tmp, &c->functions, list) { + list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) { - list_move_tail(&f->list, &cfg->func_list); + list_move(&f->list, &cfg->func_list); if (f->unbind) { dev_err(&gi->cdev.gadget->dev, "unbind function" " '%s'/%pK\n", f->name, f); 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/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 9edc01692142..4ec0dd4f0a8c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -759,11 +759,15 @@ static void ffs_user_copy_worker(struct work_struct *work) ffs_log("enter: ret %d", ret); if (io_data->read && ret > 0) { + mm_segment_t oldfs = get_fs(); + + set_fs(USER_DS); use_mm(io_data->mm); ret = copy_to_iter(io_data->buf, ret, &io_data->data); if (ret != io_data->req->actual && iov_iter_count(&io_data->data)) ret = -EFAULT; unuse_mm(io_data->mm); + set_fs(oldfs); } io_data->kiocb->ki_complete(io_data->kiocb, ret, ret); @@ -3561,7 +3565,7 @@ static int ffs_func_setup(struct usb_function *f, ffs_log("exit"); - return 0; + return USB_GADGET_DELAYED_STATUS; } static void ffs_func_suspend(struct usb_function *f) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 12064d3bddf6..b5dab103be38 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1052,6 +1052,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } + iad_desc.bFirstInterface = ret; + std_ac_if_desc.bInterfaceNumber = ret; agdev->ac_intf = ret; agdev->ac_alt = 0; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index aac0ce8aeb0b..8991a4070792 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1310,7 +1310,7 @@ static void udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe) { struct fsl_ep *ep = get_ep_by_pipe(udc, pipe); - if (ep->name) + if (ep->ep.name) nuke(ep, -ESHUTDOWN); } @@ -1698,7 +1698,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) curr_ep = get_ep_by_pipe(udc, i); /* If the ep is configured */ - if (curr_ep->name == NULL) { + if (!curr_ep->ep.name) { WARNING("Invalid EP?"); continue; } diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 86d2adafe149..64eb0f2b5ea0 100644 --- a/drivers/usb/gadget/udc/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h @@ -28,7 +28,7 @@ struct goku_udc_regs { # define INT_EP1DATASET 0x00040 # define INT_EP2DATASET 0x00080 # define INT_EP3DATASET 0x00100 -#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */ +#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */ # define INT_EP1NAK 0x00200 # define INT_EP2NAK 0x00400 # define INT_EP3NAK 0x00800 diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index c0ce3db6a7c0..be8c618e42db 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -444,7 +444,8 @@ static int ohci_init (struct ohci_hcd *ohci) struct usb_hcd *hcd = ohci_to_hcd(ohci); /* Accept arbitrarily long scatter-gather lists */ - hcd->self.sg_tablesize = ~0; + if (!(hcd->driver->flags & HCD_LOCAL_MEM)) + hcd->self.sg_tablesize = ~0; if (distrust_firmware) ohci->flags |= OHCI_QUIRK_HUB_POWER; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 225d1de17c3e..ccff562d2f48 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -960,6 +960,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) if (dev->out_ctx) xhci_free_container_ctx(xhci, dev->out_ctx); + if (dev->udev && dev->udev->slot_id) + dev->udev->slot_id = 0; kfree(xhci->devs[slot_id]); xhci->devs[slot_id] = NULL; } diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c index 35f652c281bb..4004ba0437d5 100644 --- a/drivers/usb/misc/ks_bridge.c +++ b/drivers/usb/misc/ks_bridge.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, 2017, Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014, 2017-2018, 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 @@ -463,6 +463,8 @@ static const struct usb_device_id ksb_usb_ids[] = { { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9091, 0), }, { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x901D, 0), }, { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x900E, 0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9900, 0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9901, 0), }, { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 2), .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904C, 2), diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 06d83825923a..3a81b4c4d0dd 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1775,6 +1775,7 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) int vbus; u8 devctl; + pm_runtime_get_sync(dev); spin_lock_irqsave(&musb->lock, flags); val = musb->a_wait_bcon; vbus = musb_platform_get_vbus_status(musb); @@ -1788,6 +1789,7 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) vbus = 0; } spin_unlock_irqrestore(&musb->lock, flags); + pm_runtime_put_sync(dev); return sprintf(buf, "Vbus %s, timeout %lu msec\n", vbus ? "on" : "off", val); @@ -2522,7 +2524,8 @@ static int musb_resume(struct device *dev) pm_runtime_set_active(dev); pm_runtime_enable(dev); - musb_start(musb); + musb_enable_interrupts(musb); + musb_platform_enable(musb); return 0; } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 32cadca198b2..e7a051386b32 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -33,7 +33,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *); static void cp210x_close(struct usb_serial_port *); static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *); static void cp210x_get_termios_port(struct usb_serial_port *port, - unsigned int *cflagp, unsigned int *baudp); + tcflag_t *cflagp, unsigned int *baudp); static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *, struct ktermios *); static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, @@ -515,7 +515,7 @@ static void cp210x_get_termios(struct tty_struct *tty, &tty->termios.c_cflag, &baud); tty_encode_baud_rate(tty, baud, baud); } else { - unsigned int cflag; + tcflag_t cflag; cflag = 0; cp210x_get_termios_port(port, &cflag, &baud); } @@ -526,10 +526,11 @@ static void cp210x_get_termios(struct tty_struct *tty, * This is the heart of cp210x_get_termios which always uses a &usb_serial_port. */ static void cp210x_get_termios_port(struct usb_serial_port *port, - unsigned int *cflagp, unsigned int *baudp) + tcflag_t *cflagp, unsigned int *baudp) { struct device *dev = &port->dev; - unsigned int cflag, modem_ctl[4]; + tcflag_t cflag; + unsigned int modem_ctl[4]; unsigned int baud; unsigned int bits; diff --git a/drivers/video/fbdev/sbuslib.c b/drivers/video/fbdev/sbuslib.c index a350209ffbd3..31c301d6be62 100644 --- a/drivers/video/fbdev/sbuslib.c +++ b/drivers/video/fbdev/sbuslib.c @@ -121,7 +121,7 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg, unsigned char __user *ured; unsigned char __user *ugreen; unsigned char __user *ublue; - int index, count, i; + unsigned int index, count, i; if (get_user(index, &c->index) || __get_user(count, &c->count) || @@ -160,7 +160,7 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg, unsigned char __user *ugreen; unsigned char __user *ublue; struct fb_cmap *cmap = &info->cmap; - int index, count, i; + unsigned int index, count, i; u8 red, green, blue; if (get_user(index, &c->index) || diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c index 42a737713465..d00f6169bdd9 100644 --- a/drivers/video/msm/ba/msm_ba.c +++ b/drivers/video/msm/ba/msm_ba.c @@ -347,6 +347,7 @@ int msm_ba_g_fmt(void *instance, struct v4l2_format *f) } else { f->fmt.pix.height = sd_fmt.format.height; f->fmt.pix.width = sd_fmt.format.width; + f->fmt.pix.field = sd_fmt.format.field; switch (sd_fmt.format.code) { case MEDIA_BUS_FMT_YUYV8_2X8: f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index aa93df5833dc..2048aad91add 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -520,7 +520,8 @@ static ssize_t watchdog_write(struct file *file, const char __user *buf, char c; if (get_user(c, buf + i)) return -EFAULT; - expect_close = (c == 'V'); + if (c == 'V') + expect_close = true; } /* Properly order writes across fork()ed processes */ diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index 2b28c00da0df..dfe20b81ced5 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -54,7 +54,7 @@ #define SB800_PM_WATCHDOG_CONFIG 0x4C #define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0) -#define SB800_PM_WATCHDOG_DISABLE (1 << 2) +#define SB800_PM_WATCHDOG_DISABLE (1 << 1) #define SB800_PM_WATCHDOG_SECOND_RES (3 << 0) #define SB800_ACPI_MMIO_DECODE_EN (1 << 0) #define SB800_ACPI_MMIO_SEL (1 << 1) diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 83ec7b89d308..468961c59fa5 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -764,8 +764,8 @@ out: mutex_unlock(&irq_mapping_update_lock); return irq; error_irq: - for (; i >= 0; i--) - __unbind_from_irq(irq + i); + while (nvec--) + __unbind_from_irq(irq + nvec); mutex_unlock(&irq_mapping_update_lock); return ret; } diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index c49f79ed58c5..4b7ce442d8e5 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -328,7 +328,7 @@ static void gnttab_handle_deferred(unsigned long unused) if (entry->page) { pr_debug("freeing g.e. %#x (pfn %#lx)\n", entry->ref, page_to_pfn(entry->page)); - __free_page(entry->page); + put_page(entry->page); } else pr_info("freeing g.e. %#x\n", entry->ref); kfree(entry); @@ -384,7 +384,7 @@ void gnttab_end_foreign_access(grant_ref_t ref, int readonly, if (gnttab_end_foreign_access_ref(ref, readonly)) { put_free_entry(ref); if (page != 0) - free_page(page); + put_page(virt_to_page(page)); } else gnttab_add_deferred(ref, readonly, page ? virt_to_page(page) : NULL); diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index f7b19c25c3a4..1889e928a0da 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -359,7 +359,7 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, * physical address */ phys = xen_bus_to_phys(dev_addr); - if (((dev_addr + size - 1 > dma_mask)) || + if (((dev_addr + size - 1 <= dma_mask)) || range_straddles_page_boundary(phys, size)) xen_destroy_contiguous_region(phys, order); diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 2e319d0c395d..84cc98f3cabe 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -362,9 +362,9 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv) } /* There are more ACPI Processor objects than in x2APIC or MADT. * This can happen with incorrect ACPI SSDT declerations. */ - if (acpi_id > nr_acpi_bits) { - pr_debug("We only have %u, trying to set %u\n", - nr_acpi_bits, acpi_id); + if (acpi_id >= nr_acpi_bits) { + pr_debug("max acpi id %u, trying to set %u\n", + nr_acpi_bits - 1, acpi_id); return AE_OK; } /* OK, There is a ACPI Processor object */ diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 33a31cfef55d..c2d447687e33 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -470,8 +470,11 @@ int xenbus_probe_node(struct xen_bus_type *bus, /* Register with generic device framework. */ err = device_register(&xendev->dev); - if (err) + if (err) { + put_device(&xendev->dev); + xendev = NULL; goto fail; + } return 0; fail: diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c index d295d9878dff..8ec79385d3cc 100644 --- a/drivers/zorro/zorro.c +++ b/drivers/zorro/zorro.c @@ -16,6 +16,7 @@ #include <linux/bitops.h> #include <linux/string.h> #include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include <linux/slab.h> #include <asm/byteorder.h> @@ -185,6 +186,17 @@ static int __init amiga_zorro_probe(struct platform_device *pdev) z->dev.parent = &bus->dev; z->dev.bus = &zorro_bus_type; z->dev.id = i; + switch (z->rom.er_Type & ERT_TYPEMASK) { + case ERT_ZORROIII: + z->dev.coherent_dma_mask = DMA_BIT_MASK(32); + break; + + case ERT_ZORROII: + default: + z->dev.coherent_dma_mask = DMA_BIT_MASK(24); + break; + } + z->dev.dma_mask = &z->dev.coherent_dma_mask; } /* ... then register them */ diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 181e05b46e72..92448d0ad900 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -224,9 +224,10 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) affs_lock_dir(dir); bh = affs_find_entry(dir, dentry); - affs_unlock_dir(dir); - if (IS_ERR(bh)) + if (IS_ERR(bh)) { + affs_unlock_dir(dir); return ERR_CAST(bh); + } if (bh) { u32 ino = bh->b_blocknr; @@ -240,10 +241,13 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) } affs_brelse(bh); inode = affs_iget(sb, ino); - if (IS_ERR(inode)) + if (IS_ERR(inode)) { + affs_unlock_dir(dir); return ERR_CAST(inode); + } } d_add(dentry, inode); + affs_unlock_dir(dir); return NULL; } @@ -629,9 +629,8 @@ static void free_ioctx_users(struct percpu_ref *ref) while (!list_empty(&ctx->active_reqs)) { req = list_first_entry(&ctx->active_reqs, struct aio_kiocb, ki_list); - - list_del_init(&req->ki_list); kiocb_cancel(req); + list_del_init(&req->ki_list); } spin_unlock_irq(&ctx->ctx_lock); @@ -1067,8 +1066,8 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) ctx = rcu_dereference(table->table[id]); if (ctx && ctx->user_id == ctx_id) { - percpu_ref_get(&ctx->users); - ret = ctx; + if (percpu_ref_tryget_live(&ctx->users)) + ret = ctx; } out: rcu_read_unlock(); diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index e2f5be261532..38ee08675468 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2769,6 +2769,8 @@ again: * contention with the cow code */ if (cow) { + bool last_level = (level == (BTRFS_MAX_LEVEL - 1)); + /* * if we don't really need to cow this block * then we don't want to set the path blocking, @@ -2793,9 +2795,13 @@ again: } btrfs_set_path_blocking(p); - err = btrfs_cow_block(trans, root, b, - p->nodes[level + 1], - p->slots[level + 1], &b); + if (last_level) + err = btrfs_cow_block(trans, root, b, NULL, 0, + &b); + else + err = btrfs_cow_block(trans, root, b, + p->nodes[level + 1], + p->slots[level + 1], &b); if (err) { ret = err; goto done; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 85b207d19aa5..7efd70bfeaf7 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -923,7 +923,7 @@ static int check_async_write(struct inode *inode, unsigned long bio_flags) if (bio_flags & EXTENT_BIO_TREE_LOG) return 0; #ifdef CONFIG_X86 - if (cpu_has_xmm4_2) + if (static_cpu_has_safe(X86_FEATURE_XMM4_2)) return 0; #endif return 1; @@ -1196,7 +1196,7 @@ static struct btrfs_subvolume_writers *btrfs_alloc_subvolume_writers(void) if (!writers) return ERR_PTR(-ENOMEM); - ret = percpu_counter_init(&writers->counter, 0, GFP_KERNEL); + ret = percpu_counter_init(&writers->counter, 0, GFP_NOFS); if (ret < 0) { kfree(writers); return ERR_PTR(ret); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 260f94b019c9..982a9d509817 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4392,6 +4392,7 @@ again: if (wait_for_alloc) { mutex_unlock(&fs_info->chunk_mutex); wait_for_alloc = 0; + cond_resched(); goto again; } diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index d4a6eef31854..052973620595 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1861,10 +1861,19 @@ int btrfs_release_file(struct inode *inode, struct file *filp) static int start_ordered_ops(struct inode *inode, loff_t start, loff_t end) { int ret; + struct blk_plug plug; + /* + * This is only called in fsync, which would do synchronous writes, so + * a plug can merge adjacent IOs as much as possible. Esp. in case of + * multiple disks using raid profile, a large IO can be split to + * several segments of stripe length (currently 64K). + */ + blk_start_plug(&plug); atomic_inc(&BTRFS_I(inode)->sync_writers); ret = btrfs_fdatawrite_range(inode, start, end); atomic_dec(&BTRFS_I(inode)->sync_writers); + blk_finish_plug(&plug); return ret; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 81b5a461d94e..1f01a8172308 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6413,8 +6413,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry, goto out_unlock_inode; } else { btrfs_update_inode(trans, root, inode); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); } out_unlock: @@ -6489,8 +6488,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, goto out_unlock_inode; BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops; - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); out_unlock: btrfs_end_transaction(trans, root); @@ -6633,12 +6631,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) if (err) goto out_fail_inode; - d_instantiate(dentry, inode); - /* - * mkdir is special. We're unlocking after we call d_instantiate - * to avoid a race with nfsd calling d_instantiate. - */ - unlock_new_inode(inode); + d_instantiate_new(dentry, inode); drop_on_err = 0; out_fail: @@ -9789,8 +9782,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, goto out_unlock_inode; } - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); out_unlock: btrfs_end_transaction(trans, root); diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 1a33d3eb36de..b9fa99577bf7 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -2160,11 +2160,21 @@ int raid56_parity_recover(struct btrfs_root *root, struct bio *bio, } /* - * reconstruct from the q stripe if they are - * asking for mirror 3 + * Loop retry: + * for 'mirror == 2', reconstruct from all other stripes. + * for 'mirror_num > 2', select a stripe to fail on every retry. */ - if (mirror_num == 3) - rbio->failb = rbio->real_stripes - 2; + if (mirror_num > 2) { + /* + * 'mirror == 3' is to fail the p stripe and + * reconstruct from the q stripe. 'mirror > 3' is to + * fail a data stripe and reconstruct from p+q stripe. + */ + rbio->failb = rbio->real_stripes - (mirror_num - 1); + ASSERT(rbio->failb > 0); + if (rbio->failb <= rbio->faila) + rbio->failb--; + } ret = lock_stripe_add(rbio); diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 19b56873b797..83c73738165e 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -4674,6 +4674,9 @@ static int send_hole(struct send_ctx *sctx, u64 end) u64 len; int ret = 0; + if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) + return send_update_extent(sctx, offset, end - offset); + p = fs_path_alloc(); if (!p) return -ENOMEM; diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index 846d277b1901..2b2978c04e80 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -70,7 +70,7 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr, btrfs_set_extent_generation(leaf, item, 1); btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_TREE_BLOCK); block_info = (struct btrfs_tree_block_info *)(item + 1); - btrfs_set_tree_block_level(leaf, block_info, 1); + btrfs_set_tree_block_level(leaf, block_info, 0); iref = (struct btrfs_extent_inline_ref *)(block_info + 1); if (parent > 0) { btrfs_set_extent_inline_ref_type(leaf, iref, diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 6ba022ed4a52..738f5d6beb95 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2223,8 +2223,10 @@ again: nritems = btrfs_header_nritems(path->nodes[0]); if (path->slots[0] >= nritems) { ret = btrfs_next_leaf(root, path); - if (ret) + if (ret == 1) break; + else if (ret < 0) + goto out; } btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); @@ -3378,8 +3380,11 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans, * from this directory and from this transaction */ ret = btrfs_next_leaf(root, path); - if (ret == 1) { - last_offset = (u64)-1; + if (ret) { + if (ret == 1) + last_offset = (u64)-1; + else + err = ret; goto done; } btrfs_item_key_to_cpu(path->nodes[0], &tmp, path->slots[0]); @@ -3830,6 +3835,7 @@ fill_holes: ASSERT(ret == 0); src = src_path->nodes[0]; i = 0; + need_find_last_extent = true; } btrfs_item_key_to_cpu(src, &key, i); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index ed75d70b4bc2..b4d63a9842fa 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -5056,7 +5056,14 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len) else if (map->type & BTRFS_BLOCK_GROUP_RAID5) ret = 2; else if (map->type & BTRFS_BLOCK_GROUP_RAID6) - ret = 3; + /* + * There could be two corrupted data stripes, we need + * to loop retry in order to rebuild the correct data. + * + * Fail a stripe at a time on every retry except the + * stripe under reconstruction. + */ + ret = map->num_stripes; else ret = 1; free_extent_map(em); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 0c92af11f4f4..8632380d2b94 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -6421,9 +6421,7 @@ SetEARetry: pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_EA); - parm_data = - (struct fealist *) (((char *) &pSMB->hdr.Protocol) + - offset); + parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; diff --git a/fs/dcache.c b/fs/dcache.c index 750ddc627855..7df640155468 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1897,6 +1897,28 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) EXPORT_SYMBOL(d_instantiate_unique); +/* + * This should be equivalent to d_instantiate() + unlock_new_inode(), + * with lockdep-related part of unlock_new_inode() done before + * anything else. Use that instead of open-coding d_instantiate()/ + * unlock_new_inode() combinations. + */ +void d_instantiate_new(struct dentry *entry, struct inode *inode) +{ + BUG_ON(!hlist_unhashed(&entry->d_u.d_alias)); + BUG_ON(!inode); + lockdep_annotate_inode_mutex_key(inode); + security_d_instantiate(entry, inode); + spin_lock(&inode->i_lock); + __d_instantiate(entry, inode); + WARN_ON(!(inode->i_state & I_NEW)); + inode->i_state &= ~I_NEW; + smp_mb(); + wake_up_bit(&inode->i_state, __I_NEW); + spin_unlock(&inode->i_lock); +} +EXPORT_SYMBOL(d_instantiate_new); + /** * d_instantiate_no_diralias - instantiate a non-aliased dentry * @entry: dentry to complete diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index cb3ecf442d96..d108ed116858 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -291,7 +291,6 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, iput(ecryptfs_inode); goto out; } - unlock_new_inode(ecryptfs_inode); crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat; if (get_events() && get_events()->open_cb) @@ -299,7 +298,7 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, ecryptfs_inode_to_lower(ecryptfs_inode), crypt_stat); - d_instantiate(ecryptfs_dentry, ecryptfs_inode); + d_instantiate_new(ecryptfs_dentry, ecryptfs_inode); out: return rc; } diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3267a80dbbe2..da3d40ef1668 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -40,8 +40,7 @@ static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode) { int err = ext2_add_link(dentry, inode); if (!err) { - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; } inode_dec_link_count(inode); @@ -267,8 +266,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode) if (err) goto out_fail; - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); out: return err; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index b9324d0ff218..cf09d5f71ad2 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2426,8 +2426,7 @@ static int ext4_add_nondir(handle_t *handle, int err = ext4_add_entry(handle, dentry, inode); if (!err) { ext4_mark_inode_dirty(handle, inode); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; } drop_nlink(inode); @@ -2666,8 +2665,7 @@ out_clear_inode: err = ext4_mark_inode_dirty(handle, dir); if (err) goto out_clear_inode; - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); if (IS_DIRSYNC(dir)) ext4_handle_sync(handle); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d0bfcfed35e2..97c17b3d984c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -238,15 +238,12 @@ enum { #define CP_DISCARD 0x00000010 #define CP_TRIMMED 0x00000020 -#define DEF_BATCHED_TRIM_SECTIONS 2048 -#define BATCHED_TRIM_SEGMENTS(sbi) \ - (GET_SEG_FROM_SEC(sbi, SM_I(sbi)->trim_sections)) -#define BATCHED_TRIM_BLOCKS(sbi) \ - (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) #define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi) #define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */ +#define DEF_MAX_DISCARD_LEN 512 /* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000 /* 60 s, if no candidates */ +#define DEF_DISCARD_URGENT_UTIL 80 /* do more discard over 80% */ #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ @@ -753,7 +750,8 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, static inline bool __is_discard_mergeable(struct discard_info *back, struct discard_info *front) { - return back->lstart + back->len == front->lstart; + return (back->lstart + back->len == front->lstart) && + (back->len + front->len < DEF_MAX_DISCARD_LEN); } static inline bool __is_discard_back_mergeable(struct discard_info *cur, @@ -1139,6 +1137,7 @@ enum { enum fsync_mode { FSYNC_MODE_POSIX, /* fsync follows posix semantics */ FSYNC_MODE_STRICT, /* fsync behaves in line with ext4 */ + FSYNC_MODE_NOBARRIER, /* fsync behaves nobarrier based on posix */ }; #ifdef CONFIG_F2FS_FS_ENCRYPTION @@ -2853,8 +2852,6 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi); void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); -void init_discard_policy(struct discard_policy *dpolicy, int discard_type, - unsigned int granularity); void drop_discard_cmd(struct f2fs_sb_info *sbi); void stop_discard_thread(struct f2fs_sb_info *sbi); bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7587758a285f..40d03d58b390 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -309,7 +309,7 @@ sync_nodes: remove_ino_entry(sbi, ino, APPEND_INO); clear_inode_flag(inode, FI_APPEND_WRITE); flush_out: - if (!atomic) + if (!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER) ret = f2fs_issue_flush(sbi, inode->i_ino); if (!ret) { remove_ino_entry(sbi, ino, UPDATE_INO); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index fecae8685d2a..0355891dbbf8 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -294,8 +294,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, alloc_nid_done(sbi, ino); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); @@ -594,8 +593,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, err = page_symlink(inode, disk_link.name, disk_link.len); err_out: - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); /* * Let's flush symlink data in order to avoid broken symlink as much as @@ -658,8 +656,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) alloc_nid_done(sbi, inode->i_ino); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); @@ -710,8 +707,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, alloc_nid_done(sbi, inode->i_ino); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 01bc94df9f00..a02d5c1a7ed2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -996,6 +996,39 @@ static void __check_sit_bitmap(struct f2fs_sb_info *sbi, #endif } +static void __init_discard_policy(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy, + int discard_type, unsigned int granularity) +{ + /* common policy */ + dpolicy->type = discard_type; + dpolicy->sync = true; + dpolicy->granularity = granularity; + + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + + if (discard_type == DPOLICY_BG) { + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->io_aware = true; + dpolicy->sync = false; + if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) { + dpolicy->granularity = 1; + dpolicy->max_interval = DEF_MIN_DISCARD_ISSUE_TIME; + } + } else if (discard_type == DPOLICY_FORCE) { + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->io_aware = false; + } else if (discard_type == DPOLICY_FSTRIM) { + dpolicy->io_aware = false; + } else if (discard_type == DPOLICY_UMOUNT) { + dpolicy->io_aware = false; + } +} + + /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, struct discard_policy *dpolicy, @@ -1210,68 +1243,6 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, return 0; } -static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi, - struct discard_policy *dpolicy, - unsigned int start, unsigned int end) -{ - struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct discard_cmd *prev_dc = NULL, *next_dc = NULL; - struct rb_node **insert_p = NULL, *insert_parent = NULL; - struct discard_cmd *dc; - struct blk_plug plug; - int issued; - -next: - issued = 0; - - mutex_lock(&dcc->cmd_lock); - f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); - - dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root, - NULL, start, - (struct rb_entry **)&prev_dc, - (struct rb_entry **)&next_dc, - &insert_p, &insert_parent, true); - if (!dc) - dc = next_dc; - - blk_start_plug(&plug); - - while (dc && dc->lstart <= end) { - struct rb_node *node; - - if (dc->len < dpolicy->granularity) - goto skip; - - if (dc->state != D_PREP) { - list_move_tail(&dc->list, &dcc->fstrim_list); - goto skip; - } - - __submit_discard_cmd(sbi, dpolicy, dc); - - if (++issued >= dpolicy->max_requests) { - start = dc->lstart + dc->len; - - blk_finish_plug(&plug); - mutex_unlock(&dcc->cmd_lock); - - schedule(); - - goto next; - } -skip: - node = rb_next(&dc->rb_node); - dc = rb_entry_safe(node, struct discard_cmd, rb_node); - - if (fatal_signal_pending(current)) - break; - } - - blk_finish_plug(&plug); - mutex_unlock(&dcc->cmd_lock); -} - static int __issue_discard_cmd(struct f2fs_sb_info *sbi, struct discard_policy *dpolicy) { @@ -1412,7 +1383,18 @@ next: static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, struct discard_policy *dpolicy) { - __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); + struct discard_policy dp; + + if (dpolicy) { + __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); + return; + } + + /* wait all */ + __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1); + __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); + __init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1); + __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); } /* This should be covered by global mutex, &sit_i->sentry_lock */ @@ -1457,11 +1439,13 @@ bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) struct discard_policy dpolicy; bool dropped; - init_discard_policy(&dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity); + __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT, + dcc->discard_granularity); __issue_discard_cmd(sbi, &dpolicy); dropped = __drop_discard_cmd(sbi); - __wait_all_discard_cmd(sbi, &dpolicy); + /* just to make sure there is no pending discard commands */ + __wait_all_discard_cmd(sbi, NULL); return dropped; } @@ -1477,7 +1461,7 @@ static int issue_discard_thread(void *data) set_freezable(); do { - init_discard_policy(&dpolicy, DPOLICY_BG, + __init_discard_policy(sbi, &dpolicy, DPOLICY_BG, dcc->discard_granularity); wait_event_interruptible_timeout(*q, @@ -1495,7 +1479,7 @@ static int issue_discard_thread(void *data) dcc->discard_wake = 0; if (sbi->gc_thread && sbi->gc_thread->gc_urgent) - init_discard_policy(&dpolicy, DPOLICY_FORCE, 1); + __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1); sb_start_intwrite(sbi->sb); @@ -1788,32 +1772,6 @@ skip: wake_up_discard_thread(sbi, false); } -void init_discard_policy(struct discard_policy *dpolicy, - int discard_type, unsigned int granularity) -{ - /* common policy */ - dpolicy->type = discard_type; - dpolicy->sync = true; - dpolicy->granularity = granularity; - - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; - - if (discard_type == DPOLICY_BG) { - dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; - dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->io_aware = true; - } else if (discard_type == DPOLICY_FORCE) { - dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; - dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->io_aware = false; - } else if (discard_type == DPOLICY_FSTRIM) { - dpolicy->io_aware = false; - } else if (discard_type == DPOLICY_UMOUNT) { - dpolicy->io_aware = false; - } -} - static int create_discard_cmd_control(struct f2fs_sb_info *sbi) { dev_t dev = sbi->sb->s_bdev->bd_dev; @@ -2453,11 +2411,72 @@ bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc) return has_candidate; } +static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy, + unsigned int start, unsigned int end) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct discard_cmd *prev_dc = NULL, *next_dc = NULL; + struct rb_node **insert_p = NULL, *insert_parent = NULL; + struct discard_cmd *dc; + struct blk_plug plug; + int issued; + +next: + issued = 0; + + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); + + dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root, + NULL, start, + (struct rb_entry **)&prev_dc, + (struct rb_entry **)&next_dc, + &insert_p, &insert_parent, true); + if (!dc) + dc = next_dc; + + blk_start_plug(&plug); + + while (dc && dc->lstart <= end) { + struct rb_node *node; + + if (dc->len < dpolicy->granularity) + goto skip; + + if (dc->state != D_PREP) { + list_move_tail(&dc->list, &dcc->fstrim_list); + goto skip; + } + + __submit_discard_cmd(sbi, dpolicy, dc); + + if (++issued >= dpolicy->max_requests) { + start = dc->lstart + dc->len; + + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); + __wait_all_discard_cmd(sbi, NULL); + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto next; + } +skip: + node = rb_next(&dc->rb_node); + dc = rb_entry_safe(node, struct discard_cmd, rb_node); + + if (fatal_signal_pending(current)) + break; + } + + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); +} + int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) { __u64 start = F2FS_BYTES_TO_BLK(range->start); __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; - unsigned int start_segno, end_segno, cur_segno; + unsigned int start_segno, end_segno; block_t start_block, end_block; struct cp_control cpc; struct discard_policy dpolicy; @@ -2483,40 +2502,36 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) cpc.reason = CP_DISCARD; cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); + cpc.trim_start = start_segno; + cpc.trim_end = end_segno; - /* do checkpoint to issue discard commands safely */ - for (cur_segno = start_segno; cur_segno <= end_segno; - cur_segno = cpc.trim_end + 1) { - cpc.trim_start = cur_segno; - - if (sbi->discard_blks == 0) - break; - else if (sbi->discard_blks < BATCHED_TRIM_BLOCKS(sbi)) - cpc.trim_end = end_segno; - else - cpc.trim_end = min_t(unsigned int, - rounddown(cur_segno + - BATCHED_TRIM_SEGMENTS(sbi), - sbi->segs_per_sec) - 1, end_segno); - - mutex_lock(&sbi->gc_mutex); - err = write_checkpoint(sbi, &cpc); - mutex_unlock(&sbi->gc_mutex); - if (err) - break; + if (sbi->discard_blks == 0) + goto out; - schedule(); - } + mutex_lock(&sbi->gc_mutex); + err = write_checkpoint(sbi, &cpc); + mutex_unlock(&sbi->gc_mutex); + if (err) + goto out; start_block = START_BLOCK(sbi, start_segno); - end_block = START_BLOCK(sbi, min(cur_segno, end_segno) + 1); + end_block = START_BLOCK(sbi, end_segno + 1); - init_discard_policy(&dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); + __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block); - trimmed = __wait_discard_cmd_range(sbi, &dpolicy, + + /* + * We filed discard candidates, but actually we don't need to wait for + * all of them, since they'll be issued in idle time along with runtime + * discard option. User configuration looks like using runtime discard + * or periodic fstrim instead of it. + */ + if (!test_opt(sbi, DISCARD)) { + trimmed = __wait_discard_cmd_range(sbi, &dpolicy, start_block, end_block); + range->len = F2FS_BLK_TO_BYTES(trimmed); + } out: - range->len = F2FS_BLK_TO_BYTES(trimmed); return err; } @@ -3904,8 +3919,6 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; sm_info->min_ssr_sections = reserved_sections(sbi); - sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; - INIT_LIST_HEAD(&sm_info->sit_entry_set); init_rwsem(&sm_info->curseg_lock); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 55b2bad55671..cb57ad3ca32d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -740,6 +740,10 @@ static int parse_options(struct super_block *sb, char *options) } else if (strlen(name) == 6 && !strncmp(name, "strict", 6)) { F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT; + } else if (strlen(name) == 9 && + !strncmp(name, "nobarrier", 9)) { + F2FS_OPTION(sbi).fsync_mode = + FSYNC_MODE_NOBARRIER; } else { kfree(name); return -EINVAL; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index f33a56d6e6dd..2c53de9251be 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -245,6 +245,9 @@ out: return count; } + if (!strcmp(a->attr.name, "trim_sections")) + return -EINVAL; + *ui = t; if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0) diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 6b35fc4860a0..1de16a5a5c4e 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -776,6 +776,7 @@ static void fscache_write_op(struct fscache_operation *_op) _enter("{OP%x,%d}", op->op.debug_id, atomic_read(&op->op.usage)); +again: spin_lock(&object->lock); cookie = object->cookie; @@ -816,10 +817,6 @@ static void fscache_write_op(struct fscache_operation *_op) goto superseded; page = results[0]; _debug("gang %d [%lx]", n, page->index); - if (page->index >= op->store_limit) { - fscache_stat(&fscache_n_store_pages_over_limit); - goto superseded; - } radix_tree_tag_set(&cookie->stores, page->index, FSCACHE_COOKIE_STORING_TAG); @@ -829,6 +826,9 @@ static void fscache_write_op(struct fscache_operation *_op) spin_unlock(&cookie->stores_lock); spin_unlock(&object->lock); + if (page->index >= op->store_limit) + goto discard_page; + fscache_stat(&fscache_n_store_pages); fscache_stat(&fscache_n_cop_write_page); ret = object->cache->ops->write_page(op, page); @@ -844,6 +844,11 @@ static void fscache_write_op(struct fscache_operation *_op) _leave(""); return; +discard_page: + fscache_stat(&fscache_n_store_pages_over_limit); + fscache_end_page_write(object, page); + goto again; + superseded: /* this writer is going away and there aren't any more things to * write */ diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 1543aa1b2a93..8744bd773823 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -806,7 +806,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_alloc_parms ap = { .aflags = 0, }; unsigned int data_blocks = 0, ind_blocks = 0, rblocks; - loff_t bytes, max_bytes, max_blks = UINT_MAX; + loff_t bytes, max_bytes, max_blks; int error; const loff_t pos = offset; const loff_t count = len; @@ -858,7 +858,8 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t return error; /* ap.allowed tells us how many blocks quota will allow * us to write. Check if this reduces max_blks */ - if (ap.allowed && ap.allowed < max_blks) + max_blks = UINT_MAX; + if (ap.allowed) max_blks = ap.allowed; error = gfs2_inplace_reserve(ip, &ap); diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index ad04b3acae2b..a81ed38d8442 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -43,6 +43,8 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip, { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); int ret; + + ap->allowed = UINT_MAX; /* Assume we are permitted a whole lot */ if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return 0; ret = gfs2_quota_lock(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 30c4c9ebb693..e27317169697 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -207,8 +207,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, __func__, inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->pino_nlink, inode->i_mapping->nrpages); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; fail: @@ -428,8 +427,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; fail: @@ -573,8 +571,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t mode mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; fail: @@ -745,8 +742,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; fail: diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 2caf1682036d..85e2594fe95c 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -361,7 +361,6 @@ error_io: ret = -EIO; error: mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); iget_failed(inode); return ERR_PTR(ret); } diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 9d7551f5c32a..f217ae750adb 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -178,8 +178,7 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, umode_t mode, unlock_new_inode(ip); iput(ip); } else { - unlock_new_inode(ip); - d_instantiate(dentry, ip); + d_instantiate_new(dentry, ip); } out2: @@ -313,8 +312,7 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode) unlock_new_inode(ip); iput(ip); } else { - unlock_new_inode(ip); - d_instantiate(dentry, ip); + d_instantiate_new(dentry, ip); } out2: @@ -1058,8 +1056,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, unlock_new_inode(ip); iput(ip); } else { - unlock_new_inode(ip); - d_instantiate(dentry, ip); + d_instantiate_new(dentry, ip); } out2: @@ -1443,8 +1440,7 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry, unlock_new_inode(ip); iput(ip); } else { - unlock_new_inode(ip); - d_instantiate(dentry, ip); + d_instantiate_new(dentry, ip); } out1: diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0f397e62de5a..41c8ddbc80dc 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1780,7 +1780,7 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta return ret; } -static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, int err) +static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, struct file_lock *fl, int err) { switch (err) { default: @@ -1827,7 +1827,11 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct return -EAGAIN; case -ENOMEM: case -NFS4ERR_DENIED: - /* kill_proc(fl->fl_pid, SIGLOST, 1); */ + if (fl) { + struct nfs4_lock_state *lsp = fl->fl_u.nfs4_fl.owner; + if (lsp) + set_bit(NFS_LOCK_LOST, &lsp->ls_flags); + } return 0; } return err; @@ -1863,7 +1867,7 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, err = nfs4_open_recover_helper(opendata, FMODE_READ); } nfs4_opendata_put(opendata); - return nfs4_handle_delegation_recall_error(server, state, stateid, err); + return nfs4_handle_delegation_recall_error(server, state, stateid, NULL, err); } static void nfs4_open_confirm_prepare(struct rpc_task *task, void *calldata) @@ -6157,7 +6161,7 @@ int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, if (err != 0) return err; err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW); - return nfs4_handle_delegation_recall_error(server, state, stateid, err); + return nfs4_handle_delegation_recall_error(server, state, stateid, fl, err); } struct nfs_release_lockowner_data { diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 83fba40396ae..44f5cea49699 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1386,6 +1386,7 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ struct inode *inode = state->inode; struct nfs_inode *nfsi = NFS_I(inode); struct file_lock *fl; + struct nfs4_lock_state *lsp; int status = 0; struct file_lock_context *flctx = inode->i_flctx; struct list_head *list; @@ -1426,7 +1427,9 @@ restart: case -NFS4ERR_DENIED: case -NFS4ERR_RECLAIM_BAD: case -NFS4ERR_RECLAIM_CONFLICT: - /* kill_proc(fl->fl_pid, SIGLOST, 1); */ + lsp = fl->fl_u.nfs4_fl.owner; + if (lsp) + set_bit(NFS_LOCK_LOST, &lsp->ls_flags); status = 0; } spin_lock(&flctx->flc_lock); diff --git a/fs/nfs/nfs4sysctl.c b/fs/nfs/nfs4sysctl.c index 0fbd3ab1be22..44a7bbbf92f8 100644 --- a/fs/nfs/nfs4sysctl.c +++ b/fs/nfs/nfs4sysctl.c @@ -31,7 +31,7 @@ static struct ctl_table nfs4_cb_sysctls[] = { .data = &nfs_idmap_cache_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_jiffies, + .proc_handler = proc_dointvec, }, { } }; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index c9a1a491aa91..cd7f5b0abe84 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -50,8 +50,7 @@ static inline int nilfs_add_nondir(struct dentry *dentry, struct inode *inode) { int err = nilfs_add_link(dentry, inode); if (!err) { - d_instantiate(dentry, inode); - unlock_new_inode(inode); + d_instantiate_new(dentry, inode); return 0; } inode_dec_link_count(inode); @@ -246,8 +245,7 @@ static int nilfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) goto out_fail; nilfs_mark_inode_dirty(inode); - d_instantiate(dentry, inode); - unlock_new_inode(inode); + d_instantiate_new(dentry, inode); out: if (!err) err = nilfs_transaction_commit(dir->i_sb); diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 164307b99405..1e0d8da0d3cd 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -314,7 +314,9 @@ struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type) return ERR_PTR(ret); } + down_read(&OCFS2_I(inode)->ip_xattr_sem); acl = ocfs2_get_acl_nolock(inode, type, di_bh); + up_read(&OCFS2_I(inode)->ip_xattr_sem); ocfs2_inode_unlock(inode, 0); brelse(di_bh); @@ -333,7 +335,9 @@ int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh) if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL)) return 0; + down_read(&OCFS2_I(inode)->ip_xattr_sem); acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, bh); + up_read(&OCFS2_I(inode)->ip_xattr_sem); if (IS_ERR(acl) || !acl) return PTR_ERR(acl); ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); @@ -364,8 +368,10 @@ int ocfs2_init_acl(handle_t *handle, if (!S_ISLNK(inode->i_mode)) { if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { + down_read(&OCFS2_I(dir)->ip_xattr_sem); acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT, dir_bh); + up_read(&OCFS2_I(dir)->ip_xattr_sem); if (IS_ERR(acl)) return PTR_ERR(acl); } diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 2ee7fe747cea..c55a9c47ac17 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -674,20 +674,6 @@ static void dlm_leave_domain(struct dlm_ctxt *dlm) spin_unlock(&dlm->spinlock); } -int dlm_shutting_down(struct dlm_ctxt *dlm) -{ - int ret = 0; - - spin_lock(&dlm_domain_lock); - - if (dlm->dlm_state == DLM_CTXT_IN_SHUTDOWN) - ret = 1; - - spin_unlock(&dlm_domain_lock); - - return ret; -} - void dlm_unregister_domain(struct dlm_ctxt *dlm) { int leave = 0; diff --git a/fs/ocfs2/dlm/dlmdomain.h b/fs/ocfs2/dlm/dlmdomain.h index fd6122a38dbd..8a9281411c18 100644 --- a/fs/ocfs2/dlm/dlmdomain.h +++ b/fs/ocfs2/dlm/dlmdomain.h @@ -28,7 +28,30 @@ extern spinlock_t dlm_domain_lock; extern struct list_head dlm_domains; -int dlm_shutting_down(struct dlm_ctxt *dlm); +static inline int dlm_joined(struct dlm_ctxt *dlm) +{ + int ret = 0; + + spin_lock(&dlm_domain_lock); + if (dlm->dlm_state == DLM_CTXT_JOINED) + ret = 1; + spin_unlock(&dlm_domain_lock); + + return ret; +} + +static inline int dlm_shutting_down(struct dlm_ctxt *dlm) +{ + int ret = 0; + + spin_lock(&dlm_domain_lock); + if (dlm->dlm_state == DLM_CTXT_IN_SHUTDOWN) + ret = 1; + spin_unlock(&dlm_domain_lock); + + return ret; +} + void dlm_fire_domain_eviction_callbacks(struct dlm_ctxt *dlm, int node_num); diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 4a338803e7e9..88149b4387c2 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -1377,6 +1377,15 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data, if (!dlm_grab(dlm)) return -EINVAL; + if (!dlm_joined(dlm)) { + mlog(ML_ERROR, "Domain %s not joined! " + "lockres %.*s, master %u\n", + dlm->name, mres->lockname_len, + mres->lockname, mres->master); + dlm_put(dlm); + return -EINVAL; + } + BUG_ON(!(mres->flags & (DLM_MRES_RECOVERY|DLM_MRES_MIGRATION))); real_master = mres->master; diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 13534f4fe5b5..722eb5bc9b8f 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -666,23 +666,24 @@ static int __ocfs2_journal_access(handle_t *handle, /* we can safely remove this assertion after testing. */ if (!buffer_uptodate(bh)) { mlog(ML_ERROR, "giving me a buffer that's not uptodate!\n"); - mlog(ML_ERROR, "b_blocknr=%llu\n", - (unsigned long long)bh->b_blocknr); + mlog(ML_ERROR, "b_blocknr=%llu, b_state=0x%lx\n", + (unsigned long long)bh->b_blocknr, bh->b_state); lock_buffer(bh); /* - * A previous attempt to write this buffer head failed. - * Nothing we can do but to retry the write and hope for - * the best. + * A previous transaction with a couple of buffer heads fail + * to checkpoint, so all the bhs are marked as BH_Write_EIO. + * For current transaction, the bh is just among those error + * bhs which previous transaction handle. We can't just clear + * its BH_Write_EIO and reuse directly, since other bhs are + * not written to disk yet and that will cause metadata + * inconsistency. So we should set fs read-only to avoid + * further damage. */ if (buffer_write_io_error(bh) && !buffer_uptodate(bh)) { - clear_buffer_write_io_error(bh); - set_buffer_uptodate(bh); - } - - if (!buffer_uptodate(bh)) { unlock_buffer(bh); - return -EIO; + return ocfs2_error(osb->sb, "A previous attempt to " + "write this buffer head failed\n"); } unlock_buffer(bh); } diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 2de4c8a9340c..4f5141350af8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -477,9 +477,8 @@ static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb) new = ocfs2_get_system_file_inode(osb, i, osb->slot_num); if (!new) { ocfs2_release_system_inodes(osb); - status = -EINVAL; + status = ocfs2_is_soft_readonly(osb) ? -EROFS : -EINVAL; mlog_errno(status); - /* FIXME: Should ERROR_RO_FS */ mlog(ML_ERROR, "Unable to load system inode %d, " "possibly corrupt fs?", i); goto bail; @@ -508,7 +507,7 @@ static int ocfs2_init_local_system_inodes(struct ocfs2_super *osb) new = ocfs2_get_system_file_inode(osb, i, osb->slot_num); if (!new) { ocfs2_release_system_inodes(osb); - status = -EINVAL; + status = ocfs2_is_soft_readonly(osb) ? -EROFS : -EINVAL; mlog(ML_ERROR, "status=%d, sysfile=%d, slot=%d\n", status, i, osb->slot_num); goto bail; diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 877830b05e12..4f0788232f2f 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -639,9 +639,11 @@ int ocfs2_calc_xattr_init(struct inode *dir, si->value_len); if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { + down_read(&OCFS2_I(dir)->ip_xattr_sem); acl_len = ocfs2_xattr_get_nolock(dir, dir_bh, OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT, "", NULL, 0); + up_read(&OCFS2_I(dir)->ip_xattr_sem); if (acl_len > 0) { a_size = ocfs2_xattr_entry_real_size(0, acl_len); if (S_ISDIR(mode)) diff --git a/fs/proc/base.c b/fs/proc/base.c index 2b7fe41740f5..4c0989076151 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -95,6 +95,8 @@ #include "internal.h" #include "fd.h" +#include "../../lib/kstrtox.h" + /* NOTE: * Implementing inode permission operations in /proc is almost * certainly an error. Permission checks need to happen during @@ -2040,8 +2042,33 @@ end_instantiate: static int dname_to_vma_addr(struct dentry *dentry, unsigned long *start, unsigned long *end) { - if (sscanf(dentry->d_name.name, "%lx-%lx", start, end) != 2) + const char *str = dentry->d_name.name; + unsigned long long sval, eval; + unsigned int len; + + len = _parse_integer(str, 16, &sval); + if (len & KSTRTOX_OVERFLOW) + return -EINVAL; + if (sval != (unsigned long)sval) return -EINVAL; + str += len; + + if (*str != '-') + return -EINVAL; + str++; + + len = _parse_integer(str, 16, &eval); + if (len & KSTRTOX_OVERFLOW) + return -EINVAL; + if (eval != (unsigned long)eval) + return -EINVAL; + str += len; + + if (*str != '\0') + return -EINVAL; + + *start = sval; + *end = eval; return 0; } diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 4dbe1e2daeca..5e1054f028af 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -654,7 +654,10 @@ static bool proc_sys_link_fill_cache(struct file *file, struct ctl_table *table) { bool ret = true; + head = sysctl_head_grab(head); + if (IS_ERR(head)) + return false; if (S_ISLNK(table->mode)) { /* It is not an error if we can not follow the link ignore it */ diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index 3ebc70167e41..eb611bdd4725 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -687,8 +687,7 @@ static int reiserfs_create(struct inode *dir, struct dentry *dentry, umode_t mod reiserfs_update_inode_transaction(inode); reiserfs_update_inode_transaction(dir); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); retval = journal_end(&th); out_failed: @@ -771,8 +770,7 @@ static int reiserfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode goto out_failed; } - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); retval = journal_end(&th); out_failed: @@ -871,8 +869,7 @@ static int reiserfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* the above add_entry did not update dir's stat data */ reiserfs_update_sd(&th, dir); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); retval = journal_end(&th); out_failed: reiserfs_write_unlock(dir->i_sb); @@ -1186,8 +1183,7 @@ static int reiserfs_symlink(struct inode *parent_dir, goto out_failed; } - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); retval = journal_end(&th); out_failed: reiserfs_write_unlock(parent_dir->i_sb); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index c97b5a8d1e24..f34c545f4e54 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -611,8 +611,7 @@ static int udf_add_nondir(struct dentry *dentry, struct inode *inode) if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; } @@ -722,8 +721,7 @@ static int udf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inc_nlink(dir); dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); mark_inode_dirty(dir); - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); diff --git a/fs/udf/super.c b/fs/udf/super.c index ee09c97f3ab2..159977ec8e54 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -2073,8 +2073,9 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) bool lvid_open = false; uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); - uopt.uid = INVALID_UID; - uopt.gid = INVALID_GID; + /* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */ + uopt.uid = make_kuid(current_user_ns(), overflowuid); + uopt.gid = make_kgid(current_user_ns(), overflowgid); uopt.umask = 0; uopt.fmode = UDF_INVALID_MODE; uopt.dmode = UDF_INVALID_MODE; diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 47966554317c..2ec7689c25cf 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -38,8 +38,7 @@ static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode) { int err = ufs_add_link(dentry, inode); if (!err) { - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; } inode_dec_link_count(inode); @@ -191,8 +190,7 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode) if (err) goto out_fail; - unlock_new_inode(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); return 0; out_fail: diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index e1e7fe3b5424..b663b756f552 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -1924,6 +1924,93 @@ xfs_alloc_space_available( } /* + * Check the agfl fields of the agf for inconsistency or corruption. The purpose + * is to detect an agfl header padding mismatch between current and early v5 + * kernels. This problem manifests as a 1-slot size difference between the + * on-disk flcount and the active [first, last] range of a wrapped agfl. This + * may also catch variants of agfl count corruption unrelated to padding. Either + * way, we'll reset the agfl and warn the user. + * + * Return true if a reset is required before the agfl can be used, false + * otherwise. + */ +static bool +xfs_agfl_needs_reset( + struct xfs_mount *mp, + struct xfs_agf *agf) +{ + uint32_t f = be32_to_cpu(agf->agf_flfirst); + uint32_t l = be32_to_cpu(agf->agf_fllast); + uint32_t c = be32_to_cpu(agf->agf_flcount); + int agfl_size = XFS_AGFL_SIZE(mp); + int active; + + /* no agfl header on v4 supers */ + if (!xfs_sb_version_hascrc(&mp->m_sb)) + return false; + + /* + * The agf read verifier catches severe corruption of these fields. + * Repeat some sanity checks to cover a packed -> unpacked mismatch if + * the verifier allows it. + */ + if (f >= agfl_size || l >= agfl_size) + return true; + if (c > agfl_size) + return true; + + /* + * Check consistency between the on-disk count and the active range. An + * agfl padding mismatch manifests as an inconsistent flcount. + */ + if (c && l >= f) + active = l - f + 1; + else if (c) + active = agfl_size - f + l + 1; + else + active = 0; + + return active != c; +} + +/* + * Reset the agfl to an empty state. Ignore/drop any existing blocks since the + * agfl content cannot be trusted. Warn the user that a repair is required to + * recover leaked blocks. + * + * The purpose of this mechanism is to handle filesystems affected by the agfl + * header padding mismatch problem. A reset keeps the filesystem online with a + * relatively minor free space accounting inconsistency rather than suffer the + * inevitable crash from use of an invalid agfl block. + */ +static void +xfs_agfl_reset( + struct xfs_trans *tp, + struct xfs_buf *agbp, + struct xfs_perag *pag) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp); + + ASSERT(pag->pagf_agflreset); + trace_xfs_agfl_reset(mp, agf, 0, _RET_IP_); + + xfs_warn(mp, + "WARNING: Reset corrupted AGFL on AG %u. %d blocks leaked. " + "Please unmount and run xfs_repair.", + pag->pag_agno, pag->pagf_flcount); + + agf->agf_flfirst = 0; + agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1); + agf->agf_flcount = 0; + xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLFIRST | XFS_AGF_FLLAST | + XFS_AGF_FLCOUNT); + + pag->pagf_flcount = 0; + pag->pagf_agflreset = false; +} + +/* * Decide whether to use this allocation group for this allocation. * If so, fix up the btree freelist's size. */ @@ -1983,6 +2070,10 @@ xfs_alloc_fix_freelist( } } + /* reset a padding mismatched agfl before final free space check */ + if (pag->pagf_agflreset) + xfs_agfl_reset(tp, agbp, pag); + /* If there isn't enough total space or single-extent, reject it. */ need = xfs_alloc_min_freelist(mp, pag); if (!xfs_alloc_space_available(args, need, flags)) @@ -2121,6 +2212,7 @@ xfs_alloc_get_freelist( agf->agf_flfirst = 0; pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); + ASSERT(!pag->pagf_agflreset); be32_add_cpu(&agf->agf_flcount, -1); xfs_trans_agflist_delta(tp, -1); pag->pagf_flcount--; @@ -2226,6 +2318,7 @@ xfs_alloc_put_freelist( agf->agf_fllast = 0; pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); + ASSERT(!pag->pagf_agflreset); be32_add_cpu(&agf->agf_flcount, 1); xfs_trans_agflist_delta(tp, 1); pag->pagf_flcount++; @@ -2417,6 +2510,7 @@ xfs_alloc_read_agf( pag->pagb_count = 0; pag->pagb_tree = RB_ROOT; pag->pagf_init = 1; + pag->pagf_agflreset = xfs_agfl_needs_reset(mp, agf); } #ifdef DEBUG else if (!XFS_FORCED_SHUTDOWN(mp)) { diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index f949818fa1c7..fb9636cc927c 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -130,9 +130,6 @@ xfs_attr_get( if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return -EIO; - if (!xfs_inode_hasattr(ip)) - return -ENOATTR; - error = xfs_attr_args_init(&args, ip, name, flags); if (error) return error; @@ -417,9 +414,6 @@ xfs_attr_remove( if (XFS_FORCED_SHUTDOWN(dp->i_mount)) return -EIO; - if (!xfs_inode_hasattr(dp)) - return -ENOATTR; - error = xfs_attr_args_init(&args, dp, name, flags); if (error) return error; diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index e85a9519a5ae..64ad05cb831a 100644 --- a/fs/xfs/xfs_discard.c +++ b/fs/xfs/xfs_discard.c @@ -50,19 +50,19 @@ xfs_trim_extents( pag = xfs_perag_get(mp, agno); - error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp); - if (error || !agbp) - goto out_put_perag; - - cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_CNT); - /* * Force out the log. This means any transactions that might have freed - * space before we took the AGF buffer lock are now on disk, and the + * space before we take the AGF buffer lock are now on disk, and the * volatile disk cache is flushed. */ xfs_log_force(mp, XFS_LOG_SYNC); + error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp); + if (error || !agbp) + goto out_put_perag; + + cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_CNT); + /* * Look up the longest btree in the AGF and start with it. */ diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index f52c72a1a06f..73b725f965eb 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -3323,8 +3323,6 @@ maybe_sleep: */ if (iclog->ic_state & XLOG_STATE_IOERROR) return -EIO; - if (log_flushed) - *log_flushed = 1; } else { no_sleep: @@ -3432,8 +3430,6 @@ try_again: xlog_wait(&iclog->ic_prev->ic_write_wait, &log->l_icloglock); - if (log_flushed) - *log_flushed = 1; already_slept = 1; goto try_again; } @@ -3467,9 +3463,6 @@ try_again: */ if (iclog->ic_state & XLOG_STATE_IOERROR) return -EIO; - - if (log_flushed) - *log_flushed = 1; } else { /* just return */ spin_unlock(&log->l_icloglock); } diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index b57098481c10..ae3e52749f20 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -278,6 +278,7 @@ typedef struct xfs_perag { char pagi_inodeok; /* The agi is ok for inodes */ __uint8_t pagf_levels[XFS_BTNUM_AGF]; /* # of levels in bno & cnt btree */ + bool pagf_agflreset; /* agfl requires reset before use */ __uint32_t pagf_flcount; /* count of blocks in freelist */ xfs_extlen_t pagf_freeblks; /* total free blocks */ xfs_extlen_t pagf_longest; /* longest free space */ diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 877079eb0f8f..cc6fa64821d2 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1485,7 +1485,7 @@ TRACE_EVENT(xfs_trans_commit_lsn, __entry->lsn) ); -TRACE_EVENT(xfs_agf, +DECLARE_EVENT_CLASS(xfs_agf_class, TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags, unsigned long caller_ip), TP_ARGS(mp, agf, flags, caller_ip), @@ -1541,6 +1541,13 @@ TRACE_EVENT(xfs_agf, __entry->longest, (void *)__entry->caller_ip) ); +#define DEFINE_AGF_EVENT(name) \ +DEFINE_EVENT(xfs_agf_class, name, \ + TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags, \ + unsigned long caller_ip), \ + TP_ARGS(mp, agf, flags, caller_ip)) +DEFINE_AGF_EVENT(xfs_agf); +DEFINE_AGF_EVENT(xfs_agfl_reset); TRACE_EVENT(xfs_free_extent, TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno, diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 4814cf971048..25b793325b09 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -237,6 +237,21 @@ extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, extern pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp); #endif +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +/* + * This is an implementation of pmdp_establish() that is only suitable for an + * architecture that doesn't have hardware dirty/accessed bits. In this case we + * can't race with CPU which sets these bits and non-atomic aproach is fine. + */ +static inline pmd_t generic_pmdp_establish(struct vm_area_struct *vma, + unsigned long address, pmd_t *pmdp, pmd_t pmd) +{ + pmd_t old_pmd = *pmdp; + set_pmd_at(vma->vm_mm, address, pmdp, pmd); + return old_pmd; +} +#endif + #ifndef __HAVE_ARCH_PMDP_INVALIDATE extern void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index c066f6b56e58..9df992b818a4 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -238,6 +238,7 @@ extern seqlock_t rename_lock; * These are the low-level FS interfaces to the dcache.. */ extern void d_instantiate(struct dentry *, struct inode *); +extern void d_instantiate_new(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern int d_instantiate_no_diralias(struct dentry *, struct inode *); extern void __d_drop(struct dentry *dentry); diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h index 2cd41e0cd394..b5ed9b114317 100644 --- a/include/linux/diagchar.h +++ b/include/linux/diagchar.h @@ -145,7 +145,7 @@ the appropriate macros. */ /* This needs to be modified manually now, when we add a new RANGE of SSIDs to the msg_mask_tbl */ #define MSG_MASK_TBL_CNT 26 -#define APPS_EVENT_LAST_ID 0x0C5B +#define APPS_EVENT_LAST_ID 0xC7A #define MSG_SSID_0 0 #define MSG_SSID_0_LAST 125 diff --git a/include/linux/fs.h b/include/linux/fs.h index 250f4d1ce9c5..f5f4e7871865 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -964,9 +964,9 @@ static inline struct file *get_file(struct file *f) /* Page cache limit. The filesystems should put that into their s_maxbytes limits, otherwise bad things can happen in VM. */ #if BITS_PER_LONG==32 -#define MAX_LFS_FILESIZE (((loff_t)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1) +#define MAX_LFS_FILESIZE ((loff_t)ULONG_MAX << PAGE_SHIFT) #elif BITS_PER_LONG==64 -#define MAX_LFS_FILESIZE ((loff_t)0x7fffffffffffffffLL) +#define MAX_LFS_FILESIZE ((loff_t)LLONG_MAX) #endif #define FL_POSIX 1 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/linux/suspend.h b/include/linux/suspend.h index c59803dc68de..be1ab158ad1a 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -377,6 +377,8 @@ extern int swsusp_page_is_forbidden(struct page *); extern void swsusp_set_page_free(struct page *); extern void swsusp_unset_page_free(struct page *); extern unsigned long get_safe_page(gfp_t gfp_mask); +extern asmlinkage int swsusp_arch_suspend(void); +extern asmlinkage int swsusp_arch_resume(void); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 2260f92f1492..5b6df1a8dc74 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -324,7 +324,7 @@ struct tcp_sock { /* Receiver queue space */ struct { - int space; + u32 space; u32 seq; u32 time; } rcvq_space; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 4cde40dac778..dc35fa77b3d2 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -57,6 +57,9 @@ /* big enough to hold our biggest descriptor */ #define USB_COMP_EP0_BUFSIZ 4096 +/* OS feature descriptor length <= 4kB */ +#define USB_COMP_EP0_OS_DESC_BUFSIZ 4096 + #define USB_MS_TO_HS_INTERVAL(x) (ilog2((x * 1000 / 125)) + 1) struct usb_configuration; diff --git a/include/net/cnss.h b/include/net/cnss.h index d6f27759af17..ea0082dc1219 100644 --- a/include/net/cnss.h +++ b/include/net/cnss.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, 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 @@ -55,6 +55,7 @@ struct cnss_wlan_driver { int (*suspend)(struct pci_dev *pdev, pm_message_t state); int (*resume)(struct pci_dev *pdev); void (*modem_status)(struct pci_dev *, int state); + void (*update_status)(struct pci_dev *pdev, uint32_t status); struct cnss_wlan_runtime_ops *runtime_ops; const struct pci_device_id *id_table; }; @@ -93,11 +94,14 @@ struct cnss_platform_cap { u32 cap_flag; }; -/* WLAN driver status */ +/* WLAN driver status, keep it aligned with cnss2 */ enum cnss_driver_status { CNSS_UNINITIALIZED, CNSS_INITIALIZED, - CNSS_LOAD_UNLOAD + CNSS_LOAD_UNLOAD, + CNSS_RECOVERY, + CNSS_FW_DOWN, + CNSS_SSR_FAIL, }; enum cnss_runtime_request { diff --git a/include/net/ip.h b/include/net/ip.h index 17997b48102d..81c7408deb83 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -282,6 +282,13 @@ int ip_decrease_ttl(struct iphdr *iph) return --iph->ttl; } +static inline int ip_mtu_locked(const struct dst_entry *dst) +{ + const struct rtable *rt = (const struct rtable *)dst; + + return rt->rt_mtu_locked || dst_metric_locked(dst, RTAX_MTU); +} + static inline int ip_dont_fragment(const struct sock *sk, const struct dst_entry *dst) { @@ -289,7 +296,7 @@ int ip_dont_fragment(const struct sock *sk, const struct dst_entry *dst) return pmtudisc == IP_PMTUDISC_DO || (pmtudisc == IP_PMTUDISC_WANT && - !(dst_metric_locked(dst, RTAX_MTU))); + !ip_mtu_locked(dst)); } static inline bool ip_sk_accept_pmtu(const struct sock *sk) @@ -315,7 +322,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, struct net *net = dev_net(dst->dev); if (net->ipv4.sysctl_ip_fwd_use_pmtu || - dst_metric_locked(dst, RTAX_MTU) || + ip_mtu_locked(dst) || !forwarding) return dst_mtu(dst); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index bda1721e9622..3afb7c4c7098 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -56,6 +56,7 @@ struct fib_nh_exception { int fnhe_genid; __be32 fnhe_daddr; u32 fnhe_pmtu; + bool fnhe_mtu_locked; __be32 fnhe_gw; unsigned long fnhe_expires; struct rtable __rcu *fnhe_rth_input; diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h index ea985aa7a6c5..df528a623548 100644 --- a/include/net/llc_conn.h +++ b/include/net/llc_conn.h @@ -104,7 +104,7 @@ void llc_sk_reset(struct sock *sk); /* Access to a connection */ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb); -void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb); +int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb); void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb); void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit); void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4ca1c04a6f1f..c1744c2f8aca 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3910,7 +3910,7 @@ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, * The TX headroom reserved by mac80211 for its own tx_status functions. * This is enough for the radiotap header. */ -#define IEEE80211_TX_STATUS_HEADROOM 14 +#define IEEE80211_TX_STATUS_HEADROOM ALIGN(14, 4) /** * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames diff --git a/include/net/regulatory.h b/include/net/regulatory.h index ebc5a2ed8631..f83cacce3308 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -78,7 +78,7 @@ struct regulatory_request { int wiphy_idx; enum nl80211_reg_initiator initiator; enum nl80211_user_reg_hint_type user_reg_hint_type; - char alpha2[2]; + char alpha2[3]; enum nl80211_dfs_regions dfs_region; bool intersect; bool processed; diff --git a/include/net/route.h b/include/net/route.h index 3adb9c724818..11dfd0df0e67 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -64,7 +64,8 @@ struct rtable { __be32 rt_gateway; /* Miscellaneous cached information */ - u32 rt_pmtu; + u32 rt_mtu_locked:1, + rt_pmtu:31; u32 rt_table_id; diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index ee65bdae9971..48fe32252e8d 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -11587,4 +11587,9 @@ struct admx_sec_primary_mic_ch { uint16_t reserved1; } __packed; +/* +* ID of the DTMF Detection module. +*/ +#define AUDPROC_MODULE_ID_DTMF_DETECTION 0x00010940 + #endif /*_APR_AUDIO_V2_H_ */ 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/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 9df3e77da05b..3523fac586ce 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -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 @@ -309,6 +309,10 @@ int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, int q6asm_open_write_v5(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample); + +int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, int32_t stream_id, bool is_gapless_mode); diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h index 073b9ac245ba..e844556794dc 100644 --- a/include/trace/events/timer.h +++ b/include/trace/events/timer.h @@ -125,6 +125,20 @@ DEFINE_EVENT(timer_class, timer_cancel, TP_ARGS(timer) ); +#define decode_clockid(type) \ + __print_symbolic(type, \ + { CLOCK_REALTIME, "CLOCK_REALTIME" }, \ + { CLOCK_MONOTONIC, "CLOCK_MONOTONIC" }, \ + { CLOCK_BOOTTIME, "CLOCK_BOOTTIME" }, \ + { CLOCK_TAI, "CLOCK_TAI" }) + +#define decode_hrtimer_mode(mode) \ + __print_symbolic(mode, \ + { HRTIMER_MODE_ABS, "ABS" }, \ + { HRTIMER_MODE_REL, "REL" }, \ + { HRTIMER_MODE_ABS_PINNED, "ABS|PINNED" }, \ + { HRTIMER_MODE_REL_PINNED, "REL|PINNED" }) + /** * hrtimer_init - called when the hrtimer is initialized * @hrtimer: pointer to struct hrtimer @@ -151,10 +165,8 @@ TRACE_EVENT(hrtimer_init, ), TP_printk("hrtimer=%p clockid=%s mode=%s", __entry->hrtimer, - __entry->clockid == CLOCK_REALTIME ? - "CLOCK_REALTIME" : "CLOCK_MONOTONIC", - __entry->mode == HRTIMER_MODE_ABS ? - "HRTIMER_MODE_ABS" : "HRTIMER_MODE_REL") + decode_clockid(__entry->clockid), + decode_hrtimer_mode(__entry->mode)) ); /** diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h index 9ed3a13953ef..5f5ca0345140 100644 --- a/include/uapi/drm/msm_drm_pp.h +++ b/include/uapi/drm/msm_drm_pp.h @@ -52,6 +52,34 @@ struct drm_msm_pa_vlut { __u32 val[PA_VLUT_SIZE]; }; +#define PA_HSIC_HUE_ENABLE (1 << 0) +#define PA_HSIC_SAT_ENABLE (1 << 1) +#define PA_HSIC_VAL_ENABLE (1 << 2) +#define PA_HSIC_CONT_ENABLE (1 << 3) +#define PA_HSIC_LEFT_DISPLAY_ONLY (1 << 4) +#define PA_HSIC_RIGHT_DISPLAY_ONLY (1 << 5) +/** + * struct drm_msm_pa_hsic - pa hsic feature structure + * @flags: flags for the feature customization, values can be: + * - PA_HSIC_HUE_ENABLE: Enable hue adjustment + * - PA_HSIC_SAT_ENABLE: Enable saturation adjustment + * - PA_HSIC_VAL_ENABLE: Enable value adjustment + * - PA_HSIC_CONT_ENABLE: Enable contrast adjustment + * + * @hue: hue setting + * @saturation: saturation setting + * @value: value setting + * @contrast: contrast setting + */ +#define DRM_MSM_PA_HSIC +struct drm_msm_pa_hsic { + __u64 flags; + __u32 hue; + __u32 saturation; + __u32 value; + __u32 contrast; +}; + /* struct drm_msm_memcol - Memory color feature strucuture. * Skin, sky, foliage features are supported. * @prot_flags: Bit mask for enabling protection feature. @@ -79,4 +107,94 @@ struct drm_msm_memcol { __u32 val_region; }; +#define GAMUT_3D_MODE_17 1 +#define GAMUT_3D_MODE_5 2 +#define GAMUT_3D_MODE_13 3 + +#define GAMUT_3D_MODE17_TBL_SZ 1229 +#define GAMUT_3D_MODE5_TBL_SZ 32 +#define GAMUT_3D_MODE13_TBL_SZ 550 +#define GAMUT_3D_SCALE_OFF_SZ 16 +#define GAMUT_3D_SCALEB_OFF_SZ 12 +#define GAMUT_3D_TBL_NUM 4 +#define GAMUT_3D_SCALE_OFF_TBL_NUM 3 +#define GAMUT_3D_MAP_EN (1 << 0) + +/** + * struct drm_msm_3d_col - 3d gamut color component structure + * @c0: Holds c0 value + * @c2_c1: Holds c2/c1 values + */ +struct drm_msm_3d_col { + __u32 c2_c1; + __u32 c0; +}; +/** + * struct drm_msm_3d_gamut - 3d gamut feature structure + * @flags: flags for the feature values are: + * 0 - no map + * GAMUT_3D_MAP_EN - enable map + * @mode: lut mode can take following values: + * - GAMUT_3D_MODE_17 + * - GAMUT_3D_MODE_5 + * - GAMUT_3D_MODE_13 + * @scale_off: Scale offset table + * @col: Color component tables + */ +struct drm_msm_3d_gamut { + __u64 flags; + __u32 mode; + __u32 scale_off[GAMUT_3D_SCALE_OFF_TBL_NUM][GAMUT_3D_SCALE_OFF_SZ]; + struct drm_msm_3d_col col[GAMUT_3D_TBL_NUM][GAMUT_3D_MODE17_TBL_SZ]; +}; + +#define PGC_TBL_LEN 512 +#define PGC_8B_ROUND (1 << 0) +/** + * struct drm_msm_pgc_lut - pgc lut feature structure + * @flags: flags for the featue values can be: + * - PGC_8B_ROUND + * @c0: color0 component lut + * @c1: color1 component lut + * @c2: color2 component lut + */ +struct drm_msm_pgc_lut { + __u64 flags; + __u32 c0[PGC_TBL_LEN]; + __u32 c1[PGC_TBL_LEN]; + __u32 c2[PGC_TBL_LEN]; +}; + + +#define IGC_TBL_LEN 256 +#define IGC_DITHER_ENABLE (1 << 0) +/** + * struct drm_msm_igc_lut - igc lut feature structure + * @flags: flags for the feature customization, values can be: + * - IGC_DITHER_ENABLE: Enable dither functionality + * @c0: color0 component lut + * @c1: color1 component lut + * @c2: color2 component lut + * @strength: dither strength, considered valid when IGC_DITHER_ENABLE + * is set in flags. Strength value based on source bit width. + */ +struct drm_msm_igc_lut { + __u64 flags; + __u32 c0[IGC_TBL_LEN]; + __u32 c1[IGC_TBL_LEN]; + __u32 c2[IGC_TBL_LEN]; + __u32 strength; +}; + +#define HIST_V_SIZE 256 +/** + * struct drm_msm_hist - histogram feature structure + * @flags: for customizing operations + * @data: histogram data + */ +struct drm_msm_hist { + __u64 flags; + __u32 data[HIST_V_SIZE]; +}; + #endif /* _MSM_DRM_PP_H_ */ diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h index fc9e2d6e5e2f..232367124712 100644 --- a/include/uapi/drm/virtgpu_drm.h +++ b/include/uapi/drm/virtgpu_drm.h @@ -60,6 +60,7 @@ struct drm_virtgpu_execbuffer { }; #define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */ +#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */ struct drm_virtgpu_getparam { uint64_t param; diff --git a/include/uapi/linux/hab_ioctl.h b/include/uapi/linux/hab_ioctl.h index 83b5da236888..70e16433044e 100644 --- a/include/uapi/linux/hab_ioctl.h +++ b/include/uapi/linux/hab_ioctl.h @@ -69,7 +69,6 @@ struct hab_info { }; #define HAB_IOC_TYPE 0x0A -#define HAB_MAX_MSG_SIZEBYTES 0x1000 #define IOCTL_HAB_SEND \ _IOW(HAB_IOC_TYPE, 0x2, struct hab_send) diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index a3e44bc9889b..602aca385a27 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -29,6 +29,7 @@ */ #define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_TLEN 2 /* Octets in ethernet type field */ #define ETH_HLEN 14 /* Total octets in header. */ #define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ #define ETH_DATA_LEN 1500 /* Max. octets in payload */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index dd57d8bba233..03c9d5aef5c7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2492,7 +2492,7 @@ enum nl80211_attrs { #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS -#define NL80211_WIPHY_NAME_MAXLEN 128 +#define NL80211_WIPHY_NAME_MAXLEN 64 #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 diff --git a/include/uapi/media/ais/msm_ais_isp.h b/include/uapi/media/ais/msm_ais_isp.h index 55bc5290ce28..649173dd4404 100644 --- a/include/uapi/media/ais/msm_ais_isp.h +++ b/include/uapi/media/ais/msm_ais_isp.h @@ -619,6 +619,33 @@ struct msm_vfe_axi_src_state { uint32_t src_frame_id; }; +enum msm_vfe_cmd_ext_type_t { + VFE_GET_BUFQ_STATE, +}; + +enum msm_isp_buffer_state { + MSM_ISP_BUFFER_STATE_UNUSED, /* not used */ + MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */ + MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */ + MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */ + MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */ + MSM_ISP_BUFFER_STATE_DIVERTED, /* Sent to other hardware*/ + MSM_ISP_BUFFER_STATE_DISPATCHED, /* Sent to HAL*/ +}; + +struct msm_vfe_bufq_state { + uint32_t handle; + uint32_t nbufs; + int32_t __user *buf_state; +}; + +struct msm_vfe_cmd_ext { + enum msm_vfe_cmd_ext_type_t type; + union { + struct msm_vfe_bufq_state bufq_state; + } data; +}; + enum msm_isp_event_mask_index { ISP_EVENT_MASK_INDEX_STATS_NOTIFY = 0, ISP_EVENT_MASK_INDEX_ERROR = 1, @@ -909,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; @@ -975,6 +1009,8 @@ enum msm_isp_ioctl_cmd_code { MSM_ISP_STOP, MSM_ISP_SET_CLK_STATUS, + MSM_ISP_CMD_EXT, + MSM_ISP_FRAMEDROP_UPDATE, }; @@ -1102,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) @@ -1110,4 +1150,8 @@ enum msm_isp_ioctl_cmd_code { _IOWR('V', MSM_ISP_SET_CLK_STATUS, \ unsigned int) +#define VIDIOC_MSM_ISP_CMD_EXT \ + _IOWR('V', MSM_ISP_CMD_EXT, \ + struct msm_vfe_cmd_ext) + #endif /* __UAPI_MSM_AIS_ISP__ */ diff --git a/include/uapi/media/ais/msm_ais_ispif.h b/include/uapi/media/ais/msm_ais_ispif.h index b12175d787c2..a184e2983ab6 100644 --- a/include/uapi/media/ais/msm_ais_ispif.h +++ b/include/uapi/media/ais/msm_ais_ispif.h @@ -4,6 +4,7 @@ #include <linux/types.h> #include <linux/ioctl.h> #include <linux/videodev2.h> +#include <media/ais/msm_ais_mgr.h> #define CSID_VERSION_V20 0x02000011 #define CSID_VERSION_V22 0x02001000 @@ -141,10 +142,13 @@ enum ispif_cfg_type_t { ISPIF_STOP, ISPIF_ENABLE_REG_DUMP, ISPIF_SET_VFE_INFO, - ISPIF_CFG2 + ISPIF_CFG2, + ISPIF_READ_REG_LIST_CMD, + ISPIF_WRITE_REG_LIST_CMD, }; + struct ispif_cfg_data_ext { enum ispif_cfg_type_t cfg_type; void __user *data; @@ -158,10 +162,12 @@ struct ispif_cfg_data { uint32_t csid_version; /* ISPIF_INIT */ struct msm_ispif_vfe_info vfe_info; /* ISPIF_SET_VFE_INFO */ struct msm_ispif_param_data params; /* CFG, START, STOP */ + struct msm_camera_reg_list_cmd *reg_list; }; }; #define ISPIF_RDI_PACK_MODE_SUPPORT 1 +#define ISPIF_RW_REG_LIST_SUPPORT #define VIDIOC_MSM_ISPIF_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data) diff --git a/include/uapi/media/ais/msm_ais_mgr.h b/include/uapi/media/ais/msm_ais_mgr.h index 43ae16df65f9..bfac1ac8296a 100644 --- a/include/uapi/media/ais/msm_ais_mgr.h +++ b/include/uapi/media/ais/msm_ais_mgr.h @@ -3,16 +3,87 @@ #include <media/ais/msm_ais.h> +#define VREGNAME_SIZE 32 +#define CLKNAME_SIZE 32 + +enum cam_ahb_clk_vote { + /* need to update the voting requests + * according to dtsi entries. + */ + CAM_AHB_SUSPEND_VOTE = 0x0, + CAM_AHB_SVS_VOTE = 0x01, + CAM_AHB_NOMINAL_VOTE = 0x02, + CAM_AHB_TURBO_VOTE = 0x03, + CAM_AHB_DYNAMIC_VOTE = 0xFF, +}; + enum clk_mgr_cfg_type_t { AIS_CLK_ENABLE, AIS_CLK_DISABLE, + AIS_CLK_ENABLE_ALLCLK, + AIS_CLK_DISABLE_ALLCLK +}; + +enum ais_mgr_cfg_ext_type_t { + AIS_DIAG_GET_REGULATOR_INFO_LIST, + AIS_DIAG_GET_BUS_INFO_STATE, + AIS_DIAG_GET_CLK_INFO_LIST, + AIS_DIAG_GET_GPIO_LIST, + AIS_DIAG_SET_GPIO_LIST, }; #define AIS_CLK_ENABLE AIS_CLK_ENABLE #define AIS_CLK_DISABLE AIS_CLK_DISABLE + +struct msm_camera_reg_list_cmd { + void __user *value_list; + void __user *regaddr_list; + uint32_t reg_num; +}; + +struct msm_ais_diag_regulator_info_t { + int enable; + char regulatorname[VREGNAME_SIZE]; +}; + +struct msm_ais_diag_regulator_info_list_t { + struct msm_ais_diag_regulator_info_t *infolist; + uint32_t regulator_num; +}; + +struct msm_ais_diag_bus_info_t { + enum cam_ahb_clk_vote ahb_clk_vote_state; + uint32_t isp_bus_vector_idx; /* 0 - init 1- ping 2 - pong */ + uint64_t isp_ab; + uint64_t isp_ib; +}; + +struct msm_ais_diag_clk_info_t { + char clk_name[CLKNAME_SIZE]; + long clk_rate; + uint8_t enable; +}; + +struct msm_ais_diag_clk_list_t { + void __user *clk_info; + uint32_t clk_num; +}; + +struct msm_ais_diag_gpio_list_t { + uint32_t __user *gpio_idx_list; + int32_t __user *gpio_val_list; + uint32_t gpio_num; +}; + struct clk_mgr_cfg_data_ext { - enum clk_mgr_cfg_type_t cfg_type; + enum ais_mgr_cfg_ext_type_t cfg_type; + union { + struct msm_ais_diag_regulator_info_list_t vreg_infolist; + struct msm_ais_diag_bus_info_t bus_info; + struct msm_ais_diag_clk_list_t clk_infolist; + struct msm_ais_diag_gpio_list_t gpio_list; + } data; }; struct clk_mgr_cfg_data { diff --git a/include/uapi/media/ais/msm_ais_sensor.h b/include/uapi/media/ais/msm_ais_sensor.h index ca9bcf96bcb0..633f3f227174 100644 --- a/include/uapi/media/ais/msm_ais_sensor.h +++ b/include/uapi/media/ais/msm_ais_sensor.h @@ -3,7 +3,7 @@ #include <linux/v4l2-mediabus.h> #include <media/ais/msm_ais_sensor_sdk.h> - +#include <media/ais/msm_ais_mgr.h> #include <linux/types.h> #include <linux/i2c.h> @@ -152,6 +152,8 @@ enum csid_cfg_type_t { CSID_START, CSID_STOP, CSID_RELEASE, + CSID_READ_REG_LIST_CMD, + CSID_WRITE_REG_LIST_CMD, }; enum csiphy_cfg_type_t { @@ -160,6 +162,8 @@ enum csiphy_cfg_type_t { CSIPHY_START, CSIPHY_STOP, CSIPHY_RELEASE, + CSIPHY_READ_REG_LIST_CMD, + CSIPHY_WRITE_REG_LIST_CMD, }; enum camera_vreg_type { @@ -291,6 +295,7 @@ struct csid_cfg_data { struct msm_camera_csid_params *csid_params; struct msm_camera_csid_testmode_parms *csid_testmode_params; uint32_t csid_cidmask; + struct msm_camera_reg_list_cmd *csid_reg_list_cmd; } cfg; }; @@ -299,6 +304,7 @@ struct csiphy_cfg_data { union { struct msm_camera_csiphy_params *csiphy_params; struct msm_camera_csi_lane_params *csi_lane_params; + struct msm_camera_reg_list_cmd *csiphy_reg_list_cmd; } cfg; }; @@ -613,6 +619,8 @@ struct sensor_init_cfg_data { } cfg; }; +#define CSI_RW_REG_LIST_SUPPORT + #define VIDIOC_MSM_SENSOR_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data) diff --git a/init/Kconfig b/init/Kconfig index cda05668350c..ec2342b3befe 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1426,6 +1426,17 @@ source "usr/Kconfig" endif +choice + prompt "Compiler optimization level" + default CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE + +config CC_OPTIMIZE_FOR_PERFORMANCE + bool "Optimize for performance" + help + This is the default optimization level for the kernel, building + with the "-O2" compiler flag for best performance and most + helpful compile-time warnings. + config CC_OPTIMIZE_FOR_SIZE bool "Optimize for size" help @@ -1434,6 +1445,8 @@ config CC_OPTIMIZE_FOR_SIZE If unsure, say N. +endchoice + config SYSCTL bool diff --git a/ipc/shm.c b/ipc/shm.c index 9fa852a6473b..5095eb3a6a6c 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1113,14 +1113,17 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, goto out; else if ((addr = (ulong)shmaddr)) { if (addr & (shmlba - 1)) { - /* - * Round down to the nearest multiple of shmlba. - * For sane do_mmap_pgoff() parameters, avoid - * round downs that trigger nil-page and MAP_FIXED. - */ - if ((shmflg & SHM_RND) && addr >= shmlba) - addr &= ~(shmlba - 1); - else + if (shmflg & SHM_RND) { + addr &= ~(shmlba - 1); /* round down */ + + /* + * Ensure that the round-down is non-nil + * when remapping. This can happen for + * cases when addr < shmlba. + */ + if (!addr && (shmflg & SHM_REMAP)) + goto out; + } else #ifndef __ARCH_FORCE_SHMLBA if (addr & ~PAGE_MASK) #endif diff --git a/kernel/audit.c b/kernel/audit.c index d8e9fba58cbc..d440c25cb3be 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -744,6 +744,8 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature return; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE); + if (!ab) + return; audit_log_task_info(ab, current); audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d", audit_feature_names[which], !!old_feature, !!new_feature, diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 4121345498e0..ebc52c7bd8a6 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -1564,6 +1564,7 @@ static int kdb_md(int argc, const char **argv) int symbolic = 0; int valid = 0; int phys = 0; + int raw = 0; kdbgetintenv("MDCOUNT", &mdcount); kdbgetintenv("RADIX", &radix); @@ -1573,9 +1574,10 @@ static int kdb_md(int argc, const char **argv) repeat = mdcount * 16 / bytesperword; if (strcmp(argv[0], "mdr") == 0) { - if (argc != 2) + if (argc == 2 || (argc == 0 && last_addr != 0)) + valid = raw = 1; + else return KDB_ARGCOUNT; - valid = 1; } else if (isdigit(argv[0][2])) { bytesperword = (int)(argv[0][2] - '0'); if (bytesperword == 0) { @@ -1611,7 +1613,10 @@ static int kdb_md(int argc, const char **argv) radix = last_radix; bytesperword = last_bytesperword; repeat = last_repeat; - mdcount = ((repeat * bytesperword) + 15) / 16; + if (raw) + mdcount = repeat; + else + mdcount = ((repeat * bytesperword) + 15) / 16; } if (argc) { @@ -1628,7 +1633,10 @@ static int kdb_md(int argc, const char **argv) diag = kdbgetularg(argv[nextarg], &val); if (!diag) { mdcount = (int) val; - repeat = mdcount * 16 / bytesperword; + if (raw) + repeat = mdcount; + else + repeat = mdcount * 16 / bytesperword; } } if (argc >= nextarg+1) { @@ -1638,8 +1646,15 @@ static int kdb_md(int argc, const char **argv) } } - if (strcmp(argv[0], "mdr") == 0) - return kdb_mdr(addr, mdcount); + if (strcmp(argv[0], "mdr") == 0) { + int ret; + last_addr = addr; + ret = kdb_mdr(addr, mdcount); + last_addr += mdcount; + last_repeat = mdcount; + last_bytesperword = bytesperword; // to make REPEAT happy + return ret; + } switch (radix) { case 10: diff --git a/kernel/events/core.c b/kernel/events/core.c index 1a73b31b5fe7..a8f984b4fcff 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -427,9 +427,15 @@ static inline void __update_cgrp_time(struct perf_cgroup *cgrp) static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx) { - struct perf_cgroup *cgrp_out = cpuctx->cgrp; - if (cgrp_out) - __update_cgrp_time(cgrp_out); + struct perf_cgroup *cgrp = cpuctx->cgrp; + struct cgroup_subsys_state *css; + + if (cgrp) { + for (css = &cgrp->css; css; css = css->parent) { + cgrp = container_of(css, struct perf_cgroup, css); + __update_cgrp_time(cgrp); + } + } } static inline void update_cgrp_time_from_event(struct perf_event *event) @@ -457,6 +463,7 @@ perf_cgroup_set_timestamp(struct task_struct *task, { struct perf_cgroup *cgrp; struct perf_cgroup_info *info; + struct cgroup_subsys_state *css; /* * ctx->lock held by caller @@ -467,8 +474,12 @@ perf_cgroup_set_timestamp(struct task_struct *task, return; cgrp = perf_cgroup_from_task(task, ctx); - info = this_cpu_ptr(cgrp->info); - info->timestamp = ctx->timestamp; + + for (css = &cgrp->css; css; css = css->parent) { + cgrp = container_of(css, struct perf_cgroup, css); + info = this_cpu_ptr(cgrp->info); + info->timestamp = ctx->timestamp; + } } #define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */ @@ -5404,7 +5415,8 @@ static void perf_output_read_group(struct perf_output_handle *handle, if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) values[n++] = running; - if (leader != event) + if ((leader != event) && + (leader->state == PERF_EVENT_STATE_ACTIVE)) leader->pmu->read(leader); values[n++] = perf_event_count(leader); diff --git a/kernel/locking/qspinlock.c b/kernel/locking/qspinlock.c index 8173bc7fec92..3b40c8809e52 100644 --- a/kernel/locking/qspinlock.c +++ b/kernel/locking/qspinlock.c @@ -423,6 +423,14 @@ queue: tail = encode_tail(smp_processor_id(), idx); node += idx; + + /* + * Ensure that we increment the head node->count before initialising + * the actual node. If the compiler is kind enough to reorder these + * stores, then an IRQ could overwrite our assignments. + */ + barrier(); + node->locked = 0; node->next = NULL; pv_init_node(node); 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/kernel/power/power.h b/kernel/power/power.h index 51f02ecaf125..2610516601ee 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -94,9 +94,6 @@ extern int in_suspend; extern dev_t swsusp_resume_device; extern sector_t swsusp_resume_block; -extern asmlinkage int swsusp_arch_suspend(void); -extern asmlinkage int swsusp_arch_resume(void); - extern int create_basic_memory_bitmaps(void); extern void free_basic_memory_bitmaps(void); extern int hibernate_preallocate_memory(void); diff --git a/kernel/relay.c b/kernel/relay.c index 0b4570cfacae..f6d5f08bdfaa 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -163,7 +163,7 @@ static struct rchan_buf *relay_create_buf(struct rchan *chan) { struct rchan_buf *buf; - if (chan->n_subbufs > UINT_MAX / sizeof(size_t *)) + if (chan->n_subbufs > KMALLOC_MAX_SIZE / sizeof(size_t *)) return NULL; buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f962ab1eb046..621793d3ec6d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7130,7 +7130,7 @@ boosted_task_util(struct task_struct *task) static unsigned long capacity_spare_wake(int cpu, struct task_struct *p) { - return capacity_orig_of(cpu) - cpu_util_wake(cpu, p); + return max_t(long, capacity_of(cpu) - cpu_util_wake(cpu, p), 0); } /* diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index fb7f36fd6e5b..af21389466b8 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -831,6 +831,8 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun) struct rq *rq = rq_of_rt_rq(rt_rq); raw_spin_lock(&rq->lock); + update_rq_clock(rq); + if (rt_rq->rt_time) { u64 runtime; diff --git a/kernel/signal.c b/kernel/signal.c index 7d75bc2d042f..8bfbc47f0a23 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1392,6 +1392,10 @@ static int kill_something_info(int sig, struct siginfo *info, pid_t pid) return ret; } + /* -INT_MIN is undefined. Exclude this case to avoid a UBSAN warning */ + if (pid == INT_MIN) + return -ESRCH; + read_lock(&tasklist_lock); if (pid != -1) { ret = __kill_pgrp_info(sig, info, diff --git a/kernel/sys.c b/kernel/sys.c index ba3ddb43dd9f..cf40663a54c2 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -55,6 +55,8 @@ #include <linux/uidgid.h> #include <linux/cred.h> +#include <linux/nospec.h> + #include <linux/kmsg_dump.h> /* Move somewhere else to avoid recompiling? */ #include <generated/utsrelease.h> @@ -1313,6 +1315,7 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource, if (resource >= RLIM_NLIMITS) return -EINVAL; + resource = array_index_nospec(resource, RLIM_NLIMITS); task_lock(current->group_leader); x = current->signal->rlim[resource]; task_unlock(current->group_leader); diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 42a4009fd75a..2172dd61577e 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -469,9 +469,10 @@ clear_event_triggers(struct trace_array *tr) struct trace_event_file *file; list_for_each_entry(file, &tr->events, list) { - struct event_trigger_data *data; - list_for_each_entry_rcu(data, &file->triggers, list) { + struct event_trigger_data *data, *n; + list_for_each_entry_safe(data, n, &file->triggers, list) { trace_event_trigger_enable_disable(file, 0); + list_del_rcu(&data->list); if (data->ops->free) data->ops->free(data->ops, data); } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index acccc6c7437f..89a0f1171f90 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -5223,7 +5223,7 @@ int workqueue_sysfs_register(struct workqueue_struct *wq) ret = device_register(&wq_dev->dev); if (ret) { - kfree(wq_dev); + put_device(&wq_dev->dev); wq->wq_dev = NULL; return ret; } diff --git a/lib/test_bpf.c b/lib/test_bpf.c index b7908d949a5f..b1495f586f29 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -83,6 +83,7 @@ struct bpf_test { __u32 result; } test[MAX_SUBTESTS]; int (*fill_helper)(struct bpf_test *self); + int expected_errcode; /* used when FLAG_EXPECTED_FAIL is set in the aux */ __u8 frag_data[MAX_DATA]; }; @@ -1780,7 +1781,9 @@ static struct bpf_test tests[] = { }, CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, - { } + { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { "check: div_k_0", @@ -1790,7 +1793,9 @@ static struct bpf_test tests[] = { }, CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, - { } + { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { "check: unknown insn", @@ -1801,7 +1806,9 @@ static struct bpf_test tests[] = { }, CLASSIC | FLAG_EXPECTED_FAIL, { }, - { } + { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { "check: out of range spill/fill", @@ -1811,7 +1818,9 @@ static struct bpf_test tests[] = { }, CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, - { } + { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { "JUMPS + HOLES", @@ -1903,6 +1912,8 @@ static struct bpf_test tests[] = { CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { "check: LDX + RET X", @@ -1913,6 +1924,8 @@ static struct bpf_test tests[] = { CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { /* Mainly checking JIT here. */ "M[]: alt STX + LDX", @@ -2087,6 +2100,8 @@ static struct bpf_test tests[] = { CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, { }, + .fill_helper = NULL, + .expected_errcode = -EINVAL, }, { /* Passes checker but fails during runtime. */ "LD [SKF_AD_OFF-1]", @@ -4462,6 +4477,7 @@ static struct bpf_test tests[] = { { }, { }, .fill_helper = bpf_fill_maxinsns4, + .expected_errcode = -EINVAL, }, { /* Mainly checking JIT here. */ "BPF_MAXINSNS: Very long jump", @@ -4517,10 +4533,15 @@ static struct bpf_test tests[] = { { "BPF_MAXINSNS: Jump, gap, jump, ...", { }, +#ifdef CONFIG_BPF_JIT_ALWAYS_ON + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, +#else CLASSIC | FLAG_NO_DATA, +#endif { }, { { 0, 0xababcbac } }, .fill_helper = bpf_fill_maxinsns11, + .expected_errcode = -ENOTSUPP, }, { "BPF_MAXINSNS: ld_abs+get_processor_id", @@ -5290,7 +5311,7 @@ static struct bpf_prog *generate_filter(int which, int *err) *err = bpf_prog_create(&fp, &fprog); if (tests[which].aux & FLAG_EXPECTED_FAIL) { - if (*err == -EINVAL) { + if (*err == tests[which].expected_errcode) { pr_cont("PASS\n"); /* Verifier rejected filter as expected. */ *err = 0; diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index cfdbe1ce9ef8..eeaaac0fb24a 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c @@ -818,5 +818,5 @@ static int __init kasan_memhotplug_init(void) return 0; } -module_init(kasan_memhotplug_init); +core_initcall(kasan_memhotplug_init); #endif diff --git a/mm/kmemleak.c b/mm/kmemleak.c index ddc6c93966d2..de479ce41690 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1533,8 +1533,7 @@ static void start_scan_thread(void) } /* - * Stop the automatic memory scanning thread. This function must be called - * with the scan_mutex held. + * Stop the automatic memory scanning thread. */ static void stop_scan_thread(void) { @@ -1797,12 +1796,15 @@ static void kmemleak_do_cleanup(struct work_struct *work) { stop_scan_thread(); + mutex_lock(&scan_mutex); /* - * Once the scan thread has stopped, it is safe to no longer track - * object freeing. Ordering of the scan thread stopping and the memory - * accesses below is guaranteed by the kthread_stop() function. + * Once it is made sure that kmemleak_scan has stopped, it is safe to no + * longer track object freeing. Ordering of the scan thread stopping and + * the memory accesses below is guaranteed by the kthread_stop() + * function. */ kmemleak_free_enabled = 0; + mutex_unlock(&scan_mutex); if (!kmemleak_found_leaks) __kmemleak_do_cleanup(); @@ -1512,8 +1512,22 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item) tree_rmap_item = unstable_tree_search_insert(rmap_item, page, &tree_page); if (tree_rmap_item) { + bool split; + kpage = try_to_merge_two_pages(rmap_item, page, tree_rmap_item, tree_page); + /* + * If both pages we tried to merge belong to the same compound + * page, then we actually ended up increasing the reference + * count of the same compound page twice, and split_huge_page + * failed. + * Here we set a flag if that happened, and we use it later to + * try split_huge_page again. Since we call put_page right + * afterwards, the reference count will be correct and + * split_huge_page should succeed. + */ + split = PageTransCompound(page) + && compound_head(page) == compound_head(tree_page); put_page(tree_page); if (kpage) { /* @@ -1538,6 +1552,20 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item) break_cow(tree_rmap_item); break_cow(rmap_item); } + } else if (split) { + /* + * We are here if we tried to merge two pages and + * failed because they both belonged to the same + * compound page. We will split the page now, but no + * merging will take place. + * We do not want to add the cost of a full lock; if + * the page is locked, it is better to skip it and + * perhaps try again later. + */ + if (!trylock_page(page)) + return; + split_huge_page(page); + unlock_page(page); } } } diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 62e4af5b287f..b9b2e25342d4 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1233,6 +1233,7 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask, unsigned long maxnode) { unsigned long k; + unsigned long t; unsigned long nlongs; unsigned long endmask; @@ -1249,13 +1250,19 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask, else endmask = (1UL << (maxnode % BITS_PER_LONG)) - 1; - /* When the user specified more nodes than supported just check - if the non supported part is all zero. */ + /* + * When the user specified more nodes than supported just check + * if the non supported part is all zero. + * + * If maxnode have more longs than MAX_NUMNODES, check + * the bits in that area first. And then go through to + * check the rest bits which equal or bigger than MAX_NUMNODES. + * Otherwise, just check bits [MAX_NUMNODES, maxnode). + */ if (nlongs > BITS_TO_LONGS(MAX_NUMNODES)) { if (nlongs > PAGE_SIZE/sizeof(long)) return -EINVAL; for (k = BITS_TO_LONGS(MAX_NUMNODES); k < nlongs; k++) { - unsigned long t; if (get_user(t, nmask + k)) return -EFAULT; if (k == nlongs - 1) { @@ -1268,6 +1275,16 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask, endmask = ~0UL; } + if (maxnode > MAX_NUMNODES && MAX_NUMNODES % BITS_PER_LONG != 0) { + unsigned long valid_mask = endmask; + + valid_mask &= ~((1UL << (MAX_NUMNODES % BITS_PER_LONG)) - 1); + if (get_user(t, nmask + nlongs - 1)) + return -EFAULT; + if (t & valid_mask) + return -EINVAL; + } + if (copy_from_user(nodes_addr(*nodes), nmask, nlongs*sizeof(unsigned long))) return -EFAULT; nodes_addr(*nodes)[nlongs-1] &= endmask; @@ -1394,10 +1411,14 @@ SYSCALL_DEFINE4(migrate_pages, pid_t, pid, unsigned long, maxnode, goto out_put; } - if (!nodes_subset(*new, node_states[N_MEMORY])) { - err = -EINVAL; + task_nodes = cpuset_mems_allowed(current); + nodes_and(*new, *new, task_nodes); + if (nodes_empty(*new)) + goto out_put; + + nodes_and(*new, *new, node_states[N_MEMORY]); + if (nodes_empty(*new)) goto out_put; - } err = security_task_movememory(task); if (err) @@ -2122,6 +2143,9 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b) case MPOL_INTERLEAVE: return !!nodes_equal(a->v.nodes, b->v.nodes); case MPOL_PREFERRED: + /* a's ->flags is the same as b's */ + if (a->flags & MPOL_F_LOCAL) + return true; return a->v.preferred_node == b->v.preferred_node; default: BUG(); diff --git a/mm/mmap.c b/mm/mmap.c index 5457c5f4935b..c9d2061257ac 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1307,6 +1307,35 @@ static inline int mlock_future_check(struct mm_struct *mm, return 0; } +static inline u64 file_mmap_size_max(struct file *file, struct inode *inode) +{ + if (S_ISREG(inode->i_mode)) + return MAX_LFS_FILESIZE; + + if (S_ISBLK(inode->i_mode)) + return MAX_LFS_FILESIZE; + + /* Special "we do even unsigned file positions" case */ + if (file->f_mode & FMODE_UNSIGNED_OFFSET) + return 0; + + /* Yes, random drivers might want more. But I'm tired of buggy drivers */ + return ULONG_MAX; +} + +static inline bool file_mmap_ok(struct file *file, struct inode *inode, + unsigned long pgoff, unsigned long len) +{ + u64 maxsize = file_mmap_size_max(file, inode); + + if (maxsize && len > maxsize) + return false; + maxsize -= len; + if (pgoff > maxsize >> PAGE_SHIFT) + return false; + return true; +} + /* * The caller must hold down_write(¤t->mm->mmap_sem). */ @@ -1377,6 +1406,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr, if (file) { struct inode *inode = file_inode(file); + if (!file_mmap_ok(file, inode, pgoff, len)) + return -EOVERFLOW; + switch (flags & MAP_TYPE) { case MAP_SHARED: if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE)) diff --git a/mm/swapfile.c b/mm/swapfile.c index fca04a53cf78..2968b505ca9a 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2301,6 +2301,10 @@ static unsigned long read_swap_header(struct swap_info_struct *p, maxpages = swp_offset(pte_to_swp_entry( swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1; last_page = swap_header->info.last_page; + if (!last_page) { + pr_warn("Empty swap-file\n"); + return 0; + } if (last_page > maxpages) { pr_warn("Truncating oversized swap area, only using %luk out of %luk\n", maxpages << (PAGE_SHIFT - 10), diff --git a/mm/vmscan.c b/mm/vmscan.c index c7fe805048da..abeea66bc429 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1409,6 +1409,7 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode) if (PageDirty(page)) { struct address_space *mapping; + bool migrate_dirty; /* ISOLATE_CLEAN means only clean pages */ if (mode & ISOLATE_CLEAN) @@ -1417,10 +1418,19 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode) /* * Only pages without mappings or that have a * ->migratepage callback are possible to migrate - * without blocking + * without blocking. However, we can be racing with + * truncation so it's necessary to lock the page + * to stabilise the mapping as truncation holds + * the page lock until after the page is removed + * from the page cache. */ + if (!trylock_page(page)) + return ret; + mapping = page_mapping(page); - if (mapping && !mapping->a_ops->migratepage) + migrate_dirty = !mapping || mapping->a_ops->migratepage; + unlock_page(page); + if (!migrate_dirty) return ret; } } @@ -3963,7 +3973,13 @@ int zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) */ int page_evictable(struct page *page) { - return !mapping_unevictable(page_mapping(page)) && !PageMlocked(page); + int ret; + + /* Prevent address_space of inode and swap cache from being freed */ + rcu_read_lock(); + ret = !mapping_unevictable(page_mapping(page)) && !PageMlocked(page); + rcu_read_unlock(); + return ret; } #ifdef CONFIG_SHMEM diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 5f19133c5530..c2dff7c6e960 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -374,7 +374,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, batadv_arp_hw_src(skb, hdr_size), &ip_src, batadv_arp_hw_dst(skb, hdr_size), &ip_dst); - if (hdr_size == 0) + if (hdr_size < sizeof(struct batadv_unicast_packet)) return; unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 700c96c82a15..5d2f9d4879b2 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -278,7 +278,8 @@ batadv_frag_merge_packets(struct hlist_head *chain) /* Move the existing MAC header to just before the payload. (Override * the fragment header.) */ - skb_pull_rcsum(skb_out, hdr_size); + skb_pull(skb_out, hdr_size); + skb_out->ip_summed = CHECKSUM_NONE; memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN); skb_set_mac_header(skb_out, -ETH_HLEN); skb_reset_network_header(skb_out); diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index e6c8382c79ba..6abfba1e227f 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -798,6 +798,9 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, vid = batadv_get_vid(skb, 0); + if (is_multicast_ether_addr(ethhdr->h_dest)) + goto out; + orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, ethhdr->h_dest, vid); if (!orig_dst_node) diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index eb76386f8d4b..8aa2d65df86f 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -428,8 +428,8 @@ static struct batadv_orig_node * batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv, struct ethhdr *ethhdr) { - return batadv_transtable_search(bat_priv, ethhdr->h_source, - ethhdr->h_dest, BATADV_NO_FLAGS); + return batadv_transtable_search(bat_priv, NULL, ethhdr->h_dest, + BATADV_NO_FLAGS); } /** diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 720f1a5b81ac..9f1fe6169bef 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -430,13 +430,7 @@ void batadv_interface_rx(struct net_device *soft_iface, /* skb->dev & skb->pkt_type are set here */ skb->protocol = eth_type_trans(skb, soft_iface); - - /* should not be necessary anymore as we use skb_pull_rcsum() - * TODO: please verify this and remove this TODO - * -- Dec 21st 2009, Simon Wunderlich - */ - - /* skb->ip_summed = CHECKSUM_UNNECESSARY; */ + skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); batadv_inc_counter(bat_priv, BATADV_CNT_RX); batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES, diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 50b76011f470..51eab9b5baa1 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1614,7 +1614,8 @@ static int compat_match_to_user(struct ebt_entry_match *m, void __user **dstptr, int off = ebt_compat_match_offset(match, m->match_size); compat_uint_t msize = m->match_size - off; - BUG_ON(off >= m->match_size); + if (WARN_ON(off >= m->match_size)) + return -EINVAL; if (copy_to_user(cm->u.name, match->name, strlen(match->name) + 1) || put_user(msize, &cm->match_size)) @@ -1641,7 +1642,8 @@ static int compat_target_to_user(struct ebt_entry_target *t, int off = xt_compat_target_offset(target); compat_uint_t tsize = t->target_size - off; - BUG_ON(off >= t->target_size); + if (WARN_ON(off >= t->target_size)) + return -EINVAL; if (copy_to_user(cm->u.name, target->name, strlen(target->name) + 1) || put_user(tsize, &cm->match_size)) @@ -1869,7 +1871,8 @@ static int ebt_buf_add(struct ebt_entries_buf_state *state, if (state->buf_kern_start == NULL) goto count_only; - BUG_ON(state->buf_kern_offset + sz > state->buf_kern_len); + if (WARN_ON(state->buf_kern_offset + sz > state->buf_kern_len)) + return -EINVAL; memcpy(state->buf_kern_start + state->buf_kern_offset, data, sz); @@ -1882,7 +1885,8 @@ static int ebt_buf_add_pad(struct ebt_entries_buf_state *state, unsigned int sz) { char *b = state->buf_kern_start; - BUG_ON(b && state->buf_kern_offset > state->buf_kern_len); + if (WARN_ON(b && state->buf_kern_offset > state->buf_kern_len)) + return -EINVAL; if (b != NULL && sz > 0) memset(b + state->buf_kern_offset, 0, sz); @@ -1959,8 +1963,10 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, pad = XT_ALIGN(size_kern) - size_kern; if (pad > 0 && dst) { - BUG_ON(state->buf_kern_len <= pad); - BUG_ON(state->buf_kern_offset - (match_size + off) + size_kern > state->buf_kern_len - pad); + if (WARN_ON(state->buf_kern_len <= pad)) + return -EINVAL; + if (WARN_ON(state->buf_kern_offset - (match_size + off) + size_kern > state->buf_kern_len - pad)) + return -EINVAL; memset(dst + size_kern, 0, pad); } return off + match_size; @@ -2011,7 +2017,8 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, if (ret < 0) return ret; - BUG_ON(ret < match32->match_size); + if (WARN_ON(ret < match32->match_size)) + return -EINVAL; growth += ret - match32->match_size; growth += ebt_compat_entry_padsize(); @@ -2081,8 +2088,12 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, * offsets are relative to beginning of struct ebt_entry (i.e., 0). */ for (i = 0; i < 4 ; ++i) { - if (offsets[i] >= *total) + if (offsets[i] > *total) + return -EINVAL; + + if (i < 3 && offsets[i] == *total) return -EINVAL; + if (i == 0) continue; if (offsets[i-1] > offsets[i]) @@ -2121,7 +2132,8 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, startoff = state->buf_user_offset - startoff; - BUG_ON(*total < startoff); + if (WARN_ON(*total < startoff)) + return -EINVAL; *total -= startoff; return 0; } @@ -2249,7 +2261,8 @@ static int compat_do_replace(struct net *net, void __user *user, state.buf_kern_len = size64; ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state); - BUG_ON(ret < 0); /* parses same data again */ + if (WARN_ON(ret < 0)) + goto out_unlock; vfree(entries_tmp); tmp.entries_size = size64; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 5b3d611d8b5f..2017ffa5197a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1691,6 +1691,10 @@ static int do_setlink(const struct sk_buff *skb, const struct net_device_ops *ops = dev->netdev_ops; int err; + err = validate_linkmsg(dev, tb); + if (err < 0) + return err; + if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]) { struct net *net = rtnl_link_get_net(dev_net(dev), tb); if (IS_ERR(net)) { @@ -1982,10 +1986,6 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) goto errout; } - err = validate_linkmsg(dev, tb); - if (err < 0) - goto errout; - err = do_setlink(skb, dev, ifm, tb, ifname, 0); errout: return err; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0d8383c8a117..03928b406d4c 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4321,13 +4321,18 @@ EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb) { + int mac_len; + if (skb_cow(skb, skb_headroom(skb)) < 0) { kfree_skb(skb); return NULL; } - memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN, - 2 * ETH_ALEN); + mac_len = skb->data - skb_mac_header(skb); + if (likely(mac_len > VLAN_HLEN + ETH_TLEN)) { + memmove(skb_mac_header(skb) + VLAN_HLEN, skb_mac_header(skb), + mac_len - VLAN_HLEN - ETH_TLEN); + } skb->mac_header += VLAN_HLEN; return skb; } diff --git a/net/dccp/proto.c b/net/dccp/proto.c index ff3b058cf58c..936dab12f99f 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -280,9 +280,7 @@ int dccp_disconnect(struct sock *sk, int flags) dccp_clear_xmit_timers(sk); ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); - ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); dp->dccps_hc_rx_ccid = NULL; - dp->dccps_hc_tx_ccid = NULL; __skb_queue_purge(&sk->sk_receive_queue); __skb_queue_purge(&sk->sk_write_queue); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 44abc52bae13..9d144cbd4e62 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -979,6 +979,8 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg) if (val == TCP_CA_UNSPEC) return -EINVAL; } else { + if (nla_len(nla) != sizeof(u32)) + return false; val = nla_get_u32(nla); } if (type == RTAX_ADVMSS && val > 65535 - 40) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 1b93ea766916..ce9a7fbb7c5f 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -493,8 +493,6 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) int err; int copied; - WARN_ON_ONCE(sk->sk_family == AF_INET6); - err = -EAGAIN; skb = sock_dequeue_err_skb(sk); if (!skb) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index a03f834f16d5..4b7c81f88abf 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -366,7 +366,6 @@ static int vti_tunnel_init(struct net_device *dev) memcpy(dev->dev_addr, &iph->saddr, 4); memcpy(dev->broadcast, &iph->daddr, 4); - dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr); dev->mtu = ETH_DATA_LEN; dev->flags = IFF_NOARP; dev->addr_len = 4; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index ce4f10e9513a..75406603fa1e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -615,6 +615,7 @@ static inline u32 fnhe_hashfun(__be32 daddr) static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe) { rt->rt_pmtu = fnhe->fnhe_pmtu; + rt->rt_mtu_locked = fnhe->fnhe_mtu_locked; rt->dst.expires = fnhe->fnhe_expires; if (fnhe->fnhe_gw) { @@ -625,7 +626,7 @@ static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnh } static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, - u32 pmtu, unsigned long expires) + u32 pmtu, bool lock, unsigned long expires) { struct fnhe_hash_bucket *hash; struct fib_nh_exception *fnhe; @@ -662,8 +663,10 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_genid = genid; if (gw) fnhe->fnhe_gw = gw; - if (pmtu) + if (pmtu) { fnhe->fnhe_pmtu = pmtu; + fnhe->fnhe_mtu_locked = lock; + } fnhe->fnhe_expires = max(1UL, expires); /* Update all cached dsts too */ rt = rcu_dereference(fnhe->fnhe_rth_input); @@ -687,6 +690,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_daddr = daddr; fnhe->fnhe_gw = gw; fnhe->fnhe_pmtu = pmtu; + fnhe->fnhe_mtu_locked = lock; fnhe->fnhe_expires = expires; /* Exception created; mark the cached routes for the nexthop @@ -768,7 +772,8 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow struct fib_nh *nh = &FIB_RES_NH(res); update_or_create_fnhe(nh, fl4->daddr, new_gw, - 0, jiffies + ip_rt_gc_timeout); + 0, false, + jiffies + ip_rt_gc_timeout); } if (kill_route) rt->dst.obsolete = DST_OBSOLETE_KILL; @@ -981,15 +986,18 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu) { struct dst_entry *dst = &rt->dst; struct fib_result res; + bool lock = false; - if (dst_metric_locked(dst, RTAX_MTU)) + if (ip_mtu_locked(dst)) return; if (ipv4_mtu(dst) < mtu) return; - if (mtu < ip_rt_min_pmtu) + if (mtu < ip_rt_min_pmtu) { + lock = true; mtu = ip_rt_min_pmtu; + } if (rt->rt_pmtu == mtu && time_before(jiffies, dst->expires - ip_rt_mtu_expires / 2)) @@ -999,7 +1007,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu) if (fib_lookup(dev_net(dst->dev), fl4, &res, 0) == 0) { struct fib_nh *nh = &FIB_RES_NH(res); - update_or_create_fnhe(nh, fl4->daddr, 0, mtu, + update_or_create_fnhe(nh, fl4->daddr, 0, mtu, lock, jiffies + ip_rt_mtu_expires); } rcu_read_unlock(); @@ -1256,7 +1264,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst) mtu = READ_ONCE(dst->dev->mtu); - if (unlikely(dst_metric_locked(dst, RTAX_MTU))) { + if (unlikely(ip_mtu_locked(dst))) { if (rt->rt_uses_gateway && mtu > 576) mtu = 576; } @@ -1479,6 +1487,7 @@ static struct rtable *rt_dst_alloc(struct net_device *dev, rt->rt_is_input = 0; rt->rt_iif = 0; rt->rt_pmtu = 0; + rt->rt_mtu_locked = 0; rt->rt_gateway = 0; rt->rt_uses_gateway = 0; rt->rt_table_id = 0; @@ -2399,6 +2408,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_is_input = ort->rt_is_input; rt->rt_iif = ort->rt_iif; rt->rt_pmtu = ort->rt_pmtu; + rt->rt_mtu_locked = ort->rt_mtu_locked; rt->rt_genid = rt_genid_ipv4(net); rt->rt_flags = ort->rt_flags; @@ -2501,6 +2511,8 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id, memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); if (rt->rt_pmtu && expires) metrics[RTAX_MTU - 1] = rt->rt_pmtu; + if (rt->rt_mtu_locked && expires) + metrics[RTAX_LOCK - 1] |= BIT(RTAX_MTU); if (rtnetlink_put_metrics(skb, metrics) < 0) goto nla_put_failure; diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index 2ab9bbb6faff..5ed6a89894fd 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -6,7 +6,7 @@ * The algorithm is described in: * "TCP-Illinois: A Loss and Delay-Based Congestion Control Algorithm * for High-Speed Networks" - * http://www.ifp.illinois.edu/~srikant/Papers/liubassri06perf.pdf + * http://tamerbasar.csl.illinois.edu/LiuBasarSrikantPerfEvalArtJun2008.pdf * * Implemented from description in paper and ns-2 simulation. * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b1cfce7f8e85..b4e95494b05b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -558,8 +558,8 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk, void tcp_rcv_space_adjust(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); + u32 copied; int time; - int copied; time = tcp_time_stamp - tp->rcvq_space.time; if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) @@ -581,12 +581,13 @@ void tcp_rcv_space_adjust(struct sock *sk) if (sysctl_tcp_moderate_rcvbuf && !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) { - int rcvwin, rcvmem, rcvbuf; + int rcvmem, rcvbuf; + u64 rcvwin; /* minimal window to cope with packet losses, assuming * steady state. Add some cushion because of small variations. */ - rcvwin = (copied << 1) + 16 * tp->advmss; + rcvwin = ((u64)copied << 1) + 16 * tp->advmss; /* If rate increased by 25%, * assume slow start, rcvwin = 3 * copied @@ -606,7 +607,8 @@ void tcp_rcv_space_adjust(struct sock *sk) while (tcp_win_from_space(rcvmem) < tp->advmss) rcvmem += 128; - rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]); + do_div(rcvwin, tp->advmss); + rcvbuf = min_t(u64, rcvwin * rcvmem, sysctl_tcp_rmem[2]); if (rcvbuf > sk->sk_rcvbuf) { sk->sk_rcvbuf = rcvbuf; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 39eebc7b2831..528f13718687 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -101,6 +101,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_gateway = rt->rt_gateway; xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway; xdst->u.rt.rt_pmtu = rt->rt_pmtu; + xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked; xdst->u.rt.rt_table_id = rt->rt_table_id; INIT_LIST_HEAD(&xdst->u.rt.rt_uncached); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index e5846d1f9b55..9b92960f024d 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1787,7 +1787,8 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns ret = 0; if (!ip6mr_new_table(net, v)) ret = -ENOMEM; - raw6_sk(sk)->ip6mr_table = v; + else + raw6_sk(sk)->ip6mr_table = v; rtnl_unlock(); return ret; } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 51f7c32f04d7..dec4e7bda5f3 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1574,6 +1574,13 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev, if (err < 0) return err; + if (tb[IFLA_MTU]) { + u32 mtu = nla_get_u32(tb[IFLA_MTU]); + + if (mtu >= IPV6_MIN_MTU && mtu <= 0xFFF8 - dev->hard_header_len) + dev->mtu = mtu; + } + #ifdef CONFIG_IPV6_SIT_6RD if (ipip6_netlink_6rd_parms(data, &ip6rd)) err = ipip6_tunnel_update_6rd(nt, &ip6rd); diff --git a/net/key/af_key.c b/net/key/af_key.c index 15150b412930..3ba903ff2bb0 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -437,6 +437,24 @@ static int verify_address_len(const void *p) return 0; } +static inline int sadb_key_len(const struct sadb_key *key) +{ + int key_bytes = DIV_ROUND_UP(key->sadb_key_bits, 8); + + return DIV_ROUND_UP(sizeof(struct sadb_key) + key_bytes, + sizeof(uint64_t)); +} + +static int verify_key_len(const void *p) +{ + const struct sadb_key *key = p; + + if (sadb_key_len(key) > key->sadb_key_len) + return -EINVAL; + + return 0; +} + static inline int pfkey_sec_ctx_len(const struct sadb_x_sec_ctx *sec_ctx) { return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) + @@ -533,16 +551,25 @@ static int parse_exthdrs(struct sk_buff *skb, const struct sadb_msg *hdr, void * return -EINVAL; if (ext_hdrs[ext_type-1] != NULL) return -EINVAL; - if (ext_type == SADB_EXT_ADDRESS_SRC || - ext_type == SADB_EXT_ADDRESS_DST || - ext_type == SADB_EXT_ADDRESS_PROXY || - ext_type == SADB_X_EXT_NAT_T_OA) { + switch (ext_type) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_X_EXT_NAT_T_OA: if (verify_address_len(p)) return -EINVAL; - } - if (ext_type == SADB_X_EXT_SEC_CTX) { + break; + case SADB_X_EXT_SEC_CTX: if (verify_sec_ctx_len(p)) return -EINVAL; + break; + case SADB_EXT_KEY_AUTH: + case SADB_EXT_KEY_ENCRYPT: + if (verify_key_len(p)) + return -EINVAL; + break; + default: + break; } ext_hdrs[ext_type-1] = (void *) p; } @@ -1111,14 +1138,12 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, key = ext_hdrs[SADB_EXT_KEY_AUTH - 1]; if (key != NULL && sa->sadb_sa_auth != SADB_X_AALG_NULL && - ((key->sadb_key_bits+7) / 8 == 0 || - (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t))) + key->sadb_key_bits == 0) return ERR_PTR(-EINVAL); key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1]; if (key != NULL && sa->sadb_sa_encrypt != SADB_EALG_NULL && - ((key->sadb_key_bits+7) / 8 == 0 || - (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t))) + key->sadb_key_bits == 0) return ERR_PTR(-EINVAL); x = xfrm_state_alloc(net); diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c index f8d4ab8ca1a5..4b60f68cb492 100644 --- a/net/llc/llc_c_ac.c +++ b/net/llc/llc_c_ac.c @@ -389,7 +389,7 @@ static int llc_conn_ac_send_i_cmd_p_set_0(struct sock *sk, struct sk_buff *skb) llc_pdu_init_as_i_cmd(skb, 0, llc->vS, llc->vR); rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac); if (likely(!rc)) { - llc_conn_send_pdu(sk, skb); + rc = llc_conn_send_pdu(sk, skb); llc_conn_ac_inc_vs_by_1(sk, skb); } return rc; @@ -916,7 +916,7 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk, llc_pdu_init_as_i_cmd(skb, llc->ack_pf, llc->vS, llc->vR); rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac); if (likely(!rc)) { - llc_conn_send_pdu(sk, skb); + rc = llc_conn_send_pdu(sk, skb); llc_conn_ac_inc_vs_by_1(sk, skb); } return rc; @@ -935,14 +935,17 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk, int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb) { struct llc_sock *llc = llc_sk(sk); + int ret; if (llc->ack_must_be_send) { - llc_conn_ac_send_i_rsp_f_set_ackpf(sk, skb); + ret = llc_conn_ac_send_i_rsp_f_set_ackpf(sk, skb); llc->ack_must_be_send = 0 ; llc->ack_pf = 0; - } else - llc_conn_ac_send_i_cmd_p_set_0(sk, skb); - return 0; + } else { + ret = llc_conn_ac_send_i_cmd_p_set_0(sk, skb); + } + + return ret; } /** diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index d861b74ad068..79c346fd859b 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -30,7 +30,7 @@ #endif static int llc_find_offset(int state, int ev_type); -static void llc_conn_send_pdus(struct sock *sk); +static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *skb); static int llc_conn_service(struct sock *sk, struct sk_buff *skb); static int llc_exec_conn_trans_actions(struct sock *sk, struct llc_conn_state_trans *trans, @@ -193,11 +193,11 @@ out_skb_put: return rc; } -void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb) +int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb) { /* queue PDU to send to MAC layer */ skb_queue_tail(&sk->sk_write_queue, skb); - llc_conn_send_pdus(sk); + return llc_conn_send_pdus(sk, skb); } /** @@ -255,7 +255,7 @@ void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit) if (howmany_resend > 0) llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO; /* any PDUs to re-send are queued up; start sending to MAC */ - llc_conn_send_pdus(sk); + llc_conn_send_pdus(sk, NULL); out:; } @@ -296,7 +296,7 @@ void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit) if (howmany_resend > 0) llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO; /* any PDUs to re-send are queued up; start sending to MAC */ - llc_conn_send_pdus(sk); + llc_conn_send_pdus(sk, NULL); out:; } @@ -340,12 +340,16 @@ out: /** * llc_conn_send_pdus - Sends queued PDUs * @sk: active connection + * @hold_skb: the skb held by caller, or NULL if does not care * - * Sends queued pdus to MAC layer for transmission. + * Sends queued pdus to MAC layer for transmission. When @hold_skb is + * NULL, always return 0. Otherwise, return 0 if @hold_skb is sent + * successfully, or 1 for failure. */ -static void llc_conn_send_pdus(struct sock *sk) +static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *hold_skb) { struct sk_buff *skb; + int ret = 0; while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) { struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb); @@ -357,10 +361,20 @@ static void llc_conn_send_pdus(struct sock *sk) skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb); if (!skb2) break; - skb = skb2; + dev_queue_xmit(skb2); + } else { + bool is_target = skb == hold_skb; + int rc; + + if (is_target) + skb_get(skb); + rc = dev_queue_xmit(skb); + if (is_target) + ret = rc; } - dev_queue_xmit(skb); } + + return ret; } /** diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index b0380927f05f..3f33ec44bd28 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1469,6 +1469,16 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb, iface = rcu_dereference(netlbl_unlhsh_def); if (iface == NULL || !iface->valid) goto unlabel_getattr_nolabel; + +#if IS_ENABLED(CONFIG_IPV6) + /* When resolving a fallback label, check the sk_buff version as + * it is possible (e.g. SCTP) to have family = PF_INET6 while + * receiving ip_hdr(skb)->version = 4. + */ + if (family == PF_INET6 && ip_hdr(skb)->version == 4) + family = PF_INET; +#endif /* IPv6 */ + switch (family) { case PF_INET: { struct iphdr *hdr4; diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 3621a902cb6e..d25212b135ea 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -149,6 +149,10 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri, pr_debug("uri: %s, len: %zu\n", uri, uri_len); + /* sdreq->tlv_len is u8, takes uri_len, + 3 for header, + 1 for NULL */ + if (WARN_ON_ONCE(uri_len > U8_MAX - 4)) + return NULL; + sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); if (sdreq == NULL) return NULL; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 12dfb457275d..32cb0c87e852 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -68,7 +68,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { }; static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { - [NFC_SDP_ATTR_URI] = { .type = NLA_STRING }, + [NFC_SDP_ATTR_URI] = { .type = NLA_STRING, + .len = U8_MAX - 4 }, [NFC_SDP_ATTR_SAP] = { .type = NLA_U8 }, }; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 392d4e2c0a24..7814e5f744e6 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2779,7 +2779,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) if (unlikely(offset < 0)) goto out_free; } else if (reserve) { - skb_push(skb, reserve); + skb_reserve(skb, -reserve); } /* Returns -EFAULT on error */ @@ -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"); @@ -4198,7 +4199,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, goto out; if (po->tp_version >= TPACKET_V3 && req->tp_block_size <= - BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv)) + BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv) + sizeof(struct tpacket3_hdr)) goto out; if (unlikely(req->tp_frame_size < po->tp_hdrlen + po->tp_reserve)) @@ -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/rds/ib.c b/net/rds/ib.c index f222885ac0c7..ed51ccc84b3a 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -336,7 +336,8 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr) /* Create a CMA ID and try to bind it. This catches both * IB and iWARP capable NICs. */ - cm_id = rdma_create_id(&init_net, NULL, NULL, RDMA_PS_TCP, IB_QPT_RC); + cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler, + NULL, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) return PTR_ERR(cm_id); 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/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index dd243d2abd87..138d7f100f7e 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -743,7 +743,7 @@ int conf_write(const char *name) struct menu *menu; const char *basename; const char *str; - char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1]; + char dirname[PATH_MAX+1], tmpname[PATH_MAX+22], newname[PATH_MAX+8]; char *env; dirname[0] = 0; diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index cbf4996dd9c1..ed29bad1f03a 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -113,7 +113,7 @@ void expr_free(struct expr *e) break; case E_NOT: expr_free(e->left.expr); - return; + break; case E_EQUAL: case E_GEQ: case E_GTH: diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index b05cc3d4a9be..8360feaf51ce 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -364,6 +364,7 @@ void menu_finalize(struct menu *parent) menu->parent = parent; last_menu = menu; } + expr_free(basedep); if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y index 71bf8bff696a..5122ed2d839a 100644 --- a/scripts/kconfig/zconf.y +++ b/scripts/kconfig/zconf.y @@ -107,7 +107,27 @@ static struct menu *current_menu, *current_entry; %% input: nl start | start; -start: mainmenu_stmt stmt_list | stmt_list; +start: mainmenu_stmt stmt_list | no_mainmenu_stmt stmt_list; + +/* mainmenu entry */ + +mainmenu_stmt: T_MAINMENU prompt nl +{ + menu_add_prompt(P_MENU, $2, NULL); +}; + +/* Default main menu, if there's no mainmenu entry */ + +no_mainmenu_stmt: /* empty */ +{ + /* + * Hack: Keep the main menu title on the heap so we can safely free it + * later regardless of whether it comes from the 'prompt' in + * mainmenu_stmt or here + */ + menu_add_prompt(P_MENU, strdup("Linux Kernel Configuration"), NULL); +}; + stmt_list: /* empty */ @@ -344,13 +364,6 @@ if_block: | if_block choice_stmt ; -/* mainmenu entry */ - -mainmenu_stmt: T_MAINMENU prompt nl -{ - menu_add_prompt(P_MENU, $2, NULL); -}; - /* menu entry */ menu: T_MENU prompt T_EOL @@ -495,6 +508,7 @@ word_opt: /* empty */ { $$ = NULL; } void conf_parse(const char *name) { + const char *tmp; struct symbol *sym; int i; @@ -502,7 +516,6 @@ void conf_parse(const char *name) sym_init(); _menu_init(); - rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); if (getenv("ZCONF_DEBUG")) zconfdebug = 1; @@ -512,8 +525,10 @@ void conf_parse(const char *name) if (!modules_sym) modules_sym = sym_find( "n" ); + tmp = rootmenu.prompt->text; rootmenu.prompt->text = _(rootmenu.prompt->text); rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text); + free((char*)tmp); menu_finalize(&rootmenu); for_all_symbols(i, sym) { diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index df303346029b..648a0461f8ed 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -10,6 +10,7 @@ config IMA select CRYPTO_HASH_INFO select TCG_TPM if HAS_IOMEM && !UML select TCG_TIS if TCG_TPM && X86 + select TCG_CRB if TCG_TPM && ACPI select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES help The Trusted Computing Group(TCG) runtime Integrity diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 8da7c91b725d..c36b98b07d6b 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -383,14 +383,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, result = ima_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); if (result == 1) { - bool digsig; - if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) return -EINVAL; - digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG); - if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE)) - return -EPERM; - ima_reset_appraise_flags(d_backing_inode(dentry), digsig); + ima_reset_appraise_flags(d_backing_inode(dentry), + (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0); result = 0; } return result; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 6eb62936c672..a29209fa5674 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -78,6 +78,8 @@ int __init ima_init_crypto(void) hash_algo_name[ima_hash_algo], rc); return rc; } + pr_info("Allocated hash algorithm: %s\n", + hash_algo_name[ima_hash_algo]); return 0; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 98289ba2a2e6..236dce30e517 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -16,6 +16,9 @@ * implements the IMA hooks: ima_bprm_check, ima_file_mmap, * and ima_file_check. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/file.h> #include <linux/binfmts.h> @@ -353,6 +356,16 @@ static int __init init_ima(void) hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); + + if (error && strcmp(hash_algo_name[ima_hash_algo], + CONFIG_IMA_DEFAULT_HASH) != 0) { + pr_info("Allocating %s failed, going to use default hash algorithm %s\n", + hash_algo_name[ima_hash_algo], CONFIG_IMA_DEFAULT_HASH); + hash_setup_done = 0; + hash_setup(CONFIG_IMA_DEFAULT_HASH); + error = ima_init(); + } + if (!error) { ima_initialized = 1; ima_update_policy_flag(); 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/core/timer.c b/sound/core/timer.c index 0e51e5cd33fe..9b226aa83b3d 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -548,7 +548,7 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) } timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : - SNDRV_TIMER_EVENT_CONTINUE); + SNDRV_TIMER_EVENT_PAUSE); unlock: spin_unlock_irqrestore(&timer->lock, flags); return result; @@ -570,7 +570,7 @@ static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : - SNDRV_TIMER_EVENT_CONTINUE); + SNDRV_TIMER_EVENT_PAUSE); spin_unlock(&timeri->timer->lock); } spin_unlock_irqrestore(&slave_active_lock, flags); diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 6c58e6f73a01..7c6ef879c520 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -68,10 +68,13 @@ static int slave_update(struct link_slave *slave) return -ENOMEM; uctl->id = slave->slave.id; err = slave->slave.get(&slave->slave, uctl); + if (err < 0) + goto error; for (ch = 0; ch < slave->info.count; ch++) slave->vals[ch] = uctl->value.integer.value[ch]; + error: kfree(uctl); - return 0; + return err < 0 ? err : 0; } /* get the slave ctl info and save the initial values */ diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index e94cfd5c69f7..ebec1a1ae543 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -84,7 +84,6 @@ config SND_HDA_PATCH_LOADER config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC - select INPUT help Say Y or M here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6a789278970e..580b8943b965 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3495,6 +3495,7 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, } } +#if IS_REACHABLE(INPUT) static void gpio2_mic_hotkey_event(struct hda_codec *codec, struct hda_jack_callback *event) { @@ -3627,6 +3628,10 @@ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, spec->kb_dev = NULL; } } +#else /* INPUT */ +#define alc280_fixup_hp_gpio2_mic_hotkey NULL +#define alc233_fixup_lenovo_line2_mic_hotkey NULL +#endif /* INPUT */ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c index 29a97d52e8ad..66d6c52e7761 100644 --- a/sound/soc/au1x/ac97c.c +++ b/sound/soc/au1x/ac97c.c @@ -91,8 +91,8 @@ static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97, do { mutex_lock(&ctx->lock); - tmo = 5; - while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) + tmo = 6; + while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo) udelay(21); /* wait an ac97 frame time */ if (!tmo) { pr_debug("ac97rd timeout #1\n"); @@ -105,7 +105,7 @@ static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97, * poll, Forrest, poll... */ tmo = 0x10000; - while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) + while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo) asm volatile ("nop"); data = RD(ctx, AC97_CMDRESP); diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c index 43f00dcff7af..a80ac1d18298 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"); @@ -2156,6 +2157,10 @@ static int msm_dig_suspend(struct device *dev) pr_debug("%s:digcodec not initialized, return\n", __func__); return 0; } + if (!registered_digcodec->component.card) { + pr_debug("%s:component not initialized, return\n", __func__); + return 0; + } pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); if (!pdata) { pr_debug("%s:card not initialized, return\n", __func__); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 1636a1eeb002..be1b69c63bdf 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -260,7 +260,6 @@ int sst_dma_new(struct sst_dsp *sst) struct sst_pdata *sst_pdata = sst->pdata; struct sst_dma *dma; struct resource mem; - const char *dma_dev_name; int ret = 0; if (sst->pdata->resindex_dma_base == -1) @@ -271,7 +270,6 @@ int sst_dma_new(struct sst_dsp *sst) * is attached to the ADSP IP. */ switch (sst->pdata->dma_engine) { case SST_DMA_TYPE_DW: - dma_dev_name = "dw_dmac"; break; default: dev_err(sst->dev, "error: invalid DMA engine %d\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 280c665dded4..1e69ddcc3464 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -385,16 +385,10 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) return -ENOMEM; } } else { - if (q6asm_get_svc_version(APR_SVC_ASM) >= - ADSP_ASM_API_VERSION_V2) - ret = q6asm_open_write_v5(prtd->audio_client, - fmt_type, bits_per_sample); - else - ret = q6asm_open_write_v4(prtd->audio_client, + ret = q6asm_open_write_with_retry(prtd->audio_client, fmt_type, bits_per_sample); - if (ret < 0) { - pr_err("%s: q6asm_open_write_v4 failed (%d)\n", + pr_err("%s: q6asm_open_write_with_retry failed (%d)\n", __func__, ret); q6asm_audio_client_free(prtd->audio_client); prtd->audio_client = NULL; @@ -666,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; } @@ -1141,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; @@ -1607,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) @@ -1647,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; } @@ -2033,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) @@ -2073,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..d0dabcb4e337 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[] = { @@ -13725,6 +13777,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, {"SEC_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, @@ -15200,6 +15253,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 +15264,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 +15276,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 +15288,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 +15299,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 +15308,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 +15317,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 +15326,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 +16188,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 +16266,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 +16286,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/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c index e890e6a71fb3..29a5fd18081a 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c @@ -172,6 +172,45 @@ static int msm_qti_pp_put_eq_band_count_audio_mixer( return 0; } +static int msm_qti_pp_put_dtmf_module_enable + (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + struct msm_pcm_routing_fdai_data fe_dai; + struct audio_client *ac = NULL; + struct param_hdr_v3 param_hdr; + int ret = 0; + u32 flag = ucontrol->value.integer.value[0]; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + msm_pcm_routing_get_fedai_info(fe_id, SESSION_TYPE_RX, &fe_dai); + ac = q6asm_get_audio_client(fe_dai.strm_id); + + if (ac == NULL) { + pr_err("%s ac is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + param_hdr.module_id = AUDPROC_MODULE_ID_DTMF_DETECTION; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE; + param_hdr.param_size = 4; + + ret = q6asm_pack_and_set_pp_param_in_band(ac, + param_hdr, (u8 *)&flag); + +done: + return ret; +} + static int msm_qti_pp_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1364,6 +1403,18 @@ static const struct snd_kcontrol_new asphere_mixer_controls[] = { 0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set), }; +static const struct snd_kcontrol_new dtmf_detect_enable_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 DTMF Detect Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, NULL, + msm_qti_pp_put_dtmf_module_enable), + SOC_SINGLE_EXT("MultiMedia6 DTMF Detect Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, NULL, + msm_qti_pp_put_dtmf_module_enable), + SOC_SINGLE_EXT("MultiMedia21 DTMF Detect Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, NULL, + msm_qti_pp_put_dtmf_module_enable), +}; + #ifdef CONFIG_QTI_PP void msm_qti_pp_add_controls(struct snd_soc_platform *platform) { @@ -1420,5 +1471,9 @@ void msm_qti_pp_add_controls(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, msm_multichannel_ec_controls, ARRAY_SIZE(msm_multichannel_ec_controls)); + + snd_soc_add_platform_controls(platform, + dtmf_detect_enable_mixer_controls, + ARRAY_SIZE(dtmf_detect_enable_mixer_controls)); } #endif /* CONFIG_QTI_PP */ 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 567534e1599d..0ad15e90bfc6 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -98,6 +98,7 @@ static struct asm_mmap this_mmap; struct audio_session { struct audio_client *ac; spinlock_t session_lock; + bool ignore; }; /* session id: 0 reserved */ static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; @@ -597,6 +598,7 @@ int q6asm_mmap_apr_dereg(void) static int q6asm_session_alloc(struct audio_client *ac) { int n; + for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { if (!(session[n].ac)) { session[n].ac = ac; @@ -607,6 +609,87 @@ static int q6asm_session_alloc(struct audio_client *ac) return -ENOMEM; } +static void q6asm_session_set_ignore(int id) +{ + session[id].ignore = true; +} + +static void q6asm_session_clean_ignore(void) +{ + int i; + + for (i = 1; i <= ASM_ACTIVE_STREAMS_ALLOWED; i++) + session[i].ignore = false; +} + +static void q6asm_session_deregister(struct audio_client *ac) +{ + rtac_set_asm_handle(ac->session, NULL); + apr_deregister(ac->apr2); + apr_deregister(ac->apr); + q6asm_mmap_apr_dereg(); + ac->apr2 = NULL; + ac->apr = NULL; + ac->mmap_apr = NULL; +} + +static int q6asm_session_register(struct audio_client *ac) +{ + ac->apr = apr_register("ADSP", "ASM", + (apr_fn)q6asm_callback, + ((ac->session) << 8 | 0x0001), + ac); + if (ac->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + goto fail_apr1; + } + + ac->apr2 = apr_register("ADSP", "ASM", + (apr_fn)q6asm_callback, + ((ac->session) << 8 | 0x0002), + ac); + if (ac->apr2 == NULL) { + pr_err("%s: Registration with APR-2 failed\n", __func__); + goto fail_apr2; + } + + rtac_set_asm_handle(ac->session, ac->apr); + + pr_debug("%s: Registering the common port with APR\n", __func__); + ac->mmap_apr = q6asm_mmap_apr_reg(); + if (ac->mmap_apr == NULL) + goto fail_mmap; + + return 0; + +fail_mmap: + apr_deregister(ac->apr2); +fail_apr2: + apr_deregister(ac->apr); +fail_apr1: + return -EINVAL; +} + +static int q6asm_session_try_next(struct audio_client *ac) +{ + int n; + int s = ac->session; + + for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { + if (++s > ASM_ACTIVE_STREAMS_ALLOWED) + s -= ASM_ACTIVE_STREAMS_ALLOWED; + if (!session[s].ignore && !session[s].ac) { + q6asm_session_deregister(ac); + session[ac->session].ac = NULL; + session[s].ac = ac; + ac->session = s; + return q6asm_session_register(ac); + } + } + pr_debug("%s: session not available\n", __func__); + return -EINVAL; +} + static int q6asm_get_session_id_from_audio_client(struct audio_client *ac) { int n; @@ -1003,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(); @@ -1139,13 +1222,7 @@ void q6asm_audio_client_free(struct audio_client *ac) } } - rtac_set_asm_handle(ac->session, NULL); - apr_deregister(ac->apr2); - apr_deregister(ac->apr); - q6asm_mmap_apr_dereg(); - ac->apr2 = NULL; - ac->apr = NULL; - ac->mmap_apr = NULL; + q6asm_session_deregister(ac); q6asm_session_free(ac); pr_debug("%s: APR De-Register\n", __func__); @@ -1307,34 +1384,11 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) ac->fptr_cache_ops = NULL; /* DSP expects stream id from 1 */ ac->stream_id = 1; - ac->apr = apr_register("ADSP", "ASM", \ - (apr_fn)q6asm_callback,\ - ((ac->session) << 8 | 0x0001),\ - ac); - - if (ac->apr == NULL) { - pr_err("%s: Registration with APR failed\n", __func__); - mutex_unlock(&session_lock); - goto fail_apr1; - } - ac->apr2 = apr_register("ADSP", "ASM", - (apr_fn)q6asm_callback, - ((ac->session) << 8 | 0x0002), - ac); - if (ac->apr2 == NULL) { - pr_err("%s: Registration with APR-2 failed\n", __func__); - mutex_unlock(&session_lock); - goto fail_apr2; - } - - rtac_set_asm_handle(n, ac->apr); - - pr_debug("%s: Registering the common port with APR\n", __func__); - ac->mmap_apr = q6asm_mmap_apr_reg(); - if (ac->mmap_apr == NULL) { + rc = q6asm_session_register(ac); + if (rc < 0) { mutex_unlock(&session_lock); - goto fail_mmap; + goto fail_register; } init_waitqueue_head(&ac->cmd_wait); @@ -1357,7 +1411,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) rc = send_asm_custom_topology(ac); if (rc < 0) { mutex_unlock(&session_lock); - goto fail_mmap; + goto fail_send; } pr_debug("%s: session[%d]\n", __func__, ac->session); @@ -1365,11 +1419,9 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) mutex_unlock(&session_lock); return ac; -fail_mmap: - apr_deregister(ac->apr2); -fail_apr2: - apr_deregister(ac->apr); -fail_apr1: +fail_send: + q6asm_session_deregister(ac); +fail_register: q6asm_session_free(ac); fail_session: return NULL; @@ -3241,6 +3293,49 @@ int q6asm_open_write_v5(struct audio_client *ac, uint32_t format, } EXPORT_SYMBOL(q6asm_open_write_v5); +static int q6asm_open_write_version_adaptor(struct audio_client *ac, + uint32_t format, uint16_t bits_per_sample) +{ + if (q6asm_get_svc_version(APR_SVC_ASM) >= ADSP_ASM_API_VERSION_V2) + return q6asm_open_write_v5(ac, format, bits_per_sample); + else + return q6asm_open_write_v4(ac, format, bits_per_sample); +} + +/* + * q6asm_open_write_with_retry - Opens audio playback session, with retrying + * in case of session ID conflict + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + int i, rc; + + mutex_lock(&session_lock); + for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) { + rc = q6asm_open_write_version_adaptor(ac, format, + bits_per_sample); + if (rc != -EALREADY) + break; + + pr_debug("%s: session %d is occupied, try next\n", + __func__, ac->session); + q6asm_session_set_ignore(ac->session); + rc = q6asm_session_try_next(ac); + if (rc < 0) + break; + } + q6asm_session_clean_ignore(); + mutex_unlock(&session_lock); + + return rc; +} +EXPORT_SYMBOL(q6asm_open_write_with_retry); + int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, int32_t stream_id, bool is_gapless_mode) 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"); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index fd6e247d9fd8..91bad6731c9d 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -640,8 +640,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, tmp |= mod_slave; break; case SND_SOC_DAIFMT_CBS_CFS: - /* Set default source clock in Master mode */ - if (i2s->rclk_srcrate == 0) + /* + * Set default source clock in Master mode, only when the + * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any + * clock configuration assigned in DT is not overwritten. + */ + if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL) i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, 0, SND_SOC_CLOCK_IN); break; @@ -856,6 +860,11 @@ static int config_setup(struct i2s_dai *i2s) return 0; if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC]; + + if (i2s->rclk_srcrate == 0 && rclksrc && !IS_ERR(rclksrc)) + i2s->rclk_srcrate = clk_get_rate(rclksrc); + psr = i2s->rclk_srcrate / i2s->frmclk / rfs; writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR); dev_dbg(&i2s->pdev->dev, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index e3f34a86413c..c1e76feb3529 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1188,6 +1188,9 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( kfree(sm); continue; } + + /* create any TLV data */ + soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr); } return kc; diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e176bad19bcb..ca080a129b33 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -487,6 +487,24 @@ bpf_object__init_maps(struct bpf_object *obj, void *data, return 0; } +static bool section_have_execinstr(struct bpf_object *obj, int idx) +{ + Elf_Scn *scn; + GElf_Shdr sh; + + scn = elf_getscn(obj->efile.elf, idx); + if (!scn) + return false; + + if (gelf_getshdr(scn, &sh) != &sh) + return false; + + if (sh.sh_flags & SHF_EXECINSTR) + return true; + + return false; +} + static int bpf_object__elf_collect(struct bpf_object *obj) { Elf *elf = obj->efile.elf; @@ -567,6 +585,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj) } else if (sh.sh_type == SHT_REL) { void *reloc = obj->efile.reloc; int nr_reloc = obj->efile.nr_reloc + 1; + int sec = sh.sh_info; /* points to other section */ + + /* Only do relo for section with exec instructions */ + if (!section_have_execinstr(obj, sec)) { + pr_debug("skip relo %s(%d) for section(%d)\n", + name, idx, sec); + continue; + } reloc = realloc(reloc, sizeof(*obj->efile.reloc) * nr_reloc); diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 68276f35e323..6e4a10fe9dd0 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -4905,21 +4905,22 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event else ls = 2; - if (*(ptr+1) == 'F' || *(ptr+1) == 'f' || - *(ptr+1) == 'S' || *(ptr+1) == 's') { + if (isalnum(ptr[1])) ptr++; + + if (*ptr == 'F' || *ptr == 'f' || + *ptr == 'S' || *ptr == 's') { show_func = *ptr; - } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { - print_mac_arg(s, *(ptr+1), data, size, event, arg); - ptr++; + } else if (*ptr == 'M' || *ptr == 'm') { + print_mac_arg(s, *ptr, data, size, event, arg); arg = arg->next; break; - } else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') { + } else if (*ptr == 'I' || *ptr == 'i') { int n; - n = print_ip_arg(s, ptr+1, data, size, event, arg); + n = print_ip_arg(s, ptr, data, size, event, arg); if (n > 0) { - ptr += n; + ptr += n - 1; arg = arg->next; break; } diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 88cccea3ca99..64309d73921b 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c @@ -1867,17 +1867,25 @@ static const char *get_field_str(struct filter_arg *arg, struct pevent_record *r struct pevent *pevent; unsigned long long addr; const char *val = NULL; + unsigned int size; char hex[64]; /* If the field is not a string convert it */ if (arg->str.field->flags & FIELD_IS_STRING) { val = record->data + arg->str.field->offset; + size = arg->str.field->size; + + if (arg->str.field->flags & FIELD_IS_DYNAMIC) { + addr = *(unsigned int *)val; + val = record->data + (addr & 0xffff); + size = addr >> 16; + } /* * We need to copy the data since we can't be sure the field * is null terminated. */ - if (*(val + arg->str.field->size - 1)) { + if (*(val + size - 1)) { /* copy it */ memcpy(arg->str.buffer, val, arg->str.field->size); /* the buffer is already NULL terminated */ diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index d677e018e504..bf907c50fcae 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -126,7 +126,7 @@ int test__vmlinux_matches_kallsyms(void) if (pair && UM(pair->start) == mem_start) { next_pair: - if (strcmp(sym->name, pair->name) == 0) { + if (arch__compare_symbol_names(sym->name, pair->name) == 0) { /* * kallsyms don't have the symbol end, so we * set that by using the next symbol start - 1, diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 6e0a16c7176a..8a84f82845f3 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -625,13 +625,13 @@ static void apply_config_terms(struct perf_evsel *evsel, struct perf_evsel_config_term *term; struct list_head *config_terms = &evsel->config_terms; struct perf_event_attr *attr = &evsel->attr; - struct callchain_param param; + /* callgraph default */ + struct callchain_param param = { + .record_mode = callchain_param.record_mode, + }; u32 dump_size = 0; char *callgraph_buf = NULL; - /* callgraph default */ - param.record_mode = callchain_param.record_mode; - list_for_each_entry(term, config_terms, list) { switch (term->type) { case PERF_EVSEL__CONFIG_TERM_PERIOD: diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 4fd37d6708cb..f6720afa9f34 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -720,7 +720,7 @@ iter_prepare_cumulative_entry(struct hist_entry_iter *iter, * cumulated only one time to prevent entries more than 100% * overhead. */ - he_cache = malloc(sizeof(*he_cache) * (iter->max_stack + 1)); + he_cache = malloc(sizeof(*he_cache) * (callchain_cursor.nr + 1)); if (he_cache == NULL) return -ENOMEM; @@ -881,8 +881,6 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, if (err) return err; - iter->max_stack = max_stack_depth; - err = iter->ops->prepare_entry(iter, al); if (err) goto out; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a48a2078d288..46b7591acd9c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -91,7 +91,6 @@ struct hist_entry_iter { int curr; bool hide_unresolved; - int max_stack; struct perf_evsel *evsel; struct perf_sample *sample; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 24ebd3e3eb7d..5d2e479430d1 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -90,6 +90,7 @@ ifdef INSTALL_PATH for TARGET in $(TARGETS); do \ echo "echo ; echo Running tests in $$TARGET" >> $(ALL_SCRIPT); \ echo "echo ========================================" >> $(ALL_SCRIPT); \ + echo "[ -w /dev/kmsg ] && echo \"kselftest: Running tests in $$TARGET\" >> /dev/kmsg" >> $(ALL_SCRIPT); \ echo "cd $$TARGET" >> $(ALL_SCRIPT); \ make -s --no-print-directory -C $$TARGET emit_tests >> $(ALL_SCRIPT); \ echo "cd \$$ROOT" >> $(ALL_SCRIPT); \ diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc new file mode 100644 index 000000000000..5ba73035e1d9 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc @@ -0,0 +1,46 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Kprobe event string type argument + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +echo 0 > events/enable +echo > kprobe_events + +case `uname -m` in +x86_64) + ARG2=%si + OFFS=8 +;; +i[3456]86) + ARG2=%cx + OFFS=4 +;; +aarch64) + ARG2=%x1 + OFFS=8 +;; +arm*) + ARG2=%r1 + OFFS=4 +;; +*) + echo "Please implement other architecture here" + exit_untested +esac + +: "Test get argument (1)" +echo "p:testprobe create_trace_kprobe arg1=+0(+0(${ARG2})):string" > kprobe_events +echo 1 > events/kprobes/testprobe/enable +! echo test >> kprobe_events +tail -n 1 trace | grep -qe "testprobe.* arg1=\"test\"" + +echo 0 > events/kprobes/testprobe/enable +: "Test get argument (2)" +echo "p:testprobe create_trace_kprobe arg1=+0(+0(${ARG2})):string arg2=+0(+${OFFS}(${ARG2})):string" > kprobe_events +echo 1 > events/kprobes/testprobe/enable +! echo test1 test2 >> kprobe_events +tail -n 1 trace | grep -qe "testprobe.* arg1=\"test1\" arg2=\"test2\"" + +echo 0 > events/enable +echo > kprobe_events diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc new file mode 100644 index 000000000000..231bcd2c4eb5 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc @@ -0,0 +1,97 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Kprobe event argument syntax + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +grep "x8/16/32/64" README > /dev/null || exit_unsupported # version issue + +echo 0 > events/enable +echo > kprobe_events + +PROBEFUNC="vfs_read" +GOODREG= +BADREG= +GOODSYM="_sdata" +if ! grep -qw ${GOODSYM} /proc/kallsyms ; then + GOODSYM=$PROBEFUNC +fi +BADSYM="deaqswdefr" +SYMADDR=0x`grep -w ${GOODSYM} /proc/kallsyms | cut -f 1 -d " "` +GOODTYPE="x16" +BADTYPE="y16" + +case `uname -m` in +x86_64|i[3456]86) + GOODREG=%ax + BADREG=%ex +;; +aarch64) + GOODREG=%x0 + BADREG=%ax +;; +arm*) + GOODREG=%r0 + BADREG=%ax +;; +esac + +test_goodarg() # Good-args +{ + while [ "$1" ]; do + echo "p ${PROBEFUNC} $1" > kprobe_events + shift 1 + done; +} + +test_badarg() # Bad-args +{ + while [ "$1" ]; do + ! echo "p ${PROBEFUNC} $1" > kprobe_events + shift 1 + done; +} + +echo > kprobe_events + +: "Register access" +test_goodarg ${GOODREG} +test_badarg ${BADREG} + +: "Symbol access" +test_goodarg "@${GOODSYM}" "@${SYMADDR}" "@${GOODSYM}+10" "@${GOODSYM}-10" +test_badarg "@" "@${BADSYM}" "@${GOODSYM}*10" "@${GOODSYM}/10" \ + "@${GOODSYM}%10" "@${GOODSYM}&10" "@${GOODSYM}|10" + +: "Stack access" +test_goodarg "\$stack" "\$stack0" "\$stack1" +test_badarg "\$stackp" "\$stack0+10" "\$stack1-10" + +: "Retval access" +echo "r ${PROBEFUNC} \$retval" > kprobe_events +! echo "p ${PROBEFUNC} \$retval" > kprobe_events + +: "Comm access" +test_goodarg "\$comm" + +: "Indirect memory access" +test_goodarg "+0(${GOODREG})" "-0(${GOODREG})" "+10(\$stack)" \ + "+0(\$stack1)" "+10(@${GOODSYM}-10)" "+0(+10(+20(\$stack)))" +test_badarg "+(${GOODREG})" "(${GOODREG}+10)" "-(${GOODREG})" "(${GOODREG})" \ + "+10(\$comm)" "+0(${GOODREG})+10" + +: "Name assignment" +test_goodarg "varname=${GOODREG}" +test_badarg "varname=varname2=${GOODREG}" + +: "Type syntax" +test_goodarg "${GOODREG}:${GOODTYPE}" +test_badarg "${GOODREG}::${GOODTYPE}" "${GOODREG}:${BADTYPE}" \ + "${GOODTYPE}:${GOODREG}" + +: "Combination check" + +test_goodarg "\$comm:string" "+0(\$stack):string" +test_badarg "\$comm:x64" "\$stack:string" "${GOODREG}:string" + +echo > kprobe_events diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc b/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc new file mode 100644 index 000000000000..4fda01a08da4 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc @@ -0,0 +1,43 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Kprobe events - probe points + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +TARGET_FUNC=create_trace_kprobe + +dec_addr() { # hexaddr + printf "%d" "0x"`echo $1 | tail -c 8` +} + +set_offs() { # prev target next + A1=`dec_addr $1` + A2=`dec_addr $2` + A3=`dec_addr $3` + TARGET="0x$2" # an address + PREV=`expr $A1 - $A2` # offset to previous symbol + NEXT=+`expr $A3 - $A2` # offset to next symbol + OVERFLOW=+`printf "0x%x" ${PREV}` # overflow offset to previous symbol +} + +# We have to decode symbol addresses to get correct offsets. +# If the offset is not an instruction boundary, it cause -EILSEQ. +set_offs `grep -A1 -B1 ${TARGET_FUNC} /proc/kallsyms | cut -f 1 -d " " | xargs` + +UINT_TEST=no +# printf "%x" -1 returns (unsigned long)-1. +if [ `printf "%x" -1 | wc -c` != 9 ]; then + UINT_TEST=yes +fi + +echo 0 > events/enable +echo > kprobe_events +echo "p:testprobe ${TARGET_FUNC}" > kprobe_events +echo "p:testprobe ${TARGET}" > kprobe_events +echo "p:testprobe ${TARGET_FUNC}${NEXT}" > kprobe_events +! echo "p:testprobe ${TARGET_FUNC}${PREV}" > kprobe_events +if [ "${UINT_TEST}" = yes ]; then +! echo "p:testprobe ${TARGET_FUNC}${OVERFLOW}" > kprobe_events +fi +echo > kprobe_events +clear_trace diff --git a/tools/testing/selftests/memfd/config b/tools/testing/selftests/memfd/config new file mode 100644 index 000000000000..835c7f4dadcd --- /dev/null +++ b/tools/testing/selftests/memfd/config @@ -0,0 +1 @@ +CONFIG_FUSE_FS=m diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 412459369686..9b654a070e7d 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -97,6 +97,8 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) static void sock_fanout_set_ebpf(int fd) { + static char log_buf[65536]; + const int len_off = __builtin_offsetof(struct __sk_buff, len); struct bpf_insn prog[] = { { BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0 }, @@ -109,7 +111,6 @@ static void sock_fanout_set_ebpf(int fd) { BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0 }, { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 } }; - char log_buf[512]; union bpf_attr attr; int pfd; diff --git a/tools/testing/selftests/powerpc/mm/subpage_prot.c b/tools/testing/selftests/powerpc/mm/subpage_prot.c index 440180ff8089..ca29f5872817 100644 --- a/tools/testing/selftests/powerpc/mm/subpage_prot.c +++ b/tools/testing/selftests/powerpc/mm/subpage_prot.c @@ -135,6 +135,16 @@ static int run_test(void *addr, unsigned long size) return 0; } +static int syscall_available(void) +{ + int rc; + + errno = 0; + rc = syscall(__NR_subpage_prot, 0, 0, 0); + + return rc == 0 || (errno != ENOENT && errno != ENOSYS); +} + int test_anon(void) { unsigned long align; @@ -145,6 +155,8 @@ int test_anon(void) void *mallocblock; unsigned long mallocsize; + SKIP_IF(!syscall_available()); + if (getpagesize() != 0x10000) { fprintf(stderr, "Kernel page size must be 64K!\n"); return 1; @@ -180,6 +192,8 @@ int test_file(void) off_t filesize; int fd; + SKIP_IF(!syscall_available()); + fd = open(file_name, O_RDWR); if (fd == -1) { perror("failed to open file"); diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c index 1c12536f2081..18f523557983 100644 --- a/tools/thermal/tmon/sysfs.c +++ b/tools/thermal/tmon/sysfs.c @@ -486,6 +486,7 @@ int zone_instance_to_index(int zone_inst) int update_thermal_data() { int i; + int next_thermal_record = cur_thermal_record + 1; char tz_name[256]; static unsigned long samples; @@ -495,9 +496,9 @@ int update_thermal_data() } /* circular buffer for keeping historic data */ - if (cur_thermal_record >= NR_THERMAL_RECORDS) - cur_thermal_record = 0; - gettimeofday(&trec[cur_thermal_record].tv, NULL); + if (next_thermal_record >= NR_THERMAL_RECORDS) + next_thermal_record = 0; + gettimeofday(&trec[next_thermal_record].tv, NULL); if (tmon_log) { fprintf(tmon_log, "%lu ", ++samples); fprintf(tmon_log, "%3.1f ", p_param.t_target); @@ -507,11 +508,12 @@ int update_thermal_data() snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, ptdata.tzi[i].instance); sysfs_get_ulong(tz_name, "temp", - &trec[cur_thermal_record].temp[i]); + &trec[next_thermal_record].temp[i]); if (tmon_log) fprintf(tmon_log, "%lu ", - trec[cur_thermal_record].temp[i]/1000); + trec[next_thermal_record].temp[i] / 1000); } + cur_thermal_record = next_thermal_record; for (i = 0; i < ptdata.nr_cooling_dev; i++) { char cdev_name[256]; unsigned long val; diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c index 9aa19652e8e8..b43138f8b862 100644 --- a/tools/thermal/tmon/tmon.c +++ b/tools/thermal/tmon/tmon.c @@ -336,7 +336,6 @@ int main(int argc, char **argv) show_data_w(); show_cooling_device(); } - cur_thermal_record++; time_elapsed += ticktime; controller_handler(trec[0].temp[target_tz_index] / 1000, &yk); |
