diff options
1081 files changed, 23101 insertions, 6604 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index bc0548201755..fc759598c4c9 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -1,4 +1,4 @@ subdir-y := accounting auxdisplay blackfin connector \ - filesystems filesystems ia64 laptops mic misc-devices \ + filesystems filesystems ia64 laptops misc-devices \ networking pcmcia prctl ptp spi timers vDSO video4linux \ watchdog diff --git a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt index 6ddc72576e88..a6537ebd2512 100644 --- a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +++ b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt @@ -108,6 +108,8 @@ Optional driver parameters: - qcom,sysmon-id: platform device id that sysmon is probed with for the subsystem. - qcom,pil-force-shutdown: Boolean. If set, the SSR framework will not trigger graceful shutdown on behalf of the subsystem driver. +- qcom,mdm-link-info: a string indicating additional info about the physical link. + For example: "devID_domain.bus.slot" in case of PCIe. Example: mdm0: qcom,mdm0 { diff --git a/Documentation/devicetree/bindings/arm/msm/wil6210.txt b/Documentation/devicetree/bindings/arm/msm/wil6210.txt index b381bdebdfc9..c4673279953d 100644 --- a/Documentation/devicetree/bindings/arm/msm/wil6210.txt +++ b/Documentation/devicetree/bindings/arm/msm/wil6210.txt @@ -10,6 +10,10 @@ Required properties: - compatible: "qcom,wil6210" - qcom,smmu-support: Boolean flag indicating whether PCIe has SMMU support +- qcom,smmu-s1-en: Boolean flag indicating whether SMMU stage1 should be enabled +- qcom,smmu-fast-map: Boolean flag indicating whether SMMU fast mapping should be enabled +- qcom,smmu-coherent: Boolean flag indicating SMMU dma and page table coherency +- qcom,smmu-mapping: specifies the base address and size of SMMU space - qcom,pcie-parent: phandle for the PCIe root complex to which 11ad card is connected - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for the below optional properties: @@ -33,6 +37,10 @@ Example: wil6210: qcom,wil6210 { compatible = "qcom,wil6210"; qcom,smmu-support; + qcom,smmu-s1-en; + qcom,smmu-fast-map; + qcom,smmu-coherent; + qcom,smmu-mapping = <0x20000000 0xe0000000>; qcom,pcie-parent = <&pcie1>; qcom,wigig-en = <&tlmm 94 0>; qcom,msm-bus,name = "wil6210"; diff --git a/Documentation/devicetree/bindings/clock/imx31-clock.txt b/Documentation/devicetree/bindings/clock/imx31-clock.txt index 19df842c694f..8163d565f697 100644 --- a/Documentation/devicetree/bindings/clock/imx31-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx31-clock.txt @@ -77,7 +77,7 @@ Examples: clks: ccm@53f80000{ compatible = "fsl,imx31-ccm"; reg = <0x53f80000 0x4000>; - interrupts = <0 31 0x04 0 53 0x04>; + interrupts = <31>, <53>; #clock-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 4fd0c2ecbc6e..90ccfa7c62e2 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -271,6 +271,8 @@ Optional properties: "trigger_sw_te" = Software trigger and TE - qcom,mdss-dsi-panel-framerate: Specifies the frame rate for the panel. 60 = 60 frames per second (default) +- qcom,mdss-dsi-host-esc-clk-freq-hz: Specifies the escape clock needed for the host. + 19200000 = 19.2 MHz (default) - qcom,mdss-dsi-panel-clockrate: A 64 bit value specifies the panel clock speed in Hz. 0 = default value. - qcom,mdss-mdp-kickoff-threshold: This property can be used to define a region @@ -657,6 +659,7 @@ Example: qcom,mdss-dsi-mdp-trigger = <0>; qcom,mdss-dsi-dma-trigger = <0>; qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-host-esc-clk-freq-hz = <19200000>; qcom,mdss-dsi-panel-clockrate = <424000000>; qcom,mdss-mdp-kickoff-threshold = <11 2430>; qcom,mdss-mdp-kickoff-delay = <1000>; diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt index 0a9b31a946ec..3688e3ebab48 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_keys.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt @@ -15,6 +15,8 @@ Optional properties: config defined in pin groups of interrupt and reset gpio. "gpio_ts_suspend" : Disabled configuration of pins, this should specify sleep config defined in pin groups of interrupt and reset gpio. + - name : input device name. + - use-syscore : use syscore functionality for driver. Each button (key) is represented as a sub-node of "gpio-keys": Subnode properties: @@ -41,6 +43,7 @@ gpio_keys { pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend"; pinctrl-0 = <&gpio_key_active>; pinctrl-1 = <&gpio_key_suspend>; + use-syscore; button@21 { label = "GPIO Key UP"; linux,code = <103>; diff --git a/Documentation/devicetree/bindings/media/video/laser-sensor.txt b/Documentation/devicetree/bindings/media/video/laser-sensor.txt index 1bcb0b93cb10..0003f20845a0 100644 --- a/Documentation/devicetree/bindings/media/video/laser-sensor.txt +++ b/Documentation/devicetree/bindings/media/video/laser-sensor.txt @@ -19,6 +19,7 @@ Required node properties: regulators used - pinctrl-names : should specify the pin control groups followed by the definition of each group + stm,irq-gpio : irq gpio which is to provide interrupts to host. - gpios : should contain phandle to gpio controller node and array of #gpio-cells specifying specific gpio (controller specific) - qcom,gpio-req-tbl-num : contains index to gpios specific to the sensor diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt b/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt index 626ca2124366..acc850773210 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt +++ b/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt @@ -9,8 +9,26 @@ receive(RX)/transmit(TX) control. Required properties: - compatible: "qcom,wcn3990-wifi"; + - reg: Memory regions defined as starting address and size + - reg-names: Names of the memory regions defined in reg entry + - interrupts: Copy engine interrupt table Example: - qcom,msm_ath10k@18000000 { + msm_ath10k_wlan: qcom,msm_ath10k_wlan@18800000 { compatible = "qcom,wcn3990-wifi"; + reg = <0x18800000 0x800000>; + reg-names = "membase"; + interrupts = + <0 130 0 /* CE0 */ >, + <0 131 0 /* CE1 */ >, + <0 132 0 /* CE2 */ >, + <0 133 0 /* CE3 */ >, + <0 134 0 /* CE4 */ >, + <0 135 0 /* CE5 */ >, + <0 136 0 /* CE6 */ >, + <0 137 0 /* CE7 */ >, + <0 138 0 /* CE8 */ >, + <0 139 0 /* CE9 */ >, + <0 140 0 /* CE10 */ >, + <0 141 0 /* CE11 */ >; }; diff --git a/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt b/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt index d377a28e6617..f19dfa4b69c6 100644 --- a/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt +++ b/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt @@ -41,6 +41,12 @@ Main node properties: Definition: Maximum payload interface support on transmit path. If not defined MHI_MAX_MTU is used. +- qcom,interface-name + Usage: optional + Value type: <string> + Definition: optional string to overwrite default interface name. If + not defined string RMNET_MHI_DRIVER_NAME is used. + ======== Example: ======== diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt index c8f2a5a8e496..92ef23c3a290 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt @@ -31,6 +31,12 @@ Charger specific properties: revid module. This is used to identify the SMB subtype. +- qcom,parallel-mode + Usage: optional + Value type: <u32> + Definition: Specifies parallel charging mode. If not specified, MID-MID + option is selected by default. + - qcom,suspend-input Usage: optional Value type: <empty> diff --git a/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt b/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt index 6574cfe30ce5..337649824257 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt @@ -24,8 +24,6 @@ Optional Properties: 2-bit amplitude control 0x00: 0, 0x01: vmax/4, 0x02: vmax/2, 0x03: vmax. Default values are 0x00. - qcom,sc-deb-cycles : short circuit debounce in internal pwm switching clock cycles - - qcom,use-play-irq : boolean, use this if the device uses irq for play - - qcom,use-sc-irq : boolean, use this if the device uses irq for play - interrupts: Specifies the interrupt associated with Haptics. The available interrupts are play and short circuit. The values for play and short circuit are <0x3 0xc0 0x0> and <0x3 0xc0 0x1>. @@ -54,9 +52,11 @@ Optional properties for pwm play mode: Optional properties when qcom,actuator-type is "lra" - qcom,correct-lra-drive-freq : boolean, use this to ensure LRA is driven at correct resonant frequency, which may change due to operating conditions. - - qcom,misc-trim-error-rc19p2-clk-reg-present : boolean, use this if TRIM_ERROR_RC19P2_CLK - register is present in MISC module. This register holds - the frequency error in 19.2Mhz RC clock. + - qcom,pmic-misc : phandle of misc device using which the clock + trim error can be read from. + - qcom,misc-clk-trim-error-reg : Address offset of MISC_TRIM_ERROR_RC19P2_CLK + register if present in MISC module. This + holds the frequency error in 19.2 MHz clock. - qcom,lra-auto-res-mode : auto resonance technique, four different modes "none" : no auto resonance "zxd" : zero crossing based discontinuous method @@ -66,6 +66,9 @@ Optional properties when qcom,actuator-type is "lra" - qcom,lra-high-z : High Z configuration for auto resonance. Possible string values are "none", "opt1", "opt2" and "opt3" (default). For PM660, "opt0" is valid value for 1 LRA period. + - qcom,lra-hw-auto-resonance : boolean, enable Hardware auto-resonance for PM660. + Use this property to enable Hardware auto-resonance. + If not defined then Software auto-resonance is enabled(default). - qcom,lra-qwd-drive-duration : Drive duration of LRA in QWD mode for PM660. Possible values are: 0: 1/4 LRA PERIOD and 1: 3/8 LRA PERIOD - qcom,lra-calibrate-at-eop : To calibrate at End of Pattern for PM660. @@ -118,6 +121,8 @@ Example: interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pm660_revid>; vcc_pon-supply = <&pon_perph_reg>; + qcom,pmic-misc = <&pmi8998_misc>; + qcom,misc-clk-trim-error-reg = <0xf3>; qcom,play-mode = "direct"; qcom,wave-play-rate-us = <5263>; qcom,actuator-type = "lra"; @@ -128,8 +133,6 @@ Example: qcom,int-pwm-freq-khz = <505>; qcom,en-brake; qcom,brake-pattern = [03 03 00 00]; - qcom,use-play-irq; - qcom,use-sc-irq; qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e]; qcom,wave-rep-cnt = <1>; qcom,wave-samp-rep-cnt = <1>; diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt index 5c090771c016..fa6a6dfea0e2 100644 --- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt @@ -40,7 +40,24 @@ Optional properties: receive. SPI slave nodes must be children of the SPI master node and can contain -properties described in Documentation/devicetree/bindings/spi/spi-bus.txt +the following properties. + +Required properties: +- compatible: Should contain: + "qcom,spi-msm-codec-slave" for external codec control + +- reg: Chip select address of device. + +- spi-max-frequency: Maximum SPI clocking speed of device in Hz. + +Optional properties: +- spi-cpol: Empty property indicating device requires + inverse clock polarity (CPOL) mode. +- spi-cpha: Empty property indicating device requires + shifted clock phase (CPHA) mode. + +Other optional properties described in +Documentation/devicetree/bindings/spi/spi-bus.txt Example: diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 45d680644dfe..0220f18658e8 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -750,6 +750,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. seconds. Defaults to 10*60 = 10mins. A value of 0 disables the blank timer. + core_ctl_disable_cpumask= [SMP] + Exempt the CPUs from being managed by core_ctl. + core_ctl operates on a cluster basis. So all the + CPUs in a given cluster must be specified to disable + core_ctl for that cluster. + coredump_filter= [KNL] Change the default value for /proc/<pid>/coredump_filter. @@ -1265,6 +1271,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. When zero, profiling data is discarded and associated debugfs files are removed at module unload time. + goldfish [X86] Enable the goldfish android emulator platform. + Don't use this when you are not running on the + android emulator + gpt [EFI] Forces disk with valid GPT signature but invalid Protective MBR to be treated as GPT. If the primary GPT is corrupted, it enables the backup/alternate diff --git a/Documentation/mic/Makefile b/Documentation/mic/Makefile deleted file mode 100644 index a191d453badf..000000000000 --- a/Documentation/mic/Makefile +++ /dev/null @@ -1 +0,0 @@ -subdir-y := mpssd diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile deleted file mode 100644 index 06871b0c08a6..000000000000 --- a/Documentation/mic/mpssd/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -ifndef CROSS_COMPILE -# List of programs to build -hostprogs-$(CONFIG_X86_64) := mpssd - -mpssd-objs := mpssd.o sysfs.o - -# Tell kbuild to always build the programs -always := $(hostprogs-y) - -HOSTCFLAGS += -I$(objtree)/usr/include -I$(srctree)/tools/include - -ifdef DEBUG -HOSTCFLAGS += -DDEBUG=$(DEBUG) -endif - -HOSTLOADLIBES_mpssd := -lpthread - -install: - install mpssd /usr/sbin/mpssd - install micctrl /usr/sbin/micctrl -endif diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 092ee9fbaf2b..df8ab4fc240a 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1991,6 +1991,7 @@ registers, find a list below: PPC | KVM_REG_PPC_TM_VSCR | 32 PPC | KVM_REG_PPC_TM_DSCR | 64 PPC | KVM_REG_PPC_TM_TAR | 64 + PPC | KVM_REG_PPC_TM_XER | 64 | | MIPS | KVM_REG_MIPS_R0 | 64 ... @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 38 +SUBLEVEL = 55 EXTRAVERSION = NAME = Blurry Fish Butt diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index 8531a7a79e33..f10371a981b7 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -139,7 +139,11 @@ CONFIG_PPP_DEFLATE=y CONFIG_PPP_MPPE=y CONFIG_PREEMPT=y CONFIG_PROFILING=y +CONFIG_QFMT_V2=y CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QUOTA_TREE=y +CONFIG_QUOTACTL=y CONFIG_RANDOMIZE_BASE=y CONFIG_RTC_CLASS=y CONFIG_RT_GROUP_SCHED=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 3fd0b13488a1..70aaae17ad29 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -8,6 +8,7 @@ # CONFIG_VT is not set CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ARM_KERNMEM_PERMS=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y diff --git a/arch/arc/include/asm/cacheflush.h b/arch/arc/include/asm/cacheflush.h index fbe3587c4f36..56aeb5efe604 100644 --- a/arch/arc/include/asm/cacheflush.h +++ b/arch/arc/include/asm/cacheflush.h @@ -85,6 +85,10 @@ void flush_anon_page(struct vm_area_struct *vma, */ #define PG_dc_clean PG_arch_1 +#define CACHE_COLORS_NUM 4 +#define CACHE_COLORS_MSK (CACHE_COLORS_NUM - 1) +#define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & CACHE_COLORS_MSK) + /* * Simple wrapper over config option * Bootup code ensures that hardware matches kernel configuration @@ -94,8 +98,6 @@ static inline int cache_is_vipt_aliasing(void) return IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING); } -#define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & 1) - /* * checks if two addresses (after page aligning) index into same cache set */ diff --git a/arch/arc/include/asm/delay.h b/arch/arc/include/asm/delay.h index a36e8601114d..d5da2115d78a 100644 --- a/arch/arc/include/asm/delay.h +++ b/arch/arc/include/asm/delay.h @@ -26,7 +26,9 @@ static inline void __delay(unsigned long loops) " lp 1f \n" " nop \n" "1: \n" - : : "r"(loops)); + : + : "r"(loops) + : "lp_count"); } extern void __bad_udelay(void); diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c index abd961f3e763..5f69c3bd59bb 100644 --- a/arch/arc/kernel/unaligned.c +++ b/arch/arc/kernel/unaligned.c @@ -241,8 +241,9 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs, if (state.fault) goto fault; + /* clear any remanants of delay slot */ if (delay_mode(regs)) { - regs->ret = regs->bta; + regs->ret = regs->bta & ~1U; regs->status32 &= ~STATUS_DE_MASK; } else { regs->ret += state.instr_len; diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c index aaf1e2d1d900..d81b6d7e11e7 100644 --- a/arch/arc/mm/cache.c +++ b/arch/arc/mm/cache.c @@ -960,11 +960,16 @@ void arc_cache_init(void) /* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */ if (is_isa_arcompact()) { int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING); - - if (dc->alias && !handled) - panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); - else if (!dc->alias && handled) + int num_colors = dc->sz_k/dc->assoc/TO_KB(PAGE_SIZE); + + if (dc->alias) { + if (!handled) + panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); + if (CACHE_COLORS_NUM != num_colors) + panic("CACHE_COLORS_NUM not optimized for config\n"); + } else if (!dc->alias && handled) { panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); + } } } diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts index e74df327cdd3..20618a897c99 100644 --- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts @@ -122,6 +122,8 @@ uart1: serial@f8020000 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart1_default>; + atmel,use-dma-rx; + atmel,use-dma-tx; status = "okay"; }; diff --git a/arch/arm/boot/dts/at91-sama5d4_xplained.dts b/arch/arm/boot/dts/at91-sama5d4_xplained.dts index da84e65b56ef..e27024cdf48b 100644 --- a/arch/arm/boot/dts/at91-sama5d4_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d4_xplained.dts @@ -110,6 +110,8 @@ }; usart3: serial@fc00c000 { + atmel,use-dma-rx; + atmel,use-dma-tx; status = "okay"; }; diff --git a/arch/arm/boot/dts/da850-evm.dts b/arch/arm/boot/dts/da850-evm.dts index 4f935ad9f27b..6881757b03e8 100644 --- a/arch/arm/boot/dts/da850-evm.dts +++ b/arch/arm/boot/dts/da850-evm.dts @@ -85,6 +85,7 @@ #size-cells = <1>; compatible = "m25p64"; spi-max-frequency = <30000000>; + m25p,fast-read; reg = <0>; partition@0 { label = "U-Boot-SPL"; diff --git a/arch/arm/boot/dts/imx31.dtsi b/arch/arm/boot/dts/imx31.dtsi index 5fdb222636a7..cbe5fd5ed179 100644 --- a/arch/arm/boot/dts/imx31.dtsi +++ b/arch/arm/boot/dts/imx31.dtsi @@ -30,11 +30,11 @@ }; }; - avic: avic-interrupt-controller@60000000 { + avic: interrupt-controller@68000000 { compatible = "fsl,imx31-avic", "fsl,avic"; interrupt-controller; #interrupt-cells = <1>; - reg = <0x60000000 0x100000>; + reg = <0x68000000 0x100000>; }; soc { @@ -110,13 +110,6 @@ interrupts = <19>; clocks = <&clks 25>; }; - - clks: ccm@53f80000{ - compatible = "fsl,imx31-ccm"; - reg = <0x53f80000 0x4000>; - interrupts = <0 31 0x04 0 53 0x04>; - #clock-cells = <1>; - }; }; aips@53f00000 { /* AIPS2 */ @@ -126,6 +119,13 @@ reg = <0x53f00000 0x100000>; ranges; + clks: ccm@53f80000{ + compatible = "fsl,imx31-ccm"; + reg = <0x53f80000 0x4000>; + interrupts = <31>, <53>; + #clock-cells = <1>; + }; + gpt: timer@53f90000 { compatible = "fsl,imx31-gpt"; reg = <0x53f90000 0x4000>; diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi index a35d54fd9cd3..ddfdb75a6e90 100644 --- a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi +++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi @@ -319,8 +319,6 @@ compatible = "fsl,imx6q-nitrogen6_max-sgtl5000", "fsl,imx-audio-sgtl5000"; model = "imx6q-nitrogen6_max-sgtl5000"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_sgtl5000>; ssi-controller = <&ssi1>; audio-codec = <&codec>; audio-routing = @@ -401,6 +399,8 @@ codec: sgtl5000@0a { compatible = "fsl,sgtl5000"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sgtl5000>; reg = <0x0a>; clocks = <&clks 201>; VDDA-supply = <®_2p5v>; diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi index 533861b4422a..b7a3d3f5cba5 100644 --- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi +++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi @@ -614,13 +614,15 @@ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&compr>; + <&afe>, <&lsm>, <&routing>, <&compr>, + <&loopback1>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", - "msm-pcm-routing", "msm-compr-dsp"; + "msm-pcm-routing", "msm-compr-dsp", + "msm-pcm-loopback.1"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&dai_mi2s_quat>, <&afe_pcm_rx>, <&afe_pcm_tx>, @@ -660,6 +662,11 @@ vin-supply = <&vph_pwr_vreg>; }; + loopback1: qcom,msm-pcm-loopback-low-latency { + compatible = "qcom,msm-pcm-loopback"; + qcom,msm-pcm-loopback-low-latency; + }; + qcom,msm-dai-mi2s { dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat { pinctrl-names = "default", "sleep"; @@ -876,9 +883,10 @@ }; &spi_0 { - spi_codec@2 { + spi_codec@0 { compatible = "qcom,spi-msm-codec-slave"; - reg = <2>; - spi-max-frequency = <19200000>; + reg = <0>; + spi-max-frequency = <2000000>; + spi-cpha; }; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts index 2f4037497764..9d4b1457f990 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts +++ b/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts @@ -34,6 +34,10 @@ qcom,mdss-pref-prim-intf = "hdmi"; }; +&msm_gpu { + dma-coherent; +}; + &sde_hdmi { qcom,display-type = "primary"; }; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi index 69f24bbfc3c0..8d867bb697be 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi @@ -216,6 +216,8 @@ qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; qcom,config-select = <&dsi_dual_nt35597_truly_cmd_config0>; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi index ab6266e9e6b8..1a572f97c840 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi @@ -202,6 +202,8 @@ qcom,mdss-dsi-mdp-trigger = "none"; qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 50>; qcom,mdss-dsi-tx-eot-append; + qcom,mdss-pan-physical-width-dimension = <74>; + qcom,mdss-pan-physical-height-dimension = <131>; qcom,config-select = <&dsi_dual_nt35597_truly_video_config0>; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi index 49afd34c50e7..60f0811ce987 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi @@ -75,7 +75,7 @@ 15 01 00 00 00 00 02 15 12 15 01 00 00 00 00 02 16 12 15 01 00 00 00 00 02 30 01 - 15 01 00 00 00 00 02 72 31 + 15 01 00 00 00 00 02 72 11 15 01 00 00 00 00 02 58 82 15 01 00 00 00 00 02 59 00 15 01 00 00 00 00 02 5a 02 diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi index 068459bf2504..7e83d25e9e43 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi @@ -71,7 +71,7 @@ 15 01 00 00 00 00 02 15 12 15 01 00 00 00 00 02 16 12 15 01 00 00 00 00 02 30 01 - 15 01 00 00 00 00 02 72 31 + 15 01 00 00 00 00 02 72 11 15 01 00 00 00 00 02 58 82 15 01 00 00 00 00 02 59 00 15 01 00 00 00 00 02 5a 02 diff --git a/arch/arm/boot/dts/qcom/msm-audio.dtsi b/arch/arm/boot/dts/qcom/msm-audio.dtsi index 42cf30c789d9..d450f43f8c22 100644 --- a/arch/arm/boot/dts/qcom/msm-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msm-audio.dtsi @@ -931,6 +931,8 @@ clocks = <&clock_rpmcc RPM_DIV_CLK1>; qcom,node_has_rpm_clock; #clock-cells = <1>; + qcom,codec-mclk-clk-freq = <11289600>; + qcom,mclk-clk-reg = <0x15020018 0x0>; pinctrl-names = "sleep", "active"; pinctrl-0 = <&lpi_mclk0_sleep>; pinctrl-1 = <&lpi_mclk0_active>; diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index 38f4802b1624..07bd9ea842f0 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -26,6 +26,11 @@ qcom,fab-id-valid; }; + pm660_misc: qcom,misc@900 { + compatible = "qcom,qpnp-misc"; + reg = <0x900 0x100>; + }; + qcom,power-on@800 { compatible = "qcom,qpnp-power-on"; reg = <0x800 0x100>; @@ -605,10 +610,12 @@ pm660_haptics: qcom,haptic@c000 { compatible = "qcom,qpnp-haptic"; reg = <0xc000 0x100>; - interrupts = <0x1 0xc0 0x0 IRQ_TYPE_NONE>, - <0x1 0xc0 0x1 IRQ_TYPE_NONE>; + interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pm660_revid>; + qcom,pmic-misc = <&pm660_misc>; + qcom,misc-clk-trim-error-reg = <0xf3>; qcom,actuator-type = "lra"; qcom,play-mode = "direct"; qcom,vmax-mv = <3200>; @@ -619,13 +626,10 @@ qcom,sc-deb-cycles = <8>; qcom,en-brake; qcom,brake-pattern = [03 03 00 00]; - qcom,use-play-irq; - qcom,use-sc-irq; qcom,lra-high-z = "opt0"; qcom,lra-auto-res-mode = "qwd"; qcom,lra-calibrate-at-eop = <0>; qcom,correct-lra-drive-freq; - qcom,misc-trim-error-rc19p2-clk-reg-present; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm-pm8998.dtsi b/arch/arm/boot/dts/qcom/msm-pm8998.dtsi index e91fc68d2c52..e13cdf4c28e7 100644 --- a/arch/arm/boot/dts/qcom/msm-pm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm8998.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,6 +12,7 @@ #include <dt-bindings/spmi/spmi.h> #include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/msm/power-on.h> &spmi_bus { qcom,pm8998@0 { @@ -42,10 +43,6 @@ qcom,pon-type = <0>; qcom,pull-up = <1>; linux,code = <116>; - qcom,support-reset = <1>; - qcom,s1-timer = <10256>; - qcom,s2-timer = <2000>; - qcom,s2-type = <1>; }; qcom,pon_2 { @@ -60,7 +57,7 @@ qcom,pull-up = <1>; qcom,s1-timer = <6720>; qcom,s2-timer = <2000>; - qcom,s2-type = <7>; + qcom,s2-type = <PON_POWER_OFF_DVDD_HARD_RESET>; qcom,use-bark; }; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi index c820d213165b..73ea5fae041e 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -544,8 +544,8 @@ status = "disabled"; compatible = "qcom,qpnp-haptic"; reg = <0xc000 0x100>; - interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>, - <0x3 0xc0 0x1 IRQ_TYPE_NONE>; + interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; interrupt-names = "sc-irq", "play-irq"; vcc_pon-supply = <&pon_perph_reg>; qcom,play-mode = "direct"; @@ -558,8 +558,6 @@ qcom,int-pwm-freq-khz = <505>; qcom,en-brake; qcom,brake-pattern = [03 03 00 00]; - qcom,use-play-irq; - qcom,use-sc-irq; qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e]; qcom,wave-rep-cnt = <1>; qcom,wave-samp-rep-cnt = <1>; diff --git a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi index 111d0b51b6c2..0cf67dd938e6 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi @@ -31,6 +31,11 @@ reg = <0x800 0x100>; }; + pmi8998_misc: qcom,misc@900 { + compatible = "qcom,qpnp-misc"; + reg = <0x900 0x100>; + }; + qcom,temp-alarm@2400 { compatible = "qcom,qpnp-temp-alarm"; reg = <0x2400 0x100>; @@ -628,10 +633,12 @@ status = "disabled"; compatible = "qcom,qpnp-haptic"; reg = <0xc000 0x100>; - interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>, - <0x3 0xc0 0x1 IRQ_TYPE_NONE>; + interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pmi8998_revid>; + qcom,pmic-misc = <&pmi8998_misc>; + qcom,misc-clk-trim-error-reg = <0xf3>; qcom,actuator-type = "lra"; qcom,play-mode = "direct"; qcom,vmax-mv = <3200>; @@ -642,13 +649,10 @@ qcom,sc-deb-cycles = <8>; qcom,en-brake; qcom,brake-pattern = [03 03 00 00]; - qcom,use-play-irq; - qcom,use-sc-irq; qcom,lra-high-z = "opt1"; qcom,lra-auto-res-mode = "qwd"; qcom,lra-res-cal-period = <4>; qcom,correct-lra-drive-freq; - qcom,misc-trim-error-rc19p2-clk-reg-present; }; flash_led: qcom,leds@d300 { diff --git a/arch/arm/boot/dts/qcom/msm-smb138x.dtsi b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi index ea4f05069aab..df7d30210c19 100644 --- a/arch/arm/boot/dts/qcom/msm-smb138x.dtsi +++ b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi @@ -128,3 +128,10 @@ }; }; }; + +&smb138x_parallel_slave { + smb138x_vbus: qcom,smb138x-vbus { + status = "disabled"; + regulator-name = "smb138x-vbus"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi index 48cf099b84a8..8d7309e96c0f 100644 --- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi @@ -597,13 +597,15 @@ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&compr>; + <&afe>, <&lsm>, <&routing>, <&compr>, + <&loopback1>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", - "msm-pcm-routing", "msm-compr-dsp"; + "msm-pcm-routing", "msm-compr-dsp", + "msm-pcm-loopback.1"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&dai_mi2s_quat>, <&afe_pcm_rx>, <&afe_pcm_tx>, @@ -642,6 +644,11 @@ qcom,vbus-det-irq = <&pm8994_gpios 17 0>; }; + loopback1: qcom,msm-pcm-loopback-low-latency { + compatible = "qcom,msm-pcm-loopback"; + qcom,msm-pcm-loopback-low-latency; + }; + qcom,msm-dai-mi2s { dai_mi2s_sec: qcom,msm-dai-q6-mi2s-sec { pinctrl-names = "default", "sleep"; @@ -915,6 +922,10 @@ }; }; +&slim_msm { + status = "disabled"; +}; + /delete-node/ &led_flash0; &mdss_dsi0 { @@ -971,3 +982,12 @@ &blsp1_uart2 { status = "ok"; }; + +&spi_0 { + spi_codec@0 { + compatible = "qcom,spi-msm-codec-slave"; + reg = <0>; + spi-max-frequency = <2000000>; + spi-cpha; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi index 34e41c2bf28f..84b4efd71253 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi @@ -44,6 +44,15 @@ status = "ok"; }; +&spi_0 { + spi_codec@0 { + compatible = "qcom,spi-msm-codec-slave"; + reg = <0>; + spi-max-frequency = <2000000>; + spi-cpha; + }; +}; + &uartblsp2dm1 { status = "ok"; pinctrl-names = "default"; @@ -609,13 +618,15 @@ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&compr>; + <&afe>, <&lsm>, <&routing>, <&compr>, + <&loopback1>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", - "msm-pcm-routing", "msm-compr-dsp"; + "msm-pcm-routing", "msm-compr-dsp", + "msm-pcm-loopback.1"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&dai_mi2s_quat>, <&afe_pcm_rx>, <&afe_pcm_tx>, @@ -656,6 +667,11 @@ interrupt-names = "vbus_det_irq"; }; + loopback1: qcom,msm-pcm-loopback-low-latency { + compatible = "qcom,msm-pcm-loopback"; + qcom,msm-pcm-loopback-low-latency; + }; + usb_vbus_vreg: usb_vbus_vreg { compatible = "regulator-fixed"; regulator-name = "usb_vbus_vreg"; @@ -910,6 +926,10 @@ }; }; +&slim_msm { + status = "disabled"; +}; + /delete-node/ &led_flash0; &mdss_dsi0 { diff --git a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi index ec713e1b11fd..3ffd74e15f32 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-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,18 +28,24 @@ reg-names = "csiphy", "csiphy_clk_mux"; interrupts = <0 78 0>; interrupt-names = "csiphy"; - clocks = <&clock_mmss clk_camss_top_ahb_clk>, + qcom,csi-vdd-voltage = <1250000>; + qcom,mipi-csi-vdd-supply = <&pm8994_l2>; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + qcom,cam-vreg-name = "mmagic", "gdscr"; + 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_csiphy0_3p_clk_src>, <&clock_mmss clk_camss_csiphy0_3p_clk>; - clock-names = "camss_top_ahb_clk", + clock-names = "mmagic_ahb_clk", "camss_top_ahb_clk", "ispif_ahb_clk", "csiphy_timer_src_clk", "csiphy_timer_clk", "camss_ahb_clk", "csiphy_3p_clk_src", "csi_phy_3p_clk"; - qcom,clock-rates = <0 0 200000000 0 0 100000000 0>; + qcom,clock-rates = <0 0 0 200000000 0 0 100000000 0>; }; qcom,csiphy@a35000 { @@ -49,18 +55,24 @@ reg-names = "csiphy", "csiphy_clk_mux"; interrupts = <0 79 0>; interrupt-names = "csiphy"; - clocks = <&clock_mmss clk_camss_top_ahb_clk>, + qcom,csi-vdd-voltage = <1250000>; + qcom,mipi-csi-vdd-supply = <&pm8994_l2>; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + qcom,cam-vreg-name = "mmagic", "gdscr"; + 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_csi1phytimer_clk_src>, <&clock_mmss clk_camss_csi1phytimer_clk>, <&clock_mmss clk_camss_ahb_clk>, <&clock_mmss clk_csiphy1_3p_clk_src>, <&clock_mmss clk_camss_csiphy1_3p_clk>; - clock-names = "camss_top_ahb_clk", + clock-names = "mmagic_ahb_clk", "camss_top_ahb_clk", "ispif_ahb_clk", "csiphy_timer_src_clk", "csiphy_timer_clk", "camss_ahb_clk", "csiphy_3p_clk_src", "csi_phy_3p_clk"; - qcom,clock-rates = <0 0 200000000 0 0 100000000 0>; + qcom,clock-rates = <0 0 0 200000000 0 0 100000000 0>; }; qcom,csiphy@a36000 { @@ -70,18 +82,24 @@ reg-names = "csiphy", "csiphy_clk_mux"; interrupts = <0 80 0>; interrupt-names = "csiphy"; - clocks = <&clock_mmss clk_camss_top_ahb_clk>, + qcom,csi-vdd-voltage = <1250000>; + qcom,mipi-csi-vdd-supply = <&pm8994_l2>; + mmagic-supply = <&gdsc_mmagic_camss>; + gdscr-supply = <&gdsc_camss_top>; + qcom,cam-vreg-name = "mmagic", "gdscr"; + 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_csi2phytimer_clk_src>, <&clock_mmss clk_camss_csi2phytimer_clk>, <&clock_mmss clk_camss_ahb_clk>, <&clock_mmss clk_csiphy2_3p_clk_src>, <&clock_mmss clk_camss_csiphy2_3p_clk>; - clock-names = "camss_top_ahb_clk", + clock-names = "mmagic_ahb_clk", "camss_top_ahb_clk", "ispif_ahb_clk", "csiphy_timer_src_clk", "csiphy_timer_clk", "camss_ahb_clk", "csiphy_3p_clk_src", "csi_phy_3p_clk"; - qcom,clock-rates = <0 0 200000000 0 0 100000000 0>; + qcom,clock-rates = <0 0 0 200000000 0 0 100000000 0>; }; qcom,csid@a30000 { diff --git a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi index bd8aa7fe02f7..7370422d737e 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi @@ -523,13 +523,15 @@ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&compr>; + <&afe>, <&lsm>, <&routing>, <&compr>, + <&loopback1>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", - "msm-pcm-routing", "msm-compr-dsp"; + "msm-pcm-routing", "msm-compr-dsp", + "msm-pcm-loopback.1"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&dai_mi2s_quat>, <&afe_pcm_rx>, <&afe_pcm_tx>, @@ -563,6 +565,11 @@ asoc-codec-names = "msm-stub-codec.1"; }; + loopback1: qcom,msm-pcm-loopback-low-latency { + compatible = "qcom,msm-pcm-loopback"; + qcom,msm-pcm-loopback-low-latency; + }; + qcom,msm-dai-mi2s { dai_mi2s_quat: qcom,msm-dai-q6-mi2s-quat { pinctrl-names = "default", "sleep"; @@ -805,6 +812,10 @@ }; }; +&slim_msm { + status = "disabled"; +}; + /delete-node/ &led_flash0; &mdss_dsi0 { @@ -855,3 +866,12 @@ status = "disabled"; /delete-property/ qcom,spkr-sd-n-gpio; }; + +&spi_0 { + spi_codec@0 { + compatible = "qcom,spi-msm-codec-slave"; + reg = <0>; + spi-max-frequency = <2000000>; + spi-cpha; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi index 4a1a524a7b0b..c70003a0a6dd 100644 --- a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi @@ -624,15 +624,18 @@ qcom,cpr-pd-bypass-mask = <0x07>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <16 13>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <16 13 16>; qcom,cpr-corners = /* Speed bin 0 */ <16 16 16 16 16 16 16 16>, /* Speed bin 1 */ - <13 13 13 13 13 13 13 13>; + <13 13 13 13 13 13 13 13>, + + /* Speed bin 2 */ + <16 16 16 16 16 16 16 16>; qcom,ldo-min-headroom-voltage = <150000>; qcom,ldo-max-headroom-voltage = <470000>; @@ -644,7 +647,10 @@ <1 2 7 12 16>, /* Speed bin 1 */ - <1 2 7 12 13>; + <1 2 7 12 13>, + + /* Speed bin 2 */ + <1 2 7 12 16>; qcom,cpr-voltage-ceiling = /* Speed bin 0 */ @@ -656,7 +662,13 @@ /* Speed bin 1 */ <670000 670000 745000 745000 745000 745000 745000 905000 905000 905000 - 905000 905000 1140000>; + 905000 905000 1140000>, + + /* Speed bin 2 */ + <670000 670000 745000 745000 745000 + 745000 745000 905000 905000 905000 + 905000 905000 1140000 1140000 1140000 + 1140000>; qcom,cpr-voltage-floor = /* Speed bin 0 */ @@ -717,7 +729,41 @@ 470000 470000 470000>, <470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 - 470000 470000 470000>; + 470000 470000 470000>, + + /* Speed bin 2 */ + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000>; qcom,cpr-floor-to-ceiling-max-range = /* Speed bin 0 */ @@ -729,7 +775,13 @@ /* Speed bin 1 */ <50000 50000 80000 80000 80000 80000 80000 80000 80000 80000 - 80000 80000 80000>; + 80000 80000 80000>, + + /* Speed bin 2 */ + <50000 50000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000>; qcom,corner-frequencies = /* Speed bin 0 */ @@ -745,7 +797,15 @@ 556800000 652800000 729600000 844800000 960000000 1036800000 1113600000 1190400000 1228800000 - 1363200000>; + 1363200000>, + + /* Speed bin 2 */ + <307200000 422400000 480000000 + 556800000 652800000 729600000 + 844800000 960000000 1036800000 + 1113600000 1190400000 1228800000 + 1324800000 1401600000 1478400000 + 1593600000>; qcom,cpr-ro-scaling-factor = < 0 0 3112 2666 2947 2543 2271 1979 @@ -778,6 +838,16 @@ <35000 0 40000 10000 5000>, <35000 0 40000 10000 5000>, <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, + + /* Speed bin 2 */ + <20000 0 25000 (-5000) (-10000)>, + <20000 0 25000 (-5000) (-10000)>, + <20000 0 25000 (-5000) (-10000)>, + <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, <35000 0 40000 10000 5000>; qcom,cpr-closed-loop-voltage-fuse-adjustment = @@ -799,6 +869,16 @@ <20000 10000 5000 (-5000) (-5000)>, <20000 10000 5000 (-5000) (-5000)>, <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + + /* Speed bin 2 */ + <35000 35000 40000 40000 40000>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, <20000 10000 5000 (-5000) (-5000)>; qcom,cpr-open-loop-voltage-adjustment = @@ -812,14 +892,23 @@ <(-15000) (-15000) (-15000) (-15000) (-13000) (-14000) (-15000) (-18000) (-20000) (-22000) (-24000) (-25000) - (-26000)>; + (-26000)>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-13000) (-14000) (-15000) (-18000) + (-20000) (-22000) (-24000) (-25000) + (-26000) (-27000) (-28000) (-30000)>; qcom,cpr-open-loop-voltage-min-diff = /* Speed bin 0 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0>, /* Speed bin 1 */ - <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0>; + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0>, + + /* Speed bin 2 */ + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0>; qcom,cpr-closed-loop-voltage-adjustment = /* Speed bin 0 */ @@ -832,20 +921,29 @@ <(-15000) (-15000) (-15000) (-15000) (-13000) (-14000) (-15000) (-18000) (-20000) (-22000) (-24000) (-25000) - (-26000)>; + (-26000)>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-13000) (-14000) (-15000) (-18000) + (-20000) (-22000) (-24000) (-25000) + (-26000) (-27000) (-28000) (-30000)>; qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; qcom,cpr-scaled-open-loop-voltage-as-ceiling; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <12 12>; + qcom,cpr-aging-ref-corner = <12 12 12>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = /* Speed bin 0 */ <0 0 0 1 1 1 1 1>, /* Speed bin 1 */ + <0 0 0 1 1 1 1 1>, + + /* Speed bin 2 */ <0 0 0 1 1 1 1 1>; }; @@ -859,22 +957,28 @@ qcom,cpr-pd-bypass-mask = <0x18>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <19 15>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <19 15 19>; qcom,cpr-corners = /* Speed bin 0 */ <19 19 19 19 19 19 19 19>, /* Speed bin 1 */ - <15 15 15 15 15 15 15 15>; + <15 15 15 15 15 15 15 15>, + + /* Speed bin 2 */ + <19 19 19 19 19 19 19 19>; qcom,cpr-corner-fmax-map = /* Speed bin 0 */ <1 2 5 13 19>, /* Speed bin 1 */ - <1 2 5 13 15>; + <1 2 5 13 15>, + + /* Speed bin 2 */ + <1 2 5 13 19>; qcom,cpr-voltage-ceiling = /* Speed bin 0 */ @@ -886,7 +990,13 @@ /* Speed bin 1 */ <670000 670000 745000 745000 745000 905000 905000 905000 905000 905000 - 905000 905000 905000 1140000 1140000>; + 905000 905000 905000 1140000 1140000>, + + /* Speed bin 2 */ + <670000 670000 745000 745000 745000 + 905000 905000 905000 905000 905000 + 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000 1140000>; qcom,cpr-voltage-floor = /* Speed bin 0 */ @@ -947,7 +1057,41 @@ 470000 470000 470000 470000 470000>, <470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 - 470000 470000 470000 470000 470000>; + 470000 470000 470000 470000 470000>, + + /* Speed bin 2 */ + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000>; qcom,cpr-floor-to-ceiling-max-range = /* Speed bin 0 */ @@ -959,7 +1103,13 @@ /* Speed bin 1 */ <50000 50000 80000 80000 80000 80000 80000 80000 80000 80000 - 80000 80000 80000 80000 80000>; + 80000 80000 80000 80000 80000>, + + /* Speed bin 2 */ + <50000 50000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000>; qcom,corner-frequencies = /* Speed bin 0 */ @@ -976,7 +1126,16 @@ 537600000 595200000 672000000 748800000 825600000 902400000 979200000 1056000000 1132800000 - 1190400000 1228800000 1305600000>; + 1190400000 1228800000 1305600000>, + + /* Speed bin 2 */ + <307200000 384000000 460800000 + 537600000 595200000 672000000 + 748800000 825600000 902400000 + 979200000 1056000000 1132800000 + 1190400000 1228800000 1305600000 + 1382400000 1459200000 1536000000 + 1593600000>; qcom,cpr-ro-scaling-factor = < 0 0 3112 2666 2947 2543 2271 1979 @@ -1009,6 +1168,16 @@ <45000 0 5000 5000 (-25000)>, <45000 0 5000 5000 (-25000)>, <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, + + /* Speed bin 2 */ + <30000 0 (-10000) (-10000) (-40000)>, + <30000 0 (-10000) (-10000) (-40000)>, + <30000 0 (-10000) (-10000) (-40000)>, + <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, <45000 0 5000 5000 (-25000)>; qcom,cpr-closed-loop-voltage-fuse-adjustment = @@ -1030,6 +1199,16 @@ <10000 5000 (-20000) 0 (-35000)>, <10000 5000 (-20000) 0 (-35000)>, <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + + /* Speed bin 2 */ + <10000 5000 0 0 0>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, <10000 5000 (-20000) 0 (-35000)>; qcom,allow-voltage-interpolation; @@ -1037,13 +1216,16 @@ qcom,cpr-scaled-open-loop-voltage-as-ceiling; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <13 13>; + qcom,cpr-aging-ref-corner = <13 13 13>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = /* Speed bin 0 */ <0 0 0 1 1 1 1 1>, /* Speed bin 1 */ + <0 0 0 1 1 1 1 1>, + + /* Speed bin 2 */ <0 0 0 1 1 1 1 1>; }; }; @@ -1062,15 +1244,18 @@ qcom,cpr-pd-bypass-mask = <0xe0>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <25 21>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <25 21 25>; qcom,cpr-corners = /* Speed bin 0 */ <25 25 25 25 25 25 25 25>, /* Speed bin 1 */ - <21 21 21 21 21 21 21 21>; + <21 21 21 21 21 21 21 21>, + + /* Speed bin 0 */ + <25 25 25 25 25 25 25 25>; qcom,ldo-min-headroom-voltage = <150000>; qcom,ldo-max-headroom-voltage = <470000>; @@ -1082,7 +1267,10 @@ <1 4 9 13 25>, /* Speed bin 1 */ - <1 4 9 13 21>; + <1 4 9 13 21>, + + /* Speed bin 2 */ + <1 4 9 13 25>; qcom,cpr-voltage-ceiling = /* Speed bin 0 */ @@ -1097,7 +1285,15 @@ 745000 745000 745000 745000 905000 905000 905000 905000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 - 1140000>; + 1140000>, + + /* Speed bin 2 */ + <670000 670000 670000 670000 745000 + 745000 745000 745000 745000 905000 + 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000 1140000 1140000 + 1140000 1140000 1140000 1140000 1140000>; + qcom,cpr-voltage-floor = /* Speed bin 0 */ @@ -1182,7 +1378,49 @@ 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 - 470000>; + 470000>, + + /* Speed bin 2 */ + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000>; qcom,cpr-floor-to-ceiling-max-range = /* Speed bin 0 */ @@ -1197,7 +1435,14 @@ 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 - 80000>; + 80000>, + + /* Speed bin 2 */ + <50000 50000 50000 50000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000>; qcom,corner-frequencies = /* Speed bin 0 */ @@ -1218,7 +1463,18 @@ 1036800000 1113600000 1190400000 1248000000 1324800000 1401600000 1478400000 1555200000 1632000000 - 1708800000 1785600000 1804800000>; + 1708800000 1785600000 1804800000>, + + /* Speed bin 2 */ + <307200000 403200000 480000000 + 556800000 652800000 729600000 + 806400000 883200000 940800000 + 1036800000 1113600000 1190400000 + 1248000000 1324800000 1401600000 + 1478400000 1555200000 1632000000 + 1708800000 1785600000 1824000000 + 1920000000 1996800000 2073600000 + 2150400000>; qcom,cpr-ro-scaling-factor = < 0 0 3112 2666 2947 2543 2271 1979 @@ -1251,6 +1507,16 @@ <35000 0 30000 15000 15000>, <35000 0 30000 15000 15000>, <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, + + /* Speed bin 2 */ + <20000 0 15000 (-55000) 0>, + <20000 0 15000 (-55000) 0>, + <20000 0 15000 0 0>, + <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, <35000 0 30000 15000 15000>; qcom,cpr-closed-loop-voltage-fuse-adjustment = @@ -1272,6 +1538,16 @@ < 0 0 0 0 0>, < 0 0 0 0 0>, < 0 0 0 0 0>, + < 0 0 0 0 0>, + + /* Speed bin 2 */ + <35000 35000 40000 (-30000) 40000>, + < 0 0 0 (-70000) 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,cpr-open-loop-voltage-adjustment = @@ -1290,7 +1566,17 @@ (-15000) (-18000) (-21000) (-23000) (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000) (-28000) - (-28000)>; + (-28000)>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-11000) (-12000) (-13000) (-14000) + (-15000) (-18000) (-21000) (-23000) + (-25000) (-25000) (-26000) (-26000) + (-27000) (-27000) (-28000) (-28000) + (-28000) (-29000) (-29000) (-30000) + (-30000)>; + qcom,cpr-open-loop-voltage-min-diff = /* Speed bin 0 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 @@ -1298,7 +1584,11 @@ /* Speed bin 1 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0>; + 0 0 0 0 0>, + + /* Speed bin 2 */ + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0>; qcom,cpr-closed-loop-voltage-adjustment = /* Speed bin 0 */ @@ -1316,20 +1606,32 @@ (-15000) (-18000) (-21000) (-23000) (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000) (-28000) - (-28000)>; + (-28000)>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-11000) (-12000) (-13000) (-14000) + (-15000) (-18000) (-21000) (-23000) + (-25000) (-25000) (-26000) (-26000) + (-27000) (-27000) (-28000) (-28000) + (-28000) (-29000) (-29000) (-30000) + (-30000)>; qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; qcom,cpr-scaled-open-loop-voltage-as-ceiling; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <13 13>; + qcom,cpr-aging-ref-corner = <13 13 13>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = /* Speed bin 0 */ <0 0 0 1 1 1 1 1>, /* Speed bin 1 */ + <0 0 0 1 1 1 1 1>, + + /* Speed bin 2 */ <0 0 0 1 1 1 1 1>; qcom,cpr-dynamic-floor-corner = <1>; diff --git a/arch/arm/boot/dts/qcom/msm8996-v3.dtsi b/arch/arm/boot/dts/qcom/msm8996-v3.dtsi index c30e19eacc69..7e5fa8a495c9 100644 --- a/arch/arm/boot/dts/qcom/msm8996-v3.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-v3.dtsi @@ -73,6 +73,23 @@ < 315000000 4 >, < 401800000 5 >, < 510000000 5 >; + + qcom,gfxfreq-speedbin2 = + < 0 0 0 >, + < 133000000 2 4 >, + < 214000000 3 4 >, + < 315000000 4 4 >, + < 401800000 5 5 >, + < 510000000 6 5 >, + < 560000000 7 7 >; + qcom,gfxfreq-mx-speedbin2 = + < 0 0 >, + < 133000000 4 >, + < 214000000 4 >, + < 315000000 4 >, + < 401800000 5 >, + < 510000000 5 >, + < 560000000 7 >; }; &gdsc_gpu_gx { @@ -416,6 +433,23 @@ < 1190400000 11 >, < 1228800000 12 >, < 1363200000 13 >; + qcom,pwrcl-speedbin2-v0 = + < 0 0 >, + < 307200000 1 >, + < 422400000 2 >, + < 480000000 3 >, + < 556800000 4 >, + < 652800000 5 >, + < 729600000 6 >, + < 844800000 7 >, + < 960000000 8 >, + < 1036800000 9 >, + < 1113600000 10 >, + < 1190400000 11 >, + < 1228800000 12 >, + < 1324800000 13 >, + < 1401600000 14 >, + < 1497600000 15 >; qcom,perfcl-speedbin0-v0 = < 0 0 >, < 307200000 1 >, @@ -466,6 +500,30 @@ < 1708800000 19 >, < 1785600000 20 >, < 1804800000 21 >; + qcom,perfcl-speedbin2-v0 = + < 0 0 >, + < 307200000 1 >, + < 403200000 2 >, + < 480000000 3 >, + < 556800000 4 >, + < 652800000 5 >, + < 729600000 6 >, + < 806400000 7 >, + < 883200000 8 >, + < 940800000 9 >, + < 1036800000 10 >, + < 1113600000 11 >, + < 1190400000 12 >, + < 1248000000 13 >, + < 1324800000 14 >, + < 1401600000 15 >, + < 1478400000 16 >, + < 1555200000 17 >, + < 1632000000 18 >, + < 1708800000 19 >, + < 1785600000 20 >, + < 1804800000 21 >, + < 1900800000 22 >; qcom,cbf-speedbin0-v0 = < 0 0 >, < 307200000 1 >, @@ -504,6 +562,23 @@ < 1190400000 13 >, < 1228800000 14 >, < 1305600000 15 >; + qcom,cbf-speedbin2-v0 = + < 0 0 >, + < 307200000 1 >, + < 384000000 2 >, + < 460800000 3 >, + < 537600000 4 >, + < 595200000 5 >, + < 672000000 6 >, + < 748800000 7 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1190400000 13 >, + < 1228800000 14 >, + < 1305600000 15 >; }; &msm_cpufreq { diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi index 59ffa3ce88cb..abff47ff8d58 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi @@ -33,6 +33,7 @@ &apcc_cpr { compatible = "qcom,cpr3-msm8996pro-hmss-regulator"; + qcom,cpr-interrupt-affinity = <&CPU0 &CPU1>; }; &apc0_pwrcl_vreg { @@ -40,22 +41,49 @@ regulator-max-microvolt = <20>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <20 19>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <20 20 19>; qcom,cpr-corners = /* Speed bin 0 */ <20 20 20 20 20 20 20 20>, /* Speed bin 1 */ + <20 20 20 20 20 20 20 20>, + + /* Speed bin 2 */ <19 19 19 19 19 19 19 19>; qcom,cpr-corner-fmax-map = /* Speed bin 0 */ <1 6 9 14 19>, + <1 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, /* Speed bin 1 */ - <1 6 9 14 19>; + <1 6 9 14 19>, + <1 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + + /* Speed bin 2 */ + <1 6 9 14 19>, + <1 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>, + <3 6 9 14 19>; qcom,cpr-voltage-ceiling = /* Speed bin 0 */ @@ -66,6 +94,11 @@ /* Speed bin 1 */ <670000 670000 670000 670000 670000 670000 745000 745000 745000 905000 905000 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000 1140000>, + + /* Speed bin 2 */ + <670000 670000 670000 670000 670000 670000 745000 745000 + 745000 905000 905000 905000 905000 905000 1140000 1140000 1140000 1140000 1140000>; qcom,cpr-voltage-floor = @@ -77,6 +110,11 @@ /* Speed bin 1 */ <470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000>, + + /* Speed bin 2 */ + <470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000>; qcom,cpr-floor-to-ceiling-max-range = @@ -88,6 +126,11 @@ /* Speed bin 1 */ <80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 + 80000 80000 80000 80000>, + + /* Speed bin 2 */ + <80000 80000 80000 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000>; qcom,corner-frequencies = @@ -101,6 +144,12 @@ <307200000 384000000 460800000 537600000 614400000 691200000 768000000 844800000 902400000 979200000 1056000000 1132800000 1209600000 1286400000 1363200000 + 1440000000 1516800000 1593600000 1785600000 1996800000>, + + /* Speed bin 2 */ + <307200000 384000000 460800000 537600000 614400000 + 691200000 768000000 844800000 902400000 979200000 + 1056000000 1132800000 1209600000 1286400000 1363200000 1440000000 1516800000 1593600000 1785600000>; qcom,cpr-ro-scaling-factor = @@ -129,21 +178,10 @@ < 0 0 0 0 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, - < 0 0 0 0 0 >; - - qcom,cpr-closed-loop-voltage-fuse-adjustment = - /* Speed bin 0 */ - <(-55000) (-25000) (-5000) (-20000) (-5000)>, - < 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 >, - /* Speed bin 1 */ - <(-55000) (-25000) (-5000) (-20000) (-5000)>, + /* Speed bin 2 */ + <(-45000) 0 0 (-25000) 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, @@ -152,15 +190,51 @@ < 0 0 0 0 0 >, < 0 0 0 0 0 >; + qcom,cpr-closed-loop-voltage-fuse-adjustment = + /* Speed bin 0 */ + <(-55000) (-25000) (-5000) (-20000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + + /* Speed bin 1 */ + <(-55000) (-25000) (-5000) (-20000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + + /* Speed bin 2 */ + <(-55000) (-25000) (-5000) (-20000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>, + < 0 (-15000) (-15000) (-15000) (-15000)>; + qcom,cpr-open-loop-voltage-adjustment = /* Speed bin 0 */ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) - (-26000) (-27000) (-27000) (-28000) (-30000) 130000>, + (-26000) (-27000) (-27000) (-28000) (-30000) 120000>, /* Speed bin 1 */ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) + (-26000) (-27000) (-27000) (-28000) (-30000) 70000>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) + (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) (-26000) (-27000) (-27000) (-28000) (-30000)>; qcom,cpr-open-loop-voltage-min-diff = @@ -168,24 +242,34 @@ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>, /* Speed bin 1 */ + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>, + + /* Speed bin 2 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; qcom,cpr-closed-loop-voltage-adjustment = /* Speed bin 0 */ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) - (-26000) (-27000) (-27000) (-28000) (-30000) 130000>, + (-26000) (-27000) (-27000) (-28000) (-30000) 110000>, /* Speed bin 1 */ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) + (-26000) (-27000) (-27000) (-28000) (-30000) 60000>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) + (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) (-26000) (-27000) (-27000) (-28000) (-30000)>; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <14 14>; + qcom,cpr-aging-ref-corner = <14 14 14>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = <1>; qcom,allow-aging-open-loop-voltage-adjustment = <1>; + + qcom,ldo-min-headroom-voltage = <80000>; }; &apc0_cbf_vreg { @@ -196,14 +280,17 @@ qcom,proxy-consumer-voltage = <14 19>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <19 19>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <19 19 19>; qcom,cpr-corners = /* Speed bin 0 */ <19 19 19 19 19 19 19 19>, /* Speed bin 1 */ + <19 19 19 19 19 19 19 19>, + + /* Speed bin 2 */ <19 19 19 19 19 19 19 19>; qcom,cpr-corner-fmax-map = @@ -211,6 +298,9 @@ <1 4 7 14 19>, /* Speed bin 1 */ + <1 4 7 14 19>, + + /* Speed bin 2 */ <1 4 7 14 19>; qcom,cpr-voltage-ceiling = @@ -222,6 +312,11 @@ /* Speed bin 1 */ <670000 670000 670000 670000 745000 745000 745000 905000 905000 905000 905000 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000>, + + /* Speed bin 2 */ + <670000 670000 670000 670000 745000 745000 745000 905000 + 905000 905000 905000 905000 905000 905000 1140000 1140000 1140000 1140000 1140000>; qcom,cpr-voltage-floor = @@ -233,6 +328,11 @@ /* Speed bin 1 */ <470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000>, + + /* Speed bin 2 */ + <470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000>; qcom,cpr-floor-to-ceiling-max-range = @@ -244,6 +344,11 @@ /* Speed bin 1 */ <80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 + 80000 80000 80000>, + + /* Speed bin 2 */ + <80000 80000 80000 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000>; qcom,corner-frequencies = @@ -257,6 +362,12 @@ <192000000 307200000 384000000 441600000 537600000 614400000 691200000 768000000 844800000 902400000 979200000 1056000000 1132800000 1190400000 1286400000 + 1363200000 1440000000 1516800000 1593600000>, + + /* Speed bin 2 */ + <192000000 307200000 384000000 441600000 537600000 + 614400000 691200000 768000000 844800000 902400000 + 979200000 1056000000 1132800000 1190400000 1286400000 1363200000 1440000000 1516800000 1593600000>; qcom,cpr-ro-scaling-factor = @@ -269,47 +380,67 @@ qcom,cpr-open-loop-voltage-fuse-adjustment = /* Speed bin 0 */ <(-40000) 0 0 (-10000) (-50000)>, - < 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 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, /* Speed bin 1 */ <(-40000) 0 0 (-10000) (-50000)>, - < 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 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + + /* Speed bin 2 */ + <(-40000) 0 0 (-10000) (-50000)>, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >, + < 0 0 0 15000 0 >; qcom,cpr-closed-loop-voltage-fuse-adjustment = /* Speed bin 0 */ <(-45000) (-25000) 10000 (-10000) (-40000)>, - < 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 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, /* Speed bin 1 */ <(-45000) (-25000) 10000 (-10000) (-40000)>, - < 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 >; + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + + /* Speed bin 2 */ + <(-45000) (-25000) 10000 (-10000) (-40000)>, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >, + <(-15000) (-10000) 5000 20000 0 >; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <14 14>; + qcom,cpr-aging-ref-corner = <14 14 14>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = <1>; qcom,allow-aging-open-loop-voltage-adjustment = <1>; @@ -320,22 +451,49 @@ regulator-max-microvolt = <27>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <27 25>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <27 25 25>; qcom,cpr-corners = /* Speed bin 0 */ <27 27 27 27 27 27 27 27>, /* Speed bin 1 */ + <25 25 25 25 25 25 25 25>, + + /* Speed bin 2 */ <25 25 25 25 25 25 25 25>; qcom,cpr-corner-fmax-map = /* Speed bin 0 */ <1 7 10 15 27>, + <1 7 10 15 27>, + <4 7 10 15 27>, + <4 7 10 15 27>, + <4 7 10 15 27>, + <4 7 10 15 27>, + <4 7 10 15 27>, + <4 7 10 15 27>, /* Speed bin 1 */ - <1 7 10 15 25>; + <1 7 10 15 25>, + <1 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + + /* Speed bin 2 */ + <1 7 10 15 25>, + <1 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>, + <4 7 10 15 25>; qcom,cpr-voltage-ceiling = /* Speed bin 0 */ @@ -348,6 +506,12 @@ <670000 670000 670000 670000 670000 670000 670000 745000 745000 745000 905000 905000 905000 905000 905000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 + 1140000>, + + /* Speed bin 2 */ + <670000 670000 670000 670000 670000 670000 670000 745000 + 745000 745000 905000 905000 905000 905000 905000 1140000 + 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000>; qcom,cpr-voltage-floor = @@ -361,6 +525,12 @@ <470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 470000 + 470000>, + + /* Speed bin 2 */ + <470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 470000>; qcom,cpr-floor-to-ceiling-max-range = @@ -374,6 +544,12 @@ <80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 + 80000>, + + /* Speed bin 2 */ + <80000 80000 80000 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 80000 80000 80000 80000>; qcom,corner-frequencies = @@ -390,6 +566,13 @@ 691200000 748800000 825600000 902400000 979200000 1056000000 1132800000 1209600000 1286400000 1363200000 1440000000 1516800000 1593600000 1670400000 1747200000 + 1824000000 1900800000 1977600000 2054400000 2150400000>, + + /* Speed bin 2 */ + <307200000 384000000 460800000 537600000 614400000 + 691200000 748800000 825600000 902400000 979200000 + 1056000000 1132800000 1209600000 1286400000 1363200000 + 1440000000 1516800000 1593600000 1670400000 1747200000 1824000000 1900800000 1977600000 2054400000 2150400000>; qcom,cpr-ro-scaling-factor = @@ -401,7 +584,7 @@ qcom,cpr-open-loop-voltage-fuse-adjustment = /* Speed bin 0 */ - <(-45000) 0 15000 (-20000) 20000 >, + <(-45000) 0 15000 (-20000) 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, @@ -411,18 +594,7 @@ < 0 0 0 0 0 >, /* Speed bin 1 */ - <(-45000) 0 15000 (-20000) 20000 >, - < 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,cpr-closed-loop-voltage-fuse-adjustment = - /* Speed bin 0 */ - <(-55000) (-20000) 15000 (-15000) 5000 >, + <(-45000) 0 15000 (-20000) 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, @@ -431,8 +603,8 @@ < 0 0 0 0 0 >, < 0 0 0 0 0 >, - /* Speed bin 1 */ - <(-55000) (-20000) 15000 (-15000) 5000 >, + /* Speed bin 2 */ + <(-45000) 0 15000 (-20000) 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, < 0 0 0 0 0 >, @@ -441,6 +613,37 @@ < 0 0 0 0 0 >, < 0 0 0 0 0 >; + qcom,cpr-closed-loop-voltage-fuse-adjustment = + /* Speed bin 0 */ + <(-55000) (-20000) 15000 (-15000) (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + + /* Speed bin 1 */ + <(-55000) (-20000) 15000 (-15000) (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + + /* Speed bin 2 */ + <(-55000) (-20000) 15000 (-15000) (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>, + < 0 (-10000) (-15000) 0 (-10000)>; + qcom,cpr-open-loop-voltage-adjustment = /* Speed bin 0 */ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) @@ -452,6 +655,12 @@ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000) + (-28000) (-29000) (-29000) (-30000)>, + + /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) + (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) + (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000) (-28000) (-29000) (-29000) (-30000)>; qcom,cpr-open-loop-voltage-min-diff = @@ -459,6 +668,9 @@ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>, /* Speed bin 1 */ + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>, + + /* Speed bin 2 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; qcom,cpr-closed-loop-voltage-adjustment = @@ -472,15 +684,22 @@ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000) + (-28000) (-29000) (-29000) (-30000)>, + + /* Speed bin 1 */ + <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000) + (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) + (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000) (-28000) (-29000) (-29000) (-30000)>; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <15 15>; + qcom,cpr-aging-ref-corner = <15 15 15>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = <1>; qcom,allow-aging-open-loop-voltage-adjustment = <1>; qcom,cpr-dynamic-floor-corner = <1>; + qcom,ldo-min-headroom-voltage = <130000>; }; &pmi8994_s2 { @@ -500,14 +719,17 @@ regulator-max-microvolt = <9>; qcom,cpr-fuse-corners = <4>; - qcom,cpr-fuse-combos = <16>; - qcom,cpr-speed-bins = <2>; - qcom,cpr-speed-bin-corners = <9 9>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <9 9 9>; qcom,cpr-corners = /* Speed bin 0 */ <9 9 9 9 9 9 9 9>, /* Speed bin 1 */ + <9 9 9 9 9 9 9 9>, + + /* Speed bin 2 */ <9 9 9 9 9 9 9 9>; qcom,cpr-corner-fmax-map = @@ -515,6 +737,9 @@ <2 4 6 9>, /* Speed bin 1 */ + <2 4 6 9>, + + /* Speed bin 2 */ <2 4 6 9>; qcom,cpr-voltage-ceiling = @@ -524,6 +749,10 @@ /* Speed bin 1 */ <400000 670000 670000 745000 825000 905000 960000 1015000 + 1065000>, + + /* Speed bin 2 */ + <400000 670000 670000 745000 825000 905000 960000 1015000 1065000>; qcom,cpr-voltage-floor = @@ -533,6 +762,10 @@ /* Speed bin 1 */ <400000 520000 520000 520000 520000 520000 520000 520000 + 520000>, + + /* Speed bin 2 */ + <400000 520000 520000 520000 520000 520000 520000 520000 520000>; qcom,mem-acc-voltage = @@ -540,6 +773,9 @@ <1 1 1 1 2 2 2 2 2>, /* Speed bin 1 */ + <1 1 1 1 2 2 2 2 2>, + + /* Speed bin 2 */ <1 1 1 1 2 2 2 2 2>; qcom,corner-frequencies = @@ -549,6 +785,10 @@ /* Speed bin 1 */ <0 133000000 214000000 315000000 401800000 510000000 560000000 + 624000000 652800000>, + + /* Speed bin 2 */ + <0 133000000 214000000 315000000 401800000 510000000 560000000 624000000 652800000>; qcom,cpr-target-quotients = @@ -572,6 +812,17 @@ <0 0 0 0 0 0 577 543 798 768 823 810 0 0 0 0>, <0 0 0 0 0 0 669 629 886 864 924 911 0 0 0 0>, <0 0 0 0 0 0 771 725 984 970 1036 1024 0 0 0 0>, + <0 0 0 0 0 0 908 868 1118 1106 1179 1174 0 0 0 0>, + + /* Speed bin 2 */ + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>, + <0 0 0 0 0 0 185 179 291 299 304 319 0 0 0 0>, + <0 0 0 0 0 0 287 273 425 426 443 453 0 0 0 0>, + <0 0 0 0 0 0 414 392 584 576 608 612 0 0 0 0>, + <0 0 0 0 0 0 459 431 684 644 692 679 0 0 0 0>, + <0 0 0 0 0 0 577 543 798 768 823 810 0 0 0 0>, + <0 0 0 0 0 0 669 629 886 864 924 911 0 0 0 0>, + <0 0 0 0 0 0 771 725 984 970 1036 1024 0 0 0 0>, <0 0 0 0 0 0 908 868 1118 1106 1179 1174 0 0 0 0>; qcom,cpr-ro-scaling-factor = @@ -595,27 +846,47 @@ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + + /* Speed bin 2 */ + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>; qcom,cpr-open-loop-voltage-fuse-adjustment = /* Speed bin 0 */ - <(-70000) 0 0 0>, + <(-85000) (-15000) (-15000) (-40000)>, /* Speed bin 1 */ - <(-70000) 0 0 0>; + <(-85000) (-15000) (-15000) (-40000)>, + + /* Speed bin 2 */ + <(-85000) (-15000) (-15000) (-40000)>; qcom,cpr-closed-loop-voltage-adjustment = /* Speed bin 0 */ - <0 0 30000 10000 10000 45000 25000 25000 25000>, + <0 0 30000 10000 10000 45000 25000 25000 (-35000)>, /* Speed bin 1 */ - <0 0 30000 10000 10000 45000 25000 25000 25000>; + <0 0 30000 10000 10000 45000 25000 25000 (-35000)>, + + /* Speed bin 2 */ + <0 0 30000 10000 10000 45000 25000 25000 (-35000)>; qcom,cpr-floor-to-ceiling-max-range = /* Speed bin 0 */ <0 70000 70000 75000 80000 90000 95000 100000 100000>, /* Speed bin 1 */ + <0 70000 70000 75000 80000 90000 95000 100000 100000>, + + /* Speed bin 2 */ <0 70000 70000 75000 80000 90000 95000 100000 100000>; qcom,cpr-fused-closed-loop-voltage-adjustment-map = @@ -623,6 +894,9 @@ <0 2 2 2 2 0 0 4 4>, /* Speed bin 1 */ + <0 2 2 2 2 0 0 4 4>, + + /* Speed bin 2 */ <0 2 2 2 2 0 0 4 4>; qcom,cpr-aging-max-voltage-adjustment = <15000>; @@ -632,6 +906,14 @@ qcom,allow-aging-open-loop-voltage-adjustment = <1>; }; +&kryo0_vreg { + qcom,ldo-headroom-voltage = <80000>; +}; + +&kryo1_vreg { + qcom,ldo-headroom-voltage = <130000>; +}; + &clock_cpu { compatible = "qcom,cpu-clock-8996-pro"; qcom,pwrcl-speedbin0-v0 = @@ -674,7 +956,8 @@ < 1363200000 15 >, < 1440000000 16 >, < 1516800000 17 >, - < 1593600000 18 >; + < 1593600000 18 >, + < 1996800000 20 >; qcom,perfcl-speedbin0-v0 = < 0 0 >, < 307200000 1 >, diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi index e7a61f42dff1..d5c8900f6e67 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi @@ -29,6 +29,16 @@ qcom,switch-source = <&pmi8998_switch1>; status = "ok"; }; + + actuator_regulator: gpio-regulator@0 { + compatible = "regulator-fixed"; + regulator-name = "rear_vana_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + enable-active-high; + gpio = <&tlmm 27 0>; + vin-supply = <&pmi8998_bob>; + }; }; &cci { @@ -37,14 +47,11 @@ reg = <0x0>; compatible = "qcom,actuator"; qcom,cci-master = <0>; - gpios = <&tlmm 27 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <2800000>; + qcom,cam-vreg-max-voltage = <2800000>; + qcom,cam-vreg-op-mode = <0>; }; actuator1: qcom,actuator@1 { @@ -52,14 +59,11 @@ reg = <0x1>; compatible = "qcom,actuator"; qcom,cci-master = <1>; - gpios = <&tlmm 27 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <2800000>; + qcom,cam-vreg-max-voltage = <2800000>; + qcom,cam-vreg-op-mode = <0>; }; ois0: qcom,ois@0 { @@ -78,24 +82,24 @@ status = "disabled"; }; - tof0:qcom,tof@0{ + tof0: qcom,tof@0 { cell-index = <0>; reg = <0x29>; compatible = "st,stmvl53l0"; qcom,cci-master = <0>; cam_cci-supply = <&pm8998_lvs1>; - cam_laser-supply = <&pmi8998_bob>; + cam_laser-supply = <&actuator_regulator>; qcom,cam-vreg-name = "cam_cci", "cam_laser"; qcom,cam-vreg-min-voltage = <0 0>; - qcom,cam-vreg-max-voltage = <0 3600000>; + qcom,cam-vreg-max-voltage = <0 2800000>; pinctrl-names = "cam_default", "cam_suspend"; pinctrl-0 = <&cam_tof_active>; pinctrl-1 = <&cam_tof_suspend>; - gpios = <&tlmm 27 0>, - <&tlmm 126 0>; - qcom,gpio-req-tbl-num = <0 1>; - qcom,gpio-req-tbl-flags = <0 0>; - qcom,gpio-req-tbl-label = "CAM_TOF", "CAM_CE"; + stm,irq-gpio = <&tlmm 26 0x2008>; + gpios = <&tlmm 126 0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_CE"; }; eeprom0: qcom,eeprom@0 { diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi index c5384eaf17a1..2ed0f2250de5 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi @@ -29,6 +29,16 @@ qcom,switch-source = <&pmi8998_switch1>; status = "ok"; }; + + actuator_regulator: gpio-regulator@0 { + compatible = "regulator-fixed"; + regulator-name = "rear_vana_regulator"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + enable-active-high; + gpio = <&tlmm 27 0>; + vin-supply = <&pmi8998_bob>; + }; }; &cci { @@ -37,14 +47,11 @@ reg = <0x0>; compatible = "qcom,actuator"; qcom,cci-master = <0>; - gpios = <&tlmm 27 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <2800000>; + qcom,cam-vreg-max-voltage = <2800000>; + qcom,cam-vreg-op-mode = <0>; }; actuator1: qcom,actuator@1 { @@ -52,33 +59,33 @@ reg = <0x1>; compatible = "qcom,actuator"; qcom,cci-master = <1>; - gpios = <&tlmm 27 0>; - qcom,gpio-vaf = <0>; - qcom,gpio-req-tbl-num = <0>; - qcom,gpio-req-tbl-flags = <0>; - qcom,gpio-req-tbl-label = "CAM_VAF"; - pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active>; - pinctrl-1 = <&cam_actuator_vaf_suspend>; + cam_vaf-supply = <&actuator_regulator>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <2800000>; + qcom,cam-vreg-max-voltage = <2800000>; + qcom,cam-vreg-op-mode = <0>; }; - tof0:qcom,tof@0{ + + tof0: qcom,tof@0 { cell-index = <0>; reg = <0x29>; compatible = "st,stmvl53l0"; qcom,cci-master = <0>; cam_cci-supply = <&pm8998_lvs1>; - cam_laser-supply = <&pmi8998_bob>; + cam_laser-supply = <&actuator_regulator>; qcom,cam-vreg-name = "cam_cci", "cam_laser"; qcom,cam-vreg-min-voltage = <0 0>; - qcom,cam-vreg-max-voltage = <0 3600000>; + qcom,cam-vreg-max-voltage = <0 2800000>; pinctrl-names = "cam_default", "cam_suspend"; pinctrl-0 = <&cam_tof_active>; pinctrl-1 = <&cam_tof_suspend>; - gpios = <&tlmm 27 0>, <&tlmm 126 0>; - qcom,gpio-req-tbl-num = <0 1>; - qcom,gpio-req-tbl-flags = <0 0>; - qcom,gpio-req-tbl-label = "CAM_TOF", "CAM_CE"; + stm,irq-gpio = <&tlmm 26 0x2008>; + gpios = <&tlmm 126 0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_CE"; }; + ois0: qcom,ois@0 { cell-index = <0>; reg = <0x0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-camera.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera.dtsi index e0ba982d7932..f87444465a68 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera.dtsi @@ -493,11 +493,11 @@ <&clock_mmss clk_mmss_camss_csi1_clk>, <&clock_mmss clk_mmss_camss_csi2_clk>, <&clock_mmss clk_mmss_camss_csi3_clk>, - <&clock_mmss clk_mmss_camss_vfe0_clk>, <&clock_mmss clk_vfe0_clk_src>, + <&clock_mmss clk_mmss_camss_vfe0_clk>, <&clock_mmss clk_mmss_camss_csi_vfe0_clk>, - <&clock_mmss clk_mmss_camss_vfe1_clk>, <&clock_mmss clk_vfe1_clk_src>, + <&clock_mmss clk_mmss_camss_vfe1_clk>, <&clock_mmss clk_mmss_camss_csi_vfe1_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "camss_ahb_clk", @@ -510,10 +510,12 @@ "csi2_pix_clk", "csi3_pix_clk", "camss_csi0_clk", "camss_csi1_clk", "camss_csi2_clk", "camss_csi3_clk", + "vfe0_clk_src", "camss_vfe_vfe0_clk", - "vfe0_clk_src", "camss_csi_vfe0_clk", + "camss_csi_vfe0_clk", + "vfe1_clk_src", "camss_vfe_vfe1_clk", - "vfe1_clk_src", "camss_csi_vfe1_clk"; + "camss_csi_vfe1_clk"; qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -532,10 +534,10 @@ "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "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", + "INIT_RATE", + "NO_SET_RATE", "NO_SET_RATE"; status = "ok"; }; @@ -557,23 +559,23 @@ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, + <&clock_mmss clk_vfe0_clk_src>, <&clock_mmss clk_mmss_camss_vfe0_clk>, <&clock_mmss clk_mmss_camss_vfe0_stream_clk>, <&clock_mmss clk_mmss_camss_vfe0_ahb_clk>, <&clock_mmss clk_mmss_camss_vfe_vbif_ahb_clk>, <&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>, - <&clock_mmss clk_vfe0_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe0_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", - "camss_ahb_clk", "camss_top_ahb_clk", + "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src", "camss_vfe_clk", "camss_vfe_stream_clk", "camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk", - "camss_vfe_vbif_axi_clk", "vfe_clk_src", + "camss_vfe_vbif_axi_clk", "camss_csi_vfe_clk"; - qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 480000000 0 - 0 0 0 0 0 0 0 0 0 0 0 576000000 0 - 0 0 0 0 0 0 0 0 0 0 0 600000000 0>; + qcom,clock-rates = <0 0 0 0 0 0 480000000 0 0 0 0 0 0 + 0 0 0 0 0 0 576000000 0 0 0 0 0 0 + 0 0 0 0 0 0 600000000 0 0 0 0 0 0>; status = "ok"; qos-entries = <8>; qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418 @@ -637,23 +639,23 @@ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, + <&clock_mmss clk_vfe1_clk_src>, <&clock_mmss clk_mmss_camss_vfe1_clk>, <&clock_mmss clk_mmss_camss_vfe1_stream_clk>, <&clock_mmss clk_mmss_camss_vfe1_ahb_clk>, <&clock_mmss clk_mmss_camss_vfe_vbif_ahb_clk>, <&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>, - <&clock_mmss clk_vfe1_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe1_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", - "camss_ahb_clk", "camss_top_ahb_clk", + "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src", "camss_vfe_clk", "camss_vfe_stream_clk", "camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk", - "camss_vfe_vbif_axi_clk", "vfe_clk_src", + "camss_vfe_vbif_axi_clk", "camss_csi_vfe_clk"; - qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 480000000 0 - 0 0 0 0 0 0 0 0 0 0 0 576000000 0 - 0 0 0 0 0 0 0 0 0 0 0 600000000 0>; + qcom,clock-rates = <0 0 0 0 0 0 480000000 0 0 0 0 0 0 + 0 0 0 0 0 0 576000000 0 0 0 0 0 0 + 0 0 0 0 0 0 600000000 0 0 0 0 0 0>; status = "ok"; qos-entries = <8>; qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418 diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi index 52d0fdb4a523..41e783e6bf1f 100644 --- a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -144,6 +144,10 @@ /delete-node/qcom,ois@0; }; +&soc { + /delete-node/gpio-regulator@0; +}; + &cci { actuator0: qcom,actuator@0 { cell-index = <0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi index 52d0fdb4a523..722c18a26388 100644 --- a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -132,6 +132,10 @@ }; }; +&soc { + /delete-node/gpio-regulator@0; +}; + &cci { /delete-node/qcom,camera@0; /delete-node/qcom,camera@1; diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi index 87b1146bd361..8b68ece2239f 100644 --- a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -170,6 +170,10 @@ }; }; +&soc { + /delete-node/gpio-regulator@0; +}; + &cci { /delete-node/qcom,camera@0; /delete-node/qcom,camera@1; diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi index dd10c3b1ea96..f7dcfc7c149f 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi @@ -292,6 +292,19 @@ qcom,led-strings-list = [00 01]; }; +&red_led { + /delete-property/ linux,default-trigger; + qcom,start-idx = <0>; + qcom,idx-len = <10>; + qcom,duty-pcts = [00 19 32 4b 64 + 64 4b 32 19 00]; + qcom,use-blink; +}; + +&green_led { + /delete-property/ linux,default-trigger; +}; + &dsi_dual_nt35597_video { qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-bl-min-level = <1>; diff --git a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi index 4914363b414a..d2e18db982ef 100644 --- a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi @@ -956,12 +956,12 @@ cam_tof_active: cam_tof_active { mux { - pins = "gpio27", "gpio126"; + pins = "gpio26", "gpio126"; function = "gpio"; }; config { - pins = "gpio27", "gpio126"; + pins = "gpio26", "gpio126"; bias-disable; drive-strength = <2>; /* 2 MA */ }; @@ -969,12 +969,12 @@ cam_tof_suspend: cam_tof_suspend { mux { - pins = "gpio27", "gpio126"; + pins = "gpio26", "gpio126"; function = "gpio"; }; config { - pins = "gpio27", "gpio126"; + pins = "gpio26", "gpio126"; bias-pull-down; /* PULL DOWN */ drive-strength = <2>; /* 2 MA */ }; diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi index d67d23b79d36..1c2db6833bde 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi @@ -280,6 +280,19 @@ qcom,led-strings-list = [01 02]; }; +&red_led { + /delete-property/ linux,default-trigger; + qcom,start-idx = <0>; + qcom,idx-len = <10>; + qcom,duty-pcts = [00 19 32 4b 64 + 64 4b 32 19 00]; + qcom,use-blink; +}; + +&green_led { + /delete-property/ linux,default-trigger; +}; + &dsi_dual_nt35597_video { qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-bl-min-level = <1>; diff --git a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi index 518ad33c63ea..045cdda09d18 100644 --- a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -409,6 +409,7 @@ pm8998_l24: regulator-l24 { regulator-min-microvolt = <3088000>; regulator-max-microvolt = <3088000>; + parent-supply = <&pm8998_l12>; status = "okay"; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 66ca39ee6ebc..02b7a44ee0d2 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -3068,6 +3068,8 @@ msm_ath10k_wlan: qcom,msm_ath10k_wlan { status = "disabled"; compatible = "qcom,wcn3990-wifi"; + reg = <0x18800000 0x800000>; + reg-names = "membase"; interrupts = <0 413 0 /* CE0 */ >, <0 414 0 /* CE1 */ >, diff --git a/arch/arm/boot/dts/qcom/sdm630-camera.dtsi b/arch/arm/boot/dts/qcom/sdm630-camera.dtsi index 72f606168691..8b226586ca7b 100644 --- a/arch/arm/boot/dts/qcom/sdm630-camera.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-camera.dtsi @@ -451,11 +451,11 @@ <&clock_mmss MMSS_CAMSS_CSI1_CLK>, <&clock_mmss MMSS_CAMSS_CSI2_CLK>, <&clock_mmss MMSS_CAMSS_CSI3_CLK>, - <&clock_mmss MMSS_CAMSS_VFE0_CLK>, <&clock_mmss VFE0_CLK_SRC>, + <&clock_mmss MMSS_CAMSS_VFE0_CLK>, <&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>, - <&clock_mmss MMSS_CAMSS_VFE1_CLK>, <&clock_mmss VFE1_CLK_SRC>, + <&clock_mmss MMSS_CAMSS_VFE1_CLK>, <&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "camss_ahb_clk", @@ -468,10 +468,12 @@ "csi2_pix_clk", "csi3_pix_clk", "camss_csi0_clk", "camss_csi1_clk", "camss_csi2_clk", "camss_csi3_clk", + "vfe0_clk_src", "camss_vfe_vfe0_clk", - "vfe0_clk_src", "camss_csi_vfe0_clk", + "camss_csi_vfe0_clk", + "vfe1_clk_src", "camss_vfe_vfe1_clk", - "vfe1_clk_src", "camss_csi_vfe1_clk"; + "camss_csi_vfe1_clk"; qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -490,10 +492,10 @@ "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "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", + "INIT_RATE", + "NO_SET_RATE", "NO_SET_RATE"; status = "ok"; }; @@ -516,23 +518,23 @@ <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>, <&clock_mmss MMSS_CAMSS_AHB_CLK>, <&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>, + <&clock_mmss VFE0_CLK_SRC>, <&clock_mmss MMSS_CAMSS_VFE0_CLK>, <&clock_mmss MMSS_CAMSS_VFE0_STREAM_CLK>, <&clock_mmss MMSS_CAMSS_VFE0_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>, - <&clock_mmss VFE0_CLK_SRC>, <&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", - "camss_ahb_clk", "camss_top_ahb_clk", + "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src", "camss_vfe_clk", "camss_vfe_stream_clk", "camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk", - "camss_vfe_vbif_axi_clk", "vfe_clk_src", + "camss_vfe_vbif_axi_clk", "camss_csi_vfe_clk"; - qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 256000000 0 - 0 0 0 0 0 0 0 0 0 0 0 480000000 0 - 0 0 0 0 0 0 0 0 0 0 0 576000000 0>; + qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0 + 0 0 0 0 0 0 480000000 0 0 0 0 0 0 + 0 0 0 0 0 0 576000000 0 0 0 0 0 0>; status = "ok"; qos-entries = <8>; qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418 @@ -597,23 +599,23 @@ <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>, <&clock_mmss MMSS_CAMSS_AHB_CLK>, <&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>, + <&clock_mmss VFE1_CLK_SRC>, <&clock_mmss MMSS_CAMSS_VFE1_CLK>, <&clock_mmss MMSS_CAMSS_VFE1_STREAM_CLK>, <&clock_mmss MMSS_CAMSS_VFE1_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>, - <&clock_mmss VFE1_CLK_SRC>, <&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", - "camss_ahb_clk", "camss_top_ahb_clk", + "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src", "camss_vfe_clk", "camss_vfe_stream_clk", "camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk", - "camss_vfe_vbif_axi_clk", "vfe_clk_src", + "camss_vfe_vbif_axi_clk", "camss_csi_vfe_clk"; - qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 256000000 0 - 0 0 0 0 0 0 0 0 0 0 0 480000000 0 - 0 0 0 0 0 0 0 0 0 0 0 576000000 0>; + qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0 + 0 0 0 0 0 0 480000000 0 0 0 0 0 0 + 0 0 0 0 0 0 576000000 0 0 0 0 0 0>; status = "ok"; qos-entries = <8>; qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418 @@ -758,7 +760,7 @@ qcom,msm-bus,num-cases = <2>; qcom,msm-bus,num-paths = <1>; qcom,msm-bus,vectors-KBps = <62 512 0 0>, - <62 512 1920000 2880000>; + <62 512 1200000 1200000>; status = "ok"; }; @@ -800,7 +802,7 @@ qcom,msm-bus,num-cases = <2>; qcom,msm-bus,num-paths = <1>; qcom,msm-bus,vectors-KBps = <62 512 0 0>, - <62 512 1920000 2880000>; + <62 512 1200000 1200000>; qcom,max-ds-factor = <128>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi index 2448e1894387..e0d51db067c9 100644 --- a/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi @@ -114,8 +114,8 @@ vdd-supply = <&gdsc_gpu_gx>; /* CPU latency parameter */ - qcom,pm-qos-active-latency = <349>; - qcom,pm-qos-wakeup-latency = <349>; + qcom,pm-qos-active-latency = <424>; + qcom,pm-qos-wakeup-latency = <424>; /* Quirks */ qcom,gpu-quirk-dp2clockgating-disable; @@ -132,6 +132,9 @@ qcom,gpu-speed-bin = <0x41a0 0x1fe00000 21>; + /* Enable midframe sampling */ + qcom,enable-midframe-timer; + /* GPU Mempools */ qcom,gpu-mempools { #address-cells= <1>; diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi index a0b4c423a616..81e0c6930bf3 100644 --- a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi @@ -123,6 +123,8 @@ 24 1e 08 09 05 03 04 a0 24 1e 08 09 05 03 04 a0 24 1a 08 09 05 03 04 a0]; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "bta_check"; }; &dsi_truly_1080_vid { diff --git a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi index 093eadab0413..b8272b29aa89 100644 --- a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi @@ -63,20 +63,20 @@ reg = <0>; label = "system-active"; qcom,psci-mode = <0x0>; - qcom,latency-us = <100>; - qcom,ss-power = <725>; - qcom,energy-overhead = <85000>; - qcom,time-overhead = <120>; + qcom,latency-us = <50>; + qcom,ss-power = <240>; + qcom,energy-overhead = <61750>; + qcom,time-overhead = <7870>; }; qcom,pm-cluster-level@1{ /* E3 */ reg = <1>; label = "system-pc"; qcom,psci-mode = <0x3>; - qcom,latency-us = <350>; - qcom,ss-power = <530>; - qcom,energy-overhead = <160000>; - qcom,time-overhead = <550>; + qcom,latency-us = <4026>; + qcom,ss-power = <167>; + qcom,energy-overhead = <749982>; + qcom,time-overhead = <14773>; qcom,min-child-idx = <3>; qcom,is-reset; qcom,notify-rpm; @@ -96,19 +96,19 @@ reg = <0>; label = "pwr-l2-active"; qcom,psci-mode = <0x1>; - qcom,latency-us = <40>; - qcom,ss-power = <740>; - qcom,energy-overhead = <65000>; - qcom,time-overhead = <85>; + qcom,latency-us = <51>; + qcom,ss-power = <198>; + qcom,energy-overhead = <83852>; + qcom,time-overhead = <91>; }; qcom,pm-cluster-level@1{ /* D2D */ reg = <1>; label = "pwr-l2-dynret"; qcom,psci-mode = <0x2>; - qcom,latency-us = <60>; - qcom,ss-power = <700>; - qcom,energy-overhead = <85000>; - qcom,time-overhead = <85>; + qcom,latency-us = <384>; + qcom,ss-power = <197>; + qcom,energy-overhead = <198413>; + qcom,time-overhead = <668>; qcom,min-child-idx = <1>; }; @@ -116,10 +116,10 @@ reg = <2>; label = "pwr-l2-ret"; qcom,psci-mode = <0x3>; - qcom,latency-us = <100>; - qcom,ss-power = <640>; - qcom,energy-overhead = <135000>; - qcom,time-overhead = <85>; + qcom,latency-us = <423>; + qcom,ss-power = <193>; + qcom,energy-overhead = <218055>; + qcom,time-overhead = <761>; qcom,min-child-idx = <2>; }; @@ -127,10 +127,10 @@ reg = <3>; label = "pwr-l2-pc"; qcom,psci-mode = <0x4>; - qcom,latency-us = <700>; - qcom,ss-power = <450>; - qcom,energy-overhead = <210000>; - qcom,time-overhead = <11500>; + qcom,latency-us = <1821>; + qcom,ss-power = <184>; + qcom,energy-overhead = <558835>; + qcom,time-overhead = <2336>; qcom,min-child-idx = <2>; qcom,is-reset; }; @@ -145,30 +145,30 @@ reg = <0>; qcom,spm-cpu-mode = "wfi"; qcom,psci-cpu-mode = <0x1>; - qcom,latency-us = <20>; - qcom,ss-power = <750>; - qcom,energy-overhead = <32000>; - qcom,time-overhead = <60>; + qcom,latency-us = <42>; + qcom,ss-power = <240>; + qcom,energy-overhead = <30562>; + qcom,time-overhead = <91>; }; qcom,pm-cpu-level@1 { /* C2D */ reg = <1>; qcom,psci-cpu-mode = <2>; qcom,spm-cpu-mode = "ret"; - qcom,latency-us = <40>; - qcom,ss-power = <730>; - qcom,energy-overhead = <85500>; - qcom,time-overhead = <110>; + qcom,latency-us = <90>; + qcom,ss-power = <213>; + qcom,energy-overhead = <79070>; + qcom,time-overhead = <253>; }; qcom,pm-cpu-level@2 { /* C3 */ reg = <2>; qcom,spm-cpu-mode = "pc"; qcom,psci-cpu-mode = <0x3>; - qcom,latency-us = <80>; - qcom,ss-power = <700>; - qcom,energy-overhead = <126480>; - qcom,time-overhead = <160>; + qcom,latency-us = <343>; + qcom,ss-power = <200>; + qcom,energy-overhead = <170215>; + qcom,time-overhead = <605>; qcom,is-reset; }; }; @@ -188,20 +188,20 @@ reg = <0>; label = "perf-l2-active"; qcom,psci-mode = <0x1>; - qcom,latency-us = <40>; - qcom,ss-power = <740>; - qcom,energy-overhead = <70000>; - qcom,time-overhead = <80>; + qcom,latency-us = <51>; + qcom,ss-power = <287>; + qcom,energy-overhead = <61412>; + qcom,time-overhead = <89>; }; qcom,pm-cluster-level@1{ /* D2D */ reg = <1>; label = "perf-l2-dynret"; qcom,psci-mode = <2>; - qcom,latency-us = <60>; - qcom,ss-power = <700>; - qcom,energy-overhead = <85000>; - qcom,time-overhead = <85>; + qcom,latency-us = <329>; + qcom,ss-power = <284>; + qcom,energy-overhead = <206996>; + qcom,time-overhead = <601>; qcom,min-child-idx = <1>; }; @@ -209,10 +209,10 @@ reg = <2>; label = "perf-l2-ret"; qcom,psci-mode = <3>; - qcom,latency-us = <100>; - qcom,ss-power = <640>; - qcom,energy-overhead = <135000>; - qcom,time-overhead = <85>; + qcom,latency-us = <368>; + qcom,ss-power = <277>; + qcom,energy-overhead = <240450>; + qcom,time-overhead = <700>; qcom,min-child-idx = <2>; }; @@ -220,10 +220,10 @@ reg = <3>; label = "perf-l2-pc"; qcom,psci-mode = <0x4>; - qcom,latency-us = <800>; - qcom,ss-power = <450>; - qcom,energy-overhead = <240000>; - qcom,time-overhead = <11500>; + qcom,latency-us = <1609>; + qcom,ss-power = <262>; + qcom,energy-overhead = <703061>; + qcom,time-overhead = <2154>; qcom,min-child-idx = <2>; qcom,is-reset; }; @@ -238,30 +238,30 @@ reg = <0>; qcom,spm-cpu-mode = "wfi"; qcom,psci-cpu-mode = <0x1>; - qcom,latency-us = <25>; - qcom,ss-power = <750>; - qcom,energy-overhead = <37000>; - qcom,time-overhead = <50>; + qcom,latency-us = <39>; + qcom,ss-power = <315>; + qcom,energy-overhead = <37558>; + qcom,time-overhead = <83>; }; qcom,pm-cpu-level@1 { /* C2D */ reg = <1>; qcom,psci-cpu-mode = <2>; qcom,spm-cpu-mode = "ret"; - qcom,latency-us = <40>; - qcom,ss-power = <730>; - qcom,energy-overhead = <85500>; - qcom,time-overhead = <110>; + qcom,latency-us = <87>; + qcom,ss-power = <299>; + qcom,energy-overhead = <91434>; + qcom,time-overhead = <241>; }; qcom,pm-cpu-level@2 { /* C3 */ reg = <2>; qcom,spm-cpu-mode = "pc"; qcom,psci-cpu-mode = <0x3>; - qcom,latency-us = <80>; - qcom,ss-power = <700>; - qcom,energy-overhead = <136480>; - qcom,time-overhead = <160>; + qcom,latency-us = <301>; + qcom,ss-power = <291>; + qcom,energy-overhead = <199377>; + qcom,time-overhead = <563>; qcom,is-reset; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi index cb083c0a8aa0..fb24f727fb49 100644 --- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi @@ -197,6 +197,13 @@ status = "ok"; }; +&sdc2_cd_on { + config { + /delete-property/ bias-pull-up; + bias-disable; + }; +}; + &sdhc_2 { /* device core power supply */ vdd-supply = <&pm660l_l5>; @@ -317,6 +324,12 @@ }; }; +&ssphy { + fpc-redrive-supply = <&pm660_l11>; + qcom,redrive-voltage-level = <0 1800000 1950000>; + qcom,redrive-load = <105000>; +}; + &soc { qcom,msm-ssc-sensors { compatible = "qcom,msm-ssc-sensors"; diff --git a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi index c16c83050f31..eded8b08528a 100644 --- a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi @@ -376,6 +376,7 @@ pm660l_l7: regulator-l7 { regulator-min-microvolt = <2700000>; regulator-max-microvolt = <3125000>; + parent-supply = <&pm660_l10>; status = "okay"; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 623ca54de94b..9626e0548789 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -34,7 +34,7 @@ chosen { stdout-path = "serial0"; - bootargs = "rcupdate.rcu_expedited=1"; + bootargs = "rcupdate.rcu_expedited=1 core_ctl_disable_cpumask=0-7"; }; psci { @@ -1235,8 +1235,8 @@ <90 512 206000 960000>, <1 676 206000 160000>; qcom,bus-vector-names = "MIN", "SVS", "PERF", "TURBO"; - qcom,rx-polling-sleep-ms = <2>; /* Polling sleep interval */ - qcom,ipa-polling-iteration = <5>; /* Polling Iteration */ + qcom,rx-polling-sleep-ms = <1>; /* Polling sleep interval */ + qcom,ipa-polling-iteration = <40>; /* Polling Iteration */ ipa_smmu_ap: ipa_smmu_ap { compatible = "qcom,ipa-smmu-ap-cb"; diff --git a/arch/arm/boot/dts/qcom/sdm660-audio.dtsi b/arch/arm/boot/dts/qcom/sdm660-audio.dtsi index c1cb6441cd43..402f19efd50d 100644 --- a/arch/arm/boot/dts/qcom/sdm660-audio.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-audio.dtsi @@ -34,7 +34,7 @@ clock-names = "wcd_clk", "wcd_native_clk"; clocks = <&clock_audio AUDIO_PMI_CLK>, - <&clock_audio AUDIO_AP_CLK2>; + <&clock_audio AUDIO_LPASS_MCLK>; cdc-vdd-mic-bias-supply = <&pm660l_bob>; qcom,cdc-vdd-mic-bias-voltage = <3300000 3300000>; diff --git a/arch/arm/boot/dts/qcom/sdm660-bus.dtsi b/arch/arm/boot/dts/qcom/sdm660-bus.dtsi index d555da4cbd08..6c956fc9b9d2 100644 --- a/arch/arm/boot/dts/qcom/sdm660-bus.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-bus.dtsi @@ -324,9 +324,9 @@ qcom,qport = <4>; qcom,qos-mode = "fixed"; qcom,connections = <&slv_hmss_l3 &slv_ebi>; - qcom,prio-lvl = <0>; - qcom,prio-rd = <0>; - qcom,prio-wr = <0>; + qcom,prio-lvl = <1>; + qcom,prio-rd = <1>; + qcom,prio-wr = <1>; qcom,bus-dev = <&fab_bimc>; qcom,mas-rpm-id = <ICBID_MASTER_PIMEM>; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi index e31a863ae22d..64ca4676ccd5 100644 --- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -229,7 +229,7 @@ reg = <0x0>; qcom,csiphy-sd-index = <0>; qcom,csid-sd-index = <0>; - qcom,mount-angle = <270>; + qcom,mount-angle = <90>; qcom,led-flash-src = <&led_flash0>; qcom,actuator-src = <&actuator0>; qcom,ois-src = <&ois0>; diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi index 416cd99a81cb..191beaa4d53b 100644 --- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -229,7 +229,7 @@ reg = <0x0>; qcom,csiphy-sd-index = <0>; qcom,csid-sd-index = <0>; - qcom,mount-angle = <270>; + qcom,mount-angle = <90>; qcom,led-flash-src = <&led_flash0>; qcom,actuator-src = <&actuator0>; qcom,ois-src = <&ois0>; diff --git a/arch/arm/boot/dts/qcom/sdm660-camera.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera.dtsi index 14b009656890..f3b81b5df1de 100644 --- a/arch/arm/boot/dts/qcom/sdm660-camera.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-camera.dtsi @@ -454,11 +454,11 @@ <&clock_mmss MMSS_CAMSS_CSI1_CLK>, <&clock_mmss MMSS_CAMSS_CSI2_CLK>, <&clock_mmss MMSS_CAMSS_CSI3_CLK>, - <&clock_mmss MMSS_CAMSS_VFE0_CLK>, <&clock_mmss VFE0_CLK_SRC>, + <&clock_mmss MMSS_CAMSS_VFE0_CLK>, <&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>, - <&clock_mmss MMSS_CAMSS_VFE1_CLK>, <&clock_mmss VFE1_CLK_SRC>, + <&clock_mmss MMSS_CAMSS_VFE1_CLK>, <&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "camss_ahb_clk", @@ -471,10 +471,12 @@ "csi2_pix_clk", "csi3_pix_clk", "camss_csi0_clk", "camss_csi1_clk", "camss_csi2_clk", "camss_csi3_clk", + "vfe0_clk_src", "camss_vfe_vfe0_clk", - "vfe0_clk_src", "camss_csi_vfe0_clk", + "camss_csi_vfe0_clk", + "vfe1_clk_src", "camss_vfe_vfe1_clk", - "vfe1_clk_src", "camss_csi_vfe1_clk"; + "camss_csi_vfe1_clk"; qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -493,10 +495,10 @@ "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "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", + "INIT_RATE", + "NO_SET_RATE", "NO_SET_RATE"; status = "ok"; }; @@ -518,23 +520,23 @@ <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>, <&clock_mmss MMSS_CAMSS_AHB_CLK>, <&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>, + <&clock_mmss VFE0_CLK_SRC>, <&clock_mmss MMSS_CAMSS_VFE0_CLK>, <&clock_mmss MMSS_CAMSS_VFE0_STREAM_CLK>, <&clock_mmss MMSS_CAMSS_VFE0_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>, - <&clock_mmss VFE0_CLK_SRC>, <&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", - "camss_ahb_clk", "camss_top_ahb_clk", + "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src", "camss_vfe_clk", "camss_vfe_stream_clk", "camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk", - "camss_vfe_vbif_axi_clk", "vfe_clk_src", + "camss_vfe_vbif_axi_clk", "camss_csi_vfe_clk"; - qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 404000000 0 - 0 0 0 0 0 0 0 0 0 0 0 480000000 0 - 0 0 0 0 0 0 0 0 0 0 0 576000000 0>; + qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0 + 0 0 0 0 0 0 480000000 0 0 0 0 0 0 + 0 0 0 0 0 0 576000000 0 0 0 0 0 0>; status = "ok"; qos-entries = <8>; qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418 @@ -599,23 +601,23 @@ <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>, <&clock_mmss MMSS_CAMSS_AHB_CLK>, <&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>, + <&clock_mmss VFE1_CLK_SRC>, <&clock_mmss MMSS_CAMSS_VFE1_CLK>, <&clock_mmss MMSS_CAMSS_VFE1_STREAM_CLK>, <&clock_mmss MMSS_CAMSS_VFE1_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>, <&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>, - <&clock_mmss VFE1_CLK_SRC>, <&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>; clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", - "camss_ahb_clk", "camss_top_ahb_clk", + "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src", "camss_vfe_clk", "camss_vfe_stream_clk", "camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk", - "camss_vfe_vbif_axi_clk", "vfe_clk_src", + "camss_vfe_vbif_axi_clk", "camss_csi_vfe_clk"; - qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 404000000 0 - 0 0 0 0 0 0 0 0 0 0 0 480000000 0 - 0 0 0 0 0 0 0 0 0 0 0 576000000 0>; + qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0 + 0 0 0 0 0 0 480000000 0 0 0 0 0 0 + 0 0 0 0 0 0 576000000 0 0 0 0 0 0>; status = "ok"; qos-entries = <8>; qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418 @@ -761,7 +763,7 @@ qcom,msm-bus,num-cases = <2>; qcom,msm-bus,num-paths = <1>; qcom,msm-bus,vectors-KBps = <62 512 0 0>, - <62 512 1920000 2880000>; + <62 512 1200000 1200000>; status = "ok"; }; @@ -803,7 +805,7 @@ qcom,msm-bus,num-cases = <2>; qcom,msm-bus,num-paths = <1>; qcom,msm-bus,vectors-KBps = <62 512 0 0>, - <62 512 1920000 2880000>; + <62 512 1200000 1200000>; qcom,max-ds-factor = <128>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi index 6ea151667abc..fbe6d1d8cc74 100644 --- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi @@ -479,26 +479,26 @@ /* No vote */ <78 512 0 0>, <1 606 0 0>, /* 400 KB/s*/ - <78 512 1046 3200>, - <1 606 1046 3200>, + <78 512 1046 1600>, + <1 606 1600 1600>, /* 20 MB/s */ - <78 512 52286 160000>, - <1 606 52286 160000>, + <78 512 52286 80000>, + <1 606 80000 80000>, /* 25 MB/s */ - <78 512 65360 200000>, - <1 606 65360 200000>, + <78 512 65360 100000>, + <1 606 100000 100000>, /* 50 MB/s */ - <78 512 130718 400000>, - <1 606 130718 400000>, + <78 512 130718 200000>, + <1 606 133320 133320>, /* 100 MB/s */ - <78 512 130718 400000>, - <1 606 130718 400000>, + <78 512 130718 200000>, + <1 606 150000 150000>, /* 200 MB/s */ - <78 512 261438 800000>, - <1 606 261438 800000>, + <78 512 261438 400000>, + <1 606 300000 300000>, /* 400 MB/s */ - <78 512 261438 800000>, - <1 606 261438 800000>, + <78 512 261438 400000>, + <1 606 300000 300000>, /* Max. bandwidth */ <78 512 1338562 4096000>, <1 606 1338562 4096000>; @@ -516,7 +516,9 @@ qcom,nonremovable; qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v"; - qcom,ice-clk-rates = <300000000 150000000>; + qcom,ice-clk-rates = <300000000 75000000>; + + qcom,scaling-lower-bus-speed-mode = "DDR52"; status = "disabled"; }; @@ -538,24 +540,24 @@ qcom,msm-bus,vectors-KBps = /* No vote */ <81 512 0 0>, <1 608 0 0>, - /* 400 KB/s */ - <81 512 1046 3200>, - <1 608 1046 3200>, + /* 400 KB/s*/ + <81 512 1046 1600>, + <1 608 1600 1600>, /* 20 MB/s */ - <81 512 52286 160000>, - <1 608 52286 160000>, + <81 512 52286 80000>, + <1 608 80000 80000>, /* 25 MB/s */ - <81 512 65360 200000>, - <1 608 65360 200000>, + <81 512 65360 100000>, + <1 608 100000 100000>, /* 50 MB/s */ - <81 512 130718 400000>, - <1 608 130718 400000>, + <81 512 130718 200000>, + <1 608 133320 133320>, /* 100 MB/s */ - <81 512 261438 800000>, - <1 608 261438 800000>, + <81 512 261438 200000>, + <1 608 150000 150000>, /* 200 MB/s */ - <81 512 261438 800000>, - <1 608 261438 800000>, + <81 512 261438 400000>, + <1 608 300000 300000>, /* Max. bandwidth */ <81 512 1338562 4096000>, <1 608 1338562 4096000>; diff --git a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi index c3c776be3209..f5d61d440a27 100644 --- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi @@ -141,9 +141,6 @@ qcom,gpu-speed-bin = <0x41a0 0x1fe00000 21>; - /* Enable midframe sampling */ - qcom,enable-midframe-timer; - /* GPU Mempools */ qcom,gpu-mempools { #address-cells= <1>; diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi index 941fe808188c..ed3b3d89d392 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi @@ -236,7 +236,7 @@ }; &mem_client_3_size { - qcom,peripheral-size = <0x500000>; + qcom,peripheral-size = <0xf00000>; }; &pm660_fg { diff --git a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi index 0e869f0e1352..3d2cfedc1009 100644 --- a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi @@ -196,6 +196,12 @@ }; }; +&ssphy { + fpc-redrive-supply = <&pm660_l11>; + qcom,redrive-voltage-level = <0 1800000 1950000>; + qcom,redrive-load = <105000>; +}; + &soc { gpio_keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi index 462a76ef8bfe..a93efdc38f41 100644 --- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi @@ -377,6 +377,7 @@ pm660l_l7: regulator-l7 { regulator-min-microvolt = <2700000>; regulator-max-microvolt = <3125000>; + parent-supply = <&pm660_l10>; status = "okay"; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index 001ae6e4fc04..1e0b6136e1b4 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -370,7 +370,7 @@ alloc-ranges = <0 0x00000000 0 0xffffffff>; reusable; alignment = <0 0x400000>; - size = <0 0x2000000>; + size = <0 0x2c00000>; linux,cma-default; }; @@ -613,6 +613,7 @@ compatible = "qcom,memshare-peripheral"; qcom,peripheral-size = <0x0>; qcom,client-id = <1>; + qcom,allocate-boot-time; label = "modem"; }; }; @@ -1339,26 +1340,26 @@ /* No vote */ <78 512 0 0>, <1 606 0 0>, /* 400 KB/s*/ - <78 512 1046 3200>, - <1 606 1046 3200>, + <78 512 1046 1600>, + <1 606 1600 1600>, /* 20 MB/s */ - <78 512 52286 160000>, - <1 606 52286 160000>, + <78 512 52286 80000>, + <1 606 80000 80000>, /* 25 MB/s */ - <78 512 65360 200000>, - <1 606 65360 200000>, + <78 512 65360 100000>, + <1 606 100000 100000>, /* 50 MB/s */ - <78 512 130718 400000>, - <1 606 130718 400000>, + <78 512 130718 200000>, + <1 606 133320 133320>, /* 100 MB/s */ - <78 512 130718 400000>, - <1 606 130718 400000>, + <78 512 130718 200000>, + <1 606 150000 150000>, /* 200 MB/s */ - <78 512 261438 800000>, - <1 606 261438 800000>, + <78 512 261438 400000>, + <1 606 300000 300000>, /* 400 MB/s */ - <78 512 261438 800000>, - <1 606 261438 800000>, + <78 512 261438 400000>, + <1 606 300000 300000>, /* Max. bandwidth */ <78 512 1338562 4096000>, <1 606 1338562 4096000>; @@ -1369,7 +1370,7 @@ <&clock_gcc GCC_SDCC1_APPS_CLK>, <&clock_gcc GCC_SDCC1_ICE_CORE_CLK>; clock-names = "iface_clk", "core_clk", "ice_core_clk"; - qcom,ice-clk-rates = <300000000 150000000>; + qcom,ice-clk-rates = <300000000 75000000>; status = "disabled"; }; @@ -1391,24 +1392,24 @@ qcom,msm-bus,vectors-KBps = /* No vote */ <81 512 0 0>, <1 608 0 0>, - /* 400 KB/s */ - <81 512 1046 3200>, - <1 608 1046 3200>, + /* 400 KB/s*/ + <81 512 1046 1600>, + <1 608 1600 1600>, /* 20 MB/s */ - <81 512 52286 160000>, - <1 608 52286 160000>, + <81 512 52286 80000>, + <1 608 80000 80000>, /* 25 MB/s */ - <81 512 65360 200000>, - <1 608 65360 200000>, + <81 512 65360 100000>, + <1 608 100000 100000>, /* 50 MB/s */ - <81 512 130718 400000>, - <1 608 130718 400000>, + <81 512 130718 200000>, + <1 608 133320 133320>, /* 100 MB/s */ - <81 512 261438 800000>, - <1 608 261438 800000>, + <81 512 261438 200000>, + <1 608 150000 150000>, /* 200 MB/s */ - <81 512 261438 800000>, - <1 608 261438 800000>, + <81 512 261438 400000>, + <1 608 300000 300000>, /* Max. bandwidth */ <81 512 1338562 4096000>, <1 608 1338562 4096000>; @@ -1462,8 +1463,8 @@ <90 512 206000 960000>, <1 676 206000 160000>; qcom,bus-vector-names = "MIN", "SVS", "PERF", "TURBO"; - qcom,rx-polling-sleep-ms = <2>; /* Polling sleep interval */ - qcom,ipa-polling-iteration = <5>; /* Polling Iteration */ + qcom,rx-polling-sleep-ms = <1>; /* Polling sleep interval */ + qcom,ipa-polling-iteration = <40>; /* Polling Iteration */ ipa_smmu_ap: ipa_smmu_ap { compatible = "qcom,ipa-smmu-ap-cb"; diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi index a9977d6ee81a..169653586a9f 100644 --- a/arch/arm/boot/dts/r8a7794.dtsi +++ b/arch/arm/boot/dts/r8a7794.dtsi @@ -1023,7 +1023,7 @@ mstp7_clks: mstp7_clks@e615014c { compatible = "renesas,r8a7794-mstp-clocks", "renesas,cpg-mstp-clocks"; reg = <0 0xe615014c 0 4>, <0 0xe61501c4 0 4>; - clocks = <&mp_clk>, <&mp_clk>, + clocks = <&mp_clk>, <&hp_clk>, <&zs_clk>, <&p_clk>, <&p_clk>, <&zs_clk>, <&zs_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>; #clock-cells = <1>; diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig index 35a90af941a4..49e7bbd5825a 100644 --- a/arch/arm/configs/ranchu_defconfig +++ b/arch/arm/configs/ranchu_defconfig @@ -48,6 +48,7 @@ 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 diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index 21650ead5a34..32cf48661c9b 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -472,7 +472,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y -CONFIG_USB_CONFIGFS_F_GSI=y CONFIG_USB_CONFIGFS_F_CDEV=y CONFIG_USB_CONFIGFS_F_QDSS=y CONFIG_MMC=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index 02b15745e882..c4b0eabe2fbf 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -471,7 +471,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y -CONFIG_USB_CONFIGFS_F_GSI=y CONFIG_USB_CONFIGFS_F_CDEV=y CONFIG_USB_CONFIGFS_F_QDSS=y CONFIG_MMC=y diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c index 593da7ffb449..679c589c4828 100644 --- a/arch/arm/crypto/aes-ce-glue.c +++ b/arch/arm/crypto/aes-ce-glue.c @@ -87,8 +87,13 @@ static int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, u32 *rki = ctx->key_enc + (i * kwords); u32 *rko = rki + kwords; +#ifndef CONFIG_CPU_BIG_ENDIAN rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8); rko[0] = rko[0] ^ rki[0] ^ rcon[i]; +#else + rko[0] = rol32(ce_aes_sub(rki[kwords - 1]), 8); + rko[0] = rko[0] ^ rki[0] ^ (rcon[i] << 24); +#endif rko[1] = rko[0] ^ rki[1]; rko[2] = rko[1] ^ rki[2]; rko[3] = rko[2] ^ rki[3]; diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index 85e374f873ac..e9d04f475929 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -81,6 +81,9 @@ #define ARM_CPU_XSCALE_ARCH_V2 0x4000 #define ARM_CPU_XSCALE_ARCH_V3 0x6000 +/* Qualcomm implemented cores */ +#define ARM_CPU_PART_SCORPION 0x510002d0 + extern unsigned int processor_id; #ifdef CONFIG_CPU_CP15 diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index c7ba9a42e857..ebf866a3a8c8 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h @@ -205,18 +205,12 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn, * and iterate over the range. */ - bool need_flush = !vcpu_has_cache_enabled(vcpu) || ipa_uncached; - VM_BUG_ON(size & ~PAGE_MASK); - if (!need_flush && !icache_is_pipt()) - goto vipt_cache; - while (size) { void *va = kmap_atomic_pfn(pfn); - if (need_flush) - kvm_flush_dcache_to_poc(va, PAGE_SIZE); + kvm_flush_dcache_to_poc(va, PAGE_SIZE); if (icache_is_pipt()) __cpuc_coherent_user_range((unsigned long)va, @@ -228,7 +222,6 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn, kunmap_atomic(va); } -vipt_cache: if (!icache_is_pipt() && !icache_is_vivt_asid_tagged()) { /* any kind of VIPT cache */ __flush_icache_all(); diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 6284779d64ee..abcbea1ae30b 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -1066,6 +1066,22 @@ static int __init arch_hw_breakpoint_init(void) return 0; } + /* + * Scorpion CPUs (at least those in APQ8060) seem to set DBGPRSR.SPD + * whenever a WFI is issued, even if the core is not powered down, in + * violation of the architecture. When DBGPRSR.SPD is set, accesses to + * breakpoint and watchpoint registers are treated as undefined, so + * this results in boot time and runtime failures when these are + * accessed and we unexpectedly take a trap. + * + * It's not clear if/how this can be worked around, so we blacklist + * Scorpion CPUs to avoid these issues. + */ + if (read_cpuid_part() == ARM_CPU_PART_SCORPION) { + pr_info("Scorpion CPU detected. Hardware breakpoints and watchpoints disabled\n"); + return 0; + } + has_ossr = core_has_os_save_restore(); /* Determine how many BRPs/WRPs are available. */ diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 4d9375814b53..d54c53b7ab63 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -600,7 +600,7 @@ static int gpr_set(struct task_struct *target, const void *kbuf, const void __user *ubuf) { int ret; - struct pt_regs newregs; + struct pt_regs newregs = *task_pt_regs(target); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c index 2e72be4f623e..7cb079e74010 100644 --- a/arch/arm/kernel/smp_tlb.c +++ b/arch/arm/kernel/smp_tlb.c @@ -9,6 +9,7 @@ */ #include <linux/preempt.h> #include <linux/smp.h> +#include <linux/uaccess.h> #include <asm/smp_plat.h> #include <asm/tlbflush.h> @@ -40,8 +41,11 @@ static inline void ipi_flush_tlb_mm(void *arg) static inline void ipi_flush_tlb_page(void *arg) { struct tlb_args *ta = (struct tlb_args *)arg; + unsigned int __ua_flags = uaccess_save_and_enable(); local_flush_tlb_page(ta->ta_vma, ta->ta_start); + + uaccess_restore(__ua_flags); } static inline void ipi_flush_tlb_kernel_page(void *arg) @@ -54,8 +58,11 @@ static inline void ipi_flush_tlb_kernel_page(void *arg) static inline void ipi_flush_tlb_range(void *arg) { struct tlb_args *ta = (struct tlb_args *)arg; + unsigned int __ua_flags = uaccess_save_and_enable(); local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); + + uaccess_restore(__ua_flags); } static inline void ipi_flush_tlb_kernel_range(void *arg) diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S index 8ecfd15c3a02..df73914e81c8 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -67,7 +67,7 @@ ENTRY(__get_user_4) ENDPROC(__get_user_4) ENTRY(__get_user_8) - check_uaccess r0, 8, r1, r2, __get_user_bad + check_uaccess r0, 8, r1, r2, __get_user_bad8 #ifdef CONFIG_THUMB2_KERNEL 5: TUSER(ldr) r2, [r0] 6: TUSER(ldr) r3, [r0, #4] diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 676997895e13..f5f81a107309 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -298,6 +298,16 @@ static struct clk emac_clk = { .gpsc = 1, }; +/* + * In order to avoid adding the emac_clk to the clock lookup table twice (and + * screwing up the linked list in the process) create a separate clock for + * mdio inheriting the rate from emac_clk. + */ +static struct clk mdio_clk = { + .name = "mdio", + .parent = &emac_clk, +}; + static struct clk mcasp_clk = { .name = "mcasp", .parent = &pll0_sysclk2, @@ -462,7 +472,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "arm", &arm_clk), CLK(NULL, "rmii", &rmii_clk), CLK("davinci_emac.1", NULL, &emac_clk), - CLK("davinci_mdio.0", "fck", &emac_clk), + CLK("davinci_mdio.0", "fck", &mdio_clk), CLK("davinci-mcasp.0", NULL, &mcasp_clk), CLK("da8xx_lcdc.0", "fck", &lcdc_clk), CLK("da830-mmc.0", NULL, &mmcsd0_clk), diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c index 65024af169d3..d3c14da7d216 100644 --- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c @@ -243,10 +243,9 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) save_state = 1; break; case PWRDM_POWER_RET: - if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) { + if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) save_state = 0; - break; - } + break; default: /* * CPUx CSWR is invalid hardware state. Also CPUx OSWR diff --git a/arch/arm/mach-ux500/pm.c b/arch/arm/mach-ux500/pm.c index 8538910db202..a970e7fcba9e 100644 --- a/arch/arm/mach-ux500/pm.c +++ b/arch/arm/mach-ux500/pm.c @@ -134,8 +134,8 @@ bool prcmu_pending_irq(void) */ bool prcmu_is_cpu_in_wfi(int cpu) { - return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : - PRCM_ARM_WFI_STANDBY_WFI0; + return readl(PRCM_ARM_WFI_STANDBY) & + (cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : PRCM_ARM_WFI_STANDBY_WFI0); } /* diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 6f39d03cc27e..0a43143e9ceb 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -59,7 +59,7 @@ void __iomem *zynq_scu_base; static void __init zynq_memory_init(void) { if (!__pa(PAGE_OFFSET)) - memblock_reserve(__pa(PAGE_OFFSET), __pa(swapper_pg_dir)); + memblock_reserve(__pa(PAGE_OFFSET), 0x80000); } static struct platform_device zynq_cpuidle_device = { diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 506c225c66cc..c73f10c1984f 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -610,9 +610,9 @@ static int __init early_abort_handler(unsigned long addr, unsigned int fsr, void __init early_abt_enable(void) { - fsr_info[22].fn = early_abort_handler; + fsr_info[FSR_FS_AEA].fn = early_abort_handler; local_abt_enable(); - fsr_info[22].fn = do_bad; + fsr_info[FSR_FS_AEA].fn = do_bad; } #ifndef CONFIG_ARM_LPAE diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h index 05ec5e0df32d..78830657cab3 100644 --- a/arch/arm/mm/fault.h +++ b/arch/arm/mm/fault.h @@ -11,11 +11,15 @@ #define FSR_FS5_0 (0x3f) #ifdef CONFIG_ARM_LPAE +#define FSR_FS_AEA 17 + static inline int fsr_fs(unsigned int fsr) { return fsr & FSR_FS5_0; } #else +#define FSR_FS_AEA 22 + static inline int fsr_fs(unsigned int fsr) { return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6; diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index fc7ea529f462..52c8c1f642fe 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -239,8 +239,7 @@ static int __init xen_guest_init(void) * for secondary CPUs as they are brought up. * For uniformity we use VCPUOP_register_vcpu_info even on cpu0. */ - xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info), - sizeof(struct vcpu_info)); + xen_vcpu_info = alloc_percpu(struct vcpu_info); if (xen_vcpu_info == NULL) return -ENOMEM; diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e0536945f835..cbfb3f4428e0 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -812,7 +812,7 @@ config SETEND_EMULATION endif config ARM64_SW_TTBR0_PAN - bool "Emulate Priviledged Access Never using TTBR0_EL1 switching" + bool "Emulate Privileged Access Never using TTBR0_EL1 switching" help Enabling this option prevents the kernel from accessing user-space memory directly by pointing TTBR0_EL1 to a reserved diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index a96db6bdaf4b..2b9a966791d8 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -64,13 +64,13 @@ config DEBUG_SET_MODULE_RONX config DEBUG_RODATA bool "Make kernel text and rodata read-only" + default y help If this is set, kernel text and rodata will be made read-only. This is to help catch accidental or malicious attempts to change the - kernel's executable code. Additionally splits rodata from kernel - text so it can be made explicitly non-executable. + kernel's executable code. - If in doubt, say Y + If in doubt, say Y config DEBUG_ALIGN_RODATA depends on DEBUG_RODATA diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 32b44c8c3d18..0031c55360c7 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -438,7 +438,6 @@ CONFIG_USB_CONFIGFS_SERIAL=y CONFIG_USB_CONFIGFS_NCM=y CONFIG_USB_CONFIGFS_ECM=y CONFIG_USB_CONFIGFS_QCRNDIS=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_RMNET_BAM=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y @@ -579,7 +578,6 @@ CONFIG_TIMER_STATS=y CONFIG_IPC_LOGGING=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y -CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y CONFIG_PFK=y CONFIG_SECURITY=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 988f1fbf8ea3..261a49d7944a 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -423,7 +423,6 @@ CONFIG_USB_CONFIGFS_SERIAL=y CONFIG_USB_CONFIGFS_NCM=y CONFIG_USB_CONFIGFS_ECM=y CONFIG_USB_CONFIGFS_QCRNDIS=y -CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_RMNET_BAM=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 811779ba58b2..0f51c3b3e7d6 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -294,6 +294,7 @@ CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_STMVL53L0=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set @@ -609,7 +610,6 @@ CONFIG_TIMER_STATS=y CONFIG_IPC_LOGGING=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y -CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y CONFIG_CORESIGHT=y CONFIG_CORESIGHT_EVENT=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index d3b7ec7c4f2b..43ca4093117e 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -298,6 +298,7 @@ CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y +CONFIG_INPUT_STMVL53L0=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set @@ -677,7 +678,6 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_ARM64_PTDUMP=y CONFIG_PID_IN_CONTEXTIDR=y CONFIG_DEBUG_SET_MODULE_RONX=y -CONFIG_DEBUG_RODATA=y CONFIG_FREE_PAGES_RDONLY=y CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y CONFIG_CORESIGHT=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index b6035abef7c8..ccd653eaec7d 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -282,6 +282,7 @@ CONFIG_WIL6210=m CONFIG_ATH10K=m CONFIG_ATH10K_TARGET_SNOC=m CONFIG_ATH10K_SNOC=y +CONFIG_ATH10K_DEBUG=y CONFIG_CLD_LL_CORE=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_KEYRESET=y @@ -658,7 +659,6 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_ARM64_PTDUMP=y CONFIG_PID_IN_CONTEXTIDR=y CONFIG_DEBUG_SET_MODULE_RONX=y -CONFIG_DEBUG_RODATA=y CONFIG_FREE_PAGES_RDONLY=y CONFIG_CORESIGHT=y CONFIG_CORESIGHT_EVENT=y diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig index 00eb346e0928..fc55008d8c4c 100644 --- a/arch/arm64/configs/ranchu64_defconfig +++ b/arch/arm64/configs/ranchu64_defconfig @@ -50,6 +50,7 @@ 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 diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig index cf073304e0b5..ffb983587c31 100644 --- a/arch/arm64/configs/sdm660-perf_defconfig +++ b/arch/arm64/configs/sdm660-perf_defconfig @@ -471,7 +471,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y -CONFIG_USB_CONFIGFS_F_GSI=y CONFIG_USB_CONFIGFS_F_CDEV=y CONFIG_USB_CONFIGFS_F_QDSS=y CONFIG_MMC=y @@ -623,7 +622,6 @@ CONFIG_TIMER_STATS=y CONFIG_IPC_LOGGING=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y -CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y CONFIG_CORESIGHT=y CONFIG_CORESIGHT_EVENT=y diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig index 46786b90e067..bba52749284a 100644 --- a/arch/arm64/configs/sdm660_defconfig +++ b/arch/arm64/configs/sdm660_defconfig @@ -474,7 +474,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_HID=y CONFIG_USB_CONFIGFS_F_DIAG=y -CONFIG_USB_CONFIGFS_F_GSI=y CONFIG_USB_CONFIGFS_F_CDEV=y CONFIG_USB_CONFIGFS_F_QDSS=y CONFIG_MMC=y @@ -502,6 +501,7 @@ CONFIG_EDAC=y CONFIG_EDAC_MM_EDAC=y CONFIG_EDAC_CORTEX_ARM64=y CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_CE=y +CONFIG_EDAC_CORTEX_ARM64_DBE_IRQ_ONLY=y CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_QPNP=y @@ -688,7 +688,6 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_ARM64_PTDUMP=y CONFIG_PID_IN_CONTEXTIDR=y CONFIG_DEBUG_SET_MODULE_RONX=y -CONFIG_DEBUG_RODATA=y CONFIG_FREE_PAGES_RDONLY=y CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y CONFIG_CORESIGHT=y diff --git a/arch/arm64/crypto/aes-ce-ccm-core.S b/arch/arm64/crypto/aes-ce-ccm-core.S index a2a7fbcacc14..3363560c79b7 100644 --- a/arch/arm64/crypto/aes-ce-ccm-core.S +++ b/arch/arm64/crypto/aes-ce-ccm-core.S @@ -9,6 +9,7 @@ */ #include <linux/linkage.h> +#include <asm/assembler.h> .text .arch armv8-a+crypto @@ -19,7 +20,7 @@ */ ENTRY(ce_aes_ccm_auth_data) ldr w8, [x3] /* leftover from prev round? */ - ld1 {v0.2d}, [x0] /* load mac */ + ld1 {v0.16b}, [x0] /* load mac */ cbz w8, 1f sub w8, w8, #16 eor v1.16b, v1.16b, v1.16b @@ -31,7 +32,7 @@ ENTRY(ce_aes_ccm_auth_data) beq 8f /* out of input? */ cbnz w8, 0b eor v0.16b, v0.16b, v1.16b -1: ld1 {v3.2d}, [x4] /* load first round key */ +1: ld1 {v3.16b}, [x4] /* load first round key */ prfm pldl1strm, [x1] cmp w5, #12 /* which key size? */ add x6, x4, #16 @@ -41,17 +42,17 @@ ENTRY(ce_aes_ccm_auth_data) mov v5.16b, v3.16b b 4f 2: mov v4.16b, v3.16b - ld1 {v5.2d}, [x6], #16 /* load 2nd round key */ + ld1 {v5.16b}, [x6], #16 /* load 2nd round key */ 3: aese v0.16b, v4.16b aesmc v0.16b, v0.16b -4: ld1 {v3.2d}, [x6], #16 /* load next round key */ +4: ld1 {v3.16b}, [x6], #16 /* load next round key */ aese v0.16b, v5.16b aesmc v0.16b, v0.16b -5: ld1 {v4.2d}, [x6], #16 /* load next round key */ +5: ld1 {v4.16b}, [x6], #16 /* load next round key */ subs w7, w7, #3 aese v0.16b, v3.16b aesmc v0.16b, v0.16b - ld1 {v5.2d}, [x6], #16 /* load next round key */ + ld1 {v5.16b}, [x6], #16 /* load next round key */ bpl 3b aese v0.16b, v4.16b subs w2, w2, #16 /* last data? */ @@ -60,7 +61,7 @@ ENTRY(ce_aes_ccm_auth_data) ld1 {v1.16b}, [x1], #16 /* load next input block */ eor v0.16b, v0.16b, v1.16b /* xor with mac */ bne 1b -6: st1 {v0.2d}, [x0] /* store mac */ +6: st1 {v0.16b}, [x0] /* store mac */ beq 10f adds w2, w2, #16 beq 10f @@ -79,7 +80,7 @@ ENTRY(ce_aes_ccm_auth_data) adds w7, w7, #1 bne 9b eor v0.16b, v0.16b, v1.16b - st1 {v0.2d}, [x0] + st1 {v0.16b}, [x0] 10: str w8, [x3] ret ENDPROC(ce_aes_ccm_auth_data) @@ -89,27 +90,27 @@ ENDPROC(ce_aes_ccm_auth_data) * u32 rounds); */ ENTRY(ce_aes_ccm_final) - ld1 {v3.2d}, [x2], #16 /* load first round key */ - ld1 {v0.2d}, [x0] /* load mac */ + ld1 {v3.16b}, [x2], #16 /* load first round key */ + ld1 {v0.16b}, [x0] /* load mac */ cmp w3, #12 /* which key size? */ sub w3, w3, #2 /* modified # of rounds */ - ld1 {v1.2d}, [x1] /* load 1st ctriv */ + ld1 {v1.16b}, [x1] /* load 1st ctriv */ bmi 0f bne 3f mov v5.16b, v3.16b b 2f 0: mov v4.16b, v3.16b -1: ld1 {v5.2d}, [x2], #16 /* load next round key */ +1: ld1 {v5.16b}, [x2], #16 /* load next round key */ aese v0.16b, v4.16b aesmc v0.16b, v0.16b aese v1.16b, v4.16b aesmc v1.16b, v1.16b -2: ld1 {v3.2d}, [x2], #16 /* load next round key */ +2: ld1 {v3.16b}, [x2], #16 /* load next round key */ aese v0.16b, v5.16b aesmc v0.16b, v0.16b aese v1.16b, v5.16b aesmc v1.16b, v1.16b -3: ld1 {v4.2d}, [x2], #16 /* load next round key */ +3: ld1 {v4.16b}, [x2], #16 /* load next round key */ subs w3, w3, #3 aese v0.16b, v3.16b aesmc v0.16b, v0.16b @@ -120,47 +121,47 @@ ENTRY(ce_aes_ccm_final) aese v1.16b, v4.16b /* final round key cancels out */ eor v0.16b, v0.16b, v1.16b /* en-/decrypt the mac */ - st1 {v0.2d}, [x0] /* store result */ + st1 {v0.16b}, [x0] /* store result */ ret ENDPROC(ce_aes_ccm_final) .macro aes_ccm_do_crypt,enc ldr x8, [x6, #8] /* load lower ctr */ - ld1 {v0.2d}, [x5] /* load mac */ - rev x8, x8 /* keep swabbed ctr in reg */ + ld1 {v0.16b}, [x5] /* load mac */ +CPU_LE( rev x8, x8 ) /* keep swabbed ctr in reg */ 0: /* outer loop */ - ld1 {v1.1d}, [x6] /* load upper ctr */ + ld1 {v1.8b}, [x6] /* load upper ctr */ prfm pldl1strm, [x1] add x8, x8, #1 rev x9, x8 cmp w4, #12 /* which key size? */ sub w7, w4, #2 /* get modified # of rounds */ ins v1.d[1], x9 /* no carry in lower ctr */ - ld1 {v3.2d}, [x3] /* load first round key */ + ld1 {v3.16b}, [x3] /* load first round key */ add x10, x3, #16 bmi 1f bne 4f mov v5.16b, v3.16b b 3f 1: mov v4.16b, v3.16b - ld1 {v5.2d}, [x10], #16 /* load 2nd round key */ + ld1 {v5.16b}, [x10], #16 /* load 2nd round key */ 2: /* inner loop: 3 rounds, 2x interleaved */ aese v0.16b, v4.16b aesmc v0.16b, v0.16b aese v1.16b, v4.16b aesmc v1.16b, v1.16b -3: ld1 {v3.2d}, [x10], #16 /* load next round key */ +3: ld1 {v3.16b}, [x10], #16 /* load next round key */ aese v0.16b, v5.16b aesmc v0.16b, v0.16b aese v1.16b, v5.16b aesmc v1.16b, v1.16b -4: ld1 {v4.2d}, [x10], #16 /* load next round key */ +4: ld1 {v4.16b}, [x10], #16 /* load next round key */ subs w7, w7, #3 aese v0.16b, v3.16b aesmc v0.16b, v0.16b aese v1.16b, v3.16b aesmc v1.16b, v1.16b - ld1 {v5.2d}, [x10], #16 /* load next round key */ + ld1 {v5.16b}, [x10], #16 /* load next round key */ bpl 2b aese v0.16b, v4.16b aese v1.16b, v4.16b @@ -177,14 +178,14 @@ ENDPROC(ce_aes_ccm_final) eor v0.16b, v0.16b, v2.16b /* xor mac with pt ^ rk[last] */ st1 {v1.16b}, [x0], #16 /* write output block */ bne 0b - rev x8, x8 - st1 {v0.2d}, [x5] /* store mac */ +CPU_LE( rev x8, x8 ) + st1 {v0.16b}, [x5] /* store mac */ str x8, [x6, #8] /* store lsb end of ctr (BE) */ 5: ret 6: eor v0.16b, v0.16b, v5.16b /* final round mac */ eor v1.16b, v1.16b, v5.16b /* final round enc */ - st1 {v0.2d}, [x5] /* store mac */ + st1 {v0.16b}, [x5] /* store mac */ add w2, w2, #16 /* process partial tail block */ 7: ldrb w9, [x1], #1 /* get 1 byte of input */ umov w6, v1.b[0] /* get top crypted ctr byte */ diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher.c index f7bd9bf0bbb3..50d9fe11d0c8 100644 --- a/arch/arm64/crypto/aes-ce-cipher.c +++ b/arch/arm64/crypto/aes-ce-cipher.c @@ -47,24 +47,24 @@ static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) kernel_neon_begin_partial(4); __asm__(" ld1 {v0.16b}, %[in] ;" - " ld1 {v1.2d}, [%[key]], #16 ;" + " ld1 {v1.16b}, [%[key]], #16 ;" " cmp %w[rounds], #10 ;" " bmi 0f ;" " bne 3f ;" " mov v3.16b, v1.16b ;" " b 2f ;" "0: mov v2.16b, v1.16b ;" - " ld1 {v3.2d}, [%[key]], #16 ;" + " ld1 {v3.16b}, [%[key]], #16 ;" "1: aese v0.16b, v2.16b ;" " aesmc v0.16b, v0.16b ;" - "2: ld1 {v1.2d}, [%[key]], #16 ;" + "2: ld1 {v1.16b}, [%[key]], #16 ;" " aese v0.16b, v3.16b ;" " aesmc v0.16b, v0.16b ;" - "3: ld1 {v2.2d}, [%[key]], #16 ;" + "3: ld1 {v2.16b}, [%[key]], #16 ;" " subs %w[rounds], %w[rounds], #3 ;" " aese v0.16b, v1.16b ;" " aesmc v0.16b, v0.16b ;" - " ld1 {v3.2d}, [%[key]], #16 ;" + " ld1 {v3.16b}, [%[key]], #16 ;" " bpl 1b ;" " aese v0.16b, v2.16b ;" " eor v0.16b, v0.16b, v3.16b ;" @@ -92,24 +92,24 @@ static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) kernel_neon_begin_partial(4); __asm__(" ld1 {v0.16b}, %[in] ;" - " ld1 {v1.2d}, [%[key]], #16 ;" + " ld1 {v1.16b}, [%[key]], #16 ;" " cmp %w[rounds], #10 ;" " bmi 0f ;" " bne 3f ;" " mov v3.16b, v1.16b ;" " b 2f ;" "0: mov v2.16b, v1.16b ;" - " ld1 {v3.2d}, [%[key]], #16 ;" + " ld1 {v3.16b}, [%[key]], #16 ;" "1: aesd v0.16b, v2.16b ;" " aesimc v0.16b, v0.16b ;" - "2: ld1 {v1.2d}, [%[key]], #16 ;" + "2: ld1 {v1.16b}, [%[key]], #16 ;" " aesd v0.16b, v3.16b ;" " aesimc v0.16b, v0.16b ;" - "3: ld1 {v2.2d}, [%[key]], #16 ;" + "3: ld1 {v2.16b}, [%[key]], #16 ;" " subs %w[rounds], %w[rounds], #3 ;" " aesd v0.16b, v1.16b ;" " aesimc v0.16b, v0.16b ;" - " ld1 {v3.2d}, [%[key]], #16 ;" + " ld1 {v3.16b}, [%[key]], #16 ;" " bpl 1b ;" " aesd v0.16b, v2.16b ;" " eor v0.16b, v0.16b, v3.16b ;" @@ -173,7 +173,12 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, u32 *rki = ctx->key_enc + (i * kwords); u32 *rko = rki + kwords; +#ifndef CONFIG_CPU_BIG_ENDIAN rko[0] = ror32(aes_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0]; +#else + rko[0] = rol32(aes_sub(rki[kwords - 1]), 8) ^ (rcon[i] << 24) ^ + rki[0]; +#endif rko[1] = rko[0] ^ rki[1]; rko[2] = rko[1] ^ rki[2]; rko[3] = rko[2] ^ rki[3]; diff --git a/arch/arm64/crypto/aes-ce.S b/arch/arm64/crypto/aes-ce.S index 78f3cfe92c08..b46093d567e5 100644 --- a/arch/arm64/crypto/aes-ce.S +++ b/arch/arm64/crypto/aes-ce.S @@ -10,6 +10,7 @@ */ #include <linux/linkage.h> +#include <asm/assembler.h> #define AES_ENTRY(func) ENTRY(ce_ ## func) #define AES_ENDPROC(func) ENDPROC(ce_ ## func) diff --git a/arch/arm64/crypto/aes-modes.S b/arch/arm64/crypto/aes-modes.S index f6e372c528eb..838dad5c209f 100644 --- a/arch/arm64/crypto/aes-modes.S +++ b/arch/arm64/crypto/aes-modes.S @@ -193,15 +193,16 @@ AES_ENTRY(aes_cbc_encrypt) cbz w6, .Lcbcencloop ld1 {v0.16b}, [x5] /* get iv */ - enc_prepare w3, x2, x5 + enc_prepare w3, x2, x6 .Lcbcencloop: ld1 {v1.16b}, [x1], #16 /* get next pt block */ eor v0.16b, v0.16b, v1.16b /* ..and xor with iv */ - encrypt_block v0, w3, x2, x5, w6 + encrypt_block v0, w3, x2, x6, w7 st1 {v0.16b}, [x0], #16 subs w4, w4, #1 bne .Lcbcencloop + st1 {v0.16b}, [x5] /* return iv */ ret AES_ENDPROC(aes_cbc_encrypt) @@ -211,7 +212,7 @@ AES_ENTRY(aes_cbc_decrypt) cbz w6, .LcbcdecloopNx ld1 {v7.16b}, [x5] /* get iv */ - dec_prepare w3, x2, x5 + dec_prepare w3, x2, x6 .LcbcdecloopNx: #if INTERLEAVE >= 2 @@ -248,7 +249,7 @@ AES_ENTRY(aes_cbc_decrypt) .Lcbcdecloop: ld1 {v1.16b}, [x1], #16 /* get next ct block */ mov v0.16b, v1.16b /* ...and copy to v0 */ - decrypt_block v0, w3, x2, x5, w6 + decrypt_block v0, w3, x2, x6, w7 eor v0.16b, v0.16b, v7.16b /* xor with iv => pt */ mov v7.16b, v1.16b /* ct is next iv */ st1 {v0.16b}, [x0], #16 @@ -256,6 +257,7 @@ AES_ENTRY(aes_cbc_decrypt) bne .Lcbcdecloop .Lcbcdecout: FRAME_POP + st1 {v7.16b}, [x5] /* return iv */ ret AES_ENDPROC(aes_cbc_decrypt) @@ -267,24 +269,15 @@ AES_ENDPROC(aes_cbc_decrypt) AES_ENTRY(aes_ctr_encrypt) FRAME_PUSH - cbnz w6, .Lctrfirst /* 1st time around? */ - umov x5, v4.d[1] /* keep swabbed ctr in reg */ - rev x5, x5 -#if INTERLEAVE >= 2 - cmn w5, w4 /* 32 bit overflow? */ - bcs .Lctrinc - add x5, x5, #1 /* increment BE ctr */ - b .LctrincNx -#else - b .Lctrinc -#endif -.Lctrfirst: + cbz w6, .Lctrnotfirst /* 1st time around? */ enc_prepare w3, x2, x6 ld1 {v4.16b}, [x5] - umov x5, v4.d[1] /* keep swabbed ctr in reg */ - rev x5, x5 + +.Lctrnotfirst: + umov x8, v4.d[1] /* keep swabbed ctr in reg */ + rev x8, x8 #if INTERLEAVE >= 2 - cmn w5, w4 /* 32 bit overflow? */ + cmn w8, w4 /* 32 bit overflow? */ bcs .Lctrloop .LctrloopNx: subs w4, w4, #INTERLEAVE @@ -292,11 +285,11 @@ AES_ENTRY(aes_ctr_encrypt) #if INTERLEAVE == 2 mov v0.8b, v4.8b mov v1.8b, v4.8b - rev x7, x5 - add x5, x5, #1 + rev x7, x8 + add x8, x8, #1 ins v0.d[1], x7 - rev x7, x5 - add x5, x5, #1 + rev x7, x8 + add x8, x8, #1 ins v1.d[1], x7 ld1 {v2.16b-v3.16b}, [x1], #32 /* get 2 input blocks */ do_encrypt_block2x @@ -305,7 +298,7 @@ AES_ENTRY(aes_ctr_encrypt) st1 {v0.16b-v1.16b}, [x0], #32 #else ldr q8, =0x30000000200000001 /* addends 1,2,3[,0] */ - dup v7.4s, w5 + dup v7.4s, w8 mov v0.16b, v4.16b add v7.4s, v7.4s, v8.4s mov v1.16b, v4.16b @@ -323,18 +316,12 @@ AES_ENTRY(aes_ctr_encrypt) eor v2.16b, v7.16b, v2.16b eor v3.16b, v5.16b, v3.16b st1 {v0.16b-v3.16b}, [x0], #64 - add x5, x5, #INTERLEAVE + add x8, x8, #INTERLEAVE #endif - cbz w4, .LctroutNx -.LctrincNx: - rev x7, x5 + rev x7, x8 ins v4.d[1], x7 + cbz w4, .Lctrout b .LctrloopNx -.LctroutNx: - sub x5, x5, #1 - rev x7, x5 - ins v4.d[1], x7 - b .Lctrout .Lctr1x: adds w4, w4, #INTERLEAVE beq .Lctrout @@ -342,30 +329,39 @@ AES_ENTRY(aes_ctr_encrypt) .Lctrloop: mov v0.16b, v4.16b encrypt_block v0, w3, x2, x6, w7 + + adds x8, x8, #1 /* increment BE ctr */ + rev x7, x8 + ins v4.d[1], x7 + bcs .Lctrcarry /* overflow? */ + +.Lctrcarrydone: subs w4, w4, #1 bmi .Lctrhalfblock /* blocks < 0 means 1/2 block */ ld1 {v3.16b}, [x1], #16 eor v3.16b, v0.16b, v3.16b st1 {v3.16b}, [x0], #16 - beq .Lctrout -.Lctrinc: - adds x5, x5, #1 /* increment BE ctr */ - rev x7, x5 - ins v4.d[1], x7 - bcc .Lctrloop /* no overflow? */ - umov x7, v4.d[0] /* load upper word of ctr */ - rev x7, x7 /* ... to handle the carry */ - add x7, x7, #1 - rev x7, x7 - ins v4.d[0], x7 - b .Lctrloop + bne .Lctrloop + +.Lctrout: + st1 {v4.16b}, [x5] /* return next CTR value */ + FRAME_POP + ret + .Lctrhalfblock: ld1 {v3.8b}, [x1] eor v3.8b, v0.8b, v3.8b st1 {v3.8b}, [x0] -.Lctrout: FRAME_POP ret + +.Lctrcarry: + umov x7, v4.d[0] /* load upper word of ctr */ + rev x7, x7 /* ... to handle the carry */ + add x7, x7, #1 + rev x7, x7 + ins v4.d[0], x7 + b .Lctrcarrydone AES_ENDPROC(aes_ctr_encrypt) .ltorg @@ -386,7 +382,8 @@ AES_ENDPROC(aes_ctr_encrypt) .endm .Lxts_mul_x: - .word 1, 0, 0x87, 0 +CPU_LE( .quad 1, 0x87 ) +CPU_BE( .quad 0x87, 1 ) AES_ENTRY(aes_xts_encrypt) FRAME_PUSH diff --git a/arch/arm64/crypto/aes-neon.S b/arch/arm64/crypto/aes-neon.S index b93170e1cc93..85f07ead7c5c 100644 --- a/arch/arm64/crypto/aes-neon.S +++ b/arch/arm64/crypto/aes-neon.S @@ -9,6 +9,7 @@ */ #include <linux/linkage.h> +#include <asm/assembler.h> #define AES_ENTRY(func) ENTRY(neon_ ## func) #define AES_ENDPROC(func) ENDPROC(neon_ ## func) @@ -83,13 +84,13 @@ .endm .macro do_block, enc, in, rounds, rk, rkp, i - ld1 {v15.16b}, [\rk] + ld1 {v15.4s}, [\rk] add \rkp, \rk, #16 mov \i, \rounds 1111: eor \in\().16b, \in\().16b, v15.16b /* ^round key */ tbl \in\().16b, {\in\().16b}, v13.16b /* ShiftRows */ sub_bytes \in - ld1 {v15.16b}, [\rkp], #16 + ld1 {v15.4s}, [\rkp], #16 subs \i, \i, #1 beq 2222f .if \enc == 1 @@ -229,7 +230,7 @@ .endm .macro do_block_2x, enc, in0, in1 rounds, rk, rkp, i - ld1 {v15.16b}, [\rk] + ld1 {v15.4s}, [\rk] add \rkp, \rk, #16 mov \i, \rounds 1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */ @@ -237,7 +238,7 @@ sub_bytes_2x \in0, \in1 tbl \in0\().16b, {\in0\().16b}, v13.16b /* ShiftRows */ tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */ - ld1 {v15.16b}, [\rkp], #16 + ld1 {v15.4s}, [\rkp], #16 subs \i, \i, #1 beq 2222f .if \enc == 1 @@ -254,7 +255,7 @@ .endm .macro do_block_4x, enc, in0, in1, in2, in3, rounds, rk, rkp, i - ld1 {v15.16b}, [\rk] + ld1 {v15.4s}, [\rk] add \rkp, \rk, #16 mov \i, \rounds 1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */ @@ -266,7 +267,7 @@ tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */ tbl \in2\().16b, {\in2\().16b}, v13.16b /* ShiftRows */ tbl \in3\().16b, {\in3\().16b}, v13.16b /* ShiftRows */ - ld1 {v15.16b}, [\rkp], #16 + ld1 {v15.4s}, [\rkp], #16 subs \i, \i, #1 beq 2222f .if \enc == 1 @@ -306,12 +307,16 @@ .text .align 4 .LForward_ShiftRows: - .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3 - .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb +CPU_LE( .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3 ) +CPU_LE( .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb ) +CPU_BE( .byte 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8 ) +CPU_BE( .byte 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5, 0x0 ) .LReverse_ShiftRows: - .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb - .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3 +CPU_LE( .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb ) +CPU_LE( .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3 ) +CPU_BE( .byte 0x3, 0x6, 0x9, 0xc, 0xf, 0x2, 0x5, 0x8 ) +CPU_BE( .byte 0xb, 0xe, 0x1, 0x4, 0x7, 0xa, 0xd, 0x0 ) .LForward_Sbox: .byte 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5 diff --git a/arch/arm64/crypto/ghash-ce-core.S b/arch/arm64/crypto/ghash-ce-core.S index dc457015884e..f0bb9f0b524f 100644 --- a/arch/arm64/crypto/ghash-ce-core.S +++ b/arch/arm64/crypto/ghash-ce-core.S @@ -29,8 +29,8 @@ * struct ghash_key const *k, const char *head) */ ENTRY(pmull_ghash_update) - ld1 {SHASH.16b}, [x3] - ld1 {XL.16b}, [x1] + ld1 {SHASH.2d}, [x3] + ld1 {XL.2d}, [x1] movi MASK.16b, #0xe1 ext SHASH2.16b, SHASH.16b, SHASH.16b, #8 shl MASK.2d, MASK.2d, #57 @@ -74,6 +74,6 @@ CPU_LE( rev64 T1.16b, T1.16b ) cbnz w0, 0b - st1 {XL.16b}, [x1] + st1 {XL.2d}, [x1] ret ENDPROC(pmull_ghash_update) diff --git a/arch/arm64/crypto/sha1-ce-core.S b/arch/arm64/crypto/sha1-ce-core.S index 033aae6d732a..c98e7e849f06 100644 --- a/arch/arm64/crypto/sha1-ce-core.S +++ b/arch/arm64/crypto/sha1-ce-core.S @@ -78,7 +78,7 @@ ENTRY(sha1_ce_transform) ld1r {k3.4s}, [x6] /* load state */ - ldr dga, [x0] + ld1 {dgav.4s}, [x0] ldr dgb, [x0, #16] /* load sha1_ce_state::finalize */ @@ -144,7 +144,7 @@ CPU_LE( rev32 v11.16b, v11.16b ) b 1b /* store new state */ -3: str dga, [x0] +3: st1 {dgav.4s}, [x0] str dgb, [x0, #16] ret ENDPROC(sha1_ce_transform) diff --git a/arch/arm64/crypto/sha2-ce-core.S b/arch/arm64/crypto/sha2-ce-core.S index 5df9d9d470ad..01cfee066837 100644 --- a/arch/arm64/crypto/sha2-ce-core.S +++ b/arch/arm64/crypto/sha2-ce-core.S @@ -85,7 +85,7 @@ ENTRY(sha2_ce_transform) ld1 {v12.4s-v15.4s}, [x8] /* load state */ - ldp dga, dgb, [x0] + ld1 {dgav.4s, dgbv.4s}, [x0] /* load sha256_ce_state::finalize */ ldr w4, [x0, #:lo12:sha256_ce_offsetof_finalize] @@ -148,6 +148,6 @@ CPU_LE( rev32 v19.16b, v19.16b ) b 1b /* store new state */ -3: stp dga, dgb, [x0] +3: st1 {dgav.4s, dgbv.4s}, [x0] ret ENDPROC(sha2_ce_transform) diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h index 8746ff6abd77..55101bd86b98 100644 --- a/arch/arm64/include/asm/alternative.h +++ b/arch/arm64/include/asm/alternative.h @@ -2,6 +2,7 @@ #define __ASM_ALTERNATIVE_H #include <asm/cpufeature.h> +#include <asm/insn.h> #ifndef __ASSEMBLY__ @@ -90,34 +91,55 @@ void apply_alternatives(void *start, size_t length); .endm /* - * Begin an alternative code sequence. + * Alternative sequences + * + * The code for the case where the capability is not present will be + * assembled and linked as normal. There are no restrictions on this + * code. + * + * The code for the case where the capability is present will be + * assembled into a special section to be used for dynamic patching. + * Code for that case must: + * + * 1. Be exactly the same length (in bytes) as the default code + * sequence. * - * The code that follows this macro will be assembled and linked as - * normal. There are no restrictions on this code. + * 2. Not contain a branch target that is used outside of the + * alternative sequence it is defined in (branches into an + * alternative sequence are not fixed up). + */ + +/* + * Begin an alternative code sequence. */ .macro alternative_if_not cap + .set .Lasm_alt_mode, 0 .pushsection .altinstructions, "a" altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f .popsection 661: .endm +.macro alternative_if cap + .set .Lasm_alt_mode, 1 + .pushsection .altinstructions, "a" + altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f + .popsection + .pushsection .altinstr_replacement, "ax" + .align 2 /* So GAS knows label 661 is suitably aligned */ +661: +.endm + /* - * Provide the alternative code sequence. - * - * The code that follows this macro is assembled into a special - * section to be used for dynamic patching. Code that follows this - * macro must: - * - * 1. Be exactly the same length (in bytes) as the default code - * sequence. - * - * 2. Not contain a branch target that is used outside of the - * alternative sequence it is defined in (branches into an - * alternative sequence are not fixed up). + * Provide the other half of the alternative code sequence. */ .macro alternative_else -662: .pushsection .altinstr_replacement, "ax" +662: + .if .Lasm_alt_mode==0 + .pushsection .altinstr_replacement, "ax" + .else + .popsection + .endif 663: .endm @@ -125,11 +147,25 @@ void apply_alternatives(void *start, size_t length); * Complete an alternative code sequence. */ .macro alternative_endif -664: .popsection +664: + .if .Lasm_alt_mode==0 + .popsection + .endif .org . - (664b-663b) + (662b-661b) .org . - (662b-661b) + (664b-663b) .endm +/* + * Provides a trivial alternative or default sequence consisting solely + * of NOPs. The number of NOPs is chosen automatically to match the + * previous case. + */ +.macro alternative_else_nop_endif +alternative_else + nops (662b-661b) / AARCH64_INSN_SIZE +alternative_endif +.endm + #define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \ alternative_insn insn1, insn2, cap, IS_ENABLED(cfg) diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index dfc8416d8896..02e4b7e4bdbf 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -120,6 +120,15 @@ .endm /* + * NOP sequence + */ + .macro nops, num + .rept \num + nop + .endr + .endm + +/* * Emit an entry into the exception table */ .macro _asm_extable, from, to @@ -395,15 +404,11 @@ alternative_endif */ .macro post_ttbr0_update_workaround #ifdef CONFIG_CAVIUM_ERRATUM_27456 -alternative_if_not ARM64_WORKAROUND_CAVIUM_27456 - nop - nop - nop -alternative_else +alternative_if ARM64_WORKAROUND_CAVIUM_27456 ic iallu dsb nsh isb -alternative_endif +alternative_else_nop_endif #endif .endm diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h index 9622eb48f894..c5dbc5cb8f10 100644 --- a/arch/arm64/include/asm/barrier.h +++ b/arch/arm64/include/asm/barrier.h @@ -20,6 +20,9 @@ #ifndef __ASSEMBLY__ +#define __nops(n) ".rept " #n "\nnop\n.endr\n" +#define nops(n) asm volatile(__nops(n)) + #define sev() asm volatile("sev" : : : "memory") #define wfe() asm volatile("wfe" : : : "memory") #define wfi() asm volatile("wfi" : : : "memory") diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h index 71dfa3b42313..85c4a8981d47 100644 --- a/arch/arm64/include/asm/futex.h +++ b/arch/arm64/include/asm/futex.h @@ -21,10 +21,7 @@ #include <linux/futex.h> #include <linux/uaccess.h> -#include <asm/alternative.h> -#include <asm/cpufeature.h> #include <asm/errno.h> -#include <asm/sysreg.h> #define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \ do { \ diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 342a5ac2f3da..320dc9c7e4f4 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -237,8 +237,7 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn, { void *va = page_address(pfn_to_page(pfn)); - if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached) - kvm_flush_dcache_to_poc(va, size); + kvm_flush_dcache_to_poc(va, size); if (!icache_is_aliasing()) { /* PIPT */ flush_icache_range((unsigned long)va, diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 5d5baf8096b9..f926b95928ee 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -223,9 +223,11 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, * Update the saved TTBR0_EL1 of the scheduled-in task as the previous * value may have not been initialised yet (activate_mm caller) or the * ASID has changed since the last run (following the context switch - * of another thread of the same process). + * of another thread of the same process). Avoid setting the reserved + * TTBR0_EL1 to swapper_pg_dir (init_mm; e.g. via idle_task_exit). */ - update_saved_ttbr0(tsk, next); + if (next != &init_mm) + update_saved_ttbr0(tsk, next); } #define deactivate_mm(tsk,mm) do { } while (0) diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index 5eedfd83acc7..1528d52eb8c0 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -21,8 +21,6 @@ #include <uapi/asm/ptrace.h> -#define _PSR_PAN_BIT 22 - /* Current Exception Level values, as contained in CurrentEL */ #define CurrentEL_EL1 (1 << 2) #define CurrentEL_EL2 (2 << 2) diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 4b4292147584..4bb038ec6453 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -47,10 +47,10 @@ typedef unsigned long mm_segment_t; struct thread_info { unsigned long flags; /* low level flags */ mm_segment_t addr_limit; /* address limit */ + struct task_struct *task; /* main task structure */ #ifdef CONFIG_ARM64_SW_TTBR0_PAN u64 ttbr0; /* saved TTBR0_EL1 */ #endif - struct task_struct *task; /* main task structure */ int preempt_count; /* 0 => preemptable, <0 => bug */ int cpu; /* cpu */ }; diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index c37c064d7cdd..efafdf39cb3b 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -18,6 +18,10 @@ #ifndef __ASM_UACCESS_H #define __ASM_UACCESS_H +#include <asm/alternative.h> +#include <asm/kernel-pgtable.h> +#include <asm/sysreg.h> + #ifndef __ASSEMBLY__ /* @@ -26,11 +30,8 @@ #include <linux/string.h> #include <linux/thread_info.h> -#include <asm/alternative.h> #include <asm/cpufeature.h> -#include <asm/kernel-pgtable.h> #include <asm/ptrace.h> -#include <asm/sysreg.h> #include <asm/errno.h> #include <asm/memory.h> #include <asm/compiler.h> @@ -130,7 +131,7 @@ static inline void set_fs(mm_segment_t fs) * User access enabling/disabling. */ #ifdef CONFIG_ARM64_SW_TTBR0_PAN -static inline void uaccess_ttbr0_disable(void) +static inline void __uaccess_ttbr0_disable(void) { unsigned long ttbr; @@ -140,7 +141,7 @@ static inline void uaccess_ttbr0_disable(void) isb(); } -static inline void uaccess_ttbr0_enable(void) +static inline void __uaccess_ttbr0_enable(void) { unsigned long flags; @@ -154,30 +155,44 @@ static inline void uaccess_ttbr0_enable(void) isb(); local_irq_restore(flags); } + +static inline bool uaccess_ttbr0_disable(void) +{ + if (!system_uses_ttbr0_pan()) + return false; + __uaccess_ttbr0_disable(); + return true; +} + +static inline bool uaccess_ttbr0_enable(void) +{ + if (!system_uses_ttbr0_pan()) + return false; + __uaccess_ttbr0_enable(); + return true; +} #else -static inline void uaccess_ttbr0_disable(void) +static inline bool uaccess_ttbr0_disable(void) { + return false; } -static inline void uaccess_ttbr0_enable(void) +static inline bool uaccess_ttbr0_enable(void) { + return false; } #endif #define __uaccess_disable(alt) \ do { \ - if (system_uses_ttbr0_pan()) \ - uaccess_ttbr0_disable(); \ - else \ + if (!uaccess_ttbr0_disable()) \ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), alt, \ CONFIG_ARM64_PAN)); \ } while (0) #define __uaccess_enable(alt) \ do { \ - if (system_uses_ttbr0_pan()) \ - uaccess_ttbr0_enable(); \ - else \ + if (!uaccess_ttbr0_enable()) \ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), alt, \ CONFIG_ARM64_PAN)); \ } while (0) @@ -407,69 +422,62 @@ extern __must_check long strnlen_user(const char __user *str, long n); #else /* __ASSEMBLY__ */ -#include <asm/alternative.h> #include <asm/assembler.h> -#include <asm/kernel-pgtable.h> /* * User access enabling/disabling macros. */ - .macro uaccess_ttbr0_disable, tmp1 +#ifdef CONFIG_ARM64_SW_TTBR0_PAN + .macro __uaccess_ttbr0_disable, tmp1 mrs \tmp1, ttbr1_el1 // swapper_pg_dir add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb .endm - .macro uaccess_ttbr0_enable, tmp1 + .macro __uaccess_ttbr0_enable, tmp1 get_thread_info \tmp1 - ldr \tmp1, [\tmp1, #TI_TTBR0] // load saved TTBR0_EL1 + ldr \tmp1, [\tmp1, #TSK_TI_TTBR0] // load saved TTBR0_EL1 msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1 isb .endm + .macro uaccess_ttbr0_disable, tmp1 +alternative_if_not ARM64_HAS_PAN + __uaccess_ttbr0_disable \tmp1 +alternative_else_nop_endif + .endm + + .macro uaccess_ttbr0_enable, tmp1, tmp2 +alternative_if_not ARM64_HAS_PAN + save_and_disable_irq \tmp2 // avoid preemption + __uaccess_ttbr0_enable \tmp1 + restore_irq \tmp2 +alternative_else_nop_endif + .endm +#else + .macro uaccess_ttbr0_disable, tmp1 + .endm + + .macro uaccess_ttbr0_enable, tmp1, tmp2 + .endm +#endif + /* * These macros are no-ops when UAO is present. */ .macro uaccess_disable_not_uao, tmp1 -#ifdef CONFIG_ARM64_SW_TTBR0_PAN -alternative_if_not ARM64_HAS_PAN uaccess_ttbr0_disable \tmp1 -alternative_else - nop - nop - nop - nop -alternative_endif -#endif -alternative_if_not ARM64_ALT_PAN_NOT_UAO - nop -alternative_else +alternative_if ARM64_ALT_PAN_NOT_UAO SET_PSTATE_PAN(1) -alternative_endif +alternative_else_nop_endif .endm .macro uaccess_enable_not_uao, tmp1, tmp2 -#ifdef CONFIG_ARM64_SW_TTBR0_PAN -alternative_if_not ARM64_HAS_PAN - save_and_disable_irq \tmp2 // avoid preemption - uaccess_ttbr0_enable \tmp1 - restore_irq \tmp2 -alternative_else - nop - nop - nop - nop - nop - nop - nop -alternative_endif -#endif -alternative_if_not ARM64_ALT_PAN_NOT_UAO - nop -alternative_else + uaccess_ttbr0_enable \tmp1, \tmp2 +alternative_if ARM64_ALT_PAN_NOT_UAO SET_PSTATE_PAN(0) -alternative_endif +alternative_else_nop_endif .endm #endif /* __ASSEMBLY__ */ diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h index b5c3933ed441..d1ff83dfe5de 100644 --- a/arch/arm64/include/uapi/asm/ptrace.h +++ b/arch/arm64/include/uapi/asm/ptrace.h @@ -77,6 +77,7 @@ struct user_fpsimd_state { __uint128_t vregs[32]; __u32 fpsr; __u32 fpcr; + __u32 __reserved[2]; }; struct user_hwdebug_state { diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index a0a0f2b20608..884b317e56c3 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/sysctl.h> -#include <asm/alternative.h> #include <asm/cpufeature.h> #include <asm/insn.h> #include <asm/opcodes.h> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index dac70c160289..c9ea87198789 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -38,11 +38,11 @@ int main(void) DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); -#ifdef CONFIG_ARM64_SW_TTBR0_PAN - DEFINE(TI_TTBR0, offsetof(struct thread_info, ttbr0)); -#endif DEFINE(TI_TASK, offsetof(struct thread_info, task)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); +#ifdef CONFIG_ARM64_SW_TTBR0_PAN + DEFINE(TSK_TI_TTBR0, offsetof(struct thread_info, ttbr0)); +#endif BLANK(); DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context)); BLANK(); diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index da99a728a5f9..191e3136fa6e 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -121,11 +121,9 @@ * feature as all TTBR0_EL1 accesses are disabled, not just those to * user mappings. */ -alternative_if_not ARM64_HAS_PAN - nop -alternative_else +alternative_if ARM64_HAS_PAN b 1f // skip TTBR0 PAN -alternative_endif +alternative_else_nop_endif .if \el != 0 mrs x21, ttbr0_el1 @@ -135,7 +133,7 @@ alternative_endif and x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR .endif - uaccess_ttbr0_disable x21 + __uaccess_ttbr0_disable x21 1: #endif @@ -184,17 +182,15 @@ alternative_endif * Restore access to TTBR0_EL1. If returning to EL0, no need for SPSR * PAN bit checking. */ -alternative_if_not ARM64_HAS_PAN - nop -alternative_else +alternative_if ARM64_HAS_PAN b 2f // skip TTBR0 PAN -alternative_endif +alternative_else_nop_endif .if \el != 0 - tbnz x22, #_PSR_PAN_BIT, 1f // Skip re-enabling TTBR0 access if previously disabled + tbnz x22, #22, 1f // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set .endif - uaccess_ttbr0_enable x0 + __uaccess_ttbr0_enable x0 .if \el == 0 /* @@ -687,7 +683,7 @@ el0_inv: mov x0, sp mov x1, #BAD_SYNC mov x2, x25 - bl bad_mode + bl bad_el0_sync b ret_to_user ENDPROC(el0_sync) diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 8cfd5ab37743..a1c2ac38771d 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -664,7 +664,7 @@ ENDPROC(__secondary_switched) */ .section ".idmap.text", "ax" ENTRY(__enable_mmu) - mrs x18, sctlr_el1 // preserve old SCTLR_EL1 value + mrs x22, sctlr_el1 // preserve old SCTLR_EL1 value mrs x1, ID_AA64MMFR0_EL1 ubfx x2, x1, #ID_AA64MMFR0_TGRAN_SHIFT, 4 cmp x2, #ID_AA64MMFR0_TGRAN_SUPPORTED @@ -691,7 +691,7 @@ ENTRY(__enable_mmu) * to take into account by discarding the current kernel mapping and * creating a new one. */ - msr sctlr_el1, x18 // disable the MMU + msr sctlr_el1, x22 // disable the MMU isb bl __create_page_tables // recreate kernel mapping diff --git a/arch/arm64/kernel/kaslr.c b/arch/arm64/kernel/kaslr.c index b05469173ba5..310f2f463cd4 100644 --- a/arch/arm64/kernel/kaslr.c +++ b/arch/arm64/kernel/kaslr.c @@ -130,11 +130,15 @@ u64 __init kaslr_early_init(u64 dt_phys, u64 modulo_offset) /* * The kernel Image should not extend across a 1GB/32MB/512MB alignment * boundary (for 4KB/16KB/64KB granule kernels, respectively). If this - * happens, increase the KASLR offset by the size of the kernel image. + * happens, increase the KASLR offset by the size of the kernel image + * rounded up by SWAPPER_BLOCK_SIZE. */ if ((((u64)_text + offset + modulo_offset) >> SWAPPER_TABLE_SHIFT) != - (((u64)_end + offset + modulo_offset) >> SWAPPER_TABLE_SHIFT)) - offset = (offset + (u64)(_end - _text)) & mask; + (((u64)_end + offset + modulo_offset) >> SWAPPER_TABLE_SHIFT)) { + u64 kimg_sz = _end - _text; + offset = (offset + round_up(kimg_sz, SWAPPER_BLOCK_SIZE)) + & mask; + } if (IS_ENABLED(CONFIG_KASAN)) /* diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index df4707380e66..c5ef05959813 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -550,6 +550,8 @@ static int hw_break_set(struct task_struct *target, /* (address, ctrl) registers */ limit = regset->n * regset->size; while (count && offset < limit) { + if (count < PTRACE_HBP_ADDR_SZ) + return -EINVAL; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr, offset, offset + PTRACE_HBP_ADDR_SZ); if (ret) @@ -559,6 +561,8 @@ static int hw_break_set(struct task_struct *target, return ret; offset += PTRACE_HBP_ADDR_SZ; + if (!count) + break; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, offset, offset + PTRACE_HBP_CTRL_SZ); if (ret) @@ -595,7 +599,7 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, const void *kbuf, const void __user *ubuf) { int ret; - struct user_pt_regs newregs; + struct user_pt_regs newregs = task_pt_regs(target)->user_regs; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1); if (ret) @@ -625,7 +629,8 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, const void *kbuf, const void __user *ubuf) { int ret; - struct user_fpsimd_state newstate; + struct user_fpsimd_state newstate = + target->thread.fpsimd_state.user_fpsimd; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1); if (ret) @@ -649,7 +654,7 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset, const void *kbuf, const void __user *ubuf) { int ret; - unsigned long tls; + unsigned long tls = target->thread.tp_value; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); if (ret) @@ -675,7 +680,8 @@ static int system_call_set(struct task_struct *target, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { - int syscallno, ret; + int syscallno = task_pt_regs(target)->syscallno; + int ret; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1); if (ret) @@ -947,7 +953,7 @@ static int compat_tls_set(struct task_struct *target, const void __user *ubuf) { int ret; - compat_ulong_t tls; + compat_ulong_t tls = target->thread.tp_value; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); if (ret) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index d621d1de0ac2..f4ab8bc661da 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -509,16 +509,39 @@ const char *esr_get_class_string(u32 esr) } /* - * bad_mode handles the impossible case in the exception vector. + * bad_mode handles the impossible case in the exception vector. This is always + * fatal. */ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr) { - siginfo_t info; - void __user *pc = (void __user *)instruction_pointer(regs); console_verbose(); pr_crit("Bad mode in %s handler detected, code 0x%08x -- %s\n", handler[reason], esr, esr_get_class_string(esr)); + + if (esr >> ESR_ELx_EC_SHIFT == ESR_ELx_EC_SERROR) { + pr_crit("System error detected. ESR.ISS = %08x\n", + esr & 0xffffff); + arm64_check_cache_ecc(NULL); + } + + die("Oops - bad mode", regs, 0); + local_irq_disable(); + panic("bad mode"); +} + +/* + * bad_el0_sync handles unexpected, but potentially recoverable synchronous + * exceptions taken from EL0. Unlike bad_mode, this returns. + */ +asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr) +{ + siginfo_t info; + void __user *pc = (void __user *)instruction_pointer(regs); + console_verbose(); + + pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x -- %s\n", + smp_processor_id(), esr, esr_get_class_string(esr)); __show_regs(regs); info.si_signo = SIGILL; @@ -526,13 +549,10 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr) info.si_code = ILL_ILLOPC; info.si_addr = pc; - if (esr >> ESR_ELx_EC_SHIFT == ESR_ELx_EC_SERROR) { - pr_crit("System error detected. ESR.ISS = %08x\n", - esr & 0xffffff); - arm64_check_cache_ecc(NULL); - } + current->thread.fault_address = 0; + current->thread.fault_code = 0; - arm64_notify_die("Oops - bad mode", regs, &info, 0); + force_sig_info(info.si_signo, &info, current); } void __pte_error(const char *file, int line, unsigned long val) diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S index 08b5f18ba604..d7150e30438a 100644 --- a/arch/arm64/lib/clear_user.S +++ b/arch/arm64/lib/clear_user.S @@ -17,9 +17,6 @@ */ #include <linux/linkage.h> -#include <asm/assembler.h> -#include <asm/cpufeature.h> -#include <asm/sysreg.h> #include <asm/uaccess.h> .text diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 6505ec81f1da..90154f3f7f2a 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -16,10 +16,7 @@ #include <linux/linkage.h> -#include <asm/assembler.h> #include <asm/cache.h> -#include <asm/cpufeature.h> -#include <asm/sysreg.h> #include <asm/uaccess.h> /* diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S index 9b04ff3ab610..718b1c4e2f85 100644 --- a/arch/arm64/lib/copy_in_user.S +++ b/arch/arm64/lib/copy_in_user.S @@ -18,10 +18,7 @@ #include <linux/linkage.h> -#include <asm/assembler.h> #include <asm/cache.h> -#include <asm/cpufeature.h> -#include <asm/sysreg.h> #include <asm/uaccess.h> /* diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index 8077e4f34d56..e99e31c9acac 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -16,10 +16,7 @@ #include <linux/linkage.h> -#include <asm/assembler.h> #include <asm/cache.h> -#include <asm/cpufeature.h> -#include <asm/sysreg.h> #include <asm/uaccess.h> /* diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 6c2401aeb5ce..74187261cb26 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -23,6 +23,7 @@ #include <asm/assembler.h> #include <asm/cpufeature.h> #include <asm/alternative.h> +#include <asm/uaccess.h> /* * __flush_dcache_all() @@ -121,6 +122,7 @@ ENTRY(flush_icache_range) * - end - virtual end address of region */ ENTRY(__flush_cache_user_range) + uaccess_ttbr0_enable x2, x3 dcache_line_size x2, x3 sub x3, x2, #1 bic x4, x0, x3 @@ -142,10 +144,12 @@ USER(9f, ic ivau, x4 ) // invalidate I line PoU dsb ish isb mov x0, #0 +1: + uaccess_ttbr0_disable x1 ret 9: mov x0, #-EFAULT - ret + b 1b ENDPROC(flush_icache_range) ENDPROC(__flush_cache_user_range) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index aee116e842e2..a13b9a65322f 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -528,10 +528,10 @@ static const struct fault_info { { do_bad, SIGBUS, 0, "unknown 17" }, { do_bad, SIGBUS, 0, "unknown 18" }, { do_bad, SIGBUS, 0, "unknown 19" }, - { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" }, - { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" }, - { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" }, - { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, { do_bad, SIGBUS, 0, "synchronous parity error" }, { do_bad, SIGBUS, 0, "unknown 25" }, { do_bad, SIGBUS, 0, "unknown 26" }, diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index 6d6e4af1a4bf..b96db5dafec4 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -90,7 +90,6 @@ ENTRY(privcmd_call) mov x2, x3 mov x3, x4 mov x4, x5 -#ifdef CONFIG_ARM64_SW_TTBR0_PAN /* * Privcmd calls are issued by the userspace. The kernel needs to * enable access to TTBR0_EL1 as the hypervisor would issue stage 1 @@ -99,15 +98,12 @@ ENTRY(privcmd_call) * need the explicit uaccess_enable/disable if the TTBR0 PAN emulation * is enabled (it implies that hardware UAO and PAN disabled). */ - uaccess_enable_not_uao x6, x7 -#endif + uaccess_ttbr0_enable x6, x7 hvc XEN_IMM -#ifdef CONFIG_ARM64_SW_TTBR0_PAN /* * Disable userspace access from kernel once the hyp call completed. */ - uaccess_disable_not_uao x6 -#endif + uaccess_ttbr0_disable x6 ret ENDPROC(privcmd_call); diff --git a/arch/cris/boot/rescue/Makefile b/arch/cris/boot/rescue/Makefile index 52bd0bd1dd22..d98edbb30a18 100644 --- a/arch/cris/boot/rescue/Makefile +++ b/arch/cris/boot/rescue/Makefile @@ -10,6 +10,9 @@ asflags-y += $(LINUXINCLUDE) ccflags-y += -O2 $(LINUXINCLUDE) + +ifdef CONFIG_ETRAX_AXISFLASHMAP + arch-$(CONFIG_ETRAX_ARCH_V10) = v10 arch-$(CONFIG_ETRAX_ARCH_V32) = v32 @@ -28,6 +31,11 @@ $(obj)/rescue.bin: $(obj)/rescue.o FORCE $(call if_changed,objcopy) cp -p $(obj)/rescue.bin $(objtree) +else +$(obj)/rescue.bin: + +endif + $(obj)/testrescue.bin: $(obj)/testrescue.o $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin # Pad it to 784 bytes diff --git a/arch/m68k/include/asm/delay.h b/arch/m68k/include/asm/delay.h index d28fa8fe26fe..c598d847d56b 100644 --- a/arch/m68k/include/asm/delay.h +++ b/arch/m68k/include/asm/delay.h @@ -114,6 +114,6 @@ static inline void __udelay(unsigned long usecs) */ #define HZSCALE (268435456 / (1000000 / HZ)) -#define ndelay(n) __delay(DIV_ROUND_UP((n) * ((((HZSCALE) >> 11) * (loops_per_jiffy >> 11)) >> 6), 1000)); +#define ndelay(n) __delay(DIV_ROUND_UP((n) * ((((HZSCALE) >> 11) * (loops_per_jiffy >> 11)) >> 6), 1000)) #endif /* defined(_M68K_DELAY_H) */ diff --git a/arch/mips/bcm47xx/buttons.c b/arch/mips/bcm47xx/buttons.c index 52caa75bfe4e..e2f50d690624 100644 --- a/arch/mips/bcm47xx/buttons.c +++ b/arch/mips/bcm47xx/buttons.c @@ -17,6 +17,12 @@ .active_low = 1, \ } +#define BCM47XX_GPIO_KEY_H(_gpio, _code) \ + { \ + .code = _code, \ + .gpio = _gpio, \ + } + /* Asus */ static const struct gpio_keys_button @@ -79,8 +85,8 @@ bcm47xx_buttons_asus_wl500gpv2[] __initconst = { static const struct gpio_keys_button bcm47xx_buttons_asus_wl500w[] __initconst = { - BCM47XX_GPIO_KEY(6, KEY_RESTART), - BCM47XX_GPIO_KEY(7, KEY_WPS_BUTTON), + BCM47XX_GPIO_KEY_H(6, KEY_RESTART), + BCM47XX_GPIO_KEY_H(7, KEY_WPS_BUTTON), }; static const struct gpio_keys_button diff --git a/arch/mips/cavium-octeon/octeon-memcpy.S b/arch/mips/cavium-octeon/octeon-memcpy.S index 64e08df51d65..8b7004132491 100644 --- a/arch/mips/cavium-octeon/octeon-memcpy.S +++ b/arch/mips/cavium-octeon/octeon-memcpy.S @@ -208,18 +208,18 @@ EXC( STORE t2, UNIT(6)(dst), s_exc_p10u) ADD src, src, 16*NBYTES EXC( STORE t3, UNIT(7)(dst), s_exc_p9u) ADD dst, dst, 16*NBYTES -EXC( LOAD t0, UNIT(-8)(src), l_exc_copy) -EXC( LOAD t1, UNIT(-7)(src), l_exc_copy) -EXC( LOAD t2, UNIT(-6)(src), l_exc_copy) -EXC( LOAD t3, UNIT(-5)(src), l_exc_copy) +EXC( LOAD t0, UNIT(-8)(src), l_exc_copy_rewind16) +EXC( LOAD t1, UNIT(-7)(src), l_exc_copy_rewind16) +EXC( LOAD t2, UNIT(-6)(src), l_exc_copy_rewind16) +EXC( LOAD t3, UNIT(-5)(src), l_exc_copy_rewind16) EXC( STORE t0, UNIT(-8)(dst), s_exc_p8u) EXC( STORE t1, UNIT(-7)(dst), s_exc_p7u) EXC( STORE t2, UNIT(-6)(dst), s_exc_p6u) EXC( STORE t3, UNIT(-5)(dst), s_exc_p5u) -EXC( LOAD t0, UNIT(-4)(src), l_exc_copy) -EXC( LOAD t1, UNIT(-3)(src), l_exc_copy) -EXC( LOAD t2, UNIT(-2)(src), l_exc_copy) -EXC( LOAD t3, UNIT(-1)(src), l_exc_copy) +EXC( LOAD t0, UNIT(-4)(src), l_exc_copy_rewind16) +EXC( LOAD t1, UNIT(-3)(src), l_exc_copy_rewind16) +EXC( LOAD t2, UNIT(-2)(src), l_exc_copy_rewind16) +EXC( LOAD t3, UNIT(-1)(src), l_exc_copy_rewind16) EXC( STORE t0, UNIT(-4)(dst), s_exc_p4u) EXC( STORE t1, UNIT(-3)(dst), s_exc_p3u) EXC( STORE t2, UNIT(-2)(dst), s_exc_p2u) @@ -383,6 +383,10 @@ done: nop END(memcpy) +l_exc_copy_rewind16: + /* Rewind src and dst by 16*NBYTES for l_exc_copy */ + SUB src, src, 16*NBYTES + SUB dst, dst, 16*NBYTES l_exc_copy: /* * Copy bytes from src until faulting load address (or until a diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index 57ed466e00db..2f140d75d01c 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -68,8 +68,8 @@ CONFIG_NETFILTER_NETLINK_QUEUE=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=m -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig index 48e16d98b2cc..b15508447366 100644 --- a/arch/mips/configs/ip27_defconfig +++ b/arch/mips/configs/ip27_defconfig @@ -134,7 +134,7 @@ CONFIG_LIBFC=m CONFIG_SCSI_QLOGIC_1280=y CONFIG_SCSI_PMCRAID=m CONFIG_SCSI_BFA_FC=m -CONFIG_SCSI_DH=m +CONFIG_SCSI_DH=y CONFIG_SCSI_DH_RDAC=m CONFIG_SCSI_DH_HP_SW=m CONFIG_SCSI_DH_EMC=m @@ -206,7 +206,6 @@ CONFIG_MLX4_EN=m # CONFIG_MLX4_DEBUG is not set CONFIG_TEHUTI=m CONFIG_BNX2X=m -CONFIG_QLGE=m CONFIG_SFC=m CONFIG_BE2NET=m CONFIG_LIBERTAS_THINFIRM=m diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig index 004cf52d1b7d..c24b87819ccb 100644 --- a/arch/mips/configs/lemote2f_defconfig +++ b/arch/mips/configs/lemote2f_defconfig @@ -39,7 +39,7 @@ CONFIG_HIBERNATION=y CONFIG_PM_STD_PARTITION="/dev/hda3" CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_DEBUG=y -CONFIG_CPU_FREQ_STAT=m +CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_POWERSAVE=m diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig index 5afb4840aec7..739ccd0dca64 100644 --- a/arch/mips/configs/malta_defconfig +++ b/arch/mips/configs/malta_defconfig @@ -59,8 +59,8 @@ CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=m -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/configs/malta_kvm_defconfig b/arch/mips/configs/malta_kvm_defconfig index 98f13879bb8f..47f4ecf125ba 100644 --- a/arch/mips/configs/malta_kvm_defconfig +++ b/arch/mips/configs/malta_kvm_defconfig @@ -60,8 +60,8 @@ CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=m -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/configs/malta_kvm_guest_defconfig b/arch/mips/configs/malta_kvm_guest_defconfig index 3b5d5913f548..e79d325aa085 100644 --- a/arch/mips/configs/malta_kvm_guest_defconfig +++ b/arch/mips/configs/malta_kvm_guest_defconfig @@ -59,8 +59,8 @@ CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=m -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/configs/maltaup_xpa_defconfig b/arch/mips/configs/maltaup_xpa_defconfig index 732215732751..ae87ad86243b 100644 --- a/arch/mips/configs/maltaup_xpa_defconfig +++ b/arch/mips/configs/maltaup_xpa_defconfig @@ -61,8 +61,8 @@ CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=m -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/configs/nlm_xlp_defconfig b/arch/mips/configs/nlm_xlp_defconfig index b3d1d37f85ea..47492fee2952 100644 --- a/arch/mips/configs/nlm_xlp_defconfig +++ b/arch/mips/configs/nlm_xlp_defconfig @@ -111,7 +111,7 @@ CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/configs/nlm_xlr_defconfig b/arch/mips/configs/nlm_xlr_defconfig index 3d8016d6cf3e..472a818f1eb8 100644 --- a/arch/mips/configs/nlm_xlr_defconfig +++ b/arch/mips/configs/nlm_xlr_defconfig @@ -91,7 +91,7 @@ CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S index 8c6f508e59de..554d1da97743 100644 --- a/arch/mips/dec/int-handler.S +++ b/arch/mips/dec/int-handler.S @@ -146,7 +146,25 @@ /* * Find irq with highest priority */ - PTR_LA t1,cpu_mask_nr_tbl + # open coded PTR_LA t1, cpu_mask_nr_tbl +#if (_MIPS_SZPTR == 32) + # open coded la t1, cpu_mask_nr_tbl + lui t1, %hi(cpu_mask_nr_tbl) + addiu t1, %lo(cpu_mask_nr_tbl) + +#endif +#if (_MIPS_SZPTR == 64) + # open coded dla t1, cpu_mask_nr_tbl + .set push + .set noat + lui t1, %highest(cpu_mask_nr_tbl) + lui AT, %hi(cpu_mask_nr_tbl) + daddiu t1, t1, %higher(cpu_mask_nr_tbl) + daddiu AT, AT, %lo(cpu_mask_nr_tbl) + dsll t1, 32 + daddu t1, t1, AT + .set pop +#endif 1: lw t2,(t1) nop and t2,t0 @@ -195,7 +213,25 @@ /* * Find irq with highest priority */ - PTR_LA t1,asic_mask_nr_tbl + # open coded PTR_LA t1,asic_mask_nr_tbl +#if (_MIPS_SZPTR == 32) + # open coded la t1, asic_mask_nr_tbl + lui t1, %hi(asic_mask_nr_tbl) + addiu t1, %lo(asic_mask_nr_tbl) + +#endif +#if (_MIPS_SZPTR == 64) + # open coded dla t1, asic_mask_nr_tbl + .set push + .set noat + lui t1, %highest(asic_mask_nr_tbl) + lui AT, %hi(asic_mask_nr_tbl) + daddiu t1, t1, %higher(asic_mask_nr_tbl) + daddiu AT, AT, %lo(asic_mask_nr_tbl) + dsll t1, 32 + daddu t1, t1, AT + .set pop +#endif 2: lw t2,(t1) nop and t2,t0 diff --git a/arch/mips/include/asm/checksum.h b/arch/mips/include/asm/checksum.h index 3ceacde5eb6e..17f89f9670b2 100644 --- a/arch/mips/include/asm/checksum.h +++ b/arch/mips/include/asm/checksum.h @@ -186,7 +186,9 @@ static inline __wsum csum_tcpudp_nofold(__be32 saddr, " daddu %0, %4 \n" " dsll32 $1, %0, 0 \n" " daddu %0, $1 \n" + " sltu $1, %0, $1 \n" " dsra32 %0, %0, 0 \n" + " addu %0, $1 \n" #endif " .set pop" : "=r" (sum) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 44a6f25e902e..fc537d1b649d 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -191,11 +191,9 @@ struct mips_frame_info { #define J_TARGET(pc,target) \ (((unsigned long)(pc) & 0xf0000000) | ((target) << 2)) -static inline int is_ra_save_ins(union mips_instruction *ip) +static inline int is_ra_save_ins(union mips_instruction *ip, int *poff) { #ifdef CONFIG_CPU_MICROMIPS - union mips_instruction mmi; - /* * swsp ra,offset * swm16 reglist,offset(sp) @@ -205,29 +203,71 @@ static inline int is_ra_save_ins(union mips_instruction *ip) * * microMIPS is way more fun... */ - if (mm_insn_16bit(ip->halfword[0])) { - mmi.word = (ip->halfword[0] << 16); - return (mmi.mm16_r5_format.opcode == mm_swsp16_op && - mmi.mm16_r5_format.rt == 31) || - (mmi.mm16_m_format.opcode == mm_pool16c_op && - mmi.mm16_m_format.func == mm_swm16_op); + if (mm_insn_16bit(ip->halfword[1])) { + switch (ip->mm16_r5_format.opcode) { + case mm_swsp16_op: + if (ip->mm16_r5_format.rt != 31) + return 0; + + *poff = ip->mm16_r5_format.simmediate; + *poff = (*poff << 2) / sizeof(ulong); + return 1; + + case mm_pool16c_op: + switch (ip->mm16_m_format.func) { + case mm_swm16_op: + *poff = ip->mm16_m_format.imm; + *poff += 1 + ip->mm16_m_format.rlist; + *poff = (*poff << 2) / sizeof(ulong); + return 1; + + default: + return 0; + } + + default: + return 0; + } } - else { - mmi.halfword[0] = ip->halfword[1]; - mmi.halfword[1] = ip->halfword[0]; - return (mmi.mm_m_format.opcode == mm_pool32b_op && - mmi.mm_m_format.rd > 9 && - mmi.mm_m_format.base == 29 && - mmi.mm_m_format.func == mm_swm32_func) || - (mmi.i_format.opcode == mm_sw32_op && - mmi.i_format.rs == 29 && - mmi.i_format.rt == 31); + + switch (ip->i_format.opcode) { + case mm_sw32_op: + if (ip->i_format.rs != 29) + return 0; + if (ip->i_format.rt != 31) + return 0; + + *poff = ip->i_format.simmediate / sizeof(ulong); + return 1; + + case mm_pool32b_op: + switch (ip->mm_m_format.func) { + case mm_swm32_func: + if (ip->mm_m_format.rd < 0x10) + return 0; + if (ip->mm_m_format.base != 29) + return 0; + + *poff = ip->mm_m_format.simmediate; + *poff += (ip->mm_m_format.rd & 0xf) * sizeof(u32); + *poff /= sizeof(ulong); + return 1; + default: + return 0; + } + + default: + return 0; } #else /* sw / sd $ra, offset($sp) */ - return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && - ip->i_format.rs == 29 && - ip->i_format.rt == 31; + if ((ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && + ip->i_format.rs == 29 && ip->i_format.rt == 31) { + *poff = ip->i_format.simmediate / sizeof(ulong); + return 1; + } + + return 0; #endif } @@ -242,13 +282,16 @@ static inline int is_jump_ins(union mips_instruction *ip) * * microMIPS is kind of more fun... */ - union mips_instruction mmi; - - mmi.word = (ip->halfword[0] << 16); + if (mm_insn_16bit(ip->halfword[1])) { + if ((ip->mm16_r5_format.opcode == mm_pool16c_op && + (ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op)) + return 1; + return 0; + } - if ((mmi.mm16_r5_format.opcode == mm_pool16c_op && - (mmi.mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) || - ip->j_format.opcode == mm_jal32_op) + if (ip->j_format.opcode == mm_j32_op) + return 1; + if (ip->j_format.opcode == mm_jal32_op) return 1; if (ip->r_format.opcode != mm_pool32a_op || ip->r_format.func != mm_pool32axf_op) @@ -276,15 +319,13 @@ static inline int is_sp_move_ins(union mips_instruction *ip) * * microMIPS is not more fun... */ - if (mm_insn_16bit(ip->halfword[0])) { - union mips_instruction mmi; - - mmi.word = (ip->halfword[0] << 16); - return (mmi.mm16_r3_format.opcode == mm_pool16d_op && - mmi.mm16_r3_format.simmediate && mm_addiusp_func) || - (mmi.mm16_r5_format.opcode == mm_pool16d_op && - mmi.mm16_r5_format.rt == 29); + if (mm_insn_16bit(ip->halfword[1])) { + return (ip->mm16_r3_format.opcode == mm_pool16d_op && + ip->mm16_r3_format.simmediate && mm_addiusp_func) || + (ip->mm16_r5_format.opcode == mm_pool16d_op && + ip->mm16_r5_format.rt == 29); } + return ip->mm_i_format.opcode == mm_addiu32_op && ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29; #else @@ -299,30 +340,36 @@ static inline int is_sp_move_ins(union mips_instruction *ip) static int get_frame_info(struct mips_frame_info *info) { -#ifdef CONFIG_CPU_MICROMIPS - union mips_instruction *ip = (void *) (((char *) info->func) - 1); -#else - union mips_instruction *ip = info->func; -#endif - unsigned max_insns = info->func_size / sizeof(union mips_instruction); - unsigned i; + bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS); + union mips_instruction insn, *ip, *ip_end; + const unsigned int max_insns = 128; + unsigned int i; info->pc_offset = -1; info->frame_size = 0; + ip = (void *)msk_isa16_mode((ulong)info->func); if (!ip) goto err; - if (max_insns == 0) - max_insns = 128U; /* unknown function size */ - max_insns = min(128U, max_insns); + ip_end = (void *)ip + info->func_size; - for (i = 0; i < max_insns; i++, ip++) { + for (i = 0; i < max_insns && ip < ip_end; i++, ip++) { + if (is_mmips && mm_insn_16bit(ip->halfword[0])) { + insn.halfword[0] = 0; + insn.halfword[1] = ip->halfword[0]; + } else if (is_mmips) { + insn.halfword[0] = ip->halfword[1]; + insn.halfword[1] = ip->halfword[0]; + } else { + insn.word = ip->word; + } - if (is_jump_ins(ip)) + if (is_jump_ins(&insn)) break; + if (!info->frame_size) { - if (is_sp_move_ins(ip)) + if (is_sp_move_ins(&insn)) { #ifdef CONFIG_CPU_MICROMIPS if (mm_insn_16bit(ip->halfword[0])) @@ -345,11 +392,9 @@ static int get_frame_info(struct mips_frame_info *info) } continue; } - if (info->pc_offset == -1 && is_ra_save_ins(ip)) { - info->pc_offset = - ip->i_format.simmediate / sizeof(long); + if (info->pc_offset == -1 && + is_ra_save_ins(&insn, &info->pc_offset)) break; - } } if (info->frame_size && info->pc_offset >= 0) /* nested */ return 0; diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index e86b7499921a..a017b23ee4aa 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -324,8 +324,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) #endif /* Invalidate the icache for these ranges */ - local_flush_icache_range((unsigned long)gebase, - (unsigned long)gebase + ALIGN(size, PAGE_SIZE)); + flush_icache_range((unsigned long)gebase, + (unsigned long)gebase + ALIGN(size, PAGE_SIZE)); /* * Allocate comm page for guest kernel, a TLB will be reserved for diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index 80554e8f6037..3e390a4e3897 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -545,7 +545,7 @@ void __init ltq_soc_init(void) clkdev_add_pmu("1a800000.pcie", "msi", 1, 1, PMU1_PCIE2_MSI); clkdev_add_pmu("1a800000.pcie", "pdi", 1, 1, PMU1_PCIE2_PDI); clkdev_add_pmu("1a800000.pcie", "ctl", 1, 1, PMU1_PCIE2_CTL); - clkdev_add_pmu("1e108000.eth", NULL, 1, 0, PMU_SWITCH | PMU_PPE_DP); + clkdev_add_pmu("1e108000.eth", NULL, 0, 0, PMU_SWITCH | PMU_PPE_DP); clkdev_add_pmu("1da00000.usif", "NULL", 1, 0, PMU_USIF); clkdev_add_pmu("1e103100.deu", NULL, 1, 0, PMU_DEU); } else if (of_machine_is_compatible("lantiq,ar10")) { @@ -553,7 +553,7 @@ void __init ltq_soc_init(void) ltq_ar10_fpi_hz(), ltq_ar10_pp32_hz()); clkdev_add_pmu("1e101000.usb", "ctl", 1, 0, PMU_USB0); clkdev_add_pmu("1e106000.usb", "ctl", 1, 0, PMU_USB1); - clkdev_add_pmu("1e108000.eth", NULL, 1, 0, PMU_SWITCH | + clkdev_add_pmu("1e108000.eth", NULL, 0, 0, PMU_SWITCH | PMU_PPE_DP | PMU_PPE_TC); clkdev_add_pmu("1da00000.usif", "NULL", 1, 0, PMU_USIF); clkdev_add_pmu("1f203000.rcu", "gphy", 1, 0, PMU_GPHY); @@ -575,11 +575,11 @@ void __init ltq_soc_init(void) clkdev_add_pmu(NULL, "ahb", 1, 0, PMU_AHBM | PMU_AHBS); clkdev_add_pmu("1da00000.usif", "NULL", 1, 0, PMU_USIF); - clkdev_add_pmu("1e108000.eth", NULL, 1, 0, + clkdev_add_pmu("1e108000.eth", NULL, 0, 0, PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM | PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | PMU_PPE_QSB | PMU_PPE_TOP); - clkdev_add_pmu("1f203000.rcu", "gphy", 1, 0, PMU_GPHY); + clkdev_add_pmu("1f203000.rcu", "gphy", 0, 0, PMU_GPHY); clkdev_add_pmu("1e103000.sdio", NULL, 1, 0, PMU_SDIO); clkdev_add_pmu("1e103100.deu", NULL, 1, 0, PMU_DEU); clkdev_add_pmu("1e116000.mei", "dfe", 1, 0, PMU_DFE); diff --git a/arch/mips/mm/sc-ip22.c b/arch/mips/mm/sc-ip22.c index dc7c5a5214a9..efaf364fe581 100644 --- a/arch/mips/mm/sc-ip22.c +++ b/arch/mips/mm/sc-ip22.c @@ -31,26 +31,40 @@ static inline void indy_sc_wipe(unsigned long first, unsigned long last) unsigned long tmp; __asm__ __volatile__( - ".set\tpush\t\t\t# indy_sc_wipe\n\t" - ".set\tnoreorder\n\t" - ".set\tmips3\n\t" - ".set\tnoat\n\t" - "mfc0\t%2, $12\n\t" - "li\t$1, 0x80\t\t\t# Go 64 bit\n\t" - "mtc0\t$1, $12\n\t" - - "dli\t$1, 0x9000000080000000\n\t" - "or\t%0, $1\t\t\t# first line to flush\n\t" - "or\t%1, $1\t\t\t# last line to flush\n\t" - ".set\tat\n\t" - - "1:\tsw\t$0, 0(%0)\n\t" - "bne\t%0, %1, 1b\n\t" - " daddu\t%0, 32\n\t" - - "mtc0\t%2, $12\t\t\t# Back to 32 bit\n\t" - "nop; nop; nop; nop;\n\t" - ".set\tpop" + " .set push # indy_sc_wipe \n" + " .set noreorder \n" + " .set mips3 \n" + " .set noat \n" + " mfc0 %2, $12 \n" + " li $1, 0x80 # Go 64 bit \n" + " mtc0 $1, $12 \n" + " \n" + " # \n" + " # Open code a dli $1, 0x9000000080000000 \n" + " # \n" + " # Required because binutils 2.25 will happily accept \n" + " # 64 bit instructions in .set mips3 mode but puke on \n" + " # 64 bit constants when generating 32 bit ELF \n" + " # \n" + " lui $1,0x9000 \n" + " dsll $1,$1,0x10 \n" + " ori $1,$1,0x8000 \n" + " dsll $1,$1,0x10 \n" + " \n" + " or %0, $1 # first line to flush \n" + " or %1, $1 # last line to flush \n" + " .set at \n" + " \n" + "1: sw $0, 0(%0) \n" + " bne %0, %1, 1b \n" + " daddu %0, 32 \n" + " \n" + " mtc0 %2, $12 # Back to 32 bit \n" + " nop # pipeline hazard \n" + " nop \n" + " nop \n" + " nop \n" + " .set pop \n" : "=r" (first), "=r" (last), "=&r" (tmp) : "0" (first), "1" (last)); } diff --git a/arch/mips/netlogic/common/reset.S b/arch/mips/netlogic/common/reset.S index edbab9b8691f..c474981a6c0d 100644 --- a/arch/mips/netlogic/common/reset.S +++ b/arch/mips/netlogic/common/reset.S @@ -50,7 +50,6 @@ #include <asm/netlogic/xlp-hal/sys.h> #include <asm/netlogic/xlp-hal/cpucontrol.h> -#define CP0_EBASE $15 #define SYS_CPU_COHERENT_BASE CKSEG1ADDR(XLP_DEFAULT_IO_BASE) + \ XLP_IO_SYS_OFFSET(0) + XLP_IO_PCI_HDRSZ + \ SYS_CPU_NONCOHERENT_MODE * 4 @@ -92,7 +91,7 @@ * registers. On XLPII CPUs, usual cache instructions work. */ .macro xlp_flush_l1_dcache - mfc0 t0, CP0_EBASE, 0 + mfc0 t0, CP0_PRID andi t0, t0, PRID_IMP_MASK slt t1, t0, 0x1200 beqz t1, 15f @@ -171,7 +170,7 @@ FEXPORT(nlm_reset_entry) nop 1: /* Entry point on core wakeup */ - mfc0 t0, CP0_EBASE, 0 /* processor ID */ + mfc0 t0, CP0_PRID /* processor ID */ andi t0, PRID_IMP_MASK li t1, 0x1500 /* XLP 9xx */ beq t0, t1, 2f /* does not need to set coherent */ @@ -182,8 +181,8 @@ FEXPORT(nlm_reset_entry) nop /* set bit in SYS coherent register for the core */ - mfc0 t0, CP0_EBASE, 1 - mfc0 t1, CP0_EBASE, 1 + mfc0 t0, CP0_EBASE + mfc0 t1, CP0_EBASE srl t1, 5 andi t1, 0x3 /* t1 <- node */ li t2, 0x40000 @@ -232,7 +231,7 @@ EXPORT(nlm_boot_siblings) * NOTE: All GPR contents are lost after the mtcr above! */ - mfc0 v0, CP0_EBASE, 1 + mfc0 v0, CP0_EBASE andi v0, 0x3ff /* v0 <- node/core */ /* diff --git a/arch/mips/netlogic/common/smpboot.S b/arch/mips/netlogic/common/smpboot.S index 805355b0bd05..f0cc4c9de2bb 100644 --- a/arch/mips/netlogic/common/smpboot.S +++ b/arch/mips/netlogic/common/smpboot.S @@ -48,8 +48,6 @@ #include <asm/netlogic/xlp-hal/sys.h> #include <asm/netlogic/xlp-hal/cpucontrol.h> -#define CP0_EBASE $15 - .set noreorder .set noat .set arch=xlr /* for mfcr/mtcr, XLR is sufficient */ @@ -86,7 +84,7 @@ NESTED(nlm_boot_secondary_cpus, 16, sp) PTR_L gp, 0(t1) /* a0 has the processor id */ - mfc0 a0, CP0_EBASE, 1 + mfc0 a0, CP0_EBASE andi a0, 0x3ff /* a0 <- node/core */ PTR_LA t0, nlm_early_init_secondary jalr t0 diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c index 39a9142f71be..7ecb4af79b7b 100644 --- a/arch/mips/ralink/prom.c +++ b/arch/mips/ralink/prom.c @@ -30,8 +30,10 @@ const char *get_system_type(void) return soc_info.sys_type; } -static __init void prom_init_cmdline(int argc, char **argv) +static __init void prom_init_cmdline(void) { + int argc; + char **argv; int i; pr_debug("prom: fw_arg0=%08x fw_arg1=%08x fw_arg2=%08x fw_arg3=%08x\n", @@ -60,14 +62,11 @@ static __init void prom_init_cmdline(int argc, char **argv) void __init prom_init(void) { - int argc; - char **argv; - prom_soc_init(&soc_info); pr_info("SoC Type: %s\n", get_system_type()); - prom_init_cmdline(argc, argv); + prom_init_cmdline(); } void __init prom_free_prom_memory(void) diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c index 844f5cd55c8f..15506a1ff22a 100644 --- a/arch/mips/ralink/rt288x.c +++ b/arch/mips/ralink/rt288x.c @@ -40,16 +40,6 @@ static struct rt2880_pmx_group rt2880_pinmux_data_act[] = { { 0 } }; -static void rt288x_wdt_reset(void) -{ - u32 t; - - /* enable WDT reset output on pin SRAM_CS_N */ - t = rt_sysc_r32(SYSC_REG_CLKCFG); - t |= CLKCFG_SRAM_CS_N_WDT; - rt_sysc_w32(t, SYSC_REG_CLKCFG); -} - void __init ralink_clk_init(void) { unsigned long cpu_rate, wmac_rate = 40000000; diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c index 9e4572592065..15b32cd01906 100644 --- a/arch/mips/ralink/rt305x.c +++ b/arch/mips/ralink/rt305x.c @@ -89,17 +89,6 @@ static struct rt2880_pmx_group rt5350_pinmux_data[] = { { 0 } }; -static void rt305x_wdt_reset(void) -{ - u32 t; - - /* enable WDT reset output on pin SRAM_CS_N */ - t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); - t |= RT305X_SYSCFG_SRAM_CS0_MODE_WDT << - RT305X_SYSCFG_SRAM_CS0_MODE_SHIFT; - rt_sysc_w32(t, SYSC_REG_SYSTEM_CONFIG); -} - static unsigned long rt5350_get_mem_size(void) { void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT305X_SYSC_BASE); diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c index 582995aaaf4e..f42834c7f007 100644 --- a/arch/mips/ralink/rt3883.c +++ b/arch/mips/ralink/rt3883.c @@ -63,16 +63,6 @@ static struct rt2880_pmx_group rt3883_pinmux_data[] = { { 0 } }; -static void rt3883_wdt_reset(void) -{ - u32 t; - - /* enable WDT reset output on GPIO 2 */ - t = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); - t |= RT3883_SYSCFG1_GPIO2_AS_WDT_OUT; - rt_sysc_w32(t, RT3883_SYSC_REG_SYSCFG1); -} - void __init ralink_clk_init(void) { unsigned long cpu_rate, sys_rate; diff --git a/arch/mips/sgi-ip22/Platform b/arch/mips/sgi-ip22/Platform index b7a4b7e04c38..e8f6b3a42a48 100644 --- a/arch/mips/sgi-ip22/Platform +++ b/arch/mips/sgi-ip22/Platform @@ -25,7 +25,7 @@ endif # Simplified: what IP22 does at 128MB+ in ksegN, IP28 does at 512MB+ in xkphys # ifdef CONFIG_SGI_IP28 - ifeq ($(call cc-option-yn,-mr10k-cache-barrier=store), n) + ifeq ($(call cc-option-yn,-march=r10000 -mr10k-cache-barrier=store), n) $(error gcc doesn't support needed option -mr10k-cache-barrier=store) endif endif diff --git a/arch/parisc/include/asm/bitops.h b/arch/parisc/include/asm/bitops.h index 3f9406d9b9d6..da87943328a5 100644 --- a/arch/parisc/include/asm/bitops.h +++ b/arch/parisc/include/asm/bitops.h @@ -6,7 +6,7 @@ #endif #include <linux/compiler.h> -#include <asm/types.h> /* for BITS_PER_LONG/SHIFT_PER_LONG */ +#include <asm/types.h> #include <asm/byteorder.h> #include <asm/barrier.h> #include <linux/atomic.h> @@ -17,6 +17,12 @@ * to include/asm-i386/bitops.h or kerneldoc */ +#if __BITS_PER_LONG == 64 +#define SHIFT_PER_LONG 6 +#else +#define SHIFT_PER_LONG 5 +#endif + #define CHOP_SHIFTCOUNT(x) (((unsigned long) (x)) & (BITS_PER_LONG - 1)) diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h index c2c43f714684..3a4ed9f91d57 100644 --- a/arch/parisc/include/asm/pgtable.h +++ b/arch/parisc/include/asm/pgtable.h @@ -65,9 +65,9 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr) unsigned long flags; \ spin_lock_irqsave(&pa_tlb_lock, flags); \ old_pte = *ptep; \ - set_pte(ptep, pteval); \ if (pte_inserted(old_pte)) \ purge_tlb_entries(mm, addr); \ + set_pte(ptep, pteval); \ spin_unlock_irqrestore(&pa_tlb_lock, flags); \ } while (0) @@ -478,8 +478,8 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned spin_unlock_irqrestore(&pa_tlb_lock, flags); return 0; } - set_pte(ptep, pte_mkold(pte)); purge_tlb_entries(vma->vm_mm, addr); + set_pte(ptep, pte_mkold(pte)); spin_unlock_irqrestore(&pa_tlb_lock, flags); return 1; } @@ -492,9 +492,9 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, spin_lock_irqsave(&pa_tlb_lock, flags); old_pte = *ptep; - set_pte(ptep, __pte(0)); if (pte_inserted(old_pte)) purge_tlb_entries(mm, addr); + set_pte(ptep, __pte(0)); spin_unlock_irqrestore(&pa_tlb_lock, flags); return old_pte; @@ -504,8 +504,8 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, { unsigned long flags; spin_lock_irqsave(&pa_tlb_lock, flags); - set_pte(ptep, pte_wrprotect(*ptep)); purge_tlb_entries(mm, addr); + set_pte(ptep, pte_wrprotect(*ptep)); spin_unlock_irqrestore(&pa_tlb_lock, flags); } diff --git a/arch/parisc/include/uapi/asm/bitsperlong.h b/arch/parisc/include/uapi/asm/bitsperlong.h index e0a23c7bdd43..07fa7e50bdc0 100644 --- a/arch/parisc/include/uapi/asm/bitsperlong.h +++ b/arch/parisc/include/uapi/asm/bitsperlong.h @@ -3,10 +3,8 @@ #if defined(__LP64__) #define __BITS_PER_LONG 64 -#define SHIFT_PER_LONG 6 #else #define __BITS_PER_LONG 32 -#define SHIFT_PER_LONG 5 #endif #include <asm-generic/bitsperlong.h> diff --git a/arch/parisc/include/uapi/asm/swab.h b/arch/parisc/include/uapi/asm/swab.h index e78403b129ef..928e1bbac98f 100644 --- a/arch/parisc/include/uapi/asm/swab.h +++ b/arch/parisc/include/uapi/asm/swab.h @@ -1,6 +1,7 @@ #ifndef _PARISC_SWAB_H #define _PARISC_SWAB_H +#include <asm/bitsperlong.h> #include <linux/types.h> #include <linux/compiler.h> @@ -38,7 +39,7 @@ static inline __attribute_const__ __u32 __arch_swab32(__u32 x) } #define __arch_swab32 __arch_swab32 -#if BITS_PER_LONG > 32 +#if __BITS_PER_LONG > 32 /* ** From "PA-RISC 2.0 Architecture", HP Professional Books. ** See Appendix I page 8 , "Endian Byte Swapping". @@ -61,6 +62,6 @@ static inline __attribute_const__ __u64 __arch_swab64(__u64 x) return x; } #define __arch_swab64 __arch_swab64 -#endif /* BITS_PER_LONG > 32 */ +#endif /* __BITS_PER_LONG > 32 */ #endif /* _PARISC_SWAB_H */ diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index fd5979f28ada..6857a104b2f9 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -375,6 +375,15 @@ void __init parisc_setup_cache_timing(void) /* calculate TLB flush threshold */ + /* On SMP machines, skip the TLB measure of kernel text which + * has been mapped as huge pages. */ + if (num_online_cpus() > 1 && !parisc_requires_coherency()) { + threshold = max(cache_info.it_size, cache_info.dt_size); + threshold *= PAGE_SIZE; + threshold /= num_online_cpus(); + goto set_tlb_threshold; + } + alltime = mfctl(16); flush_tlb_all(); alltime = mfctl(16) - alltime; @@ -393,6 +402,8 @@ void __init parisc_setup_cache_timing(void) alltime, size, rangetime); threshold = PAGE_ALIGN(num_online_cpus() * size * alltime / rangetime); + +set_tlb_threshold: if (threshold) parisc_tlb_flush_threshold = threshold; printk(KERN_INFO "TLB flush threshold set to %lu KiB\n", diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S index 675521919229..a4761b772406 100644 --- a/arch/parisc/kernel/pacache.S +++ b/arch/parisc/kernel/pacache.S @@ -886,19 +886,10 @@ ENTRY(flush_dcache_page_asm) fdc,m r31(%r28) fdc,m r31(%r28) fdc,m r31(%r28) - cmpb,COND(<<) %r28, %r25,1b + cmpb,COND(<<) %r28, %r25,1b fdc,m r31(%r28) sync - -#ifdef CONFIG_PA20 - pdtlb,l %r0(%r25) -#else - tlb_lock %r20,%r21,%r22 - pdtlb %r0(%r25) - tlb_unlock %r20,%r21,%r22 -#endif - bv %r0(%r2) nop .exit @@ -973,17 +964,6 @@ ENTRY(flush_icache_page_asm) fic,m %r31(%sr4,%r28) sync - -#ifdef CONFIG_PA20 - pdtlb,l %r0(%r28) - pitlb,l %r0(%sr4,%r25) -#else - tlb_lock %r20,%r21,%r22 - pdtlb %r0(%r28) - pitlb %r0(%sr4,%r25) - tlb_unlock %r20,%r21,%r22 -#endif - bv %r0(%r2) nop .exit diff --git a/arch/powerpc/boot/ps3-head.S b/arch/powerpc/boot/ps3-head.S index b6fcbaf5027b..3dc44b05fb97 100644 --- a/arch/powerpc/boot/ps3-head.S +++ b/arch/powerpc/boot/ps3-head.S @@ -57,11 +57,6 @@ __system_reset_overlay: bctr 1: - /* Save the value at addr zero for a null pointer write check later. */ - - li r4, 0 - lwz r3, 0(r4) - /* Primary delays then goes to _zimage_start in wrapper. */ or 31, 31, 31 /* db16cyc */ diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c index 4ec2d86d3c50..a05558a7e51a 100644 --- a/arch/powerpc/boot/ps3.c +++ b/arch/powerpc/boot/ps3.c @@ -119,13 +119,12 @@ void ps3_copy_vectors(void) flush_cache((void *)0x100, 512); } -void platform_init(unsigned long null_check) +void platform_init(void) { const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */ void *chosen; unsigned long ft_addr; u64 rm_size; - unsigned long val; console_ops.write = ps3_console_write; platform_ops.exit = ps3_exit; @@ -153,11 +152,6 @@ void platform_init(unsigned long null_check) printf(" flat tree at 0x%lx\n\r", ft_addr); - val = *(unsigned long *)0; - - if (val != null_check) - printf("null check failed: %lx != %lx\n\r", val, null_check); - ((kernel_entry_t)0)(ft_addr, 0, NULL); ps3_exit(); diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index cfa758c6b4f6..a92d95aee42d 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -545,6 +545,7 @@ struct kvm_vcpu_arch { u64 tfiar; u32 cr_tm; + u64 xer_tm; u64 lr_tm; u64 ctr_tm; u64 amr_tm; diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index ab4d4732c492..720b71a636c8 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -587,6 +587,7 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67) #define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68) #define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69) +#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a) /* PPC64 eXternal Interrupt Controller Specification */ #define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 221d584d089f..40da69163d51 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -584,6 +584,7 @@ int main(void) DEFINE(VCPU_VRS_TM, offsetof(struct kvm_vcpu, arch.vr_tm.vr)); DEFINE(VCPU_VRSAVE_TM, offsetof(struct kvm_vcpu, arch.vrsave_tm)); DEFINE(VCPU_CR_TM, offsetof(struct kvm_vcpu, arch.cr_tm)); + DEFINE(VCPU_XER_TM, offsetof(struct kvm_vcpu, arch.xer_tm)); DEFINE(VCPU_LR_TM, offsetof(struct kvm_vcpu, arch.lr_tm)); DEFINE(VCPU_CTR_TM, offsetof(struct kvm_vcpu, arch.ctr_tm)); DEFINE(VCPU_AMR_TM, offsetof(struct kvm_vcpu, arch.amr_tm)); diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index c07bfb52275e..c314db8b798c 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -485,7 +485,7 @@ static void *eeh_pe_detach_dev(void *data, void *userdata) static void *__eeh_clear_pe_frozen_state(void *data, void *flag) { struct eeh_pe *pe = (struct eeh_pe *)data; - bool *clear_sw_state = flag; + bool clear_sw_state = *(bool *)flag; int i, rc = 1; for (i = 0; rc && i < 3; i++) @@ -612,8 +612,10 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus) /* Clear frozen state */ rc = eeh_clear_pe_frozen_state(pe, false); - if (rc) + if (rc) { + pci_unlock_rescan_remove(); return rc; + } /* Give the system 5 seconds to finish running the user-space * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes, diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index 05e804cdecaa..fdf48785d3e9 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -227,8 +227,10 @@ int __kprobes hw_breakpoint_handler(struct die_args *args) rcu_read_lock(); bp = __this_cpu_read(bp_per_reg); - if (!bp) + if (!bp) { + rc = NOTIFY_DONE; goto out; + } info = counter_arch_bp(bp); /* diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index ac86c53e2542..e524a775fa5c 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -180,6 +180,7 @@ static int ibmebus_create_device(struct device_node *dn) static int ibmebus_create_devices(const struct of_device_id *matches) { struct device_node *root, *child; + struct device *dev; int ret = 0; root = of_find_node_by_path("/"); @@ -188,9 +189,12 @@ static int ibmebus_create_devices(const struct of_device_id *matches) if (!of_match_node(matches, child)) continue; - if (bus_find_device(&ibmebus_bus_type, NULL, child, - ibmebus_match_node)) + dev = bus_find_device(&ibmebus_bus_type, NULL, child, + ibmebus_match_node); + if (dev) { + put_device(dev); continue; + } ret = ibmebus_create_device(child); if (ret) { @@ -262,6 +266,7 @@ static ssize_t ibmebus_store_probe(struct bus_type *bus, const char *buf, size_t count) { struct device_node *dn = NULL; + struct device *dev; char *path; ssize_t rc = 0; @@ -269,8 +274,10 @@ static ssize_t ibmebus_store_probe(struct bus_type *bus, if (!path) return -ENOMEM; - if (bus_find_device(&ibmebus_bus_type, NULL, path, - ibmebus_match_path)) { + dev = bus_find_device(&ibmebus_bus_type, NULL, path, + ibmebus_match_path); + if (dev) { + put_device(dev); printk(KERN_WARNING "%s: %s has already been probed\n", __func__, path); rc = -EEXIST; @@ -307,6 +314,7 @@ static ssize_t ibmebus_store_remove(struct bus_type *bus, if ((dev = bus_find_device(&ibmebus_bus_type, NULL, path, ibmebus_match_path))) { of_device_unregister(to_platform_device(dev)); + put_device(dev); kfree(path); return count; diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S index 112ccf497562..73f638789a38 100644 --- a/arch/powerpc/kernel/idle_power7.S +++ b/arch/powerpc/kernel/idle_power7.S @@ -44,7 +44,7 @@ std r0,0(r1); \ ptesync; \ ld r0,0(r1); \ -1: cmp cr0,r0,r0; \ +1: cmpd cr0,r0,r0; \ bne 1b; \ IDLE_INST; \ b . diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index ed3ab509faca..df4efa304b2c 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -313,7 +313,7 @@ _GLOBAL(flush_instruction_cache) lis r3, KERNELBASE@h iccci 0,r3 #endif -#elif CONFIG_FSL_BOOKE +#elif defined(CONFIG_FSL_BOOKE) BEGIN_FTR_SECTION mfspr r3,SPRN_L1CSR0 ori r3,r3,L1CSR0_CFI|L1CSR0_CLFC diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 7b89e7b305e6..3139533640fc 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -2664,6 +2664,9 @@ static void __init prom_find_boot_cpu(void) cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu); + if (!PHANDLE_VALID(cpu_pkg)) + return; + prom_getprop(cpu_pkg, "reg", &rval, sizeof(rval)); prom.cpu = be32_to_cpu(rval); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index a7352b59e6f9..3c3a367b6e59 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1186,6 +1186,9 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_TM_CR: *val = get_reg_val(id, vcpu->arch.cr_tm); break; + case KVM_REG_PPC_TM_XER: + *val = get_reg_val(id, vcpu->arch.xer_tm); + break; case KVM_REG_PPC_TM_LR: *val = get_reg_val(id, vcpu->arch.lr_tm); break; @@ -1393,6 +1396,9 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_TM_CR: vcpu->arch.cr_tm = set_reg_val(id, *val); break; + case KVM_REG_PPC_TM_XER: + vcpu->arch.xer_tm = set_reg_val(id, *val); + break; case KVM_REG_PPC_TM_LR: vcpu->arch.lr_tm = set_reg_val(id, *val); break; diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index 91700518bbf3..d509ff5c87b0 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c @@ -653,6 +653,8 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags, HPTE_V_ABSENT); do_tlbies(kvm, &rb, 1, global_invalidates(kvm, flags), true); + /* Don't lose R/C bit updates done by hardware */ + r |= be64_to_cpu(hpte[1]) & (HPTE_R_R | HPTE_R_C); hpte[1] = cpu_to_be64(r); } } diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 974f73df00bb..1a743f87b37d 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -2514,11 +2514,13 @@ kvmppc_save_tm: mfctr r7 mfspr r8, SPRN_AMR mfspr r10, SPRN_TAR + mfxer r11 std r5, VCPU_LR_TM(r9) stw r6, VCPU_CR_TM(r9) std r7, VCPU_CTR_TM(r9) std r8, VCPU_AMR_TM(r9) std r10, VCPU_TAR_TM(r9) + std r11, VCPU_XER_TM(r9) /* Restore r12 as trap number. */ lwz r12, VCPU_TRAP(r9) @@ -2611,11 +2613,13 @@ kvmppc_restore_tm: ld r7, VCPU_CTR_TM(r4) ld r8, VCPU_AMR_TM(r4) ld r9, VCPU_TAR_TM(r4) + ld r10, VCPU_XER_TM(r4) mtlr r5 mtcr r6 mtctr r7 mtspr SPRN_AMR, r8 mtspr SPRN_TAR, r9 + mtxer r10 /* * Load up PPR and DSCR values but don't put them in the actual SPRs diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index dc885b30f7a6..4014881e9843 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -1806,8 +1806,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) goto instr_done; case LARX: - if (regs->msr & MSR_LE) - return 0; if (op.ea & (size - 1)) break; /* can't handle misaligned */ err = -EFAULT; @@ -1829,8 +1827,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) goto ldst_done; case STCX: - if (regs->msr & MSR_LE) - return 0; if (op.ea & (size - 1)) break; /* can't handle misaligned */ err = -EFAULT; @@ -1854,8 +1850,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) goto ldst_done; case LOAD: - if (regs->msr & MSR_LE) - return 0; err = read_mem(®s->gpr[op.reg], op.ea, size, regs); if (!err) { if (op.type & SIGNEXT) @@ -1867,8 +1861,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) #ifdef CONFIG_PPC_FPU case LOAD_FP: - if (regs->msr & MSR_LE) - return 0; if (size == 4) err = do_fp_load(op.reg, do_lfs, op.ea, size, regs); else @@ -1877,15 +1869,11 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) #endif #ifdef CONFIG_ALTIVEC case LOAD_VMX: - if (regs->msr & MSR_LE) - return 0; err = do_vec_load(op.reg, do_lvx, op.ea & ~0xfUL, regs); goto ldst_done; #endif #ifdef CONFIG_VSX case LOAD_VSX: - if (regs->msr & MSR_LE) - return 0; err = do_vsx_load(op.reg, do_lxvd2x, op.ea, regs); goto ldst_done; #endif @@ -1908,8 +1896,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) goto instr_done; case STORE: - if (regs->msr & MSR_LE) - return 0; if ((op.type & UPDATE) && size == sizeof(long) && op.reg == 1 && op.update_reg == 1 && !(regs->msr & MSR_PR) && @@ -1922,8 +1908,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) #ifdef CONFIG_PPC_FPU case STORE_FP: - if (regs->msr & MSR_LE) - return 0; if (size == 4) err = do_fp_store(op.reg, do_stfs, op.ea, size, regs); else @@ -1932,15 +1916,11 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) #endif #ifdef CONFIG_ALTIVEC case STORE_VMX: - if (regs->msr & MSR_LE) - return 0; err = do_vec_store(op.reg, do_stvx, op.ea & ~0xfUL, regs); goto ldst_done; #endif #ifdef CONFIG_VSX case STORE_VSX: - if (regs->msr & MSR_LE) - return 0; err = do_vsx_store(op.reg, do_stxvd2x, op.ea, regs); goto ldst_done; #endif diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c index d750cc0dfe30..683a966b5b16 100644 --- a/arch/s390/crypto/prng.c +++ b/arch/s390/crypto/prng.c @@ -565,8 +565,10 @@ static ssize_t prng_tdes_read(struct file *file, char __user *ubuf, prng_data->prngws.byte_counter += n; prng_data->prngws.reseed_counter += n; - if (copy_to_user(ubuf, prng_data->buf, chunk)) - return -EFAULT; + if (copy_to_user(ubuf, prng_data->buf, chunk)) { + ret = -EFAULT; + break; + } nbytes -= chunk; ret += chunk; diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index c1ea67db8404..c61ed7890cef 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -74,7 +74,8 @@ extern void execve_tail(void); * User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit. */ -#define TASK_SIZE_OF(tsk) ((tsk)->mm->context.asce_limit) +#define TASK_SIZE_OF(tsk) ((tsk)->mm ? \ + (tsk)->mm->context.asce_limit : TASK_MAX_SIZE) #define TASK_UNMAPPED_BASE (test_thread_flag(TIF_31BIT) ? \ (1UL << 30) : (1UL << 41)) #define TASK_SIZE TASK_SIZE_OF(current) diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index 171e09bb8ea2..f7c3a61040bd 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -23,6 +23,8 @@ #define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y))) #define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y)))) +#define LINUX_NOTE_NAME "LINUX" + static struct memblock_region oldmem_region; static struct memblock_type oldmem_type = { @@ -312,7 +314,7 @@ static void *nt_fpregset(void *ptr, struct save_area *sa) static void *nt_s390_timer(void *ptr, struct save_area *sa) { return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer), - KEXEC_CORE_NOTE_NAME); + LINUX_NOTE_NAME); } /* @@ -321,7 +323,7 @@ static void *nt_s390_timer(void *ptr, struct save_area *sa) static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa) { return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp, - sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME); + sizeof(sa->clk_cmp), LINUX_NOTE_NAME); } /* @@ -330,7 +332,7 @@ static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa) static void *nt_s390_tod_preg(void *ptr, struct save_area *sa) { return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg, - sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME); + sizeof(sa->tod_reg), LINUX_NOTE_NAME); } /* @@ -339,7 +341,7 @@ static void *nt_s390_tod_preg(void *ptr, struct save_area *sa) static void *nt_s390_ctrs(void *ptr, struct save_area *sa) { return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs, - sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME); + sizeof(sa->ctrl_regs), LINUX_NOTE_NAME); } /* @@ -348,7 +350,7 @@ static void *nt_s390_ctrs(void *ptr, struct save_area *sa) static void *nt_s390_prefix(void *ptr, struct save_area *sa) { return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg, - sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME); + sizeof(sa->pref_reg), LINUX_NOTE_NAME); } /* @@ -357,7 +359,7 @@ static void *nt_s390_prefix(void *ptr, struct save_area *sa) static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs) { return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16], - 16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME); + 16 * sizeof(__vector128), LINUX_NOTE_NAME); } /* @@ -370,12 +372,12 @@ static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs) int i; note = (Elf64_Nhdr *)ptr; - note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1; + note->n_namesz = strlen(LINUX_NOTE_NAME) + 1; note->n_descsz = 16 * 8; note->n_type = NT_S390_VXRS_LOW; len = sizeof(Elf64_Nhdr); - memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz); + memcpy(ptr + len, LINUX_NOTE_NAME, note->n_namesz); len = roundup(len + note->n_namesz, 4); ptr += len; diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 01c37b36caf9..02bd587b610b 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -963,6 +963,11 @@ static int s390_fpregs_set(struct task_struct *target, if (target == current) save_fpu_regs(); + if (MACHINE_HAS_VX) + convert_vx_to_fp(fprs, target->thread.fpu.vxrs); + else + memcpy(&fprs, target->thread.fpu.fprs, sizeof(fprs)); + /* If setting FPC, must validate it first. */ if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) { u32 ufpc[2] = { target->thread.fpu.fpc, 0 }; @@ -1067,6 +1072,9 @@ static int s390_vxrs_low_set(struct task_struct *target, if (target == current) save_fpu_regs(); + for (i = 0; i < __NUM_VXRS_LOW; i++) + vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1); + rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); if (rc == 0) for (i = 0; i < __NUM_VXRS_LOW; i++) diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 1f581eb61bc2..d097d71685df 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -805,10 +805,10 @@ static void __init setup_randomness(void) { struct sysinfo_3_2_2 *vmms; - vmms = (struct sysinfo_3_2_2 *) alloc_page(GFP_KERNEL); - if (vmms && stsi(vmms, 3, 2, 2) == 0 && vmms->count) - add_device_randomness(&vmms, vmms->count); - free_page((unsigned long) vmms); + vmms = (struct sysinfo_3_2_2 *) memblock_alloc(PAGE_SIZE, PAGE_SIZE); + if (stsi(vmms, 3, 2, 2) == 0 && vmms->count) + add_device_randomness(&vmms->vm, sizeof(vmms->vm[0]) * vmms->count); + memblock_free((unsigned long) vmms, PAGE_SIZE); } /* diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 575dc123bda2..23e3f5d77a24 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -295,6 +295,9 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot; int is_dirty = 0; + if (kvm_is_ucontrol(kvm)) + return -EINVAL; + mutex_lock(&kvm->slots_lock); r = -EINVAL; diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 8345ae1f117d..05ae254f84cf 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -1237,11 +1237,28 @@ EXPORT_SYMBOL_GPL(s390_reset_cmma); */ bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *gmap) { + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; pte_t *pte; spinlock_t *ptl; bool dirty = false; - pte = get_locked_pte(gmap->mm, address, &ptl); + pgd = pgd_offset(gmap->mm, address); + pud = pud_alloc(gmap->mm, pgd, address); + if (!pud) + return false; + pmd = pmd_alloc(gmap->mm, pud, address); + if (!pmd) + return false; + /* We can't run guests backed by huge pages, but userspace can + * still set them up and then try to migrate them without any + * migration support. + */ + if (pmd_large(*pmd)) + return true; + + pte = pte_alloc_map_lock(gmap->mm, pmd, address, &ptl); if (unlikely(!pte)) return false; diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index bdc126faf741..6239aa155f6d 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c @@ -111,7 +111,7 @@ static int tile_gpr_set(struct task_struct *target, const void *kbuf, const void __user *ubuf) { int ret; - struct pt_regs regs; + struct pt_regs regs = *task_pt_regs(target); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, 0, sizeof(regs)); diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig index 0206eb8cfb61..65ed8c8f8444 100644 --- a/arch/x86/configs/i386_ranchu_defconfig +++ b/arch/x86/configs/i386_ranchu_defconfig @@ -89,7 +89,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set -# CONFIG_INET_DIAG is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IPV6_OPTIMISTIC_DAD=y diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig index dd389774bacb..d977bd91e390 100644 --- a/arch/x86/configs/x86_64_ranchu_defconfig +++ b/arch/x86/configs/x86_64_ranchu_defconfig @@ -87,7 +87,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set -# CONFIG_INET_DIAG is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IPV6_OPTIMISTIC_DAD=y diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index f3b6d54e0042..ae678ad128a9 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -766,8 +766,8 @@ ftrace_graph_call: jmp ftrace_stub #endif -.globl ftrace_stub -ftrace_stub: +/* This is weak to keep gas from relaxing the jumps */ +WEAK(ftrace_stub) ret END(ftrace_caller) diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index fdb0fbfb1197..8ca533b8c606 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2115,6 +2115,7 @@ static inline void __init check_timer(void) if (idx != -1 && irq_trigger(idx)) unmask_ioapic_irq(irq_get_chip_data(0)); } + irq_domain_deactivate_irq(irq_data); irq_domain_activate_irq(irq_data); if (timer_irq_works()) { if (disable_timer_pin_1 > 0) @@ -2136,6 +2137,7 @@ static inline void __init check_timer(void) * legacy devices should be connected to IO APIC #0 */ replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2); + irq_domain_deactivate_irq(irq_data); irq_domain_activate_irq(irq_data); legacy_pic->unmask(0); if (timer_irq_works()) { diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 2b49b113d65d..637ca414d431 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1129,7 +1129,7 @@ static __init int setup_disablecpuid(char *arg) { int bit; - if (get_option(&arg, &bit) && bit < NCAPINTS*32) + if (get_option(&arg, &bit) && bit >= 0 && bit < NCAPINTS * 32) setup_clear_cpu_cap(bit); else return 0; diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index a3aeb2cc361e..1a8256dd6729 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -67,7 +67,7 @@ u64 x86_perf_event_update(struct perf_event *event) int shift = 64 - x86_pmu.cntval_bits; u64 prev_raw_count, new_raw_count; int idx = hwc->idx; - s64 delta; + u64 delta; if (idx == INTEL_PMC_IDX_FIXED_BTS) return 0; diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 5f82cd59f0e5..5cc2242d77c6 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -3636,7 +3636,7 @@ __init int intel_pmu_init(void) /* Support full width counters using alternative MSR range */ if (x86_pmu.intel_cap.full_width_write) { - x86_pmu.max_period = x86_pmu.cntval_mask; + x86_pmu.max_period = x86_pmu.cntval_mask >> 1; x86_pmu.perfctr = MSR_IA32_PMC0; pr_cont("full-width counters, "); } diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index b8e6ff5cd5d0..acc9b8f19ca8 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -351,6 +351,7 @@ static int hpet_resume(struct clock_event_device *evt, int timer) } else { struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); + irq_domain_deactivate_irq(irq_get_irq_data(hdev->irq)); irq_domain_activate_irq(irq_get_irq_data(hdev->irq)); disable_irq(hdev->irq); irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S index 87e1762e2bca..5d9afbcb6074 100644 --- a/arch/x86/kernel/mcount_64.S +++ b/arch/x86/kernel/mcount_64.S @@ -180,7 +180,8 @@ GLOBAL(ftrace_graph_call) jmp ftrace_stub #endif -GLOBAL(ftrace_stub) +/* This is weak to keep gas from relaxing the jumps */ +WEAK(ftrace_stub) retq END(ftrace_caller) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index f49e98062ea5..1dcea225977d 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -172,6 +172,7 @@ #define NearBranch ((u64)1 << 52) /* Near branches */ #define No16 ((u64)1 << 53) /* No 16 bit operand */ #define IncSP ((u64)1 << 54) /* SP is incremented before ModRM calc */ +#define Aligned16 ((u64)1 << 55) /* Aligned to 16 byte boundary (e.g. FXSAVE) */ #define DstXacc (DstAccLo | SrcAccHi | SrcWrite) @@ -434,6 +435,26 @@ FOP_END; FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET FOP_END; +/* + * XXX: inoutclob user must know where the argument is being expanded. + * Relying on CC_HAVE_ASM_GOTO would allow us to remove _fault. + */ +#define asm_safe(insn, inoutclob...) \ +({ \ + int _fault = 0; \ + \ + asm volatile("1:" insn "\n" \ + "2:\n" \ + ".pushsection .fixup, \"ax\"\n" \ + "3: movl $1, %[_fault]\n" \ + " jmp 2b\n" \ + ".popsection\n" \ + _ASM_EXTABLE(1b, 3b) \ + : [_fault] "+qm"(_fault) inoutclob ); \ + \ + _fault ? X86EMUL_UNHANDLEABLE : X86EMUL_CONTINUE; \ +}) + static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt, enum x86_intercept intercept, enum x86_intercept_stage stage) @@ -620,21 +641,24 @@ static void set_segment_selector(struct x86_emulate_ctxt *ctxt, u16 selector, * depending on whether they're AVX encoded or not. * * Also included is CMPXCHG16B which is not a vector instruction, yet it is - * subject to the same check. + * subject to the same check. FXSAVE and FXRSTOR are checked here too as their + * 512 bytes of data must be aligned to a 16 byte boundary. */ -static bool insn_aligned(struct x86_emulate_ctxt *ctxt, unsigned size) +static unsigned insn_alignment(struct x86_emulate_ctxt *ctxt, unsigned size) { if (likely(size < 16)) - return false; + return 1; if (ctxt->d & Aligned) - return true; + return size; else if (ctxt->d & Unaligned) - return false; + return 1; else if (ctxt->d & Avx) - return false; + return 1; + else if (ctxt->d & Aligned16) + return 16; else - return true; + return size; } static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt, @@ -692,7 +716,7 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt, } break; } - if (insn_aligned(ctxt, size) && ((la & (size - 1)) != 0)) + if (la & (insn_alignment(ctxt, size) - 1)) return emulate_gp(ctxt, 0); return X86EMUL_CONTINUE; bad: @@ -779,6 +803,20 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt, return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception); } +static int segmented_write_std(struct x86_emulate_ctxt *ctxt, + struct segmented_address addr, + void *data, + unsigned int size) +{ + int rc; + ulong linear; + + rc = linearize(ctxt, addr, size, true, &linear); + if (rc != X86EMUL_CONTINUE) + return rc; + return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception); +} + /* * Prefetch the remaining bytes of the instruction without crossing page * boundary if they are not in fetch_cache yet. @@ -1532,7 +1570,6 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt, &ctxt->exception); } -/* Does not support long mode */ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, u16 selector, int seg, u8 cpl, enum x86_transfer_type transfer, @@ -1569,20 +1606,34 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, rpl = selector & 3; - /* NULL selector is not valid for TR, CS and SS (except for long mode) */ - if ((seg == VCPU_SREG_CS - || (seg == VCPU_SREG_SS - && (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl)) - || seg == VCPU_SREG_TR) - && null_selector) - goto exception; - /* TR should be in GDT only */ if (seg == VCPU_SREG_TR && (selector & (1 << 2))) goto exception; - if (null_selector) /* for NULL selector skip all following checks */ + /* NULL selector is not valid for TR, CS and (except for long mode) SS */ + if (null_selector) { + if (seg == VCPU_SREG_CS || seg == VCPU_SREG_TR) + goto exception; + + if (seg == VCPU_SREG_SS) { + if (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl) + goto exception; + + /* + * ctxt->ops->set_segment expects the CPL to be in + * SS.DPL, so fake an expand-up 32-bit data segment. + */ + seg_desc.type = 3; + seg_desc.p = 1; + seg_desc.s = 1; + seg_desc.dpl = cpl; + seg_desc.d = 1; + seg_desc.g = 1; + } + + /* Skip all following checks */ goto load; + } ret = read_segment_descriptor(ctxt, selector, &seg_desc, &desc_addr); if (ret != X86EMUL_CONTINUE) @@ -1698,6 +1749,21 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt, u16 selector, int seg) { u8 cpl = ctxt->ops->cpl(ctxt); + + /* + * None of MOV, POP and LSS can load a NULL selector in CPL=3, but + * they can load it at CPL<3 (Intel's manual says only LSS can, + * but it's wrong). + * + * However, the Intel manual says that putting IST=1/DPL=3 in + * an interrupt gate will result in SS=3 (the AMD manual instead + * says it doesn't), so allow SS=3 in __load_segment_descriptor + * and only forbid it here. + */ + if (seg == VCPU_SREG_SS && selector == 3 && + ctxt->mode == X86EMUL_MODE_PROT64) + return emulate_exception(ctxt, GP_VECTOR, 0, true); + return __load_segment_descriptor(ctxt, selector, seg, cpl, X86_TRANSFER_NONE, NULL); } @@ -3646,8 +3712,8 @@ static int emulate_store_desc_ptr(struct x86_emulate_ctxt *ctxt, } /* Disable writeback. */ ctxt->dst.type = OP_NONE; - return segmented_write(ctxt, ctxt->dst.addr.mem, - &desc_ptr, 2 + ctxt->op_bytes); + return segmented_write_std(ctxt, ctxt->dst.addr.mem, + &desc_ptr, 2 + ctxt->op_bytes); } static int em_sgdt(struct x86_emulate_ctxt *ctxt) @@ -3830,6 +3896,131 @@ static int em_movsxd(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } +static int check_fxsr(struct x86_emulate_ctxt *ctxt) +{ + u32 eax = 1, ebx, ecx = 0, edx; + + ctxt->ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx); + if (!(edx & FFL(FXSR))) + return emulate_ud(ctxt); + + if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) + return emulate_nm(ctxt); + + /* + * Don't emulate a case that should never be hit, instead of working + * around a lack of fxsave64/fxrstor64 on old compilers. + */ + if (ctxt->mode >= X86EMUL_MODE_PROT64) + return X86EMUL_UNHANDLEABLE; + + return X86EMUL_CONTINUE; +} + +/* + * FXSAVE and FXRSTOR have 4 different formats depending on execution mode, + * 1) 16 bit mode + * 2) 32 bit mode + * - like (1), but FIP and FDP (foo) are only 16 bit. At least Intel CPUs + * preserve whole 32 bit values, though, so (1) and (2) are the same wrt. + * save and restore + * 3) 64-bit mode with REX.W prefix + * - like (2), but XMM 8-15 are being saved and restored + * 4) 64-bit mode without REX.W prefix + * - like (3), but FIP and FDP are 64 bit + * + * Emulation uses (3) for (1) and (2) and preserves XMM 8-15 to reach the + * desired result. (4) is not emulated. + * + * Note: Guest and host CPUID.(EAX=07H,ECX=0H):EBX[bit 13] (deprecate FPU CS + * and FPU DS) should match. + */ +static int em_fxsave(struct x86_emulate_ctxt *ctxt) +{ + struct fxregs_state fx_state; + size_t size; + int rc; + + rc = check_fxsr(ctxt); + if (rc != X86EMUL_CONTINUE) + return rc; + + ctxt->ops->get_fpu(ctxt); + + rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state)); + + ctxt->ops->put_fpu(ctxt); + + if (rc != X86EMUL_CONTINUE) + return rc; + + if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR) + size = offsetof(struct fxregs_state, xmm_space[8 * 16/4]); + else + size = offsetof(struct fxregs_state, xmm_space[0]); + + return segmented_write_std(ctxt, ctxt->memop.addr.mem, &fx_state, size); +} + +static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt, + struct fxregs_state *new) +{ + int rc = X86EMUL_CONTINUE; + struct fxregs_state old; + + rc = asm_safe("fxsave %[fx]", , [fx] "+m"(old)); + if (rc != X86EMUL_CONTINUE) + return rc; + + /* + * 64 bit host will restore XMM 8-15, which is not correct on non-64 + * bit guests. Load the current values in order to preserve 64 bit + * XMMs after fxrstor. + */ +#ifdef CONFIG_X86_64 + /* XXX: accessing XMM 8-15 very awkwardly */ + memcpy(&new->xmm_space[8 * 16/4], &old.xmm_space[8 * 16/4], 8 * 16); +#endif + + /* + * Hardware doesn't save and restore XMM 0-7 without CR4.OSFXSR, but + * does save and restore MXCSR. + */ + if (!(ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR)) + memcpy(new->xmm_space, old.xmm_space, 8 * 16); + + return rc; +} + +static int em_fxrstor(struct x86_emulate_ctxt *ctxt) +{ + struct fxregs_state fx_state; + int rc; + + rc = check_fxsr(ctxt); + if (rc != X86EMUL_CONTINUE) + return rc; + + rc = segmented_read_std(ctxt, ctxt->memop.addr.mem, &fx_state, 512); + if (rc != X86EMUL_CONTINUE) + return rc; + + if (fx_state.mxcsr >> 16) + return emulate_gp(ctxt, 0); + + ctxt->ops->get_fpu(ctxt); + + if (ctxt->mode < X86EMUL_MODE_PROT64) + rc = fxrstor_fixup(ctxt, &fx_state); + + if (rc == X86EMUL_CONTINUE) + rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state)); + + ctxt->ops->put_fpu(ctxt); + + return rc; +} + static bool valid_cr(int nr) { switch (nr) { @@ -4182,7 +4373,9 @@ static const struct gprefix pfx_0f_ae_7 = { }; static const struct group_dual group15 = { { - N, N, N, N, N, N, N, GP(0, &pfx_0f_ae_7), + I(ModRM | Aligned16, em_fxsave), + I(ModRM | Aligned16, em_fxrstor), + N, N, N, N, N, GP(0, &pfx_0f_ae_7), }, { N, N, N, N, N, N, N, N, } }; @@ -5054,21 +5247,13 @@ static bool string_insn_completed(struct x86_emulate_ctxt *ctxt) static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt) { - bool fault = false; + int rc; ctxt->ops->get_fpu(ctxt); - asm volatile("1: fwait \n\t" - "2: \n\t" - ".pushsection .fixup,\"ax\" \n\t" - "3: \n\t" - "movb $1, %[fault] \n\t" - "jmp 2b \n\t" - ".popsection \n\t" - _ASM_EXTABLE(1b, 3b) - : [fault]"+qm"(fault)); + rc = asm_safe("fwait"); ctxt->ops->put_fpu(ctxt); - if (unlikely(fault)) + if (unlikely(rc != X86EMUL_CONTINUE)) return emulate_exception(ctxt, MF_VECTOR, 0, false); return X86EMUL_CONTINUE; diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 4d30b865be30..1c96f09367ae 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2187,3 +2187,9 @@ void kvm_lapic_init(void) jump_label_rate_limit(&apic_hw_disabled, HZ); jump_label_rate_limit(&apic_sw_disabled, HZ); } + +void kvm_lapic_exit(void) +{ + static_key_deferred_flush(&apic_hw_disabled); + static_key_deferred_flush(&apic_sw_disabled); +} diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index fde8e35d5850..eb418fd670ff 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -95,6 +95,7 @@ static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu) int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data); void kvm_lapic_init(void); +void kvm_lapic_exit(void); static inline u32 kvm_apic_get_reg(struct kvm_lapic *apic, int reg_off) { diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 268df707b5ce..3a7ae80dc49d 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -1247,10 +1247,10 @@ static inline bool nested_cpu_has_posted_intr(struct vmcs12 *vmcs12) return vmcs12->pin_based_vm_exec_control & PIN_BASED_POSTED_INTR; } -static inline bool is_exception(u32 intr_info) +static inline bool is_nmi(u32 intr_info) { return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VALID_MASK)) - == (INTR_TYPE_HARD_EXCEPTION | INTR_INFO_VALID_MASK); + == (INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK); } static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, @@ -3499,7 +3499,7 @@ static void fix_rmode_seg(int seg, struct kvm_segment *save) } vmcs_write16(sf->selector, var.selector); - vmcs_write32(sf->base, var.base); + vmcs_writel(sf->base, var.base); vmcs_write32(sf->limit, var.limit); vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(&var)); } @@ -4867,6 +4867,12 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx) if (vmx_xsaves_supported()) vmcs_write64(XSS_EXIT_BITMAP, VMX_XSS_EXIT_BITMAP); + if (enable_pml) { + ASSERT(vmx->pml_pg); + vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg)); + vmcs_write16(GUEST_PML_INDEX, PML_ENTITY_NUM - 1); + } + return 0; } @@ -5234,7 +5240,7 @@ static int handle_exception(struct kvm_vcpu *vcpu) if (is_machine_check(intr_info)) return handle_machine_check(vcpu); - if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR) + if (is_nmi(intr_info)) return 1; /* already handled by vmx_vcpu_run() */ if (is_no_device(intr_info)) { @@ -7722,7 +7728,7 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu) switch (exit_reason) { case EXIT_REASON_EXCEPTION_NMI: - if (!is_exception(intr_info)) + if (is_nmi(intr_info)) return false; else if (is_page_fault(intr_info)) return enable_ept; @@ -7839,22 +7845,6 @@ static void vmx_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2) *info2 = vmcs_read32(VM_EXIT_INTR_INFO); } -static int vmx_create_pml_buffer(struct vcpu_vmx *vmx) -{ - struct page *pml_pg; - - pml_pg = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (!pml_pg) - return -ENOMEM; - - vmx->pml_pg = pml_pg; - - vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg)); - vmcs_write16(GUEST_PML_INDEX, PML_ENTITY_NUM - 1); - - return 0; -} - static void vmx_destroy_pml_buffer(struct vcpu_vmx *vmx) { if (vmx->pml_pg) { @@ -7915,7 +7905,7 @@ static void kvm_flush_pml_buffers(struct kvm *kvm) static void vmx_dump_sel(char *name, uint32_t sel) { pr_err("%s sel=0x%04x, attr=0x%05x, limit=0x%08x, base=0x%016lx\n", - name, vmcs_read32(sel), + name, vmcs_read16(sel), vmcs_read32(sel + GUEST_ES_AR_BYTES - GUEST_ES_SELECTOR), vmcs_read32(sel + GUEST_ES_LIMIT - GUEST_ES_SELECTOR), vmcs_readl(sel + GUEST_ES_BASE - GUEST_ES_SELECTOR)); @@ -8329,8 +8319,7 @@ static void vmx_complete_atomic_exit(struct vcpu_vmx *vmx) kvm_machine_check(); /* We need to handle NMIs before interrupts are enabled */ - if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR && - (exit_intr_info & INTR_INFO_VALID_MASK)) { + if (is_nmi(exit_intr_info)) { kvm_before_handle_nmi(&vmx->vcpu); asm("int $2"); kvm_after_handle_nmi(&vmx->vcpu); @@ -8790,14 +8779,26 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id) if (err) goto free_vcpu; + err = -ENOMEM; + + /* + * If PML is turned on, failure on enabling PML just results in failure + * of creating the vcpu, therefore we can simplify PML logic (by + * avoiding dealing with cases, such as enabling PML partially on vcpus + * for the guest, etc. + */ + if (enable_pml) { + vmx->pml_pg = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!vmx->pml_pg) + goto uninit_vcpu; + } + vmx->guest_msrs = kmalloc(PAGE_SIZE, GFP_KERNEL); BUILD_BUG_ON(ARRAY_SIZE(vmx_msr_index) * sizeof(vmx->guest_msrs[0]) > PAGE_SIZE); - err = -ENOMEM; - if (!vmx->guest_msrs) { - goto uninit_vcpu; - } + if (!vmx->guest_msrs) + goto free_pml; vmx->loaded_vmcs = &vmx->vmcs01; vmx->loaded_vmcs->vmcs = alloc_vmcs(); @@ -8841,18 +8842,6 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id) vmx->nested.current_vmptr = -1ull; vmx->nested.current_vmcs12 = NULL; - /* - * If PML is turned on, failure on enabling PML just results in failure - * of creating the vcpu, therefore we can simplify PML logic (by - * avoiding dealing with cases, such as enabling PML partially on vcpus - * for the guest, etc. - */ - if (enable_pml) { - err = vmx_create_pml_buffer(vmx); - if (err) - goto free_vmcs; - } - return &vmx->vcpu; free_vmcs: @@ -8860,6 +8849,8 @@ free_vmcs: free_loaded_vmcs(vmx->loaded_vmcs); free_msrs: kfree(vmx->guest_msrs); +free_pml: + vmx_destroy_pml_buffer(vmx); uninit_vcpu: kvm_vcpu_uninit(&vmx->vcpu); free_vcpu: diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 7429d481a311..e75095fa414e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2949,6 +2949,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, memset(&events->reserved, 0, sizeof(events->reserved)); } +static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags); + static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, struct kvm_vcpu_events *events) { @@ -2981,10 +2983,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, vcpu->arch.apic->sipi_vector = events->sipi_vector; if (events->flags & KVM_VCPUEVENT_VALID_SMM) { + u32 hflags = vcpu->arch.hflags; if (events->smi.smm) - vcpu->arch.hflags |= HF_SMM_MASK; + hflags |= HF_SMM_MASK; else - vcpu->arch.hflags &= ~HF_SMM_MASK; + hflags &= ~HF_SMM_MASK; + kvm_set_hflags(vcpu, hflags); + vcpu->arch.smi_pending = events->smi.pending; if (events->smi.smm_inside_nmi) vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK; @@ -3052,6 +3057,7 @@ static void fill_xsave(u8 *dest, struct kvm_vcpu *vcpu) memcpy(dest, xsave, XSAVE_HDR_OFFSET); /* Set XSTATE_BV */ + xstate_bv &= vcpu->arch.guest_supported_xcr0 | XFEATURE_MASK_FPSSE; *(u64 *)(dest + XSAVE_HDR_OFFSET) = xstate_bv; /* @@ -5837,6 +5843,7 @@ out: void kvm_arch_exit(void) { + kvm_lapic_exit(); perf_unregister_guest_info_callbacks(&kvm_guest_cbs); if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 3cd69832d7f4..3961103e9176 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -114,6 +114,16 @@ static const struct dmi_system_id pci_crs_quirks[] __initconst = { DMI_MATCH(DMI_BIOS_VERSION, "6JET85WW (1.43 )"), }, }, + /* https://bugzilla.kernel.org/show_bug.cgi?id=42606 */ + { + .callback = set_nouse_crs, + .ident = "Supermicro X8DTH", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), + DMI_MATCH(DMI_PRODUCT_NAME, "X8DTH-i/6/iF/6F"), + DMI_MATCH(DMI_BIOS_VERSION, "2.0a"), + }, + }, /* https://bugzilla.kernel.org/show_bug.cgi?id=15362 */ { diff --git a/arch/x86/platform/goldfish/goldfish.c b/arch/x86/platform/goldfish/goldfish.c index 1693107a518e..0d17c0aafeb1 100644 --- a/arch/x86/platform/goldfish/goldfish.c +++ b/arch/x86/platform/goldfish/goldfish.c @@ -42,10 +42,22 @@ static struct resource goldfish_pdev_bus_resources[] = { } }; +static bool goldfish_enable __initdata; + +static int __init goldfish_setup(char *str) +{ + goldfish_enable = true; + return 0; +} +__setup("goldfish", goldfish_setup); + static int __init goldfish_init(void) { + if (!goldfish_enable) + return -ENODEV; + platform_device_register_simple("goldfish_pdev_bus", -1, - goldfish_pdev_bus_resources, 2); + goldfish_pdev_bus_resources, 2); return 0; } device_initcall(goldfish_init); diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 9735691f37f1..49ccbd9022f6 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -133,6 +133,8 @@ static int __init parse_tag_initrd(const bp_tag_t* tag) __tagtable(BP_TAG_INITRD, parse_tag_initrd); +#endif /* CONFIG_BLK_DEV_INITRD */ + #ifdef CONFIG_OF static int __init parse_tag_fdt(const bp_tag_t *tag) @@ -145,8 +147,6 @@ __tagtable(BP_TAG_FDT, parse_tag_fdt); #endif /* CONFIG_OF */ -#endif /* CONFIG_BLK_DEV_INITRD */ - static int __init parse_tag_cmdline(const bp_tag_t* tag) { strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE); diff --git a/block/blk-mq.c b/block/blk-mq.c index 53c0f0b5f3ad..8bd548378822 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -842,7 +842,7 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) return WORK_CPU_UNBOUND; if (--hctx->next_cpu_batch <= 0) { - int cpu = hctx->next_cpu, next_cpu; + int next_cpu; next_cpu = cpumask_next(hctx->next_cpu, hctx->cpumask); if (next_cpu >= nr_cpu_ids) @@ -850,8 +850,6 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) hctx->next_cpu = next_cpu; hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH; - - return cpu; } return hctx->next_cpu; @@ -1261,12 +1259,9 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) blk_queue_split(q, &bio, q->bio_split); - if (!is_flush_fua && !blk_queue_nomerges(q)) { - if (blk_attempt_plug_merge(q, bio, &request_count, - &same_queue_rq)) - return BLK_QC_T_NONE; - } else - request_count = blk_plug_queued_count(q); + if (!is_flush_fua && !blk_queue_nomerges(q) && + blk_attempt_plug_merge(q, bio, &request_count, &same_queue_rq)) + return BLK_QC_T_NONE; rq = blk_mq_map_request(q, bio, &data); if (unlikely(!rq)) @@ -1313,9 +1308,9 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) blk_mq_put_ctx(data.ctx); if (!old_rq) goto done; - if (!blk_mq_direct_issue_request(old_rq, &cookie)) - goto done; - blk_mq_insert_request(old_rq, false, true, true); + if (test_bit(BLK_MQ_S_STOPPED, &data.hctx->state) || + blk_mq_direct_issue_request(old_rq, &cookie) != 0) + blk_mq_insert_request(old_rq, false, true, true); goto done; } @@ -1357,9 +1352,11 @@ static blk_qc_t blk_sq_make_request(struct request_queue *q, struct bio *bio) blk_queue_split(q, &bio, q->bio_split); - if (!is_flush_fua && !blk_queue_nomerges(q) && - blk_attempt_plug_merge(q, bio, &request_count, NULL)) - return BLK_QC_T_NONE; + if (!is_flush_fua && !blk_queue_nomerges(q)) { + if (blk_attempt_plug_merge(q, bio, &request_count, NULL)) + return BLK_QC_T_NONE; + } else + request_count = blk_plug_queued_count(q); rq = blk_mq_map_request(q, bio, &data); if (unlikely(!rq)) diff --git a/block/bsg.c b/block/bsg.c index d214e929ce18..b9a53615bdef 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -655,6 +655,9 @@ bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) dprintk("%s: write %Zd bytes\n", bd->name, count); + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + return -EINVAL; + bsg_set_block(bd, file); bytes_written = 0; diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 3ad307ee6029..e04a7b8492cf 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1572,7 +1572,7 @@ static struct blkcg_policy_data *cfq_cpd_alloc(gfp_t gfp) { struct cfq_group_data *cgd; - cgd = kzalloc(sizeof(*cgd), GFP_KERNEL); + cgd = kzalloc(sizeof(*cgd), gfp); if (!cgd) return NULL; return &cgd->cpd; diff --git a/crypto/Makefile b/crypto/Makefile index f7aba923458d..03e66097eb0c 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o $(obj)/rsapubkey-asn1.o: $(obj)/rsapubkey-asn1.c $(obj)/rsapubkey-asn1.h $(obj)/rsaprivkey-asn1.o: $(obj)/rsaprivkey-asn1.c $(obj)/rsaprivkey-asn1.h +$(obj)/rsa_helper.o: $(obj)/rsapubkey-asn1.h $(obj)/rsaprivkey-asn1.h clean-files += rsapubkey-asn1.c rsapubkey-asn1.h clean-files += rsaprivkey-asn1.c rsaprivkey-asn1.h @@ -61,6 +62,7 @@ obj-$(CONFIG_CRYPTO_SHA1) += sha1_generic.o obj-$(CONFIG_CRYPTO_SHA256) += sha256_generic.o obj-$(CONFIG_CRYPTO_SHA512) += sha512_generic.o obj-$(CONFIG_CRYPTO_WP512) += wp512.o +CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149 obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o obj-$(CONFIG_CRYPTO_ECB) += ecb.o @@ -84,6 +86,7 @@ obj-$(CONFIG_CRYPTO_BLOWFISH_COMMON) += blowfish_common.o obj-$(CONFIG_CRYPTO_TWOFISH) += twofish_generic.o obj-$(CONFIG_CRYPTO_TWOFISH_COMMON) += twofish_common.o obj-$(CONFIG_CRYPTO_SERPENT) += serpent_generic.o +CFLAGS_serpent_generic.o := $(call cc-option,-fsched-pressure) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149 obj-$(CONFIG_CRYPTO_AES) += aes_generic.o obj-$(CONFIG_CRYPTO_CAMELLIA) += camellia_generic.o obj-$(CONFIG_CRYPTO_CAST_COMMON) += cast_common.o diff --git a/crypto/algapi.c b/crypto/algapi.c index 59bf491fe3d8..43f5bdb6b570 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -357,6 +357,7 @@ int crypto_register_alg(struct crypto_alg *alg) struct crypto_larval *larval; int err; + alg->cra_flags &= ~CRYPTO_ALG_DEAD; err = crypto_check_alg(alg); if (err) return err; diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c index fe5b495a434d..a0ceb41d5ccc 100644 --- a/crypto/mcryptd.c +++ b/crypto/mcryptd.c @@ -258,18 +258,22 @@ out_free_inst: goto out; } -static inline void mcryptd_check_internal(struct rtattr **tb, u32 *type, +static inline bool mcryptd_check_internal(struct rtattr **tb, u32 *type, u32 *mask) { struct crypto_attr_type *algt; algt = crypto_get_attr_type(tb); if (IS_ERR(algt)) - return; - if ((algt->type & CRYPTO_ALG_INTERNAL)) - *type |= CRYPTO_ALG_INTERNAL; - if ((algt->mask & CRYPTO_ALG_INTERNAL)) - *mask |= CRYPTO_ALG_INTERNAL; + return false; + + *type |= algt->type & CRYPTO_ALG_INTERNAL; + *mask |= algt->mask & CRYPTO_ALG_INTERNAL; + + if (*type & *mask & CRYPTO_ALG_INTERNAL) + return true; + else + return false; } static int mcryptd_hash_init_tfm(struct crypto_tfm *tfm) @@ -498,7 +502,8 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, u32 mask = 0; int err; - mcryptd_check_internal(tb, &type, &mask); + if (!mcryptd_check_internal(tb, &type, &mask)) + return -EINVAL; salg = shash_attr_alg(tb[1], type, mask); if (IS_ERR(salg)) diff --git a/crypto/testmgr.h b/crypto/testmgr.h index da0a8fd765f4..0e02c60a57b6 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -21778,7 +21778,7 @@ static struct aead_testvec aes_ccm_enc_tv_template[] = { "\x09\x75\x9a\x9b\x3c\x9b\x27\x39", .klen = 32, .iv = "\x03\xf9\xd9\x4e\x63\xb5\x3d\x9d" - "\x43\xf6\x1e\x50", + "\x43\xf6\x1e\x50\0\0\0\0", .assoc = "\x57\xf5\x6b\x8b\x57\x5c\x3d\x3b" "\x13\x02\x01\x0c\x83\x4c\x96\x35" "\x8e\xd6\x39\xcf\x7d\x14\x9b\x94" diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 8f8da9f92090..eac4f3b02df9 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -847,6 +847,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (ghes_read_estatus(ghes, 1)) { ghes_clear_estatus(ghes); continue; + } else { + ret = NMI_HANDLED; } sev = ghes_severity(ghes->estatus->error_severity); @@ -858,12 +860,11 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) __process_error(ghes); ghes_clear_estatus(ghes); - - ret = NMI_HANDLED; } #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - irq_work_queue(&ghes_proc_irq_work); + if (ret == NMI_HANDLED) + irq_work_queue(&ghes_proc_irq_work); #endif atomic_dec(&ghes_in_nmi); return ret; diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index c097f477c74c..14c2a07c9f3f 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -965,7 +965,7 @@ static size_t sizeof_nfit_set_info(int num_mappings) + num_mappings * sizeof(struct nfit_set_info_map); } -static int cmp_map(const void *m0, const void *m1) +static int cmp_map_compat(const void *m0, const void *m1) { const struct nfit_set_info_map *map0 = m0; const struct nfit_set_info_map *map1 = m1; @@ -974,6 +974,14 @@ static int cmp_map(const void *m0, const void *m1) sizeof(u64)); } +static int cmp_map(const void *m0, const void *m1) +{ + const struct nfit_set_info_map *map0 = m0; + const struct nfit_set_info_map *map1 = m1; + + return map0->region_offset - map1->region_offset; +} + /* Retrieve the nth entry referencing this spa */ static struct acpi_nfit_memory_map *memdev_from_spa( struct acpi_nfit_desc *acpi_desc, u16 range_index, int n) @@ -1029,6 +1037,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), cmp_map, NULL); nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + + /* support namespaces created with the wrong sort order */ + sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), + cmp_map_compat, NULL); + nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + ndr_desc->nd_set = nd_set; devm_kfree(dev, info); diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 80e55cb0827b..b48ecbfc4498 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -271,6 +271,26 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), }, }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */ + .callback = video_detect_force_native, + .ident = "Dell XPS 17 L702X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1204476 */ + /* https://bugs.launchpad.net/ubuntu/+source/linux-lts-trusty/+bug/1416940 */ + .callback = video_detect_force_native, + .ident = "HP Pavilion dv6", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv6 Notebook PC"), + }, + }, + { }, }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 0f6591a72cf8..d0334e50f2f9 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2221,9 +2221,10 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_FAILED_REPLY; goto err_bad_offset; } - if (copy_from_user(sg_bufp, - (const void __user *)(uintptr_t) - bp->buffer, bp->length)) { + if (copy_from_user_preempt_disabled( + sg_bufp, + (const void __user *)(uintptr_t) + bp->buffer, bp->length)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; @@ -2511,7 +2512,8 @@ static int binder_thread_write(struct binder_proc *proc, case BC_REPLY_SG: { struct binder_transaction_data_sg tr; - if (copy_from_user(&tr, ptr, sizeof(tr))) + if (copy_from_user_preempt_disabled(&tr, ptr, + sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr.transaction_data, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bd370c98f77d..b0b77b61c40c 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4139,10 +4139,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "ST380013AS", "3.20", ATA_HORKAGE_MAX_SEC_1024 }, /* - * Device times out with higher max sects. + * These devices time out with higher max sects. * https://bugzilla.kernel.org/show_bug.cgi?id=121671 */ - { "LITEON CX1-JB256-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 }, + { "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 }, /* Devices we expect to fail diagnostics */ diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index bd74ee555278..729f26322095 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4121,6 +4121,9 @@ static int mv_platform_probe(struct platform_device *pdev) host->iomap = NULL; hpriv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!hpriv->base) + return -ENOMEM; + hpriv->base -= SATAHC0_REG_BASE; hpriv->clk = clk_get(&pdev->dev, NULL); diff --git a/drivers/base/core.c b/drivers/base/core.c index bbe8e2efc677..3fa9096b27c2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1116,7 +1116,13 @@ int device_add(struct device *dev) error = dpm_sysfs_add(dev); if (error) goto DPMError; - device_pm_add(dev); + if ((dev->pm_domain) || (dev->type && dev->type->pm) + || (dev->class && (dev->class->pm || dev->class->resume)) + || (dev->bus && (dev->bus->pm || dev->bus->resume)) || + (dev->driver && dev->driver->pm)) { + device_pm_add(dev); + } + if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 1c6e4da01e69..f8112c356bc5 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -1108,13 +1108,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, timeout = MAX_JIFFY_OFFSET; } - retval = wait_for_completion_interruptible_timeout(&buf->completion, + timeout = wait_for_completion_interruptible_timeout(&buf->completion, timeout); - if (retval == -ERESTARTSYS || !retval) { + if (timeout == -ERESTARTSYS || !timeout) { + retval = timeout; mutex_lock(&fw_lock); fw_load_abort(fw_priv); mutex_unlock(&fw_lock); - } else if (retval > 0) { + } else if (timeout > 0) { retval = 0; } diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 25425d3f2575..48c0a1d0dd3a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -388,30 +388,29 @@ static ssize_t show_valid_zones(struct device *dev, { struct memory_block *mem = to_memory_block(dev); unsigned long start_pfn, end_pfn; + unsigned long valid_start, valid_end; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; - struct page *first_page; struct zone *zone; start_pfn = section_nr_to_pfn(mem->start_section_nr); end_pfn = start_pfn + nr_pages; - first_page = pfn_to_page(start_pfn); /* The block contains more than one zone can not be offlined. */ - if (!test_pages_in_a_zone(start_pfn, end_pfn)) + if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end)) return sprintf(buf, "none\n"); - zone = page_zone(first_page); + zone = page_zone(pfn_to_page(valid_start)); if (zone_idx(zone) == ZONE_MOVABLE - 1) { /*The mem block is the last memoryblock of this zone.*/ - if (end_pfn == zone_end_pfn(zone)) + if (valid_end == zone_end_pfn(zone)) return sprintf(buf, "%s %s\n", zone->name, (zone + 1)->name); } if (zone_idx(zone) == ZONE_MOVABLE) { /*The mem block is the first memoryblock of ZONE_MOVABLE.*/ - if (start_pfn == zone->zone_start_pfn) + if (valid_start == zone->zone_start_pfn) return sprintf(buf, "%s %s\n", zone->name, (zone - 1)->name); } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6c5bc3fadfcf..a88590bb0b10 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -162,6 +162,12 @@ void device_pm_move_before(struct device *deva, struct device *devb) pr_debug("PM: Moving %s:%s before %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); + if (!((devb->pm_domain) || (devb->type && devb->type->pm) + || (devb->class && (devb->class->pm || devb->class->resume)) + || (devb->bus && (devb->bus->pm || devb->bus->resume)) || + (devb->driver && devb->driver->pm))) { + device_pm_add(devb); + } /* Delete deva from dpm_list and reinsert before devb. */ list_move_tail(&deva->power.entry, &devb->power.entry); } @@ -176,6 +182,12 @@ void device_pm_move_after(struct device *deva, struct device *devb) pr_debug("PM: Moving %s:%s after %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); + if (!((devb->pm_domain) || (devb->type && devb->type->pm) + || (devb->class && (devb->class->pm || devb->class->resume)) + || (devb->bus && (devb->bus->pm || devb->bus->resume)) || + (devb->driver && devb->driver->pm))) { + device_pm_add(devb); + } /* Delete deva from dpm_list and reinsert after devb. */ list_move(&deva->power.entry, &devb->power.entry); } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 297beae64314..297aa5cf393e 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -20,14 +20,22 @@ static inline void pm_runtime_early_init(struct device *dev) extern void pm_runtime_init(struct device *dev); extern void pm_runtime_remove(struct device *dev); +#define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0) +#define WAKE_IRQ_DEDICATED_MANAGED BIT(1) +#define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \ + WAKE_IRQ_DEDICATED_MANAGED) + struct wake_irq { struct device *dev; + unsigned int status; int irq; - bool dedicated_irq:1; }; extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); +extern void dev_pm_enable_wake_irq_check(struct device *dev, + bool can_change_status); +extern void dev_pm_disable_wake_irq_check(struct device *dev); #ifdef CONFIG_PM_SLEEP @@ -102,6 +110,15 @@ static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq) { } +static inline void dev_pm_enable_wake_irq_check(struct device *dev, + bool can_change_status) +{ +} + +static inline void dev_pm_disable_wake_irq_check(struct device *dev) +{ +} + #endif #ifdef CONFIG_PM_SLEEP diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 9796a1a15ef6..3252429f96af 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -515,7 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_suspend); - dev_pm_enable_wake_irq(dev); + dev_pm_enable_wake_irq_check(dev, true); retval = rpm_callback(callback, dev); if (retval) goto fail; @@ -554,7 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) return retval; fail: - dev_pm_disable_wake_irq(dev); + dev_pm_disable_wake_irq_check(dev); __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); @@ -737,12 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_resume); - dev_pm_disable_wake_irq(dev); + dev_pm_disable_wake_irq_check(dev); retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); - dev_pm_enable_wake_irq(dev); + dev_pm_enable_wake_irq_check(dev, false); } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 0d77cd6fd8d1..404d94c6c8bc 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -110,8 +110,10 @@ void dev_pm_clear_wake_irq(struct device *dev) dev->power.wakeirq = NULL; spin_unlock_irqrestore(&dev->power.lock, flags); - if (wirq->dedicated_irq) + if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) { free_irq(wirq->irq, wirq); + wirq->status &= ~WAKE_IRQ_DEDICATED_MASK; + } kfree(wirq); } EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); @@ -179,7 +181,6 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) wirq->dev = dev; wirq->irq = irq; - wirq->dedicated_irq = true; irq_set_status_flags(irq, IRQ_NOAUTOEN); /* @@ -195,6 +196,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) if (err) goto err_free_irq; + wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED; + return err; err_free_irq: @@ -210,9 +213,9 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq); * dev_pm_enable_wake_irq - Enable device wake-up interrupt * @dev: Device * - * Called from the bus code or the device driver for - * runtime_suspend() to enable the wake-up interrupt while - * the device is running. + * Optionally called from the bus code or the device driver for + * runtime_resume() to override the PM runtime core managed wake-up + * interrupt handling to enable the wake-up interrupt. * * Note that for runtime_suspend()) the wake-up interrupts * should be unconditionally enabled unlike for suspend() @@ -222,7 +225,7 @@ void dev_pm_enable_wake_irq(struct device *dev) { struct wake_irq *wirq = dev->power.wakeirq; - if (wirq && wirq->dedicated_irq) + if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) enable_irq(wirq->irq); } EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); @@ -231,20 +234,73 @@ EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); * dev_pm_disable_wake_irq - Disable device wake-up interrupt * @dev: Device * - * Called from the bus code or the device driver for - * runtime_resume() to disable the wake-up interrupt while - * the device is running. + * Optionally called from the bus code or the device driver for + * runtime_suspend() to override the PM runtime core managed wake-up + * interrupt handling to disable the wake-up interrupt. */ void dev_pm_disable_wake_irq(struct device *dev) { struct wake_irq *wirq = dev->power.wakeirq; - if (wirq && wirq->dedicated_irq) + if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) disable_irq_nosync(wirq->irq); } EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); /** + * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt + * @dev: Device + * @can_change_status: Can change wake-up interrupt status + * + * Enables wakeirq conditionally. We need to enable wake-up interrupt + * lazily on the first rpm_suspend(). This is needed as the consumer device + * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would + * otherwise try to disable already disabled wakeirq. The wake-up interrupt + * starts disabled with IRQ_NOAUTOEN set. + * + * Should be only called from rpm_suspend() and rpm_resume() path. + * Caller must hold &dev->power.lock to change wirq->status + */ +void dev_pm_enable_wake_irq_check(struct device *dev, + bool can_change_status) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK))) + return; + + if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) { + goto enable; + } else if (can_change_status) { + wirq->status |= WAKE_IRQ_DEDICATED_MANAGED; + goto enable; + } + + return; + +enable: + enable_irq(wirq->irq); +} + +/** + * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt + * @dev: Device + * + * Disables wake-up interrupt conditionally based on status. + * Should be only called from rpm_suspend() and rpm_resume() path. + */ +void dev_pm_disable_wake_irq_check(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK))) + return; + + if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) + disable_irq_nosync(wirq->irq); +} + +/** * dev_pm_arm_wake_irq - Arm device wake-up * @wirq: Device wake-up interrupt * diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 59d8d0d14824..327f9e374b44 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -640,8 +640,11 @@ static int bcma_device_probe(struct device *dev) drv); int err = 0; + get_device(dev); if (adrv->probe) err = adrv->probe(core); + if (err) + put_device(dev); return err; } @@ -654,6 +657,7 @@ static int bcma_device_remove(struct device *dev) if (adrv->remove) adrv->remove(core); + put_device(dev); return 0; } diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 80cf8add46ff..cec36d5c24f5 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1108,9 +1108,12 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE) return -EINVAL; + /* I/O need to be drained during transfer transition */ + blk_mq_freeze_queue(lo->lo_queue); + err = loop_release_xfer(lo); if (err) - return err; + goto exit; if (info->lo_encrypt_type) { unsigned int type = info->lo_encrypt_type; @@ -1125,12 +1128,14 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) err = loop_init_xfer(lo, xfer, info); if (err) - return err; + goto exit; if (lo->lo_offset != info->lo_offset || lo->lo_sizelimit != info->lo_sizelimit) - if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) - return -EFBIG; + if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { + err = -EFBIG; + goto exit; + } loop_config_discard(lo); @@ -1148,13 +1153,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) (info->lo_flags & LO_FLAGS_AUTOCLEAR)) lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; - if ((info->lo_flags & LO_FLAGS_PARTSCAN) && - !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { - lo->lo_flags |= LO_FLAGS_PARTSCAN; - lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; - loop_reread_partitions(lo, lo->lo_device); - } - lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_init[0] = info->lo_init[0]; lo->lo_init[1] = info->lo_init[1]; @@ -1167,7 +1165,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) /* update dio if lo_offset or transfer is changed */ __loop_update_dio(lo, lo->use_dio); - return 0; + exit: + blk_mq_unfreeze_queue(lo->lo_queue); + + if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) && + !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { + lo->lo_flags |= LO_FLAGS_PARTSCAN; + lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; + loop_reread_partitions(lo, lo->lo_device); + } + + return err; } static int @@ -1657,7 +1665,7 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx, blk_mq_start_request(bd->rq); if (lo->lo_state != Lo_bound) - return -EIO; + return BLK_MQ_RQ_QUEUE_ERROR; if (lo->use_dio && !(cmd->rq->cmd_flags & (REQ_FLUSH | REQ_DISCARD))) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index eada042b6eab..76ac3179f25c 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1391,8 +1391,14 @@ static ssize_t hot_remove_store(struct class *class, return ret ? ret : count; } +/* + * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a + * sense that reading from this file does alter the state of your system -- it + * creates a new un-initialized zram device and returns back this device's + * device_id (or an error code if it fails to create a new device). + */ static struct class_attribute zram_control_class_attrs[] = { - __ATTR_RO(hot_add), + __ATTR(hot_add, 0400, hot_add_show, NULL), __ATTR_WO(hot_remove), __ATTR_NULL, }; diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 0beaa52df66b..5df8e1234505 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -94,6 +94,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x04CA, 0x300f) }, { USB_DEVICE(0x04CA, 0x3010) }, { USB_DEVICE(0x04CA, 0x3014) }, + { USB_DEVICE(0x04CA, 0x3018) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0930, 0x021c) }, { USB_DEVICE(0x0930, 0x0220) }, @@ -160,6 +161,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c index 061177173ab8..1ed366fd3d71 100644 --- a/drivers/bluetooth/btfm_slim_codec.c +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -92,6 +92,9 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, return; } + if (dai->id == BTFM_FM_SLIM_TX) + goto out; + /* Search for dai->id matched port handler */ for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && @@ -105,6 +108,7 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, } btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); +out: btfm_slim_hw_deinit(btfmslim); } @@ -167,6 +171,61 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream, return ret; } +static int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = -EINVAL, i; + struct btfmslim *btfmslim = dai->dev->platform_data; + struct btfmslim_ch *ch; + uint8_t rxport, grp = false, nchan = 1; + + BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, + dai->id, dai->rate); + + switch (dai->id) { + case BTFM_FM_SLIM_TX: + grp = true; nchan = 2; + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_SLIM_TX: + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_A2DP_SLIM_RX: + case BTFM_BT_SPLIT_A2DP_SLIM_RX: + ch = btfmslim->rx_chs; + rxport = 1; + break; + case BTFM_SLIM_NUM_CODEC_DAIS: + default: + BTFMSLIM_ERR("dai->id is invalid:%d", dai->id); + goto out; + } + + if (dai->id != BTFM_FM_SLIM_TX) { + ret = 0; + goto out; + } + + /* Search for dai->id matched port handler */ + for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != dai->id); ch++, i++) + ; + + if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) || + (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) { + BTFMSLIM_ERR("ch is invalid!!"); + goto out; + } + + btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); + +out: + return ret; +} + /* This function will be called once during boot up */ static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, @@ -306,6 +365,7 @@ static struct snd_soc_dai_ops btfmslim_dai_ops = { .shutdown = btfm_slim_dai_shutdown, .hw_params = btfm_slim_dai_hw_params, .prepare = btfm_slim_dai_prepare, + .hw_free = btfm_slim_dai_hw_free, .set_channel_map = btfm_slim_dai_set_channel_map, .get_channel_map = btfm_slim_dai_get_channel_map, }; diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index 72e28da4bd3b..d61454ac6e84 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -39,6 +39,7 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim) { int ret = 0; uint8_t reg_val; + uint16_t reg; BTFMSLIM_DBG(""); @@ -46,20 +47,20 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim) return -EINVAL; /* Get SB_SLAVE_HW_REV_MSB value*/ - ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_MSB, 1, - ®_val, IFD); + reg = CHRK_SB_SLAVE_HW_REV_MSB; + ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to read (%d)", ret); + BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); goto error; } BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x", (reg_val & 0xF0) >> 4, (reg_val & 0x0F)); /* Get SB_SLAVE_HW_REV_LSB value*/ - ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_LSB, 1, - ®_val, IFD); + reg = CHRK_SB_SLAVE_HW_REV_LSB; + ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to read (%d)", ret); + BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); goto error; } BTFMSLIM_DBG("Step Rev: 0x%x", reg_val); @@ -80,41 +81,41 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, if (rxport) { /* Port enable */ reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10); - } else { /* txport */ - /* Multiple Channel Setting - only FM Tx will be multiple - * channel - */ - if (enable && (port_num == CHRK_SB_PGD_PORT_TX1_FM || - port_num == CHRK_SB_PGD_PORT_TX2_FM)) { - - reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | - (0x1 << CHRK_SB_PGD_PORT_TX2_FM); - reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); - ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); - if (ret) { - BTFMSLIM_ERR("failed to write (%d)", ret); - goto error; - } - } + goto enable_disable_rxport; + } + /* txport */ + if (!enable) + goto enable_disable_txport; - /* Enable Tx port hw auto recovery for underrun or - * overrun error - */ - reg_val = (enable) ? (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY | - CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY) : 0x0; + /* Multiple Channel Setting - only for FM Tx */ + if (port_num == CHRK_SB_PGD_PORT_TX1_FM || + port_num == CHRK_SB_PGD_PORT_TX2_FM) { - ret = btfm_slim_write(btfmslim, - CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num), 1, - ®_val, IFD); + reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | + (0x1 << CHRK_SB_PGD_PORT_TX2_FM); + reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to write (%d)", ret); + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); goto error; } + } - /* Port enable */ - reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); + /* Enable Tx port hw auto recovery for underrun or overrun error */ + reg_val = (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY | + CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY); + reg = CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); + goto error; } +enable_disable_txport: + /* Port enable */ + reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); + +enable_disable_rxport: if (enable) /* Set water mark to 1 and enable the port */ reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB; @@ -123,7 +124,7 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) - BTFMSLIM_ERR("failed to write (%d)", ret); + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); error: return ret; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c306b483de60..cd6b141b9825 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -208,6 +208,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c index 6575c0fe6a4e..27ea64fa4f9b 100644 --- a/drivers/bus/vexpress-config.c +++ b/drivers/bus/vexpress-config.c @@ -171,6 +171,7 @@ static int vexpress_config_populate(struct device_node *node) { struct device_node *bridge; struct device *parent; + int ret; bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0); if (!bridge) @@ -181,7 +182,11 @@ static int vexpress_config_populate(struct device_node *node) if (WARN_ON(!parent)) return -ENODEV; - return of_platform_populate(node, NULL, NULL, parent); + ret = of_platform_populate(node, NULL, NULL, parent); + + put_device(parent); + + return ret; } static int __init vexpress_config_init(void) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 7820f5625266..e0106a7e31fa 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1435,6 +1435,12 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, int err = 0; struct timespec invoket; + VERIFY(err, fl->sctx); + if (err) + goto bail; + VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS); + if (err) + goto bail; if (fl->profile) getnstimeofday(&invoket); if (!kernel) { diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c index b861d5f32d03..ca7dd88048ac 100644 --- a/drivers/char/diag/diag_debugfs.c +++ b/drivers/char/diag/diag_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -72,6 +72,7 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, "Uses Device Tree: %d\n" "Apps Supports Separate CMDRSP: %d\n" "Apps Supports HDLC Encoding: %d\n" + "Apps Supports Header Untagging: %d\n" "Apps Supports Sockets: %d\n" "Logging Mode: %d\n" "RSP Buffer is Busy: %d\n" @@ -86,6 +87,7 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, driver->use_device_tree, driver->supports_separate_cmdrsp, driver->supports_apps_hdlc_encoding, + driver->supports_apps_header_untagging, driver->supports_sockets, driver->logging_mode, driver->rsp_buf_busy, @@ -97,18 +99,19 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, for (i = 0; i < NUM_PERIPHERALS; i++) { ret += scnprintf(buf+ret, buf_size-ret, - "p: %s Feature: %02x %02x |%c%c%c%c%c%c%c%c|\n", + "p: %s Feature: %02x %02x |%c%c%c%c%c%c%c%c%c|\n", PERIPHERAL_STRING(i), driver->feature[i].feature_mask[0], driver->feature[i].feature_mask[1], driver->feature[i].rcvd_feature_mask ? 'F':'f', + driver->feature[i].peripheral_buffering ? 'B':'b', driver->feature[i].separate_cmd_rsp ? 'C':'c', driver->feature[i].encode_hdlc ? 'H':'h', - driver->feature[i].peripheral_buffering ? 'B':'b', driver->feature[i].mask_centralization ? 'M':'m', driver->feature[i].stm_support ? 'Q':'q', driver->feature[i].sockets_enabled ? 'S':'s', - driver->feature[i].sent_feature_mask ? 'T':'t'); + driver->feature[i].sent_feature_mask ? 'T':'t', + driver->feature[i].untag_header ? 'U':'u'); } #ifdef CONFIG_DIAG_OVER_USB diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 44e71a704e6a..0c958d855f94 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -456,6 +456,8 @@ static void diag_send_feature_mask_update(uint8_t peripheral) DIAG_SET_FEATURE_MASK(F_DIAG_REQ_RSP_SUPPORT); if (driver->supports_apps_hdlc_encoding) DIAG_SET_FEATURE_MASK(F_DIAG_APPS_HDLC_ENCODE); + if (driver->supports_apps_header_untagging) + DIAG_SET_FEATURE_MASK(F_DIAG_PKT_HEADER_UNTAG); DIAG_SET_FEATURE_MASK(F_DIAG_MASK_CENTRALIZATION); if (driver->supports_sockets) DIAG_SET_FEATURE_MASK(F_DIAG_SOCKETS_ENABLED); diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index c552f263d7e5..dc3029cc459d 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,7 @@ #include "diagmem.h" #include "diagfwd.h" #include "diagfwd_peripheral.h" +#include "diag_ipc_logging.h" struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = { { @@ -143,9 +144,24 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) if (!buf || len < 0) return -EINVAL; - peripheral = GET_BUF_PERIPHERAL(ctx); - if (peripheral > NUM_PERIPHERALS) - return -EINVAL; + if (driver->pd_logging_mode) { + peripheral = GET_PD_CTXT(ctx); + switch (peripheral) { + case UPD_WLAN: + break; + case DIAG_ID_MPSS: + default: + peripheral = GET_BUF_PERIPHERAL(ctx); + if (peripheral > NUM_PERIPHERALS) + return -EINVAL; + break; + } + } else { + /* Account for Apps data as well */ + peripheral = GET_BUF_PERIPHERAL(ctx); + if (peripheral > NUM_PERIPHERALS) + return -EINVAL; + } session_info = diag_md_session_get_peripheral(peripheral); if (!session_info) @@ -219,18 +235,41 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, uint8_t peripheral = 0; struct diag_md_session_t *session_info = NULL; + mutex_lock(&driver->diagfwd_untag_mutex); + for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) { ch = &diag_md[i]; for (j = 0; j < ch->num_tbl_entries && !err; j++) { entry = &ch->tbl[j]; if (entry->len <= 0) continue; - peripheral = GET_BUF_PERIPHERAL(entry->ctx); - /* Account for Apps data as well */ - if (peripheral > NUM_PERIPHERALS) - goto drop_data; + if (driver->pd_logging_mode) { + peripheral = GET_PD_CTXT(entry->ctx); + switch (peripheral) { + case UPD_WLAN: + break; + case DIAG_ID_MPSS: + default: + peripheral = + GET_BUF_PERIPHERAL(entry->ctx); + if (peripheral > NUM_PERIPHERALS) + goto drop_data; + break; + } + } else { + /* Account for Apps data as well */ + peripheral = GET_BUF_PERIPHERAL(entry->ctx); + if (peripheral > NUM_PERIPHERALS) + goto drop_data; + } + session_info = diag_md_session_get_peripheral(peripheral); + if (!session_info) { + mutex_unlock(&driver->diagfwd_untag_mutex); + return -EIO; + } + if (session_info && info && (session_info->pid != info->pid)) continue; @@ -303,6 +342,8 @@ drop_data: if (drain_again) chk_logging_wakeup(); + mutex_unlock(&driver->diagfwd_untag_mutex); + return err; } @@ -322,7 +363,8 @@ int diag_md_close_peripheral(int id, uint8_t peripheral) spin_lock_irqsave(&ch->lock, flags); for (i = 0; i < ch->num_tbl_entries && !found; i++) { entry = &ch->tbl[i]; - if (GET_BUF_PERIPHERAL(entry->ctx) != peripheral) + if ((GET_BUF_PERIPHERAL(entry->ctx) != peripheral) || + (GET_PD_CTXT(entry->ctx) != peripheral)) continue; found = 1; if (ch->ops && ch->ops->write_done) { diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c index 6586f5e0cf86..55c5de1ea9fc 100644 --- a/drivers/char/diag/diag_mux.c +++ b/drivers/char/diag/diag_mux.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -133,21 +133,43 @@ int diag_mux_queue_read(int proc) int diag_mux_write(int proc, unsigned char *buf, int len, int ctx) { struct diag_logger_t *logger = NULL; - int peripheral; + int peripheral, upd; if (proc < 0 || proc >= NUM_MUX_PROC) return -EINVAL; if (!diag_mux) return -EIO; - peripheral = GET_BUF_PERIPHERAL(ctx); - if (peripheral > NUM_PERIPHERALS) - return -EINVAL; - - if (MD_PERIPHERAL_MASK(peripheral) & diag_mux->mux_mask) - logger = diag_mux->md_ptr; - else - logger = diag_mux->usb_ptr; + upd = GET_PD_CTXT(ctx); + if (upd) { + switch (upd) { + case DIAG_ID_MPSS: + upd = PERIPHERAL_MODEM; + break; + case UPD_WLAN: + break; + default: + pr_err("diag: invalid pd ctxt= %d\n", upd); + return -EINVAL; + } + if (((MD_PERIPHERAL_MASK(upd)) & + (diag_mux->mux_mask)) && + driver->md_session_map[upd]) + logger = diag_mux->md_ptr; + else + logger = diag_mux->usb_ptr; + } else { + + peripheral = GET_BUF_PERIPHERAL(ctx); + if (peripheral > NUM_PERIPHERALS) + return -EINVAL; + + if (MD_PERIPHERAL_MASK(peripheral) & + diag_mux->mux_mask) + logger = diag_mux->md_ptr; + else + logger = diag_mux->usb_ptr; + } if (logger && logger->log_ops && logger->log_ops->write) return logger->log_ops->write(proc, buf, len, ctx); @@ -159,9 +181,17 @@ int diag_mux_close_peripheral(int proc, uint8_t peripheral) struct diag_logger_t *logger = NULL; if (proc < 0 || proc >= NUM_MUX_PROC) return -EINVAL; + /* Peripheral should account for Apps data as well */ - if (peripheral > NUM_PERIPHERALS) - return -EINVAL; + if (peripheral > NUM_PERIPHERALS) { + if (driver->num_pd_session) { + if (peripheral > NUM_MD_SESSIONS) + return -EINVAL; + } else { + return -EINVAL; + } + } + if (!diag_mux) return -EIO; @@ -182,7 +212,8 @@ int diag_mux_switch_logging(int *req_mode, int *peripheral_mask) if (!req_mode) return -EINVAL; - if (*peripheral_mask <= 0 || *peripheral_mask > DIAG_CON_ALL) { + if (*peripheral_mask <= 0 || + (*peripheral_mask > (DIAG_CON_ALL | DIAG_CON_UPD_ALL))) { pr_err("diag: mask %d in %s\n", *peripheral_mask, __func__); return -EINVAL; } diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 9d235b7abc58..511b019e33ec 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -64,14 +64,19 @@ #define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */ #define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */ #define DIAG_CON_SENSORS (0x0010) /* Bit mask for Sensors */ -#define DIAG_CON_WDSP (0x0020) /* Bit mask for WDSP */ -#define DIAG_CON_CDSP (0x0040) +#define DIAG_CON_WDSP (0x0020) /* Bit mask for WDSP */ +#define DIAG_CON_CDSP (0x0040) /* Bit mask for CDSP */ + +#define DIAG_CON_UPD_WLAN (0x1000) /*Bit mask for WLAN PD*/ +#define DIAG_CON_UPD_AUDIO (0x2000) /*Bit mask for AUDIO PD*/ +#define DIAG_CON_UPD_SENSORS (0x4000) /*Bit mask for SENSORS PD*/ #define DIAG_CON_NONE (0x0000) /* Bit mask for No SS*/ #define DIAG_CON_ALL (DIAG_CON_APSS | DIAG_CON_MPSS \ | DIAG_CON_LPASS | DIAG_CON_WCNSS \ | DIAG_CON_SENSORS | DIAG_CON_WDSP \ | DIAG_CON_CDSP) +#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN) #define DIAG_STM_MODEM 0x01 #define DIAG_STM_LPASS 0x02 @@ -165,7 +170,7 @@ #define PKT_ALLOC 1 #define PKT_RESET 2 -#define FEATURE_MASK_LEN 2 +#define FEATURE_MASK_LEN 4 #define DIAG_MD_NONE 0 #define DIAG_MD_PERIPHERAL 1 @@ -209,8 +214,18 @@ #define NUM_PERIPHERALS 6 #define APPS_DATA (NUM_PERIPHERALS) +#define UPD_WLAN 7 +#define UPD_AUDIO 8 +#define UPD_SENSORS 9 +#define NUM_UPD 3 + +#define DIAG_ID_APPS 1 +#define DIAG_ID_MPSS 2 +#define DIAG_ID_WLAN 3 + /* Number of sessions possible in Memory Device Mode. +1 for Apps data */ -#define NUM_MD_SESSIONS (NUM_PERIPHERALS + 1) +#define NUM_MD_SESSIONS (NUM_PERIPHERALS \ + + NUM_UPD + 1) #define MD_PERIPHERAL_MASK(x) (1 << x) @@ -407,6 +422,7 @@ struct diag_partial_pkt_t { struct diag_logging_mode_param_t { uint32_t req_mode; uint32_t peripheral_mask; + uint32_t pd_mask; uint8_t mode_param; } __packed; @@ -418,6 +434,7 @@ struct diag_md_session_t { struct diag_mask_info *msg_mask; struct diag_mask_info *log_mask; struct diag_mask_info *event_mask; + struct thread_info *md_client_thread_info; struct task_struct *task; }; @@ -453,6 +470,7 @@ struct diag_feature_t { uint8_t log_on_demand; uint8_t separate_cmd_rsp; uint8_t encode_hdlc; + uint8_t untag_header; uint8_t peripheral_buffering; uint8_t mask_centralization; uint8_t stm_support; @@ -484,6 +502,7 @@ struct diagchar_dev { int use_device_tree; int supports_separate_cmdrsp; int supports_apps_hdlc_encoding; + int supports_apps_header_untagging; int supports_sockets; /* The state requested in the STM command */ int stm_state_requested[NUM_STM_PROCESSORS]; @@ -515,6 +534,7 @@ struct diagchar_dev { struct mutex cmd_reg_mutex; uint32_t cmd_reg_count; struct mutex diagfwd_channel_mutex[NUM_PERIPHERALS]; + struct mutex diagfwd_untag_mutex; /* Sizes that reflect memory pool sizes */ unsigned int poolsize; unsigned int poolsize_hdlc; @@ -577,6 +597,10 @@ struct diagchar_dev { int in_busy_dcipktdata; int logging_mode; int logging_mask; + int pd_logging_mode; + int num_pd_session; + int cpd_len_1; + int cpd_len_2; int mask_check; uint32_t md_session_mask; uint8_t md_session_mode; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 335064352789..4f56696f52e9 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -395,7 +395,8 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask) ret |= DIAG_CON_WDSP; if (peripheral_mask & MD_PERIPHERAL_MASK(PERIPHERAL_CDSP)) ret |= DIAG_CON_CDSP; - + if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) + ret |= DIAG_CON_UPD_WLAN; return ret; } int diag_mask_param(void) @@ -453,6 +454,14 @@ static void diag_close_logging_process(const int pid) params.mode_param = 0; params.peripheral_mask = diag_translate_kernel_to_user_mask(session_peripheral_mask); + if (driver->pd_logging_mode) + params.pd_mask = + diag_translate_kernel_to_user_mask(session_peripheral_mask); + + if (session_peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) { + driver->pd_logging_mode--; + driver->num_pd_session--; + } mutex_lock(&driver->diagchar_mutex); diag_switch_logging(¶ms); mutex_unlock(&driver->diagchar_mutex); @@ -1237,11 +1246,10 @@ int diag_md_session_create(int mode, int peripheral_mask, int proc) mutex_unlock(&driver->md_session_lock); return -ENOMEM; } - new_session->peripheral_mask = 0; new_session->pid = current->tgid; new_session->task = current; - + new_session->md_client_thread_info = current_thread_info(); new_session->log_mask = kzalloc(sizeof(struct diag_mask_info), GFP_KERNEL); if (!new_session->log_mask) { @@ -1359,7 +1367,6 @@ static void diag_md_session_close(struct diag_md_session_t *session_info) struct diag_md_session_t *diag_md_session_get_pid(int pid) { int i; - for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] && driver->md_session_map[i]->pid == pid) @@ -1475,7 +1482,10 @@ static int diag_md_session_check(int curr_mode, int req_mode, * If this session owns all the requested peripherals, then * call function to switch the modes/masks for the md_session */ + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); + if (!session_info) { *change_mode = 1; return 0; @@ -1504,7 +1514,9 @@ static int diag_md_session_check(int curr_mode, int req_mode, * owned by this md session */ change_mask = driver->md_session_mask & param->peripheral_mask; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); if (session_info) { if ((session_info->peripheral_mask & change_mask) @@ -1548,6 +1560,8 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask) ret |= (1 << PERIPHERAL_WDSP); if (peripheral_mask & DIAG_CON_CDSP) ret |= (1 << PERIPHERAL_CDSP); + if (peripheral_mask & DIAG_CON_UPD_WLAN) + ret |= (1 << UPD_WLAN); return ret; } @@ -1569,8 +1583,28 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param) return -EINVAL; } - peripheral_mask = diag_translate_mask(param->peripheral_mask); - param->peripheral_mask = peripheral_mask; + switch (param->pd_mask) { + case DIAG_CON_UPD_WLAN: + if (driver->md_session_map[PERIPHERAL_MODEM] && + (MD_PERIPHERAL_MASK(PERIPHERAL_MODEM) & + diag_mux->mux_mask)) { + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "diag_fr: User PD is already logging onto active peripheral logging\n"); + return -EINVAL; + } + peripheral_mask = + diag_translate_mask(param->pd_mask); + param->peripheral_mask = peripheral_mask; + driver->pd_logging_mode++; + driver->num_pd_session++; + break; + + default: + peripheral_mask = + diag_translate_mask(param->peripheral_mask); + param->peripheral_mask = peripheral_mask; + break; + } switch (param->req_mode) { case CALLBACK_MODE: @@ -1590,7 +1624,7 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param) curr_mode = driver->logging_mode; DIAG_LOG(DIAG_DEBUG_USERSPACE, - "request to switch logging from %d mask:%0x to %d mask:%0x\n", + "request to switch logging from %d mask:%0x to new_mode %d mask:%0x\n", curr_mode, driver->md_session_mask, new_mode, peripheral_mask); err = diag_md_session_check(curr_mode, new_mode, param, &do_switch); @@ -1892,8 +1926,9 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg) { uint8_t hdlc_support; struct diag_md_session_t *session_info = NULL; - + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); if (copy_from_user(&hdlc_support, (void __user *)ioarg, sizeof(uint8_t))) return -EFAULT; @@ -1910,6 +1945,27 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg) return 0; } +static int diag_ioctl_query_pd_logging(unsigned long ioarg) +{ + int ret = -EINVAL; + + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "diag: %s: Untagging support on APPS is %s\n", __func__, + ((driver->supports_apps_header_untagging) ? + "present" : "absent")); + + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "diag: %s: Tagging support on MODEM is %s\n", __func__, + (driver->feature[PERIPHERAL_MODEM].untag_header ? + "present" : "absent")); + + if (driver->supports_apps_header_untagging && + driver->feature[PERIPHERAL_MODEM].untag_header) + ret = 0; + + return ret; +} + static int diag_ioctl_register_callback(unsigned long ioarg) { int err = 0; @@ -2149,6 +2205,9 @@ long diagchar_compat_ioctl(struct file *filp, case DIAG_IOCTL_HDLC_TOGGLE: result = diag_ioctl_hdlc_toggle(ioarg); break; + case DIAG_IOCTL_QUERY_PD_LOGGING: + result = diag_ioctl_query_pd_logging(ioarg); + break; } return result; } @@ -2272,6 +2331,9 @@ long diagchar_ioctl(struct file *filp, case DIAG_IOCTL_HDLC_TOGGLE: result = diag_ioctl_hdlc_toggle(ioarg); break; + case DIAG_IOCTL_QUERY_PD_LOGGING: + result = diag_ioctl_query_pd_logging(ioarg); + break; } return result; } @@ -2603,7 +2665,9 @@ static int diag_user_process_raw_data(const char __user *buf, int len) } else { wait_event_interruptible(driver->wait_q, (driver->in_busy_pktdata == 0)); + mutex_lock(&driver->md_session_lock); info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); ret = diag_process_apps_pkt(user_space_data, len, info); if (ret == 1) diag_send_error_rsp((void *)(user_space_data), len, @@ -2671,7 +2735,9 @@ static int diag_user_process_userspace_data(const char __user *buf, int len) /* send masks to local processor now */ if (!remote_proc) { + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); if (!session_info) { pr_err("diag:In %s request came from invalid md session pid:%d", __func__, current->tgid); @@ -2832,7 +2898,9 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); /* place holder for number of data field */ ret += sizeof(int); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); exit_stat = diag_md_copy_to_user(buf, &ret, count, session_info); goto exit; @@ -2846,7 +2914,9 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, data_type = driver->data_ready[index] & HDLC_SUPPORT_TYPE; driver->data_ready[index] ^= HDLC_SUPPORT_TYPE; COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); + mutex_unlock(&driver->md_session_lock); if (session_info) COPY_USER_SPACE_OR_EXIT(buf+4, session_info->hdlc_disabled, @@ -3275,7 +3345,7 @@ static void diag_debug_init(void) * to be logged to IPC */ diag_debug_mask = DIAG_DEBUG_PERIPHERALS | DIAG_DEBUG_DCI | - DIAG_DEBUG_BRIDGE; + DIAG_DEBUG_USERSPACE | DIAG_DEBUG_BRIDGE; } #else static void diag_debug_init(void) @@ -3404,6 +3474,8 @@ static int __init diagchar_init(void) poolsize_usb_apps + 1 + (NUM_PERIPHERALS * 6)); driver->num_clients = max_clients; driver->logging_mode = DIAG_USB_MODE; + driver->pd_logging_mode = 0; + driver->num_pd_session = 0; driver->mask_check = 0; driver->in_busy_pktdata = 0; driver->in_busy_dcipktdata = 0; @@ -3421,6 +3493,7 @@ static int __init diagchar_init(void) mutex_init(&apps_data_mutex); for (i = 0; i < NUM_PERIPHERALS; i++) mutex_init(&driver->diagfwd_channel_mutex[i]); + mutex_init(&driver->diagfwd_untag_mutex); init_waitqueue_head(&driver->wait_q); INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn); INIT_WORK(&(driver->update_user_clients), diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index 99a16dd47cd4..532d2b149317 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -1587,6 +1587,7 @@ int diagfwd_init(void) driver->real_time_mode[i] = 1; driver->supports_separate_cmdrsp = 1; driver->supports_apps_hdlc_encoding = 1; + driver->supports_apps_header_untagging = 1; mutex_init(&driver->diag_hdlc_mutex); mutex_init(&driver->diag_cntl_mutex); mutex_init(&driver->mode_lock); @@ -1616,6 +1617,8 @@ int diagfwd_init(void) driver->feature[i].rcvd_feature_mask = 0; driver->feature[i].peripheral_buffering = 0; driver->feature[i].encode_hdlc = 0; + driver->feature[i].untag_header = + DISABLE_PKT_HEADER_UNTAGGING; driver->feature[i].mask_centralization = 0; driver->feature[i].log_on_demand = 0; driver->feature[i].sent_feature_mask = 0; diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h index 4c6d86fc36ae..97ad3f60ba5e 100644 --- a/drivers/char/diag/diagfwd.h +++ b/drivers/char/diag/diagfwd.h @@ -19,9 +19,11 @@ */ #define SET_BUF_CTXT(p, d, n) \ (((p & 0xFF) << 16) | ((d & 0xFF) << 8) | (n & 0xFF)) +#define SET_PD_CTXT(u) ((u & 0xFF) << 24) #define GET_BUF_PERIPHERAL(p) ((p & 0xFF0000) >> 16) #define GET_BUF_TYPE(d) ((d & 0x00FF00) >> 8) #define GET_BUF_NUM(n) ((n & 0x0000FF)) +#define GET_PD_CTXT(u) ((u & 0xFF000000) >> 24) #define CHK_OVERFLOW(bufStart, start, end, length) \ ((((bufStart) <= (start)) && ((end) - (start) >= (length))) ? 1 : 0) diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index 62c8d0028af9..ae749725f6db 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -124,7 +124,9 @@ void diag_notify_md_client(uint8_t peripheral, int data) info.si_signo = SIGCONT; if (driver->md_session_map[peripheral] && driver->md_session_map[peripheral]->task) { - if (driver->md_session_map[peripheral]->pid == + if (driver->md_session_map[peripheral]-> + md_client_thread_info->task != NULL + && driver->md_session_map[peripheral]->pid == driver->md_session_map[peripheral]->task->tgid) { DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "md_session %d pid = %d, md_session %d task tgid = %d\n", @@ -198,6 +200,20 @@ static void process_hdlc_encoding_feature(uint8_t peripheral) } } +static void process_upd_header_untagging_feature(uint8_t peripheral) +{ + if (peripheral >= NUM_PERIPHERALS) + return; + + if (driver->supports_apps_header_untagging) { + driver->feature[peripheral].untag_header = + ENABLE_PKT_HEADER_UNTAGGING; + } else { + driver->feature[peripheral].untag_header = + DISABLE_PKT_HEADER_UNTAGGING; + } +} + static void process_command_deregistration(uint8_t *buf, uint32_t len, uint8_t peripheral) { @@ -374,6 +390,8 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len, driver->feature[peripheral].separate_cmd_rsp = 1; if (FEATURE_SUPPORTED(F_DIAG_APPS_HDLC_ENCODE)) process_hdlc_encoding_feature(peripheral); + if (FEATURE_SUPPORTED(F_DIAG_PKT_HEADER_UNTAG)) + process_upd_header_untagging_feature(peripheral); if (FEATURE_SUPPORTED(F_DIAG_STM)) enable_stm_feature(peripheral); if (FEATURE_SUPPORTED(F_DIAG_MASK_CENTRALIZATION)) diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h index 7eed8ef8779e..e8608f47ff14 100644 --- a/drivers/char/diag/diagfwd_cntl.h +++ b/drivers/char/diag/diagfwd_cntl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -67,6 +67,7 @@ #define F_DIAG_MASK_CENTRALIZATION 11 #define F_DIAG_SOCKETS_ENABLED 13 #define F_DIAG_DCI_EXTENDED_HEADER_SUPPORT 14 +#define F_DIAG_PKT_HEADER_UNTAG 16 #define ENABLE_SEPARATE_CMDRSP 1 #define DISABLE_SEPARATE_CMDRSP 0 @@ -81,6 +82,9 @@ #define ENABLE_APPS_HDLC_ENCODING 1 #define DISABLE_APPS_HDLC_ENCODING 0 +#define ENABLE_PKT_HEADER_UNTAGGING 1 +#define DISABLE_PKT_HEADER_UNTAGGING 0 + #define DIAG_MODE_PKT_LEN 36 struct diag_ctrl_pkt_header_t { diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 3f9f7a999fdf..55d36abe4679 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -46,6 +46,8 @@ static void diagfwd_cntl_open(struct diagfwd_info *fwd_info); static void diagfwd_cntl_close(struct diagfwd_info *fwd_info); static void diagfwd_dci_open(struct diagfwd_info *fwd_info); static void diagfwd_dci_close(struct diagfwd_info *fwd_info); +static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, + unsigned char *buf, int len); static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, unsigned char *buf, int len); static void diagfwd_cntl_read_done(struct diagfwd_info *fwd_info, @@ -59,7 +61,7 @@ struct diagfwd_info peripheral_info[NUM_TYPES][NUM_PERIPHERALS]; static struct diag_channel_ops data_ch_ops = { .open = NULL, .close = NULL, - .read_done = diagfwd_data_read_done + .read_done = diagfwd_data_read_untag_done }; static struct diag_channel_ops cntl_ch_ops = { @@ -214,6 +216,221 @@ static int check_bufsize_for_encoding(struct diagfwd_buf_t *buf, uint32_t len) return buf->len; } +static void diagfwd_data_process_done(struct diagfwd_info *fwd_info, + struct diagfwd_buf_t *buf, int len) +{ + int err = 0; + int write_len = 0, peripheral = 0; + unsigned char *write_buf = NULL; + struct diag_md_session_t *session_info = NULL; + uint8_t hdlc_disabled = 0; + + if (!fwd_info || !buf || len <= 0) { + diag_ws_release(); + return; + } + + switch (fwd_info->type) { + case TYPE_DATA: + case TYPE_CMD: + break; + default: + pr_err_ratelimited("diag: In %s, invalid type %d for peripheral %d\n", + __func__, fwd_info->type, + fwd_info->peripheral); + diag_ws_release(); + return; + } + + mutex_lock(&driver->hdlc_disable_mutex); + mutex_lock(&fwd_info->data_mutex); + peripheral = GET_PD_CTXT(buf->ctxt); + if (peripheral == DIAG_ID_MPSS) + peripheral = PERIPHERAL_MODEM; + + session_info = + diag_md_session_get_peripheral(peripheral); + if (session_info) + hdlc_disabled = session_info->hdlc_disabled; + else + hdlc_disabled = driver->hdlc_disabled; + + if (hdlc_disabled) { + /* The data is raw and and on APPS side HDLC is disabled */ + if (!buf) { + pr_err("diag: In %s, no match for non encode buffer %pK, peripheral %d, type: %d\n", + __func__, buf, fwd_info->peripheral, + fwd_info->type); + goto end; + } + if (len > PERIPHERAL_BUF_SZ) { + pr_err("diag: In %s, Incoming buffer too large %d, peripheral %d, type: %d\n", + __func__, len, fwd_info->peripheral, + fwd_info->type); + goto end; + } + write_len = len; + if (write_len <= 0) + goto end; + write_buf = buf->data_raw; + } else { + if (!buf) { + pr_err("diag: In %s, no match for non encode buffer %pK, peripheral %d, type: %d\n", + __func__, buf, fwd_info->peripheral, + fwd_info->type); + goto end; + } + + write_len = check_bufsize_for_encoding(buf, len); + if (write_len <= 0) { + pr_err("diag: error in checking buf for encoding\n"); + goto end; + } + write_buf = buf->data; + err = diag_add_hdlc_encoding(write_buf, &write_len, + buf->data_raw, len); + if (err) { + pr_err("diag: error in adding hdlc encoding\n"); + goto end; + } + } + + if (write_len > 0) { + err = diag_mux_write(DIAG_LOCAL_PROC, write_buf, write_len, + buf->ctxt); + if (err) { + pr_err_ratelimited("diag: In %s, unable to write to mux error: %d\n", + __func__, err); + goto end; + } + } + mutex_unlock(&fwd_info->data_mutex); + mutex_unlock(&driver->hdlc_disable_mutex); + diagfwd_queue_read(fwd_info); + return; + +end: + diag_ws_release(); + mutex_unlock(&fwd_info->data_mutex); + mutex_unlock(&driver->hdlc_disable_mutex); + if (buf) { + diagfwd_write_done(fwd_info->peripheral, fwd_info->type, + GET_BUF_NUM(buf->ctxt)); + } + diagfwd_queue_read(fwd_info); +} + +static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, + unsigned char *buf, int len) +{ + int len_cpd = 0, len_upd_1 = 0; + int ctxt_cpd = 0, ctxt_upd_1 = 0; + int buf_len = 0, processed = 0; + unsigned char *temp_buf_main = NULL; + unsigned char *temp_buf_cpd = NULL; + unsigned char *temp_buf_upd_1 = NULL; + struct diagfwd_buf_t *temp_ptr_upd = NULL; + struct diagfwd_buf_t *temp_ptr_cpd = NULL; + int flag_buf_1 = 0, flag_buf_2 = 0; + + if (!fwd_info || !buf || len <= 0) { + diag_ws_release(); + return; + } + + switch (fwd_info->type) { + case TYPE_DATA: + case TYPE_CMD: + break; + default: + pr_err_ratelimited("diag: In %s, invalid type %d for peripheral %d\n", + __func__, fwd_info->type, + fwd_info->peripheral); + diag_ws_release(); + return; + } + + if (driver->feature[fwd_info->peripheral].encode_hdlc && + driver->feature[fwd_info->peripheral].untag_header) { + mutex_lock(&driver->diagfwd_untag_mutex); + temp_buf_cpd = buf; + temp_buf_main = buf; + if (fwd_info->buf_1 && + fwd_info->buf_1->data_raw == buf) { + flag_buf_1 = 1; + if (fwd_info->type == TYPE_DATA) + temp_buf_upd_1 = + fwd_info->buf_upd_1_a->data_raw; + } else { + flag_buf_2 = 1; + if (fwd_info->type == TYPE_DATA) + temp_buf_upd_1 = + fwd_info->buf_upd_1_b->data_raw; + } + while (processed < len) { + buf_len = + *(uint16_t *) (temp_buf_main + 2); + switch ((*temp_buf_main)) { + case DIAG_ID_MPSS: + ctxt_cpd = DIAG_ID_MPSS; + len_cpd += buf_len; + if (temp_buf_cpd) { + memcpy(temp_buf_cpd, + (temp_buf_main + 4), buf_len); + temp_buf_cpd += buf_len; + } + break; + case DIAG_ID_WLAN: + ctxt_upd_1 = UPD_WLAN; + len_upd_1 += buf_len; + if (temp_buf_upd_1) { + memcpy(temp_buf_upd_1, + (temp_buf_main + 4), buf_len); + temp_buf_upd_1 += buf_len; + } + break; + } + len = len - 4; + temp_buf_main += (buf_len + 4); + processed += buf_len; + } + if (fwd_info->type == TYPE_DATA && len_upd_1) { + if (flag_buf_1) + temp_ptr_upd = fwd_info->buf_upd_1_a; + else + temp_ptr_upd = fwd_info->buf_upd_1_b; + temp_ptr_upd->ctxt &= 0x00FFFFFF; + temp_ptr_upd->ctxt |= + (SET_PD_CTXT(ctxt_upd_1)); + atomic_set(&temp_ptr_upd->in_busy, 1); + diagfwd_data_process_done(fwd_info, + temp_ptr_upd, len_upd_1); + } + if (len_cpd) { + if (flag_buf_1) { + driver->cpd_len_1 = len_cpd; + temp_ptr_cpd = fwd_info->buf_1; + } else { + driver->cpd_len_2 = len_cpd; + temp_ptr_cpd = fwd_info->buf_2; + } + temp_ptr_cpd->ctxt &= 0x00FFFFFF; + temp_ptr_cpd->ctxt |= + (SET_PD_CTXT(ctxt_cpd)); + diagfwd_data_process_done(fwd_info, + temp_ptr_cpd, len_cpd); + } else { + if (flag_buf_1) + driver->cpd_len_1 = 0; + if (flag_buf_2) + driver->cpd_len_2 = 0; + } + mutex_unlock(&driver->diagfwd_untag_mutex); + } else { + diagfwd_data_read_done(fwd_info, buf, len); + } +} + static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, unsigned char *buf, int len) { @@ -223,6 +440,7 @@ static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, struct diagfwd_buf_t *temp_buf = NULL; struct diag_md_session_t *session_info = NULL; uint8_t hdlc_disabled = 0; + if (!fwd_info || !buf || len <= 0) { diag_ws_release(); return; @@ -234,8 +452,8 @@ static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, break; default: pr_err_ratelimited("diag: In %s, invalid type %d for peripheral %d\n", - __func__, fwd_info->type, - fwd_info->peripheral); + __func__, fwd_info->type, + fwd_info->peripheral); diag_ws_release(); return; } @@ -855,7 +1073,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info) __func__, fwd_info->peripheral, fwd_info->type); return 0; } - + mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); fwd_info->ch_open = 1; diagfwd_buffers_init(fwd_info); diagfwd_write_buffers_init(fwd_info); @@ -873,7 +1091,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info) if (fwd_info->p_ops && fwd_info->p_ops->open) fwd_info->p_ops->open(fwd_info->ctxt); } - + mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); return 0; } @@ -883,6 +1101,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info) if (!fwd_info) return -EIO; + mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); fwd_info->ch_open = 0; if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->close) fwd_info->c_ops->close(fwd_info); @@ -898,7 +1117,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info) } DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d considered closed\n", fwd_info->peripheral, fwd_info->type); - + mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); return 0; } @@ -940,7 +1159,15 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) atomic_set(&fwd_info->buf_1->in_busy, 0); else if (ctxt == 2 && fwd_info->buf_2) atomic_set(&fwd_info->buf_2->in_busy, 0); - else + else if (ctxt == 3 && fwd_info->buf_upd_1_a) { + atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0); + if (driver->cpd_len_1 == 0) + atomic_set(&fwd_info->buf_1->in_busy, 0); + } else if (ctxt == 4 && fwd_info->buf_upd_1_b) { + atomic_set(&fwd_info->buf_upd_1_b->in_busy, 0); + if (driver->cpd_len_2 == 0) + atomic_set(&fwd_info->buf_2->in_busy, 0); + } else pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt); diagfwd_queue_read(fwd_info); @@ -1072,6 +1299,7 @@ static void diagfwd_queue_read(struct diagfwd_info *fwd_info) void diagfwd_buffers_init(struct diagfwd_info *fwd_info) { + unsigned char *temp_buf; if (!fwd_info) return; @@ -1124,6 +1352,54 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) fwd_info->type, 2); } + if (driver->feature[fwd_info->peripheral].untag_header) { + if (!fwd_info->buf_upd_1_a) { + fwd_info->buf_upd_1_a = + kzalloc(sizeof(struct diagfwd_buf_t), + GFP_KERNEL); + if (!fwd_info->buf_upd_1_a) + goto err; + kmemleak_not_leak(fwd_info->buf_upd_1_a); + } + + if (!fwd_info->buf_upd_1_a->data) { + fwd_info->buf_upd_1_a->data = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + if (!fwd_info->buf_upd_1_a->data) + goto err; + fwd_info->buf_upd_1_a->len = PERIPHERAL_BUF_SZ; + kmemleak_not_leak(fwd_info->buf_upd_1_a->data); + fwd_info->buf_upd_1_a->ctxt = SET_BUF_CTXT( + fwd_info->peripheral, + fwd_info->type, 3); + } + if (!fwd_info->buf_upd_1_b) { + fwd_info->buf_upd_1_b = + kzalloc(sizeof(struct diagfwd_buf_t), + GFP_KERNEL); + if (!fwd_info->buf_upd_1_b) + goto err; + kmemleak_not_leak(fwd_info->buf_upd_1_b); + } + + if (!fwd_info->buf_upd_1_b->data) { + fwd_info->buf_upd_1_b->data = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + if (!fwd_info->buf_upd_1_b->data) + goto err; + fwd_info->buf_upd_1_b->len = + PERIPHERAL_BUF_SZ; + kmemleak_not_leak(fwd_info->buf_upd_1_b->data); + fwd_info->buf_upd_1_b->ctxt = SET_BUF_CTXT( + fwd_info->peripheral, + fwd_info->type, 4); + } + } + if (driver->supports_apps_hdlc_encoding) { /* In support of hdlc encoding */ if (!fwd_info->buf_1->data_raw) { @@ -1133,7 +1409,8 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) GFP_KERNEL); if (!fwd_info->buf_1->data_raw) goto err; - fwd_info->buf_1->len_raw = PERIPHERAL_BUF_SZ; + fwd_info->buf_1->len_raw = + PERIPHERAL_BUF_SZ; kmemleak_not_leak(fwd_info->buf_1->data_raw); } if (!fwd_info->buf_2->data_raw) { @@ -1143,13 +1420,45 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) GFP_KERNEL); if (!fwd_info->buf_2->data_raw) goto err; - fwd_info->buf_2->len_raw = PERIPHERAL_BUF_SZ; + fwd_info->buf_2->len_raw = + PERIPHERAL_BUF_SZ; kmemleak_not_leak(fwd_info->buf_2->data_raw); } + + if (driver->feature[fwd_info->peripheral]. + untag_header) { + if (!fwd_info->buf_upd_1_a->data_raw) { + fwd_info->buf_upd_1_a->data_raw = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + if (!fwd_info->buf_upd_1_a->data_raw) + goto err; + fwd_info->buf_upd_1_a->len_raw = + PERIPHERAL_BUF_SZ; + temp_buf = + fwd_info->buf_upd_1_a->data_raw; + kmemleak_not_leak(temp_buf); + } + if (!fwd_info->buf_upd_1_b->data_raw) { + fwd_info->buf_upd_1_b->data_raw = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + if (!fwd_info->buf_upd_1_b->data_raw) + goto err; + fwd_info->buf_upd_1_b->len_raw = + PERIPHERAL_BUF_SZ; + temp_buf = + fwd_info->buf_upd_1_b->data_raw; + kmemleak_not_leak(temp_buf); + } + } } } - if (fwd_info->type == TYPE_CMD && driver->supports_apps_hdlc_encoding) { + if (fwd_info->type == TYPE_CMD && + driver->supports_apps_hdlc_encoding) { /* In support of hdlc encoding */ if (!fwd_info->buf_1->data_raw) { fwd_info->buf_1->data_raw = kzalloc(PERIPHERAL_BUF_SZ + diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h index 23aa526b2c09..f483da81cc96 100644 --- a/drivers/char/diag/diagfwd_peripheral.h +++ b/drivers/char/diag/diagfwd_peripheral.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -78,6 +78,8 @@ struct diagfwd_info { void *ctxt; struct diagfwd_buf_t *buf_1; struct diagfwd_buf_t *buf_2; + struct diagfwd_buf_t *buf_upd_1_a; + struct diagfwd_buf_t *buf_upd_1_b; struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS]; struct diag_peripheral_ops *p_ops; struct diag_channel_ops *c_ops; diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c index 96fb986402eb..296b23960815 100644 --- a/drivers/char/hw_random/msm-rng.c +++ b/drivers/char/hw_random/msm-rng.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2013,2015,2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -156,6 +156,7 @@ static int msm_rng_probe(struct platform_device *pdev) rng->hwrng.init = msm_rng_init, rng->hwrng.cleanup = msm_rng_cleanup, rng->hwrng.read = msm_rng_read, + rng->hwrng.quality = 700; ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); if (ret) { diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 3111f2778079..849f2e29c243 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -305,7 +305,6 @@ static int tpmfront_probe(struct xenbus_device *dev, rv = setup_ring(dev, priv); if (rv) { chip = dev_get_drvdata(&dev->dev); - tpm_chip_unregister(chip); ring_free(priv); return rv; } diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 6029313aa995..35ab89fe9d7b 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1082,7 +1082,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); - cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + cprman_write(cprman, data->a2w_reg, + cprman_read(cprman, data->a2w_reg) | + A2W_PLL_CHANNEL_DISABLE); spin_unlock(&cprman->regs_lock); } diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 43f9d15255f4..763aed2de893 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -247,7 +247,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw) if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", ret); - return true; + return false; } return (ret & WM831X_CLKOUT_ENA) != 0; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index e8e48015d3af..4a9e034f939f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2874,8 +2874,6 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) goto err_out; } - clk_debug_measure_add(core->hw, core->dentry); - ret = 0; goto out; @@ -3005,10 +3003,8 @@ static int __init clk_debug_init(void) return -ENOMEM; mutex_lock(&clk_debug_lock); - hlist_for_each_entry(core, &clk_debug_list, debug_node) { - clk_register_debug(core->hw, core->dentry); + hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir); - } inited = 1; mutex_unlock(&clk_debug_lock); diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index 25bf4bc85f5c..87af7af7aac2 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -23,8 +23,6 @@ void __clk_free_clk(struct clk *clk); /* Debugfs API to print the enabled clocks */ void clock_debug_print_enabled(void); -int clk_register_debug(struct clk_hw *hw, struct dentry *dentry); -void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f); #else diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c index 6a964144a5b5..6a49ba2b9671 100644 --- a/drivers/clk/imx/clk-imx31.c +++ b/drivers/clk/imx/clk-imx31.c @@ -157,10 +157,8 @@ static void __init _mx31_clocks_init(unsigned long fref) } } -int __init mx31_clocks_init(void) +int __init mx31_clocks_init(unsigned long fref) { - u32 fref = 26000000; /* default */ - _mx31_clocks_init(fref); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c index b9a1167a790d..834315aa4993 100644 --- a/drivers/clk/msm/clock-alpha-pll.c +++ b/drivers/clk/msm/clock-alpha-pll.c @@ -612,13 +612,17 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate) return -EINVAL; } + if (pll->no_irq_dis) + spin_lock(&c->lock); + else + spin_lock_irqsave(&c->lock, flags); + /* * For PLLs that do not support dynamic programming (dynamic_update * is not set), ensure PLL is off before changing rate. For * optimization reasons, assume no downstream clock is actively * using it. */ - spin_lock_irqsave(&c->lock, flags); if (c->count && !pll->dynamic_update) c->ops->disable(c); @@ -644,7 +648,10 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate) if (c->count && !pll->dynamic_update) c->ops->enable(c); - spin_unlock_irqrestore(&c->lock, flags); + if (pll->no_irq_dis) + spin_unlock(&c->lock); + else + spin_unlock_irqrestore(&c->lock, flags); return 0; } diff --git a/drivers/clk/msm/clock-cpu-8996.c b/drivers/clk/msm/clock-cpu-8996.c index 7e03a599fccc..bcda6f31d6f5 100644 --- a/drivers/clk/msm/clock-cpu-8996.c +++ b/drivers/clk/msm/clock-cpu-8996.c @@ -238,6 +238,7 @@ static struct alpha_pll_clk perfcl_alt_pll = { .post_div_config = 0x100, /* Div-2 */ .config_ctl_val = 0x4001051B, .offline_bit_workaround = true, + .no_irq_dis = true, .c = { .always_on = true, .parent = &alpha_xo_ao.c, @@ -300,6 +301,7 @@ static struct alpha_pll_clk pwrcl_alt_pll = { .post_div_config = 0x100, /* Div-2 */ .config_ctl_val = 0x4001051B, .offline_bit_workaround = true, + .no_irq_dis = true, .c = { .always_on = true, .dbg_name = "pwrcl_alt_pll", diff --git a/drivers/clk/msm/clock-gcc-8996.c b/drivers/clk/msm/clock-gcc-8996.c index a9e0b53c3b22..e93e9c494023 100644 --- a/drivers/clk/msm/clock-gcc-8996.c +++ b/drivers/clk/msm/clock-gcc-8996.c @@ -105,6 +105,8 @@ DEFINE_CLK_DUMMY(gcc_ce1_axi_m_clk, 0); DEFINE_CLK_DUMMY(measure_only_bimc_hmss_axi_clk, 0); DEFINE_CLK_RPM_SMD_XO_BUFFER(ln_bb_clk, ln_bb_a_clk, LN_BB_CLK_ID); +DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(ln_bb_clk_pin, ln_bb_a_clk_pin, + LN_BB_CLK_PIN_ID); static DEFINE_CLK_VOTER(mcd_ce1_clk, &ce1_clk.c, 85710000); static DEFINE_CLK_VOTER(pnoc_keepalive_a_clk, &pnoc_a_clk.c, LONG_MAX); static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX); @@ -1340,9 +1342,11 @@ static struct rcg_clk pdm2_clk_src = { }, }; -/* Frequency table might change later */ static struct clk_freq_tbl ftbl_qspi_ser_clk_src[] = { - F( 192000000, gpll4_out_main, 2, 0, 0), + F( 75000000, gpll0_out_main, 8, 0, 0), + F( 150000000, gpll0_out_main, 4, 0, 0), + F( 256000000, gpll4_out_main, 1.5, 0, 0), + F( 300000000, gpll0_out_main, 2, 0, 0), F_END }; @@ -3387,6 +3391,8 @@ static struct clk_lookup msm_clocks_rpm_8996[] = { CLK_LIST(ipa_clk), CLK_LIST(ln_bb_clk), CLK_LIST(ln_bb_a_clk), + CLK_LIST(ln_bb_clk_pin), + CLK_LIST(ln_bb_a_clk_pin), CLK_LIST(mcd_ce1_clk), CLK_LIST(pnoc_keepalive_a_clk), CLK_LIST(pnoc_msmbus_clk), diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 317091a8003d..4c18181c047c 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,7 +12,7 @@ clk-qcom-y += clk-regmap-mux.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-voter.o -clk-qcom-y += clk-dummy.o +clk-qcom-y += clk-dummy.o clk-debug.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index ca6010db8d78..3e9cd9909b86 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include "clk-branch.h" +#include "clk-debug.h" #include "clk-regmap.h" #include "common.h" @@ -250,6 +251,7 @@ const struct clk_ops clk_branch2_ops = { .is_enabled = clk_is_enabled_regmap, .set_flags = clk_branch_set_flags, .list_registers = clk_branch2_list_registers, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); @@ -384,6 +386,7 @@ const struct clk_ops clk_gate2_ops = { .is_enabled = clk_is_enabled_regmap, .list_registers = clk_gate2_list_registers, .set_flags = clk_gate2_set_flags, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_gate2_ops); diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index 6b00bee337a1..f82ddc3b008b 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -39,6 +39,7 @@ #include "common.h" #include "clk-regmap.h" #include "clk-rcg.h" +#include "clk-debug.h" enum { LMH_LITE_CLK_SRC, @@ -757,6 +758,7 @@ static struct clk_ops clk_ops_cpu_osm = { .round_rate = clk_osm_round_rate, .list_rate = clk_osm_list_rate, .recalc_rate = clk_osm_recalc_rate, + .debug_init = clk_debug_measure_add, }; static const struct parent_map gcc_parent_map_1[] = { diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c new file mode 100644 index 000000000000..50d0d01188ed --- /dev/null +++ b/drivers/clk/qcom/clk-debug.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2013-2014, 2016-2017, + * + * The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * 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/clk.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/of.h> + +#include "clk-regmap.h" +#include "clk-debug.h" +#include "common.h" + +static struct clk_hw *measure; + +static DEFINE_SPINLOCK(clk_reg_lock); +static DEFINE_MUTEX(clk_debug_lock); + +#define TCXO_DIV_4_HZ 4800000 +#define SAMPLE_TICKS_1_MS 0x1000 +#define SAMPLE_TICKS_14_MS 0x10000 + +#define XO_DIV4_CNT_DONE BIT(25) +#define CNT_EN BIT(20) +#define MEASURE_CNT BM(24, 0) + +/* Sample clock for 'ticks' reference clock ticks. */ +static u32 run_measurement(unsigned ticks, struct regmap *regmap, + u32 ctl_reg, u32 status_reg) +{ + u32 regval; + + /* Stop counters and set the XO4 counter start value. */ + regmap_write(regmap, ctl_reg, ticks); + + regmap_read(regmap, status_reg, ®val); + + /* Wait for timer to become ready. */ + while ((regval & XO_DIV4_CNT_DONE) != 0) { + cpu_relax(); + regmap_read(regmap, status_reg, ®val); + } + + /* Run measurement and wait for completion. */ + regmap_write(regmap, ctl_reg, (CNT_EN|ticks)); + + regmap_read(regmap, status_reg, ®val); + + while ((regval & XO_DIV4_CNT_DONE) == 0) { + cpu_relax(); + regmap_read(regmap, status_reg, ®val); + } + + /* Return measured ticks. */ + regmap_read(regmap, status_reg, ®val); + regval &= MEASURE_CNT; + + return regval; +} + +/* + * Perform a hardware rate measurement for a given clock. + * FOR DEBUG USE ONLY: Measurements take ~15 ms! + */ +static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw) +{ + unsigned long flags, ret = 0; + u32 gcc_xo4_reg, multiplier; + u64 raw_count_short, raw_count_full; + struct clk_debug_mux *meas = to_clk_measure(hw); + struct measure_clk_data *data = meas->priv; + + clk_prepare_enable(data->cxo); + + spin_lock_irqsave(&clk_reg_lock, flags); + + multiplier = meas->multiplier + 1; + + /* Enable CXO/4 and RINGOSC branch. */ + regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg); + gcc_xo4_reg |= BIT(0); + regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(SAMPLE_TICKS_1_MS, meas->regmap[GCC], + data->ctl_reg, data->status_reg); + + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, meas->regmap[GCC], + data->ctl_reg, data->status_reg); + + gcc_xo4_reg &= ~BIT(0); + regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * TCXO_DIV_4_HZ; + do_div(raw_count_full, ((SAMPLE_TICKS_14_MS * 10) + 35)); + ret = (raw_count_full * multiplier); + } + + spin_unlock_irqrestore(&clk_reg_lock, flags); + + clk_disable_unprepare(data->cxo); + + return ret; +} + +static u8 clk_debug_mux_get_parent(struct clk_hw *hw) +{ + struct clk_debug_mux *meas = to_clk_measure(hw); + int i, num_parents = clk_hw_get_num_parents(hw); + + for (i = 0; i < num_parents; i++) { + if (!strcmp(meas->parent[i].parents, + hw->init->parent_names[i])) { + pr_debug("%s: Clock name %s index %d\n", __func__, + hw->init->name, i); + return i; + } + } + + return 0; +} + +static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_debug_mux *meas = to_clk_measure(hw); + unsigned long lsb = 0; + u32 regval = 0; + int dbg_cc = 0; + + dbg_cc = meas->parent[index].dbg_cc; + + if (dbg_cc != GCC) { + regmap_read(meas->regmap[dbg_cc], 0x0, ®val); + + /* Clear & Set post divider bits */ + if (meas->parent[index].post_div_mask) { + regval &= ~meas->parent[index].post_div_mask; + lsb = find_first_bit((unsigned long *) + &meas->parent[index].post_div_mask, 32); + regval |= (meas->parent[index].post_div_val << lsb) & + meas->parent[index].post_div_mask; + meas->multiplier = meas->parent[index].post_div_val; + } + + if (meas->parent[index].mask) + regval &= ~meas->parent[index].mask << + meas->parent[index].shift; + else + regval &= ~meas->mask; + + regval |= (meas->parent[index].next_sel & meas->mask); + + if (meas->parent[index].en_mask == 0xFF) + /* Skip en_mask */ + regval = regval; + else if (meas->parent[index].en_mask) + regval |= meas->parent[index].en_mask; + else + regval |= meas->en_mask; + + regmap_write(meas->regmap[dbg_cc], 0x0, regval); + } + + /* update the debug sel for GCC */ + regmap_read(meas->regmap[GCC], meas->debug_offset, ®val); + + /* clear post divider bits */ + regval &= ~BM(15, 12); + lsb = find_first_bit((unsigned long *) + &meas->parent[index].post_div_mask, 32); + regval |= (meas->parent[index].post_div_val << lsb) & + meas->parent[index].post_div_mask; + meas->multiplier = meas->parent[index].post_div_val; + regval &= ~meas->mask; + regval |= (meas->parent[index].sel & meas->mask); + regval |= meas->en_mask; + + regmap_write(meas->regmap[GCC], meas->debug_offset, regval); + + return 0; +} + +const struct clk_ops clk_debug_mux_ops = { + .get_parent = clk_debug_mux_get_parent, + .set_parent = clk_debug_mux_set_parent, +}; +EXPORT_SYMBOL(clk_debug_mux_ops); + +static int clk_debug_measure_get(void *data, u64 *val) +{ + struct clk_hw *hw = data, *par; + int ret = 0; + unsigned long meas_rate, sw_rate; + + mutex_lock(&clk_debug_lock); + + ret = clk_set_parent(measure->clk, hw->clk); + if (!ret) { + par = measure; + while (par && par != hw) { + if (par->init->ops->enable) + par->init->ops->enable(par); + par = clk_hw_get_parent(par); + } + *val = clk_debug_mux_measure_rate(measure); + } + + meas_rate = clk_get_rate(hw->clk); + sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk); + if (sw_rate && meas_rate >= (sw_rate * 2)) + *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate); + + mutex_unlock(&clk_debug_lock); + + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, + NULL, "%lld\n"); + +int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) +{ + if (IS_ERR_OR_NULL(measure)) { + pr_err_once("Please check if `measure` clk is registered!!!\n"); + return 0; + } + + if (clk_set_parent(measure->clk, hw->clk)) + return 0; + + debugfs_create_file("clk_measure", S_IRUGO, dentry, hw, + &clk_measure_fops); + return 0; +} +EXPORT_SYMBOL(clk_debug_measure_add); + +int clk_register_debug(struct clk_hw *hw) +{ + if (IS_ERR_OR_NULL(measure)) { + if (hw->init->flags & CLK_IS_MEASURE) { + measure = hw; + return 0; + } + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(clk_register_debug); + diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h new file mode 100644 index 000000000000..40e8730a9466 --- /dev/null +++ b/drivers/clk/qcom/clk-debug.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * 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 __QCOM_CLK_DEBUG_H__ +#define __QCOM_CLK_DEBUG_H__ + +#include "../clk.h" + +/* Debugfs Measure Clocks */ + +/** + * struct measure_clk_data - Structure of clk measure + * + * @cxo: XO clock. + * @xo_div4_cbcr: offset of debug XO/4 div register. + * @ctl_reg: offset of debug control register. + * @status_reg: offset of debug status register. + * + */ +struct measure_clk_data { + struct clk *cxo; + u32 xo_div4_cbcr; + u32 ctl_reg; + u32 status_reg; +}; + +/** + * List of Debug clock controllers. + */ +enum debug_cc { + GCC, + MMCC, + GPU, + CPU, +}; + +/** + * struct clk_src - Struture of clock source for debug mux + * + * @parents: clock name to be used as parent for debug mux. + * @sel: debug mux index at global clock controller. + * @dbg_cc: indicates the clock controller for recursive debug clock + * controllers. + * @next_sel: indicates the debug mux index at recursive debug mux. + * @mask: indicates the mask required at recursive debug mux. + * @shift: indicates the shift required at recursive debug mux. + * @en_mask: indicates the enable bit mask at recursive debug mux. + * Incase the recursive debug mux does not have a enable bit, + * 0xFF should be used to indicate the same, otherwise global + * enable bit would be used. + * @post_div_mask: indicates the post div mask to be used at debug/recursive + * debug mux. + * @post_div_val: indicates the post div value to be used at debug/recursive + * debug mux. + */ +struct clk_src { + const char *parents; + int sel; + enum debug_cc dbg_cc; + int next_sel; + u32 mask; + u32 shift; + u32 en_mask; + u32 post_div_mask; + u32 post_div_val; +}; + +#define MUX_SRC_LIST(...) \ + .parent = (struct clk_src[]){__VA_ARGS__}, \ + .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) + +/** + * struct clk_debug_mux - Struture of clock debug mux + * + * @parent: structure of clk_src + * @num_parents: number of parents + * @regmap: regmaps of debug mux + * @num_parent_regmap: number of regmap of debug mux + * @priv: private measure_clk_data to be used by debug mux + * @en_mask: indicates the enable bit mask at global clock + * controller debug mux. + * @mask: indicates the mask to be used at global clock + * controller debug mux. + * @debug_offset: debug mux offset. + * @hw: handle between common and hardware-specific interfaces. + * @multiplier: internally used by debug mux as post div multiplier. + */ +struct clk_debug_mux { + struct clk_src *parent; + int num_parents; + struct regmap **regmap; + int num_parent_regmap; + void *priv; + u32 en_mask; + u32 mask; + u32 debug_offset; + struct clk_hw hw; + + /* internal */ + u32 multiplier; +}; + +#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) + +extern const struct clk_ops clk_debug_mux_ops; + +int clk_register_debug(struct clk_hw *hw); +int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); + +#endif diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c index e06a829e508c..c08136563e31 100644 --- a/drivers/clk/qcom/clk-dummy.c +++ b/drivers/clk/qcom/clk-dummy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,8 @@ #include <linux/slab.h> #include <linux/reset-controller.h> +#include "clk-debug.h" + struct clk_dummy { struct clk_hw hw; struct reset_controller_dev reset; @@ -65,6 +67,7 @@ struct clk_ops clk_dummy_ops = { .round_rate = dummy_clk_round_rate, .recalc_rate = dummy_clk_recalc_rate, .set_flags = dummy_clk_set_flags, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_dummy_ops); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 5345e9086627..8dcdf2929ca6 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -30,6 +30,7 @@ #include <dt-bindings/mfd/qcom-rpm.h> #include "clk-voter.h" +#include "clk-debug.h" #define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 #define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 @@ -463,6 +464,7 @@ static const struct clk_ops clk_smd_rpm_ops = { .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, .is_enabled = clk_smd_rpm_is_enabled, + .debug_init = clk_debug_measure_add, }; static const struct clk_ops clk_smd_rpm_branch_ops = { @@ -471,6 +473,7 @@ static const struct clk_ops clk_smd_rpm_branch_ops = { .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, .is_enabled = clk_smd_rpm_is_enabled, + .debug_init = clk_debug_measure_add, }; /* msm8916 */ diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index f6ce3106ab6f..8ecb207a97e5 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -1,5 +1,7 @@ /* - * Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016-2017, + * + * 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 @@ -287,239 +289,4 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) } EXPORT_SYMBOL_GPL(qcom_cc_probe); -/* Debugfs Support */ -static struct clk_hw *measure; - -DEFINE_SPINLOCK(clk_reg_lock); - -/* Sample clock for 'ticks' reference clock ticks. */ -static u32 run_measurement(unsigned ticks, struct regmap *regmap, - u32 ctl_reg, u32 status_reg) -{ - u32 regval; - - /* Stop counters and set the XO4 counter start value. */ - regmap_write(regmap, ctl_reg, ticks); - - regmap_read(regmap, status_reg, ®val); - - /* Wait for timer to become ready. */ - while ((regval & BIT(25)) != 0) { - cpu_relax(); - regmap_read(regmap, status_reg, ®val); - } - - /* Run measurement and wait for completion. */ - regmap_write(regmap, ctl_reg, (BIT(20)|ticks)); - regmap_read(regmap, ctl_reg, ®val); - - regmap_read(regmap, status_reg, ®val); - - while ((regval & BIT(25)) == 0) { - cpu_relax(); - regmap_read(regmap, status_reg, ®val); - } - - /* Return measured ticks. */ - regmap_read(regmap, status_reg, ®val); - regval &= BM(24, 0); - - return regval; -} - -/* - * Perform a hardware rate measurement for a given clock. - * FOR DEBUG USE ONLY: Measurements take ~15 ms! - */ -static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw) -{ - unsigned long flags, ret = 0; - u32 gcc_xo4_reg, sample_ticks = 0x10000, multiplier; - u64 raw_count_short, raw_count_full; - struct clk_debug_mux *meas = to_clk_measure(hw); - struct measure_clk_data *data = meas->priv; - - clk_prepare_enable(data->cxo); - - spin_lock_irqsave(&clk_reg_lock, flags); - - multiplier = meas->multiplier + 1; - - /* Enable CXO/4 and RINGOSC branch. */ - regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg); - gcc_xo4_reg |= BIT(0); - regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); - - /* - * The ring oscillator counter will not reset if the measured clock - * is not running. To detect this, run a short measurement before - * the full measurement. If the raw results of the two are the same - * then the clock must be off. - */ - - /* Run a short measurement. (~1 ms) */ - raw_count_short = run_measurement(0x1000, meas->regmap[GCC], - data->ctl_reg, data->status_reg); - - /* Run a full measurement. (~14 ms) */ - raw_count_full = run_measurement(sample_ticks, meas->regmap[GCC], - data->ctl_reg, data->status_reg); - - gcc_xo4_reg &= ~BIT(0); - regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); - - /* Return 0 if the clock is off. */ - if (raw_count_full == raw_count_short) - ret = 0; - else { - /* Compute rate in Hz. */ - raw_count_full = ((raw_count_full * 10) + 15) * 4800000; - do_div(raw_count_full, ((sample_ticks * 10) + 35)); - ret = (raw_count_full * multiplier); - } - - spin_unlock_irqrestore(&clk_reg_lock, flags); - - clk_disable_unprepare(data->cxo); - - return ret; -} - -static u8 clk_debug_mux_get_parent(struct clk_hw *hw) -{ - struct clk_debug_mux *meas = to_clk_measure(hw); - int i, num_parents = clk_hw_get_num_parents(hw); - - for (i = 0; i < num_parents; i++) { - if (!strcmp(meas->parent[i].parents, - hw->init->parent_names[i])) { - pr_debug("%s :Clock name %s index %d\n", __func__, - hw->init->name, i); - return i; - } - } - - return 0; -} - -static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) -{ - struct clk_debug_mux *meas = to_clk_measure(hw); - unsigned long lsb = 0; - u32 regval = 0; - int dbg_cc = 0; - - dbg_cc = meas->parent[index].dbg_cc; - - if (dbg_cc != GCC) { - regmap_read(meas->regmap[dbg_cc], 0x0, ®val); - - /* Clear & Set post divider bits */ - if (meas->parent[index].post_div_mask) { - regval &= ~meas->parent[index].post_div_mask; - lsb = find_first_bit((unsigned long *) - &meas->parent[index].post_div_mask, 32); - regval |= (meas->parent[index].post_div_val << lsb) & - meas->parent[index].post_div_mask; - meas->multiplier = meas->parent[index].post_div_val; - } - - if (meas->parent[index].mask) - regval &= ~meas->parent[index].mask << - meas->parent[index].shift; - else - regval &= ~meas->mask; - - regval |= (meas->parent[index].next_sel & meas->mask); - - if (meas->parent[index].en_mask == 0xFF) - /* Skip en_mask */ - regval = regval; - else if (meas->parent[index].en_mask) - regval |= meas->parent[index].en_mask; - else - regval |= meas->en_mask; - - regmap_write(meas->regmap[dbg_cc], 0x0, regval); - } - - /* update the debug sel for GCC */ - regmap_read(meas->regmap[GCC], meas->debug_offset, ®val); - - /* clear post divider bits */ - regval &= ~BM(15, 12); - lsb = find_first_bit((unsigned long *) - &meas->parent[index].post_div_mask, 32); - regval |= (meas->parent[index].post_div_val << lsb) & - meas->parent[index].post_div_mask; - meas->multiplier = meas->parent[index].post_div_val; - regval &= ~meas->mask; - regval |= (meas->parent[index].sel & meas->mask); - regval |= meas->en_mask; - - regmap_write(meas->regmap[GCC], meas->debug_offset, regval); - - return 0; -} - -const struct clk_ops clk_debug_mux_ops = { - .get_parent = clk_debug_mux_get_parent, - .set_parent = clk_debug_mux_set_parent, -}; -EXPORT_SYMBOL_GPL(clk_debug_mux_ops); - -static int clk_debug_measure_get(void *data, u64 *val) -{ - struct clk_hw *hw = data, *par; - int ret = 0; - unsigned long meas_rate, sw_rate; - - ret = clk_set_parent(measure->clk, hw->clk); - if (!ret) { - par = measure; - while (par && par != hw) { - if (par->init->ops->enable) - par->init->ops->enable(par); - par = clk_hw_get_parent(par); - } - *val = clk_debug_mux_measure_rate(measure); - } - - meas_rate = clk_get_rate(hw->clk); - sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk); - if (sw_rate && meas_rate >= (sw_rate * 2)) - *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate); - - return ret; -} - -DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, - NULL, "%lld\n"); - -void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) -{ - if (IS_ERR_OR_NULL(measure)) - return; - - if (clk_set_parent(measure->clk, hw->clk)) - return; - - debugfs_create_file("measure", S_IRUGO, dentry, hw, - &clk_measure_fops); -} -EXPORT_SYMBOL_GPL(clk_debug_measure_add); - -int clk_register_debug(struct clk_hw *hw, struct dentry *dentry) -{ - if (IS_ERR_OR_NULL(measure)) { - if (hw->init->flags & CLK_IS_MEASURE) - measure = hw; - if (!IS_ERR_OR_NULL(measure)) - clk_debug_measure_add(hw, dentry); - } - - return 0; -} -EXPORT_SYMBOL_GPL(clk_register_debug); - MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 95408a47fef0..c532444a0d40 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -13,7 +13,7 @@ #ifndef __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__ -#include "../clk.h" +#include "clk-debug.h" struct platform_device; struct regmap_config; @@ -54,107 +54,9 @@ extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern struct clk_ops clk_dummy_ops; -/* Debugfs Measure Clocks */ - -/** - * struct measure_clk_data - Structure of clk measure - * - * @cxo: XO clock. - * @xo_div4_cbcr: offset of debug XO/4 div register. - * @ctl_reg: offset of debug control register. - * @status_reg: offset of debug status register. - * - */ -struct measure_clk_data { - struct clk *cxo; - u32 xo_div4_cbcr; - u32 ctl_reg; - u32 status_reg; -}; - -/** - * List of Debug clock controllers. - */ -enum debug_cc { - GCC, - MMCC, - GPU, - CPU, -}; - -/** - * struct clk_src - Struture of clock source for debug mux - * - * @parents: clock name to be used as parent for debug mux. - * @sel: debug mux index at global clock controller. - * @dbg_cc: indicates the clock controller for recursive debug clock - * controllers. - * @next_sel: indicates the debug mux index at recursive debug mux. - * @mask: indicates the mask required at recursive debug mux. - * @shift: indicates the shift required at recursive debug mux. - * @en_mask: indicates the enable bit mask at recursive debug mux. - * Incase the recursive debug mux does not have a enable bit, - * 0xFF should be used to indicate the same, otherwise global - * enable bit would be used. - * @post_div_mask: indicates the post div mask to be used at debug/recursive - * debug mux. - * @post_div_val: indicates the post div value to be used at debug/recursive - * debug mux. - */ -struct clk_src { - const char *parents; - int sel; - enum debug_cc dbg_cc; - int next_sel; - u32 mask; - u32 shift; - u32 en_mask; - u32 post_div_mask; - u32 post_div_val; -}; - -#define MUX_SRC_LIST(...) \ - .parent = (struct clk_src[]){__VA_ARGS__}, \ - .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) - -/** - * struct clk_debug_mux - Struture of clock debug mux - * - * @parent: structure of clk_src - * @num_parents: number of parents - * @regmap: regmaps of debug mux - * @num_parent_regmap: number of regmap of debug mux - * @priv: private measure_clk_data to be used by debug mux - * @en_mask: indicates the enable bit mask at global clock - * controller debug mux. - * @mask: indicates the mask to be used at global clock - * controller debug mux. - * @debug_offset: Start of debug mux offset. - * @hw: handle between common and hardware-specific interfaces. - * @multiplier: internally used by debug mux as post div multiplier. - */ -struct clk_debug_mux { - struct clk_src *parent; - int num_parents; - struct regmap **regmap; - int num_parent_regmap; - void *priv; - u32 en_mask; - u32 mask; - u32 debug_offset; - struct clk_hw hw; - - /* internal */ - u32 multiplier; -}; - #define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) #define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) -#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) - -extern const struct clk_ops clk_debug_mux_ops; - #define WARN_CLK(core, name, cond, fmt, ...) do { \ clk_debug_print_hw(core, NULL); \ WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \ diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c index c282253da584..b55310e091af 100644 --- a/drivers/clk/qcom/gcc-sdm660.c +++ b/drivers/clk/qcom/gcc-sdm660.c @@ -27,6 +27,7 @@ #include "clk-alpha-pll.h" #include "clk-branch.h" +#include "clk-debug.h" #include "common.h" #include "clk-pll.h" #include "clk-regmap.h" @@ -3328,7 +3329,11 @@ static int clk_debug_660_probe(struct platform_device *pdev) return PTR_ERR(clk); } - dev_info(&pdev->dev, "Registered debug mux successfully\n"); + ret = clk_register_debug(&gcc_debug_mux.hw); + if (ret) + dev_err(&pdev->dev, "Could not register Measure clock\n"); + else + dev_info(&pdev->dev, "Registered debug mux successfully\n"); return ret; } diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index 8831e1a05367..11d8aa3ec186 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -22,13 +22,6 @@ #include "clock.h" -/* - * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks - * that are sourced by DPLL5, and both of these require this clock - * to be at 120 MHz for proper operation. - */ -#define DPLL5_FREQ_FOR_USBHOST 120000000 - #define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 #define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 @@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void) struct clk *dpll5_clk; struct clk *dpll5_m2_clk; + /* + * Errata sprz319f advisory 2.1 documents a USB host clock drift issue + * that can be worked around using specially crafted dpll5 settings + * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB + * host clock rate, its .set_rate handler() will detect that frequency + * and use the errata settings. + */ dpll5_clk = clk_get(NULL, "dpll5_ck"); - clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); clk_prepare_enable(dpll5_clk); - /* Program dpll5_m2_clk divider for no division */ + /* Program dpll5_m2_clk divider */ dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); clk_prepare_enable(dpll5_m2_clk); - clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); clk_disable_unprepare(dpll5_m2_clk); clk_disable_unprepare(dpll5_clk); diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 90f3f472ae1c..13c37f48d9d6 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, unsigned long parent_rate); +/* + * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks + * that are sourced by DPLL5, and both of these require this clock + * to be at 120 MHz for proper operation. + */ +#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000 + unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate, unsigned long parent_rate); int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); void omap3_clk_lock_dpll5(void); unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 5519b386edc0..f9a5089ddc79 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = { .round_rate = &omap2_dpll_round_rate, }; +static const struct clk_ops omap3_dpll5_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .get_parent = &omap2_init_dpll_parent, + .recalc_rate = &omap3_dpll_recalc, + .set_rate = &omap3_dpll5_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, + .round_rate = &omap2_dpll_round_rate, +}; + static const struct clk_ops omap3_dpll_per_ck_ops = { .enable = &omap3_noncore_dpll_enable, .disable = &omap3_noncore_dpll_disable, @@ -461,7 +473,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node) .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; - of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); + if ((of_machine_is_compatible("ti,omap3630") || + of_machine_is_compatible("ti,omap36xx")) && + !strcmp(node->name, "dpll5_ck")) + of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd); + else + of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); } CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock", of_ti_omap3_dpll_setup); diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c index f4dec00fb684..0e9119fae760 100644 --- a/drivers/clk/ti/dpll3xxx.c +++ b/drivers/clk/ti/dpll3xxx.c @@ -815,3 +815,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, index); } + +/* Apply DM3730 errata sprz319 advisory 2.1. */ +static bool omap3_dpll5_apply_errata(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct omap3_dpll5_settings { + unsigned int rate, m, n; + }; + + static const struct omap3_dpll5_settings precomputed[] = { + /* + * From DM3730 errata advisory 2.1, table 35 and 36. + * The N value is increased by 1 compared to the tables as the + * errata lists register values while last_rounded_field is the + * real divider value. + */ + { 12000000, 80, 0 + 1 }, + { 13000000, 443, 5 + 1 }, + { 19200000, 50, 0 + 1 }, + { 26000000, 443, 11 + 1 }, + { 38400000, 25, 0 + 1 } + }; + + const struct omap3_dpll5_settings *d; + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct dpll_data *dd; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(precomputed); ++i) { + if (parent_rate == precomputed[i].rate) + break; + } + + if (i == ARRAY_SIZE(precomputed)) + return false; + + d = &precomputed[i]; + + /* Update the M, N and rounded rate values and program the DPLL. */ + dd = clk->dpll_data; + dd->last_rounded_m = d->m; + dd->last_rounded_n = d->n; + dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n); + omap3_noncore_dpll_program(clk, 0); + + return true; +} + +/** + * omap3_dpll5_set_rate - set rate for omap3 dpll5 + * @hw: clock to change + * @rate: target rate for clock + * @parent_rate: rate of the parent clock + * + * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if + * the DPLL is used for USB host (detected through the requested rate). + */ +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) { + if (omap3_dpll5_apply_errata(hw, parent_rate)) + return 0; + } + + return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); +} diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index ff44082a0827..47f8aafe3344 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -482,6 +482,7 @@ static void exynos4_local_timer_stop(struct mct_clock_event_device *mevt) if (mct_int_type == MCT_INT_SPI) { if (evt->irq != -1) disable_irq_nosync(evt->irq); + exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); } else { disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); } diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index cb501386eb6e..c4b0ef65988c 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -373,8 +373,14 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, if (unlikely(rebooting) && new_index != get_nominal_index()) return 0; - if (!throttled) + if (!throttled) { + /* we don't want to be preempted while + * checking if the CPU frequency has been throttled + */ + preempt_disable(); powernv_cpufreq_throttle_check(NULL); + preempt_enable(); + } freq_data.pstate_id = powernv_freqs[new_index].driver_data; diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 2cde3796cb82..f3307fc38e79 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -702,7 +702,9 @@ copy_iv: /* Will read cryptlen */ append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ); - aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2); + append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | KEY_VLF | + FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LASTBOTH); + append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF); /* Write ICV */ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB | diff --git a/drivers/devfreq/governor_spdm_bw_hyp.c b/drivers/devfreq/governor_spdm_bw_hyp.c index cd068a3097ce..f18b72af5fc4 100644 --- a/drivers/devfreq/governor_spdm_bw_hyp.c +++ b/drivers/devfreq/governor_spdm_bw_hyp.c @@ -1,5 +1,5 @@ /* -*Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +*Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * *This program is free software; you can redistribute it and/or modify *it under the terms of the GNU General Public License version 2 and @@ -84,6 +84,10 @@ static irqreturn_t threaded_isr(int irq, void *dev_id) (int)desc.arg[0], ext_status); mutex_lock(&devfreqs_lock); list_for_each_entry(data, &devfreqs, list) { + if (data == NULL || data->devfreq == NULL) { + pr_err("Spurious interrupts\n"); + break; + } if (data->spdm_client == desc.ret[0]) { devfreq_monitor_suspend(data->devfreq); mutex_lock(&data->devfreq->lock); diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index dd184b50e5b4..284627806b88 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -272,7 +272,7 @@ static void ipu_irq_handler(struct irq_desc *desc) u32 status; int i, line; - for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) { + for (i = 0; i < IPU_IRQ_NR_BANKS; i++) { struct ipu_irq_bank *bank = irq_bank + i; raw_spin_lock(&bank_lock); diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 17ee758b419f..8250950aab8b 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -445,6 +445,9 @@ struct dma_pl330_chan { /* for cyclic capability */ bool cyclic; + + /* for runtime pm tracking */ + bool active; }; struct pl330_dmac { @@ -1994,6 +1997,7 @@ static void pl330_tasklet(unsigned long data) _stop(pch->thread); spin_unlock(&pch->thread->dmac->lock); power_down = true; + pch->active = false; } else { /* Make sure the PL330 Channel thread is active */ spin_lock(&pch->thread->dmac->lock); @@ -2015,6 +2019,7 @@ static void pl330_tasklet(unsigned long data) desc->status = PREP; list_move_tail(&desc->node, &pch->work_list); if (power_down) { + pch->active = true; spin_lock(&pch->thread->dmac->lock); _start(pch->thread); spin_unlock(&pch->thread->dmac->lock); @@ -2129,6 +2134,7 @@ static int pl330_terminate_all(struct dma_chan *chan) unsigned long flags; struct pl330_dmac *pl330 = pch->dmac; LIST_HEAD(list); + bool power_down = false; pm_runtime_get_sync(pl330->ddma.dev); spin_lock_irqsave(&pch->lock, flags); @@ -2139,6 +2145,8 @@ static int pl330_terminate_all(struct dma_chan *chan) pch->thread->req[0].desc = NULL; pch->thread->req[1].desc = NULL; pch->thread->req_running = -1; + power_down = pch->active; + pch->active = false; /* Mark all desc done */ list_for_each_entry(desc, &pch->submitted_list, node) { @@ -2156,6 +2164,8 @@ static int pl330_terminate_all(struct dma_chan *chan) list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); pm_runtime_mark_last_busy(pl330->ddma.dev); + if (power_down) + pm_runtime_put_autosuspend(pl330->ddma.dev); pm_runtime_put_autosuspend(pl330->ddma.dev); return 0; @@ -2302,6 +2312,7 @@ static void pl330_issue_pending(struct dma_chan *chan) * updated on work_list emptiness status. */ WARN_ON(list_empty(&pch->submitted_list)); + pch->active = true; pm_runtime_get_sync(pch->dmac->ddma.dev); } list_splice_tail_init(&pch->submitted_list, &pch->work_list); diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 7275a99873b8..1e5f35d8422d 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -434,11 +434,12 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id) { int value; struct esoc_clink *esoc; + struct device *dev; struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id; - struct device *dev = mdm->dev; if (!mdm) return IRQ_HANDLED; + dev = mdm->dev; esoc = mdm->esoc; value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)); if (value == 0 && mdm->ready) { @@ -499,7 +500,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm) struct device_node *node = mdm->dev->of_node; addr = of_iomap(node, 0); - if (IS_ERR(addr)) { + if (IS_ERR_OR_NULL(addr)) { dev_err(mdm->dev, "failed to get debug base addres\n"); return; } @@ -508,7 +509,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm) if (val == MDM_DBG_MODE) { mdm->dbg_mode = true; mdm->cti = coresight_cti_get(MDM_CTI_NAME); - if (IS_ERR(mdm->cti)) { + if (IS_ERR_OR_NULL(mdm->cti)) { dev_err(mdm->dev, "unable to get cti handle\n"); goto cti_get_err; } @@ -743,7 +744,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm, mdm->dev = &pdev->dev; mdm->pon_ops = pon_ops; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); - if (IS_ERR(esoc)) { + if (IS_ERR_OR_NULL(esoc)) { dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } @@ -813,7 +814,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm, mdm->pon_ops = pon_ops; node = pdev->dev.of_node; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); - if (IS_ERR(esoc)) { + if (IS_ERR_OR_NULL(esoc)) { dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } @@ -901,7 +902,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, mdm->pon_ops = pon_ops; node = pdev->dev.of_node; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); - if (IS_ERR(esoc)) { + if (IS_ERR_OR_NULL(esoc)) { dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } @@ -936,6 +937,10 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, mdm->dual_interface = of_property_read_bool(node, "qcom,mdm-dual-link"); esoc->link_name = MDM9x55_PCIE; + ret = of_property_read_string(node, "qcom,mdm-link-info", + &esoc->link_info); + if (ret) + dev_info(mdm->dev, "esoc link info missing\n"); esoc->clink_ops = clink_ops; esoc->parent = mdm->dev; esoc->owner = THIS_MODULE; @@ -1001,11 +1006,11 @@ static int mdm_probe(struct platform_device *pdev) struct mdm_ctrl *mdm; match = of_match_node(mdm_dt_match, node); - if (IS_ERR(match)) + if (IS_ERR_OR_NULL(match)) return PTR_ERR(match); mdm_ops = match->data; mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL); - if (IS_ERR(mdm)) + if (IS_ERR_OR_NULL(mdm)) return PTR_ERR(mdm); return mdm_ops->config_hw(mdm, mdm_ops, pdev); } diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index 9abe125f28b5..8697428eceb2 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -230,7 +230,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv) struct esoc_eng *esoc_eng; mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL); - if (IS_ERR(mdm_drv)) + if (IS_ERR_OR_NULL(mdm_drv)) return PTR_ERR(mdm_drv); esoc_eng = &mdm_drv->cmd_eng; esoc_eng->handle_clink_evt = mdm_handle_clink_evt; diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h index efa2a1d5e5a6..755fb24bd60a 100644 --- a/drivers/esoc/esoc.h +++ b/drivers/esoc/esoc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,7 @@ struct esoc_eng { * struct esoc_clink: Representation of external esoc device * @name: Name of the external esoc. * @link_name: name of the physical link. + * @link_info: additional info about the physical link. * @parent: parent device. * @dev: device for userspace interface. * @id: id of the external device. @@ -62,6 +63,7 @@ struct esoc_eng { struct esoc_clink { const char *name; const char *link_name; + const char *link_info; struct device *parent; struct device dev; unsigned int id; diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c index 110a5dabed10..f925607511ba 100644 --- a/drivers/esoc/esoc_bus.c +++ b/drivers/esoc/esoc_bus.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -32,10 +32,19 @@ esoc_link_show(struct device *dev, struct device_attribute *attr, to_esoc_clink(dev)->link_name); } +static ssize_t +esoc_link_info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, ESOC_LINK_LEN, "%s", + to_esoc_clink(dev)->link_info); +} + static struct device_attribute esoc_clink_attrs[] = { __ATTR_RO(esoc_name), __ATTR_RO(esoc_link), + __ATTR_RO(esoc_link_info), __ATTR_NULL, }; @@ -129,7 +138,7 @@ struct esoc_clink *get_esoc_clink(int id) struct device *dev; dev = bus_find_device(&esoc_bus_type, NULL, &id, esoc_clink_match_id); - if (IS_ERR(dev)) + if (IS_ERR_OR_NULL(dev)) return NULL; esoc_clink = to_esoc_clink(dev); return esoc_clink; @@ -143,7 +152,7 @@ struct esoc_clink *get_esoc_clink_by_node(struct device_node *node) dev = bus_find_device(&esoc_bus_type, NULL, node, esoc_clink_match_node); - if (IS_ERR(dev)) + if (IS_ERR_OR_NULL(dev)) return NULL; esoc_clink = to_esoc_clink(dev); return esoc_clink; @@ -175,14 +184,14 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink) len = strlen("esoc") + sizeof(esoc_clink->id); subsys_name = kzalloc(len, GFP_KERNEL); - if (IS_ERR(subsys_name)) + if (IS_ERR_OR_NULL(subsys_name)) return PTR_ERR(subsys_name); snprintf(subsys_name, len, "esoc%d", esoc_clink->id); esoc_clink->subsys.name = subsys_name; esoc_clink->dev.of_node = esoc_clink->np; esoc_clink->subsys.dev = &esoc_clink->dev; esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys); - if (IS_ERR(esoc_clink->subsys_dev)) { + if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) { dev_err(&esoc_clink->dev, "failed to register ssr node\n"); ret = PTR_ERR(esoc_clink->subsys_dev); goto subsys_err; diff --git a/drivers/esoc/esoc_client.c b/drivers/esoc/esoc_client.c index e9932ea3e964..0fe7a83cc3c2 100644 --- a/drivers/esoc/esoc_client.c +++ b/drivers/esoc/esoc_client.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -43,7 +43,7 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, struct device_node *np = dev->of_node; struct esoc_clink *esoc_clink; struct esoc_desc *desc; - char *esoc_name, *esoc_link; + char *esoc_name, *esoc_link, *esoc_link_info; for (index = 0;; index++) { esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index); @@ -85,16 +85,26 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, kfree(esoc_name); return ERR_PTR(-ENOMEM); } + esoc_link_info = kasprintf(GFP_KERNEL, "%s", + esoc_clink->link_info); + if (IS_ERR_OR_NULL(esoc_link_info)) { + dev_err(dev, "unable to alloc link info name\n"); + kfree(esoc_name); + kfree(esoc_link); + return ERR_PTR(-ENOMEM); + } desc = devres_alloc(devm_esoc_desc_release, sizeof(*desc), GFP_KERNEL); if (IS_ERR_OR_NULL(desc)) { kfree(esoc_name); kfree(esoc_link); + kfree(esoc_link_info); dev_err(dev, "unable to allocate esoc descriptor\n"); return ERR_PTR(-ENOMEM); } desc->name = esoc_name; desc->link = esoc_link; + desc->link_info = esoc_link_info; desc->priv = esoc_clink; devres_add(dev, desc); return desc; diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c index 56b767b29093..ffb2237da5fa 100644 --- a/drivers/esoc/esoc_dev.c +++ b/drivers/esoc/esoc_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -214,7 +214,7 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd, esoc_clink->name); return -EIO; } - put_user(req, (unsigned long __user *)uarg); + put_user(req, (unsigned int __user *)uarg); } return err; @@ -227,7 +227,7 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd, err = clink_ops->get_status(&status, esoc_clink); if (err) return err; - put_user(status, (unsigned long __user *)uarg); + put_user(status, (unsigned int __user *)uarg); break; case ESOC_WAIT_FOR_CRASH: err = wait_event_interruptible(esoc_udev->evt_wait, @@ -241,7 +241,7 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd, esoc_clink->name); return -EIO; } - put_user(evt, (unsigned long __user *)uarg); + put_user(evt, (unsigned int __user *)uarg); } return err; break; @@ -260,7 +260,16 @@ static int esoc_dev_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); esoc_udev = esoc_udev_get_by_minor(minor); + if (!esoc_udev) { + pr_err("failed to get udev\n"); + return -ENOMEM; + } + esoc_clink = get_esoc_clink(esoc_udev->clink->id); + if (!esoc_clink) { + pr_err("failed to get clink\n"); + return -ENOMEM; + } uhandle = kzalloc(sizeof(*uhandle), GFP_KERNEL); if (!uhandle) { @@ -306,12 +315,12 @@ int esoc_clink_add_device(struct device *dev, void *dummy) struct esoc_clink *esoc_clink = to_esoc_clink(dev); esoc_udev = get_free_esoc_udev(esoc_clink); - if (IS_ERR(esoc_udev)) + if (IS_ERR_OR_NULL(esoc_udev)) return PTR_ERR(esoc_udev); esoc_udev->dev = device_create(esoc_class, &esoc_clink->dev, MKDEV(esoc_major, esoc_clink->id), esoc_clink, "esoc-%d", esoc_clink->id); - if (IS_ERR(esoc_udev->dev)) { + if (IS_ERR_OR_NULL(esoc_udev->dev)) { pr_err("failed to create user device\n"); goto dev_err; } @@ -358,7 +367,7 @@ int __init esoc_dev_init(void) { int ret = 0; esoc_class = class_create(THIS_MODULE, "esoc-dev"); - if (IS_ERR(esoc_class)) { + if (IS_ERR_OR_NULL(esoc_class)) { pr_err("coudn't create class"); return PTR_ERR(esoc_class); } diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 9e15d571b53c..a76c35fc0b92 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -203,7 +203,19 @@ void __init efi_init(void) reserve_regions(); early_memunmap(memmap.map, params.mmap_size); - memblock_mark_nomap(params.mmap & PAGE_MASK, - PAGE_ALIGN(params.mmap_size + - (params.mmap & ~PAGE_MASK))); + + if (IS_ENABLED(CONFIG_ARM)) { + /* + * ARM currently does not allow ioremap_cache() to be called on + * memory regions that are covered by struct page. So remove the + * UEFI memory map from the linear mapping. + */ + memblock_mark_nomap(params.mmap & PAGE_MASK, + PAGE_ALIGN(params.mmap_size + + (params.mmap & ~PAGE_MASK))); + } else { + memblock_reserve(params.mmap & PAGE_MASK, + PAGE_ALIGN(params.mmap_size + + (params.mmap & ~PAGE_MASK))); + } } diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c index dc4214f7a302..fa90330db93b 100644 --- a/drivers/firmware/qcom/tz_log.c +++ b/drivers/firmware/qcom/tz_log.c @@ -57,6 +57,10 @@ * TZ 3.X version info */ #define QSEE_VERSION_TZ_3_X 0x800000 +/* + * TZ 4.X version info + */ +#define QSEE_VERSION_TZ_4_X 0x1000000 #define TZBSP_AES_256_ENCRYPTED_KEY_SIZE 256 #define TZBSP_NONCE_LEN 12 @@ -130,6 +134,18 @@ struct tzdbg_int_t { uint64_t int_count[TZBSP_MAX_CPU_COUNT]; /* # of times seen per CPU */ }; +/* + * Interrupt Info Table used in tz version >=4.X + */ +struct tzdbg_int_t_tz40 { + uint16_t int_info; + uint8_t avail; + uint8_t spare; + uint32_t int_num; + uint8_t int_desc[TZBSP_MAX_INT_DESC]; + uint32_t int_count[TZBSP_MAX_CPU_COUNT]; /* uint32_t in TZ ver >= 4.x*/ +}; + /* warm boot reason for cores */ struct tzbsp_diag_wakeup_info_t { /* Wake source info : APCS_GICC_HPPIR */ @@ -291,6 +307,7 @@ struct tzdbg { struct tzdbg_stat stat[TZDBG_STATS_MAX]; uint32_t hyp_debug_rw_buf_size; bool is_hyplog_enabled; + uint32_t tz_version; }; static struct tzdbg tzdbg = { @@ -364,31 +381,9 @@ static int _disp_tz_boot_stats(void) int len = 0; struct tzdbg_boot_info_t *ptr = NULL; struct tzdbg_boot_info64_t *ptr_64 = NULL; - int ret = 0; - uint32_t smc_id = 0; - uint32_t feature = 10; - struct qseecom_command_scm_resp resp = {}; - struct scm_desc desc = {0}; - if (!is_scm_armv8()) { - ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature, - sizeof(feature), &resp, sizeof(resp)); - } else { - smc_id = TZ_INFO_GET_FEATURE_VERSION_ID; - desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID; - desc.args[0] = feature; - ret = scm_call2(smc_id, &desc); - resp.result = desc.ret[0]; - } - - if (ret) { - pr_err("%s: scm_call to register log buffer failed\n", - __func__); - return 0; - } - pr_info("qsee_version = 0x%x\n", resp.result); - - if (resp.result >= QSEE_VERSION_TZ_3_X) { + pr_info("qsee_version = 0x%x\n", tzdbg.tz_version); + if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) { ptr_64 = (struct tzdbg_boot_info64_t *)((unsigned char *) tzdbg.diag_buf + tzdbg.diag_buf->boot_info_off); } else { @@ -397,7 +392,7 @@ static int _disp_tz_boot_stats(void) } for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) { - if (resp.result >= QSEE_VERSION_TZ_3_X) { + if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) { len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " CPU #: %d\n" @@ -487,6 +482,7 @@ static int _disp_tz_interrupt_stats(void) int *num_int; unsigned char *ptr; struct tzdbg_int_t *tzdbg_ptr; + struct tzdbg_int_t_tz40 *tzdbg_ptr_tz40; num_int = (uint32_t *)((unsigned char *)tzdbg.diag_buf + (tzdbg.diag_buf->int_info_off - sizeof(uint32_t))); @@ -495,9 +491,12 @@ static int _disp_tz_interrupt_stats(void) int_info_size = ((tzdbg.diag_buf->ring_off - tzdbg.diag_buf->int_info_off)/(*num_int)); - for (i = 0; i < (*num_int); i++) { - tzdbg_ptr = (struct tzdbg_int_t *)ptr; - len += snprintf(tzdbg.disp_buf + len, + pr_info("qsee_version = 0x%x\n", tzdbg.tz_version); + + if (tzdbg.tz_version < QSEE_VERSION_TZ_4_X) { + for (i = 0; i < (*num_int); i++) { + tzdbg_ptr = (struct tzdbg_int_t *)ptr; + len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " Interrupt Number : 0x%x\n" " Type of Interrupt : 0x%x\n" @@ -505,24 +504,53 @@ static int _disp_tz_interrupt_stats(void) tzdbg_ptr->int_num, (uint32_t)tzdbg_ptr->int_info, (uint8_t *)tzdbg_ptr->int_desc); - for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { - len += snprintf(tzdbg.disp_buf + len, + for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { + len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " int_count on CPU # %d : %u\n", (uint32_t)j, (uint32_t)tzdbg_ptr->int_count[j]); - } - len += snprintf(tzdbg.disp_buf + len, debug_rw_buf_size - 1, - "\n"); + } + len += snprintf(tzdbg.disp_buf + len, + debug_rw_buf_size - 1, "\n"); - if (len > (debug_rw_buf_size - 1)) { - pr_warn("%s: Cannot fit all info into the buffer\n", + if (len > (debug_rw_buf_size - 1)) { + pr_warn("%s: Cannot fit all info into buf\n", __func__); - break; + break; + } + ptr += int_info_size; } + } else { + for (i = 0; i < (*num_int); i++) { + tzdbg_ptr_tz40 = (struct tzdbg_int_t_tz40 *)ptr; + len += snprintf(tzdbg.disp_buf + len, + (debug_rw_buf_size - 1) - len, + " Interrupt Number : 0x%x\n" + " Type of Interrupt : 0x%x\n" + " Description of interrupt : %s\n", + tzdbg_ptr_tz40->int_num, + (uint32_t)tzdbg_ptr_tz40->int_info, + (uint8_t *)tzdbg_ptr_tz40->int_desc); + for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { + len += snprintf(tzdbg.disp_buf + len, + (debug_rw_buf_size - 1) - len, + " int_count on CPU # %d : %u\n", + (uint32_t)j, + (uint32_t)tzdbg_ptr_tz40->int_count[j]); + } + len += snprintf(tzdbg.disp_buf + len, + debug_rw_buf_size - 1, "\n"); - ptr += int_info_size; + if (len > (debug_rw_buf_size - 1)) { + pr_warn("%s: Cannot fit all info into buf\n", + __func__); + break; + } + ptr += int_info_size; + } } + tzdbg.stat[TZDBG_INTERRUPT].data = tzdbg.disp_buf; return len; } @@ -1015,6 +1043,33 @@ static int __update_hypdbg_base(struct platform_device *pdev, return 0; } +static void tzdbg_get_tz_version(void) +{ + uint32_t smc_id = 0; + uint32_t feature = 10; + struct qseecom_command_scm_resp resp = {0}; + struct scm_desc desc = {0}; + int ret = 0; + + if (!is_scm_armv8()) { + ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature, + sizeof(feature), &resp, sizeof(resp)); + } else { + smc_id = TZ_INFO_GET_FEATURE_VERSION_ID; + desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID; + desc.args[0] = feature; + ret = scm_call2(smc_id, &desc); + resp.result = desc.ret[0]; + } + + if (ret) + pr_err("%s: scm_call to get tz version failed\n", + __func__); + else + tzdbg.tz_version = resp.result; + +} + /* * Driver functions */ @@ -1103,6 +1158,9 @@ static int tz_log_probe(struct platform_device *pdev) goto err; tzdbg_register_qsee_log_buf(); + + tzdbg_get_tz_version(); + return 0; err: kfree(tzdbg.diag_buf); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index c161eeda417b..267749a94c5a 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -3704,9 +3704,15 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev, default: encoder->possible_crtcs = 0x3; break; + case 3: + encoder->possible_crtcs = 0x7; + break; case 4: encoder->possible_crtcs = 0xf; break; + case 5: + encoder->possible_crtcs = 0x1f; + break; case 6: encoder->possible_crtcs = 0x3f; break; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index e0b4586a26fd..9b8f0b975ca6 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -223,7 +223,8 @@ static int ast_get_dram_info(struct drm_device *dev) ast_write32(ast, 0x10000, 0xfc600309); do { - ; + if (pci_channel_offline(dev->pdev)) + return -EIO; } while (ast_read32(ast, 0x10000) != 0x01); data = ast_read32(ast, 0x10004); @@ -429,7 +430,9 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ast_detect_chip(dev, &need_post); if (ast->chip != AST1180) { - ast_get_dram_info(dev); + ret = ast_get_dram_info(dev); + if (ret) + goto out_free; ast->vram_size = ast_get_vram_info(dev); DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); } diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 810c51d92b99..30672a3df8a9 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -58,13 +58,9 @@ bool ast_is_vga_enabled(struct drm_device *dev) /* TODO 1180 */ } else { ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); - if (ch) { - ast_open_key(ast); - ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff); - return ch & 0x04; - } + return !!(ch & 0x01); } - return 0; + return false; } static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; @@ -375,8 +371,8 @@ void ast_post_gpu(struct drm_device *dev) pci_write_config_dword(ast->dev->pdev, 0x04, reg); ast_enable_vga(dev); - ast_enable_mmio(dev); ast_open_key(ast); + ast_enable_mmio(dev); ast_set_def_ext_reg(dev); if (ast->chip == AST2300 || ast->chip == AST2400) @@ -1630,12 +1626,44 @@ static void ast_init_dram_2300(struct drm_device *dev) temp |= 0x73; ast_write32(ast, 0x12008, temp); + param.dram_freq = 396; param.dram_type = AST_DDR3; + temp = ast_mindwm(ast, 0x1e6e2070); if (temp & 0x01000000) param.dram_type = AST_DDR2; - param.dram_chipid = ast->dram_type; - param.dram_freq = ast->mclk; - param.vram_size = ast->vram_size; + switch (temp & 0x18000000) { + case 0: + param.dram_chipid = AST_DRAM_512Mx16; + break; + default: + case 0x08000000: + param.dram_chipid = AST_DRAM_1Gx16; + break; + case 0x10000000: + param.dram_chipid = AST_DRAM_2Gx16; + break; + case 0x18000000: + param.dram_chipid = AST_DRAM_4Gx16; + break; + } + switch (temp & 0x0c) { + default: + case 0x00: + param.vram_size = AST_VIDMEM_SIZE_8M; + break; + + case 0x04: + param.vram_size = AST_VIDMEM_SIZE_16M; + break; + + case 0x08: + param.vram_size = AST_VIDMEM_SIZE_32M; + break; + + case 0x0c: + param.vram_size = AST_VIDMEM_SIZE_64M; + break; + } if (param.dram_type == AST_DDR3) { get_ddr3_info(ast, ¶m); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 1ac29d703c12..ea443fafb934 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -265,7 +265,7 @@ mode_fixup(struct drm_atomic_state *state) struct drm_connector *connector; struct drm_connector_state *conn_state; int i; - bool ret; + int ret; for_each_crtc_in_state(state, crtc, crtc_state, i) { if (!crtc_state->mode_changed && diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 7cb2815e815e..a3b96d691ac9 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1812,7 +1812,7 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) mgr->payloads[i].num_slots = req_payload.num_slots; } else if (mgr->payloads[i].num_slots) { mgr->payloads[i].num_slots = 0; - drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]); + drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]); req_payload.payload_state = mgr->payloads[i].payload_state; mgr->payloads[i].start_slot = 0; } diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 8c9ac021608f..cc1e16fd7e76 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -144,6 +144,9 @@ static struct edid_quirk { /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, + + /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/ + { "ETR", 13896, EDID_QUIRK_FORCE_8BPC }, }; /* diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 39e30abddf08..71a10f08522e 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1401,6 +1401,13 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, return NULL; mode->type |= DRM_MODE_TYPE_USERDEF; + /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ + if (cmd->xres == 1366 && mode->hdisplay == 1368) { + mode->hdisplay = 1366; + mode->hsync_start--; + mode->hsync_end--; + drm_mode_set_name(mode); + } drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); return mode; } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 92e7e5795398..db98ab5cde3d 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -484,6 +484,9 @@ static const struct file_operations psb_gem_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = psb_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif .mmap = drm_gem_mmap, .poll = drm_poll, .read = drm_read, diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 97d1ed20418b..63fea6a2869c 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -445,6 +445,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; struct edid *edid; struct i2c_adapter *i2c; + bool ret = false; BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); @@ -461,17 +462,17 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) */ if (!is_digital) { DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); - return true; + ret = true; + } else { + DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); } - - DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); } else { DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n"); } kfree(edid); - return false; + return ret; } static enum drm_connector_status diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 909d1d71d130..4f5d07bb3511 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3948,10 +3948,10 @@ static void page_flip_completed(struct intel_crtc *intel_crtc) drm_crtc_vblank_put(&intel_crtc->base); wake_up_all(&dev_priv->pending_flip_queue); - queue_work(dev_priv->wq, &work->work); - trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); + + queue_work(dev_priv->wq, &work->work); } void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 3f802163f7d4..e7c18519274a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6803,7 +6803,18 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv) { - I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); + u32 val; + + /* + * On driver load, a pipe may be active and driving a DSI display. + * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck + * (and never recovering) in this case. intel_dsi_post_disable() will + * clear it when we turn off the display. + */ + val = I915_READ(DSPCLK_GATE_D); + val &= DPOUNIT_CLOCK_GATE_DISABLE; + val |= VRHUNIT_CLOCK_GATE_DISABLE; + I915_WRITE(DSPCLK_GATE_D, val); /* * Disable trickle feed and enable pnd deadline calculation diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 79ea5a9f90ea..ebf8be80a3d9 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -59,7 +59,8 @@ msm_drm-y += adreno/adreno_device.o \ adreno/a5xx_gpu.o \ adreno/a5xx_power.o \ adreno/a5xx_preempt.o \ - adreno/a5xx_snapshot.o + adreno/a5xx_snapshot.o \ + adreno/a5xx_counters.o endif msm_drm-$(CONFIG_DRM_MSM_MDP4) += mdp/mdp4/mdp4_crtc.o \ diff --git a/drivers/gpu/drm/msm/adreno/a5xx.xml.h b/drivers/gpu/drm/msm/adreno/a5xx.xml.h index 56dad2217289..b73f4efb1b9d 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a5xx.xml.h @@ -8,17 +8,17 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /local3/projects/drm/envytools/rnndb//adreno.xml ( 431 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//freedreno_copyright.xml ( 1572 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//adreno/a2xx.xml ( 32901 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//adreno/adreno_common.xml ( 12025 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//adreno/adreno_pm4.xml ( 19684 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//adreno/a3xx.xml ( 83840 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//adreno/a4xx.xml ( 110708 bytes, from 2016-10-24 21:12:27) -- /local3/projects/drm/envytools/rnndb//adreno/a5xx.xml ( 81546 bytes, from 2016-10-31 16:38:41) -- /local3/projects/drm/envytools/rnndb//adreno/ocmem.xml ( 1773 bytes, from 2016-10-24 21:12:27) - -Copyright (C) 2013-2016 by the following authors: +- ./rnndb/adreno.xml ( 431 bytes, from 2016-10-24 21:12:27) +- ./rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-10-24 21:12:27) +- ./rnndb/adreno/a2xx.xml ( 32901 bytes, from 2016-10-24 21:12:27) +- ./rnndb/adreno/adreno_common.xml ( 12025 bytes, from 2016-10-24 21:12:27) +- ./rnndb/adreno/adreno_pm4.xml ( 19684 bytes, from 2016-10-24 21:12:27) +- ./rnndb/adreno/a3xx.xml ( 83840 bytes, from 2016-10-24 21:12:27) +- ./rnndb/adreno/a4xx.xml ( 110708 bytes, from 2016-10-24 21:12:27) +- ./rnndb/adreno/a5xx.xml ( 86963 bytes, from 2017-03-03 16:01:09) +- ./rnndb/adreno/ocmem.xml ( 1773 bytes, from 2016-10-24 21:12:27) + +Copyright (C) 2013-2017 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) - Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) @@ -1759,13 +1759,11 @@ static inline uint32_t A5XX_VBIF_TEST_BUS2_CTRL1_TEST_BUS2_DATA_SEL(uint32_t val #define REG_A5XX_VBIF_TEST_BUS_OUT 0x0000308c -#define REG_A5XX_VBIF_PERF_CNT_SEL0 0x000030d0 +static inline uint32_t REG_A5XX_VBIF_PERF_CNT_EN(uint32_t i0) { return 0x000030c0 + 0x1*i0; } -#define REG_A5XX_VBIF_PERF_CNT_SEL1 0x000030d1 +static inline uint32_t REG_A5XX_VBIF_PERF_CNT_CLR(uint32_t i0) { return 0x000030c8 + 0x1*i0; } -#define REG_A5XX_VBIF_PERF_CNT_SEL2 0x000030d2 - -#define REG_A5XX_VBIF_PERF_CNT_SEL3 0x000030d3 +static inline uint32_t REG_A5XX_VBIF_PERF_CNT_SEL(uint32_t i0) { return 0x000030d0 + 0x1*i0; } #define REG_A5XX_VBIF_PERF_CNT_LOW0 0x000030d8 @@ -1783,11 +1781,9 @@ static inline uint32_t A5XX_VBIF_TEST_BUS2_CTRL1_TEST_BUS2_DATA_SEL(uint32_t val #define REG_A5XX_VBIF_PERF_CNT_HIGH3 0x000030e3 -#define REG_A5XX_VBIF_PERF_PWR_CNT_EN0 0x00003100 - -#define REG_A5XX_VBIF_PERF_PWR_CNT_EN1 0x00003101 +static inline uint32_t REG_A5XX_VBIF_PERF_PWR_CNT_EN(uint32_t i0) { return 0x00003100 + 0x1*i0; } -#define REG_A5XX_VBIF_PERF_PWR_CNT_EN2 0x00003102 +static inline uint32_t REG_A5XX_VBIF_PERF_PWR_CNT_CLR(uint32_t i0) { return 0x00003108 + 0x1*i0; } #define REG_A5XX_VBIF_PERF_PWR_CNT_LOW0 0x00003110 diff --git a/drivers/gpu/drm/msm/adreno/a5xx_counters.c b/drivers/gpu/drm/msm/adreno/a5xx_counters.c new file mode 100644 index 000000000000..f1fac5535359 --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/a5xx_counters.c @@ -0,0 +1,689 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "a5xx_gpu.h" + +/* + * Fixed counters are not selectable, they always count the same thing. + * The countable is an index into the group: countable 0 = register 0, + * etc and they have no select register + */ +static int a5xx_counter_get_fixed(struct msm_gpu *gpu, + struct adreno_counter_group *group, + u32 countable, u32 *lo, u32 *hi) +{ + if (countable >= group->nr_counters) + return -EINVAL; + + if (lo) + *lo = group->counters[countable].lo; + if (hi) + *hi = group->counters[countable].hi; + + return countable; +} + +/* + * Most counters are selectable in that they can be programmed to count + * different events; in most cases there are many more countables than + * counters. When a new counter is requested, first walk the list to see if any + * other counters in that group are counting the same countable and if so reuse + * that counter. If not find the first empty counter in the list and register + * that for the desired countable. If we are out of counters too bad so sad. + */ +static int a5xx_counter_get(struct msm_gpu *gpu, + struct adreno_counter_group *group, + u32 countable, u32 *lo, u32 *hi) +{ + struct adreno_counter *counter; + int i, empty = -1; + + spin_lock(&group->lock); + + for (i = 0; i < group->nr_counters; i++) { + counter = &group->counters[i]; + + if (counter->refcount) { + if (counter->countable == countable) { + counter->refcount++; + + if (lo) + *lo = counter->lo; + if (hi) + *hi = counter->hi; + + spin_unlock(&group->lock); + return i; + } + } else + empty = (empty == -1) ? i : empty; + } + + if (empty == -1) { + spin_unlock(&group->lock); + return -EBUSY; + } + + counter = &group->counters[empty]; + + counter->refcount = 1; + counter->countable = countable; + + if (lo) + *lo = counter->lo; + if (hi) + *hi = counter->hi; + + spin_unlock(&group->lock); + + if (group->funcs.enable) + group->funcs.enable(gpu, group, empty); + + return empty; +} + +/* The majority of the non-fixed counter selects can be programmed by the CPU */ +static void a5xx_counter_enable_cpu(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + struct adreno_counter *counter = &group->counters[counterid]; + + gpu_write(gpu, counter->sel, counter->countable); +} + +static void a5xx_counter_enable_pm4(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); + struct msm_ringbuffer *ring = gpu->rb[MSM_GPU_MAX_RINGS - 1]; + struct adreno_counter *counter = &group->counters[counterid]; + + mutex_lock(&gpu->dev->struct_mutex); + + /* Turn off preemption for the duration of this command */ + OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1); + OUT_RING(ring, 0x02); + + /* Turn off protected mode to write to special registers */ + OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1); + OUT_RING(ring, 0); + + /* Set the save preemption record for the ring/command */ + OUT_PKT4(ring, REG_A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_LO, 2); + OUT_RING(ring, lower_32_bits(a5xx_gpu->preempt_iova[ring->id])); + OUT_RING(ring, upper_32_bits(a5xx_gpu->preempt_iova[ring->id])); + + /* Turn back on protected mode */ + OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1); + OUT_RING(ring, 1); + + /* Idle the GPU */ + OUT_PKT7(ring, CP_WAIT_FOR_IDLE, 0); + + /* Enable the counter */ + OUT_PKT4(ring, counter->sel, 1); + OUT_RING(ring, counter->countable); + + /* Re-enable preemption */ + OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1); + OUT_RING(ring, 0x00); + + OUT_PKT7(ring, CP_PREEMPT_ENABLE_LOCAL, 1); + OUT_RING(ring, 0x01); + + OUT_PKT7(ring, CP_YIELD_ENABLE, 1); + OUT_RING(ring, 0x01); + + /* Yield */ + OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4); + OUT_RING(ring, 0x00); + OUT_RING(ring, 0x00); + OUT_RING(ring, 0x01); + OUT_RING(ring, 0x01); + + gpu->funcs->flush(gpu, ring); + + /* Preempt into our ring if we need to */ + a5xx_preempt_trigger(gpu); + + /* wait for the operation to complete */ + a5xx_idle(gpu, ring); + + mutex_unlock(&gpu->dev->struct_mutex); +} + +/* + * GPMU counters are selectable but the selects are muxed together in two + * registers + */ +static void a5xx_counter_enable_gpmu(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + struct adreno_counter *counter = &group->counters[counterid]; + u32 reg; + int shift; + + /* + * The selects for the GPMU counters are grouped together in two + * registers, a nibble for each counter. Counters 0-3 are located in + * GPMU_POWER_COUNTER_SELECT0 and 4-5 are in GPMU_POWER_COUNTER_SELECT1 + */ + if (counterid <= 3) { + shift = counterid << 3; + reg = REG_A5XX_GPMU_POWER_COUNTER_SELECT_0; + } else { + shift = (counterid - 4) << 3; + reg = REG_A5XX_GPMU_POWER_COUNTER_SELECT_1; + } + + gpu_rmw(gpu, reg, 0xFF << shift, (counter->countable & 0xff) << shift); +} + +/* VBIF counters are selectable but have their own programming process */ +static void a5xx_counter_enable_vbif(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + struct adreno_counter *counter = &group->counters[counterid]; + + gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_CLR(counterid), 1); + gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_CLR(counterid), 0); + gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_SEL(counterid), + counter->countable); + gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_EN(counterid), 1); +} + +/* + * VBIF power counters are not slectable but need to be cleared/enabled before + * use + */ +static void a5xx_counter_enable_vbif_power(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + gpu_write(gpu, REG_A5XX_VBIF_PERF_PWR_CNT_CLR(counterid), 1); + gpu_write(gpu, REG_A5XX_VBIF_PERF_PWR_CNT_CLR(counterid), 0); + gpu_write(gpu, REG_A5XX_VBIF_PERF_PWR_CNT_EN(counterid), 1); +} + +/* GPMU always on counter needs to be enabled before use */ +static void a5xx_counter_enable_alwayson_power(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + gpu_write(gpu, REG_A5XX_GPMU_ALWAYS_ON_COUNTER_RESET, 1); +} + +static u64 a5xx_counter_read(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + if (counterid >= group->nr_counters) + return 0; + + return gpu_read64(gpu, group->counters[counterid].lo, + group->counters[counterid].hi); +} + +/* + * Selectable counters that are no longer used reset the countable to 0 to mark + * the counter as free + */ +static void a5xx_counter_put(struct msm_gpu *gpu, + struct adreno_counter_group *group, int counterid) +{ + struct adreno_counter *counter; + + if (counterid >= group->nr_counters) + return; + + counter = &group->counters[counterid]; + + spin_lock(&group->lock); + if (counter->refcount > 0) + counter->refcount--; + spin_unlock(&group->lock); +} + +static struct adreno_counter a5xx_counters_alwayson[1] = { + { REG_A5XX_RBBM_ALWAYSON_COUNTER_LO, + REG_A5XX_RBBM_ALWAYSON_COUNTER_HI }, +}; + +static struct adreno_counter a5xx_counters_ccu[] = { + { REG_A5XX_RBBM_PERFCTR_CCU_0_LO, REG_A5XX_RBBM_PERFCTR_CCU_0_HI, + REG_A5XX_RB_PERFCTR_CCU_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_CCU_1_LO, REG_A5XX_RBBM_PERFCTR_CCU_1_HI, + REG_A5XX_RB_PERFCTR_CCU_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_CCU_2_LO, REG_A5XX_RBBM_PERFCTR_CCU_2_HI, + REG_A5XX_RB_PERFCTR_CCU_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_CCU_3_LO, REG_A5XX_RBBM_PERFCTR_CCU_3_HI, + REG_A5XX_RB_PERFCTR_CCU_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_cmp[] = { + { REG_A5XX_RBBM_PERFCTR_CMP_0_LO, REG_A5XX_RBBM_PERFCTR_CMP_0_HI, + REG_A5XX_RB_PERFCTR_CMP_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_CMP_1_LO, REG_A5XX_RBBM_PERFCTR_CMP_1_HI, + REG_A5XX_RB_PERFCTR_CMP_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_CMP_2_LO, REG_A5XX_RBBM_PERFCTR_CMP_2_HI, + REG_A5XX_RB_PERFCTR_CMP_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_CMP_3_LO, REG_A5XX_RBBM_PERFCTR_CMP_3_HI, + REG_A5XX_RB_PERFCTR_CMP_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_cp[] = { + { REG_A5XX_RBBM_PERFCTR_CP_0_LO, REG_A5XX_RBBM_PERFCTR_CP_0_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_CP_1_LO, REG_A5XX_RBBM_PERFCTR_CP_1_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_CP_2_LO, REG_A5XX_RBBM_PERFCTR_CP_2_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_CP_3_LO, REG_A5XX_RBBM_PERFCTR_CP_3_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_CP_4_LO, REG_A5XX_RBBM_PERFCTR_CP_4_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_CP_5_LO, REG_A5XX_RBBM_PERFCTR_CP_5_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_CP_6_LO, REG_A5XX_RBBM_PERFCTR_CP_6_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_CP_7_LO, REG_A5XX_RBBM_PERFCTR_CP_7_HI, + REG_A5XX_CP_PERFCTR_CP_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_hlsq[] = { + { REG_A5XX_RBBM_PERFCTR_HLSQ_0_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_0_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_1_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_1_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_2_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_2_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_3_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_3_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_4_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_4_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_5_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_5_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_6_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_6_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_HLSQ_7_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_7_HI, + REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_lrz[] = { + { REG_A5XX_RBBM_PERFCTR_LRZ_0_LO, REG_A5XX_RBBM_PERFCTR_LRZ_0_HI, + REG_A5XX_GRAS_PERFCTR_LRZ_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_LRZ_1_LO, REG_A5XX_RBBM_PERFCTR_LRZ_1_HI, + REG_A5XX_GRAS_PERFCTR_LRZ_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_LRZ_2_LO, REG_A5XX_RBBM_PERFCTR_LRZ_2_HI, + REG_A5XX_GRAS_PERFCTR_LRZ_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_LRZ_3_LO, REG_A5XX_RBBM_PERFCTR_LRZ_3_HI, + REG_A5XX_GRAS_PERFCTR_LRZ_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_pc[] = { + { REG_A5XX_RBBM_PERFCTR_PC_0_LO, REG_A5XX_RBBM_PERFCTR_PC_0_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_PC_1_LO, REG_A5XX_RBBM_PERFCTR_PC_1_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_PC_2_LO, REG_A5XX_RBBM_PERFCTR_PC_2_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_PC_3_LO, REG_A5XX_RBBM_PERFCTR_PC_3_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_PC_4_LO, REG_A5XX_RBBM_PERFCTR_PC_4_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_PC_5_LO, REG_A5XX_RBBM_PERFCTR_PC_5_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_PC_6_LO, REG_A5XX_RBBM_PERFCTR_PC_6_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_PC_7_LO, REG_A5XX_RBBM_PERFCTR_PC_7_HI, + REG_A5XX_PC_PERFCTR_PC_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_ras[] = { + { REG_A5XX_RBBM_PERFCTR_RAS_0_LO, REG_A5XX_RBBM_PERFCTR_RAS_0_HI, + REG_A5XX_GRAS_PERFCTR_RAS_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_RAS_1_LO, REG_A5XX_RBBM_PERFCTR_RAS_1_HI, + REG_A5XX_GRAS_PERFCTR_RAS_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_RAS_2_LO, REG_A5XX_RBBM_PERFCTR_RAS_2_HI, + REG_A5XX_GRAS_PERFCTR_RAS_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_RAS_3_LO, REG_A5XX_RBBM_PERFCTR_RAS_3_HI, + REG_A5XX_GRAS_PERFCTR_RAS_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_rb[] = { + { REG_A5XX_RBBM_PERFCTR_RB_0_LO, REG_A5XX_RBBM_PERFCTR_RB_0_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_RB_1_LO, REG_A5XX_RBBM_PERFCTR_RB_1_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_RB_2_LO, REG_A5XX_RBBM_PERFCTR_RB_2_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_RB_3_LO, REG_A5XX_RBBM_PERFCTR_RB_3_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_RB_4_LO, REG_A5XX_RBBM_PERFCTR_RB_4_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_RB_5_LO, REG_A5XX_RBBM_PERFCTR_RB_5_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_RB_6_LO, REG_A5XX_RBBM_PERFCTR_RB_6_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_RB_7_LO, REG_A5XX_RBBM_PERFCTR_RB_7_HI, + REG_A5XX_RB_PERFCTR_RB_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_rbbm[] = { + { REG_A5XX_RBBM_PERFCTR_RBBM_0_LO, REG_A5XX_RBBM_PERFCTR_RBBM_0_HI, + REG_A5XX_RBBM_PERFCTR_RBBM_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_RBBM_1_LO, REG_A5XX_RBBM_PERFCTR_RBBM_1_HI, + REG_A5XX_RBBM_PERFCTR_RBBM_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_RBBM_2_LO, REG_A5XX_RBBM_PERFCTR_RBBM_2_HI, + REG_A5XX_RBBM_PERFCTR_RBBM_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_RBBM_3_LO, REG_A5XX_RBBM_PERFCTR_RBBM_3_HI, + REG_A5XX_RBBM_PERFCTR_RBBM_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_sp[] = { + { REG_A5XX_RBBM_PERFCTR_SP_0_LO, REG_A5XX_RBBM_PERFCTR_SP_0_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_SP_1_LO, REG_A5XX_RBBM_PERFCTR_SP_1_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_SP_2_LO, REG_A5XX_RBBM_PERFCTR_SP_2_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_SP_3_LO, REG_A5XX_RBBM_PERFCTR_SP_3_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_SP_4_LO, REG_A5XX_RBBM_PERFCTR_SP_4_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_SP_5_LO, REG_A5XX_RBBM_PERFCTR_SP_5_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_SP_6_LO, REG_A5XX_RBBM_PERFCTR_SP_6_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_SP_7_LO, REG_A5XX_RBBM_PERFCTR_SP_7_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_7 }, + { REG_A5XX_RBBM_PERFCTR_SP_8_LO, REG_A5XX_RBBM_PERFCTR_SP_8_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_8 }, + { REG_A5XX_RBBM_PERFCTR_SP_9_LO, REG_A5XX_RBBM_PERFCTR_SP_9_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_9 }, + { REG_A5XX_RBBM_PERFCTR_SP_10_LO, REG_A5XX_RBBM_PERFCTR_SP_10_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_10 }, + { REG_A5XX_RBBM_PERFCTR_SP_11_LO, REG_A5XX_RBBM_PERFCTR_SP_11_HI, + REG_A5XX_SP_PERFCTR_SP_SEL_11 }, +}; + +static struct adreno_counter a5xx_counters_tp[] = { + { REG_A5XX_RBBM_PERFCTR_TP_0_LO, REG_A5XX_RBBM_PERFCTR_TP_0_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_TP_1_LO, REG_A5XX_RBBM_PERFCTR_TP_1_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_TP_2_LO, REG_A5XX_RBBM_PERFCTR_TP_2_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_TP_3_LO, REG_A5XX_RBBM_PERFCTR_TP_3_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_TP_4_LO, REG_A5XX_RBBM_PERFCTR_TP_4_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_TP_5_LO, REG_A5XX_RBBM_PERFCTR_TP_5_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_TP_6_LO, REG_A5XX_RBBM_PERFCTR_TP_6_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_TP_7_LO, REG_A5XX_RBBM_PERFCTR_TP_7_HI, + REG_A5XX_TPL1_PERFCTR_TP_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_tse[] = { + { REG_A5XX_RBBM_PERFCTR_TSE_0_LO, REG_A5XX_RBBM_PERFCTR_TSE_0_HI, + REG_A5XX_GRAS_PERFCTR_TSE_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_TSE_1_LO, REG_A5XX_RBBM_PERFCTR_TSE_1_HI, + REG_A5XX_GRAS_PERFCTR_TSE_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_TSE_2_LO, REG_A5XX_RBBM_PERFCTR_TSE_2_HI, + REG_A5XX_GRAS_PERFCTR_TSE_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_TSE_3_LO, REG_A5XX_RBBM_PERFCTR_TSE_3_HI, + REG_A5XX_GRAS_PERFCTR_TSE_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_uche[] = { + { REG_A5XX_RBBM_PERFCTR_UCHE_0_LO, REG_A5XX_RBBM_PERFCTR_UCHE_0_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_1_LO, REG_A5XX_RBBM_PERFCTR_UCHE_1_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_2_LO, REG_A5XX_RBBM_PERFCTR_UCHE_2_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_3_LO, REG_A5XX_RBBM_PERFCTR_UCHE_3_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_4_LO, REG_A5XX_RBBM_PERFCTR_UCHE_4_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_5_LO, REG_A5XX_RBBM_PERFCTR_UCHE_5_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_6_LO, REG_A5XX_RBBM_PERFCTR_UCHE_6_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_UCHE_7_LO, REG_A5XX_RBBM_PERFCTR_UCHE_7_HI, + REG_A5XX_UCHE_PERFCTR_UCHE_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_vfd[] = { + { REG_A5XX_RBBM_PERFCTR_VFD_0_LO, REG_A5XX_RBBM_PERFCTR_VFD_0_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_VFD_1_LO, REG_A5XX_RBBM_PERFCTR_VFD_1_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_VFD_2_LO, REG_A5XX_RBBM_PERFCTR_VFD_2_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_VFD_3_LO, REG_A5XX_RBBM_PERFCTR_VFD_3_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_3 }, + { REG_A5XX_RBBM_PERFCTR_VFD_4_LO, REG_A5XX_RBBM_PERFCTR_VFD_4_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_4 }, + { REG_A5XX_RBBM_PERFCTR_VFD_5_LO, REG_A5XX_RBBM_PERFCTR_VFD_5_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_5 }, + { REG_A5XX_RBBM_PERFCTR_VFD_6_LO, REG_A5XX_RBBM_PERFCTR_VFD_6_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_6 }, + { REG_A5XX_RBBM_PERFCTR_VFD_7_LO, REG_A5XX_RBBM_PERFCTR_VFD_7_HI, + REG_A5XX_VFD_PERFCTR_VFD_SEL_7 }, +}; + +static struct adreno_counter a5xx_counters_vpc[] = { + { REG_A5XX_RBBM_PERFCTR_VPC_0_LO, REG_A5XX_RBBM_PERFCTR_VPC_0_HI, + REG_A5XX_VPC_PERFCTR_VPC_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_VPC_1_LO, REG_A5XX_RBBM_PERFCTR_VPC_1_HI, + REG_A5XX_VPC_PERFCTR_VPC_SEL_1 }, + { REG_A5XX_RBBM_PERFCTR_VPC_2_LO, REG_A5XX_RBBM_PERFCTR_VPC_2_HI, + REG_A5XX_VPC_PERFCTR_VPC_SEL_2 }, + { REG_A5XX_RBBM_PERFCTR_VPC_3_LO, REG_A5XX_RBBM_PERFCTR_VPC_3_HI, + REG_A5XX_VPC_PERFCTR_VPC_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_vsc[] = { + { REG_A5XX_RBBM_PERFCTR_VSC_0_LO, REG_A5XX_RBBM_PERFCTR_VSC_0_HI, + REG_A5XX_VSC_PERFCTR_VSC_SEL_0 }, + { REG_A5XX_RBBM_PERFCTR_VSC_1_LO, REG_A5XX_RBBM_PERFCTR_VSC_1_HI, + REG_A5XX_VSC_PERFCTR_VSC_SEL_1 }, +}; + +static struct adreno_counter a5xx_counters_power_ccu[] = { + { REG_A5XX_CCU_POWER_COUNTER_0_LO, REG_A5XX_CCU_POWER_COUNTER_0_HI, + REG_A5XX_RB_POWERCTR_CCU_SEL_0 }, + { REG_A5XX_CCU_POWER_COUNTER_1_LO, REG_A5XX_CCU_POWER_COUNTER_1_HI, + REG_A5XX_RB_POWERCTR_CCU_SEL_1 }, +}; + +static struct adreno_counter a5xx_counters_power_cp[] = { + { REG_A5XX_CP_POWER_COUNTER_0_LO, REG_A5XX_CP_POWER_COUNTER_0_HI, + REG_A5XX_CP_POWERCTR_CP_SEL_0 }, + { REG_A5XX_CP_POWER_COUNTER_1_LO, REG_A5XX_CP_POWER_COUNTER_1_HI, + REG_A5XX_CP_POWERCTR_CP_SEL_1 }, + { REG_A5XX_CP_POWER_COUNTER_2_LO, REG_A5XX_CP_POWER_COUNTER_2_HI, + REG_A5XX_CP_POWERCTR_CP_SEL_2 }, + { REG_A5XX_CP_POWER_COUNTER_3_LO, REG_A5XX_CP_POWER_COUNTER_3_HI, + REG_A5XX_CP_POWERCTR_CP_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_power_rb[] = { + { REG_A5XX_RB_POWER_COUNTER_0_LO, REG_A5XX_RB_POWER_COUNTER_0_HI, + REG_A5XX_RB_POWERCTR_RB_SEL_0 }, + { REG_A5XX_RB_POWER_COUNTER_1_LO, REG_A5XX_RB_POWER_COUNTER_1_HI, + REG_A5XX_RB_POWERCTR_RB_SEL_1 }, + { REG_A5XX_RB_POWER_COUNTER_2_LO, REG_A5XX_RB_POWER_COUNTER_2_HI, + REG_A5XX_RB_POWERCTR_RB_SEL_2 }, + { REG_A5XX_RB_POWER_COUNTER_3_LO, REG_A5XX_RB_POWER_COUNTER_3_HI, + REG_A5XX_RB_POWERCTR_RB_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_power_sp[] = { + { REG_A5XX_SP_POWER_COUNTER_0_LO, REG_A5XX_SP_POWER_COUNTER_0_HI, + REG_A5XX_SP_POWERCTR_SP_SEL_0 }, + { REG_A5XX_SP_POWER_COUNTER_1_LO, REG_A5XX_SP_POWER_COUNTER_1_HI, + REG_A5XX_SP_POWERCTR_SP_SEL_1 }, + { REG_A5XX_SP_POWER_COUNTER_2_LO, REG_A5XX_SP_POWER_COUNTER_2_HI, + REG_A5XX_SP_POWERCTR_SP_SEL_2 }, + { REG_A5XX_SP_POWER_COUNTER_3_LO, REG_A5XX_SP_POWER_COUNTER_3_HI, + REG_A5XX_SP_POWERCTR_SP_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_power_tp[] = { + { REG_A5XX_TP_POWER_COUNTER_0_LO, REG_A5XX_TP_POWER_COUNTER_0_HI, + REG_A5XX_TPL1_POWERCTR_TP_SEL_0 }, + { REG_A5XX_TP_POWER_COUNTER_1_LO, REG_A5XX_TP_POWER_COUNTER_1_HI, + REG_A5XX_TPL1_POWERCTR_TP_SEL_1 }, + { REG_A5XX_TP_POWER_COUNTER_2_LO, REG_A5XX_TP_POWER_COUNTER_2_HI, + REG_A5XX_TPL1_POWERCTR_TP_SEL_2 }, + { REG_A5XX_TP_POWER_COUNTER_3_LO, REG_A5XX_TP_POWER_COUNTER_3_HI, + REG_A5XX_TPL1_POWERCTR_TP_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_power_uche[] = { + { REG_A5XX_UCHE_POWER_COUNTER_0_LO, REG_A5XX_UCHE_POWER_COUNTER_0_HI, + REG_A5XX_UCHE_POWERCTR_UCHE_SEL_0 }, + { REG_A5XX_UCHE_POWER_COUNTER_1_LO, REG_A5XX_UCHE_POWER_COUNTER_1_HI, + REG_A5XX_UCHE_POWERCTR_UCHE_SEL_1 }, + { REG_A5XX_UCHE_POWER_COUNTER_2_LO, REG_A5XX_UCHE_POWER_COUNTER_2_HI, + REG_A5XX_UCHE_POWERCTR_UCHE_SEL_2 }, + { REG_A5XX_UCHE_POWER_COUNTER_3_LO, REG_A5XX_UCHE_POWER_COUNTER_3_HI, + REG_A5XX_UCHE_POWERCTR_UCHE_SEL_3 }, +}; + +static struct adreno_counter a5xx_counters_vbif[] = { + { REG_A5XX_VBIF_PERF_CNT_LOW0, REG_A5XX_VBIF_PERF_CNT_HIGH0 }, + { REG_A5XX_VBIF_PERF_CNT_LOW1, REG_A5XX_VBIF_PERF_CNT_HIGH1 }, + { REG_A5XX_VBIF_PERF_CNT_LOW2, REG_A5XX_VBIF_PERF_CNT_HIGH2 }, + { REG_A5XX_VBIF_PERF_CNT_LOW3, REG_A5XX_VBIF_PERF_CNT_HIGH3 }, +}; + +static struct adreno_counter a5xx_counters_gpmu[] = { + { REG_A5XX_GPMU_POWER_COUNTER_0_LO, REG_A5XX_GPMU_POWER_COUNTER_0_HI }, + { REG_A5XX_GPMU_POWER_COUNTER_1_LO, REG_A5XX_GPMU_POWER_COUNTER_1_HI }, + { REG_A5XX_GPMU_POWER_COUNTER_2_LO, REG_A5XX_GPMU_POWER_COUNTER_2_HI }, + { REG_A5XX_GPMU_POWER_COUNTER_3_LO, REG_A5XX_GPMU_POWER_COUNTER_3_HI }, + { REG_A5XX_GPMU_POWER_COUNTER_4_LO, REG_A5XX_GPMU_POWER_COUNTER_4_HI }, + { REG_A5XX_GPMU_POWER_COUNTER_5_LO, REG_A5XX_GPMU_POWER_COUNTER_5_HI }, +}; + +static struct adreno_counter a5xx_counters_vbif_power[] = { + { REG_A5XX_VBIF_PERF_PWR_CNT_LOW0, REG_A5XX_VBIF_PERF_PWR_CNT_HIGH0 }, + { REG_A5XX_VBIF_PERF_PWR_CNT_LOW1, REG_A5XX_VBIF_PERF_PWR_CNT_HIGH1 }, + { REG_A5XX_VBIF_PERF_PWR_CNT_LOW2, REG_A5XX_VBIF_PERF_PWR_CNT_HIGH2 }, +}; + +static struct adreno_counter a5xx_counters_alwayson_power[] = { + { REG_A5XX_GPMU_ALWAYS_ON_COUNTER_LO, + REG_A5XX_GPMU_ALWAYS_ON_COUNTER_HI }, +}; + +#define DEFINE_COUNTER_GROUP(_name, _array, _get, _enable, _put) \ +static struct adreno_counter_group _name = { \ + .counters = _array, \ + .nr_counters = ARRAY_SIZE(_array), \ + .lock = __SPIN_LOCK_UNLOCKED(_name.lock), \ + .funcs = { \ + .get = _get, \ + .enable = _enable, \ + .read = a5xx_counter_read, \ + .put = _put, \ + }, \ +} + +#define DEFAULT_COUNTER_GROUP(_name, _array) DEFINE_COUNTER_GROUP(_name, \ + _array, a5xx_counter_get, a5xx_counter_enable_cpu, a5xx_counter_put) + +#define SPTP_COUNTER_GROUP(_name, _array) DEFINE_COUNTER_GROUP(_name, \ + _array, a5xx_counter_get, a5xx_counter_enable_pm4, a5xx_counter_put) + +/* "standard" counters */ +DEFAULT_COUNTER_GROUP(a5xx_counter_group_cp, a5xx_counters_cp); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_rbbm, a5xx_counters_rbbm); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_pc, a5xx_counters_pc); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_vfd, a5xx_counters_vfd); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_vpc, a5xx_counters_vpc); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_ccu, a5xx_counters_ccu); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_cmp, a5xx_counters_cmp); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_tse, a5xx_counters_tse); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_ras, a5xx_counters_ras); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_uche, a5xx_counters_uche); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_rb, a5xx_counters_rb); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_vsc, a5xx_counters_vsc); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_lrz, a5xx_counters_lrz); + +/* SP/TP counters */ +SPTP_COUNTER_GROUP(a5xx_counter_group_hlsq, a5xx_counters_hlsq); +SPTP_COUNTER_GROUP(a5xx_counter_group_tp, a5xx_counters_tp); +SPTP_COUNTER_GROUP(a5xx_counter_group_sp, a5xx_counters_sp); + +/* Power counters */ +DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_ccu, a5xx_counters_power_ccu); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_cp, a5xx_counters_power_cp); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_rb, a5xx_counters_power_rb); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_sp, a5xx_counters_power_sp); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_tp, a5xx_counters_power_tp); +DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_uche, a5xx_counters_power_uche); + +DEFINE_COUNTER_GROUP(a5xx_counter_group_alwayson, a5xx_counters_alwayson, + a5xx_counter_get_fixed, NULL, NULL); +DEFINE_COUNTER_GROUP(a5xx_counter_group_vbif, a5xx_counters_vbif, + a5xx_counter_get, a5xx_counter_enable_vbif, a5xx_counter_put); +DEFINE_COUNTER_GROUP(a5xx_counter_group_gpmu, a5xx_counters_gpmu, + a5xx_counter_get, a5xx_counter_enable_gpmu, a5xx_counter_put); +DEFINE_COUNTER_GROUP(a5xx_counter_group_vbif_power, a5xx_counters_vbif_power, + a5xx_counter_get_fixed, a5xx_counter_enable_vbif_power, NULL); +DEFINE_COUNTER_GROUP(a5xx_counter_group_alwayson_power, + a5xx_counters_alwayson_power, a5xx_counter_get_fixed, + a5xx_counter_enable_alwayson_power, NULL); + +static const struct adreno_counter_group *a5xx_counter_groups[] = { + [MSM_COUNTER_GROUP_ALWAYSON] = &a5xx_counter_group_alwayson, + [MSM_COUNTER_GROUP_CCU] = &a5xx_counter_group_ccu, + [MSM_COUNTER_GROUP_CMP] = &a5xx_counter_group_cmp, + [MSM_COUNTER_GROUP_CP] = &a5xx_counter_group_cp, + [MSM_COUNTER_GROUP_HLSQ] = &a5xx_counter_group_hlsq, + [MSM_COUNTER_GROUP_LRZ] = &a5xx_counter_group_lrz, + [MSM_COUNTER_GROUP_PC] = &a5xx_counter_group_pc, + [MSM_COUNTER_GROUP_RAS] = &a5xx_counter_group_ras, + [MSM_COUNTER_GROUP_RB] = &a5xx_counter_group_rb, + [MSM_COUNTER_GROUP_RBBM] = &a5xx_counter_group_rbbm, + [MSM_COUNTER_GROUP_SP] = &a5xx_counter_group_sp, + [MSM_COUNTER_GROUP_TP] = &a5xx_counter_group_tp, + [MSM_COUNTER_GROUP_TSE] = &a5xx_counter_group_tse, + [MSM_COUNTER_GROUP_UCHE] = &a5xx_counter_group_uche, + [MSM_COUNTER_GROUP_VFD] = &a5xx_counter_group_vfd, + [MSM_COUNTER_GROUP_VPC] = &a5xx_counter_group_vpc, + [MSM_COUNTER_GROUP_VSC] = &a5xx_counter_group_vsc, + [MSM_COUNTER_GROUP_VBIF] = &a5xx_counter_group_vbif, + [MSM_COUNTER_GROUP_GPMU_PWR] = &a5xx_counter_group_gpmu, + [MSM_COUNTER_GROUP_CCU_PWR] = &a5xx_counter_group_power_ccu, + [MSM_COUNTER_GROUP_CP_PWR] = &a5xx_counter_group_power_cp, + [MSM_COUNTER_GROUP_RB_PWR] = &a5xx_counter_group_power_rb, + [MSM_COUNTER_GROUP_SP_PWR] = &a5xx_counter_group_power_sp, + [MSM_COUNTER_GROUP_TP_PWR] = &a5xx_counter_group_power_tp, + [MSM_COUNTER_GROUP_UCHE_PWR] = &a5xx_counter_group_power_uche, + [MSM_COUNTER_GROUP_VBIF_PWR] = &a5xx_counter_group_vbif_power, + [MSM_COUNTER_GROUP_ALWAYSON_PWR] = + &a5xx_counter_group_alwayson_power, +}; + +int a5xx_counters_init(struct adreno_gpu *adreno_gpu) +{ + adreno_gpu->counter_groups = a5xx_counter_groups; + adreno_gpu->nr_counter_groups = ARRAY_SIZE(a5xx_counter_groups); + + return 0; +} diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index f5847bc60c49..02c4f2e3155d 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -856,14 +856,6 @@ static inline bool _a5xx_check_idle(struct msm_gpu *gpu) bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); - - if (ring != a5xx_gpu->cur_ring) { - WARN(1, "Tried to idle a non-current ringbuffer\n"); - return false; - } - /* wait for CP to drain ringbuffer: */ if (!adreno_idle(gpu, ring)) return false; @@ -1218,6 +1210,9 @@ static const struct adreno_gpu_funcs funcs = { .show = a5xx_show, #endif .snapshot = a5xx_snapshot, + .get_counter = adreno_get_counter, + .read_counter = adreno_read_counter, + .put_counter = adreno_put_counter, }, .get_timestamp = a5xx_get_timestamp, }; @@ -1341,5 +1336,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) /* Set up the preemption specific bits and pieces for each ringbuffer */ a5xx_preempt_init(gpu); + a5xx_counters_init(adreno_gpu); + return gpu; } diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index 3de14fe42a1b..8eb3838ffe90 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h @@ -184,4 +184,6 @@ static inline bool a5xx_in_preempt(struct a5xx_gpu *a5xx_gpu) return !(atomic_read(&a5xx_gpu->preempt_state) == PREEMPT_NONE); } +int a5xx_counters_init(struct adreno_gpu *adreno_gpu); + #endif /* __A5XX_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index f1883825354e..969ed810ce9d 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -709,3 +709,52 @@ void adreno_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot) adreno_snapshot_os(gpu, snapshot); adreno_snapshot_ringbuffers(gpu, snapshot); } + +/* Return the group struct associated with the counter id */ + +static struct adreno_counter_group *get_counter_group(struct msm_gpu *gpu, + u32 groupid) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + + if (!adreno_gpu->counter_groups) + return ERR_PTR(-ENODEV); + + if (groupid >= adreno_gpu->nr_counter_groups) + return ERR_PTR(-EINVAL); + + return (struct adreno_counter_group *) + adreno_gpu->counter_groups[groupid]; +} + +int adreno_get_counter(struct msm_gpu *gpu, u32 groupid, u32 countable, + u32 *lo, u32 *hi) +{ + struct adreno_counter_group *group = + get_counter_group(gpu, groupid); + + if (!IS_ERR_OR_NULL(group) && group->funcs.get) + return group->funcs.get(gpu, group, countable, lo, hi); + + return -ENODEV; +} + +u64 adreno_read_counter(struct msm_gpu *gpu, u32 groupid, int counterid) +{ + struct adreno_counter_group *group = + get_counter_group(gpu, groupid); + + if (!IS_ERR(group) && group->funcs.read) + return group->funcs.read(gpu, group, counterid); + + return 0; +} + +void adreno_put_counter(struct msm_gpu *gpu, u32 groupid, int counterid) +{ + struct adreno_counter_group *group = + get_counter_group(gpu, groupid); + + if (!IS_ERR(group) && group->funcs.put) + group->funcs.put(gpu, group, counterid); +} diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 30461115281c..8e8f3e5182d6 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -99,6 +99,30 @@ struct adreno_rbmemptrs { volatile unsigned int contextidr[MSM_GPU_MAX_RINGS]; }; +struct adreno_counter { + u32 lo; + u32 hi; + u32 sel; + u32 countable; + u32 refcount; +}; + +struct adreno_counter_group { + struct adreno_counter *counters; + size_t nr_counters; + spinlock_t lock; + struct { + int (*get)(struct msm_gpu *, + struct adreno_counter_group *, u32, u32 *, u32 *); + void (*enable)(struct msm_gpu *, + struct adreno_counter_group *, int); + u64 (*read)(struct msm_gpu *, + struct adreno_counter_group *, int); + void (*put)(struct msm_gpu *, + struct adreno_counter_group *, int); + } funcs; +}; + struct adreno_gpu { struct msm_gpu base; struct adreno_rev rev; @@ -129,6 +153,9 @@ struct adreno_gpu { uint32_t quirks; uint32_t speed_bin; + + const struct adreno_counter_group **counter_groups; + int nr_counter_groups; }; #define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base) @@ -235,6 +262,11 @@ void adreno_gpu_cleanup(struct adreno_gpu *gpu); void adreno_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot); +int adreno_get_counter(struct msm_gpu *gpu, u32 groupid, u32 countable, + u32 *lo, u32 *hi); +u64 adreno_read_counter(struct msm_gpu *gpu, u32 groupid, int counterid); +void adreno_put_counter(struct msm_gpu *gpu, u32 groupid, int counterid); + /* ringbuffer helpers (the parts that are adreno specific) */ static inline void diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 532ff8677259..276329b7b10c 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -606,6 +606,8 @@ static int msm_open(struct drm_device *dev, struct drm_file *file) if (IS_ERR(ctx)) return PTR_ERR(ctx); + INIT_LIST_HEAD(&ctx->counters); + file->driver_priv = ctx; kms = priv->kms; @@ -634,6 +636,9 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file) if (kms && kms->funcs && kms->funcs->postclose) kms->funcs->postclose(kms, file); + if (priv->gpu) + msm_gpu_cleanup_counters(priv->gpu, ctx); + mutex_lock(&dev->struct_mutex); if (ctx && ctx->aspace && ctx->aspace != priv->gpu->aspace) { ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu); @@ -1584,6 +1589,41 @@ void msm_send_crtc_notification(struct drm_crtc *crtc, spin_unlock_irqrestore(&dev->event_lock, flags); } +static int msm_ioctl_counter_get(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_file_private *ctx = file->driver_priv; + struct msm_drm_private *priv = dev->dev_private; + + if (priv->gpu) + return msm_gpu_counter_get(priv->gpu, data, ctx); + + return -ENODEV; +} + +static int msm_ioctl_counter_put(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_file_private *ctx = file->driver_priv; + struct msm_drm_private *priv = dev->dev_private; + + if (priv->gpu) + return msm_gpu_counter_put(priv->gpu, data, ctx); + + return -ENODEV; +} + +static int msm_ioctl_counter_read(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + + if (priv->gpu) + return msm_gpu_counter_read(priv->gpu, data); + + return -ENODEV; +} + int msm_release(struct inode *inode, struct file *filp) { struct drm_file *file_priv = filp->private_data; @@ -1619,6 +1659,12 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_UNLOCKED|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF_DRV(MSM_DEREGISTER_EVENT, msm_ioctl_deregister_event, DRM_UNLOCKED|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_COUNTER_GET, msm_ioctl_counter_get, + DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_COUNTER_PUT, msm_ioctl_counter_put, + DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_COUNTER_READ, msm_ioctl_counter_read, + DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index d8a4c34e9be0..d2d118cf7e07 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -76,6 +76,7 @@ struct msm_gem_vma; struct msm_file_private { struct msm_gem_address_space *aspace; + struct list_head counters; }; enum msm_mdp_plane_property { diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 63128d11767e..d1455fbc980e 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -750,7 +750,10 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, size = PAGE_ALIGN(size); + mutex_lock(&dev->struct_mutex); ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); + mutex_unlock(&dev->struct_mutex); + if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 7ca96831a9b3..a227f1ba0573 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -17,7 +17,7 @@ #include "msm_drv.h" #include "msm_gem.h" -#include "msm_mmu.h" +#include "msm_iommu.h" static void msm_gem_address_space_destroy(struct kref *kref) @@ -149,6 +149,9 @@ static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace, if (flags & MSM_BO_PRIVILEGED) iommu_flags |= IOMMU_PRIV; + if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(aspace->mmu)) + iommu_flags |= IOMMU_CACHE; + if (WARN_ON(drm_mm_node_allocated(&vma->node))) return 0; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 3176f301e7a8..5a505a8bf328 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -576,8 +576,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) if (submit->bos[i].flags & MSM_SUBMIT_BO_READ) msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence); - - if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) + else if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence); } @@ -588,6 +587,118 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) return ret; } +struct msm_context_counter { + u32 groupid; + int counterid; + struct list_head node; +}; + +int msm_gpu_counter_get(struct msm_gpu *gpu, struct drm_msm_counter *data, + struct msm_file_private *ctx) +{ + struct msm_context_counter *entry; + int counterid; + u32 lo = 0, hi = 0; + + if (!ctx || !gpu->funcs->get_counter) + return -ENODEV; + + counterid = gpu->funcs->get_counter(gpu, data->groupid, data->countable, + &lo, &hi); + + if (counterid < 0) + return counterid; + + /* + * Check to see if the counter in question is already held by this + * process. If it does, put it back and return an error. + */ + list_for_each_entry(entry, &ctx->counters, node) { + if (entry->groupid == data->groupid && + entry->counterid == counterid) { + gpu->funcs->put_counter(gpu, data->groupid, counterid); + return -EBUSY; + } + } + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + gpu->funcs->put_counter(gpu, data->groupid, counterid); + return -ENOMEM; + } + + entry->groupid = data->groupid; + entry->counterid = counterid; + list_add_tail(&entry->node, &ctx->counters); + + data->counterid = counterid; + data->counter_lo = lo; + data->counter_hi = hi; + + return 0; +} + +int msm_gpu_counter_put(struct msm_gpu *gpu, struct drm_msm_counter *data, + struct msm_file_private *ctx) +{ + struct msm_context_counter *entry; + + list_for_each_entry(entry, &ctx->counters, node) { + if (entry->groupid == data->groupid && + entry->counterid == data->counterid) { + gpu->funcs->put_counter(gpu, data->groupid, + data->counterid); + + list_del(&entry->node); + kfree(entry); + + return 0; + } + } + + return -EINVAL; +} + +void msm_gpu_cleanup_counters(struct msm_gpu *gpu, + struct msm_file_private *ctx) +{ + struct msm_context_counter *entry, *tmp; + + if (!ctx) + return; + + list_for_each_entry_safe(entry, tmp, &ctx->counters, node) { + gpu->funcs->put_counter(gpu, entry->groupid, entry->counterid); + list_del(&entry->node); + kfree(entry); + } +} + +u64 msm_gpu_counter_read(struct msm_gpu *gpu, struct drm_msm_counter_read *data) +{ + int i; + + if (!gpu->funcs->read_counter) + return 0; + + for (i = 0; i < data->nr_ops; i++) { + struct drm_msm_counter_read_op op; + void __user *ptr = (void __user *)(uintptr_t) + (data->ops + (i * sizeof(op))); + + if (copy_from_user(&op, ptr, sizeof(op))) + return -EFAULT; + + op.value = gpu->funcs->read_counter(gpu, op.groupid, + op.counterid); + + if (copy_to_user(ptr, &op, sizeof(op))) + return -EFAULT; + } + + return 0; +} + /* * Init/Cleanup: */ diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 06dfaabbfcfe..3fac423929c5 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -71,6 +71,10 @@ struct msm_gpu_funcs { void (*show)(struct msm_gpu *gpu, struct seq_file *m); #endif int (*snapshot)(struct msm_gpu *gpu, struct msm_snapshot *snapshot); + int (*get_counter)(struct msm_gpu *gpu, u32 groupid, u32 countable, + u32 *lo, u32 *hi); + void (*put_counter)(struct msm_gpu *gpu, u32 groupid, int counterid); + u64 (*read_counter)(struct msm_gpu *gpu, u32 groupid, int counterid); }; struct msm_gpu { @@ -258,4 +262,16 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev); void __init adreno_register(void); void __exit adreno_unregister(void); +int msm_gpu_counter_get(struct msm_gpu *gpu, struct drm_msm_counter *data, + struct msm_file_private *ctx); + +int msm_gpu_counter_put(struct msm_gpu *gpu, struct drm_msm_counter *data, + struct msm_file_private *ctx); + +void msm_gpu_cleanup_counters(struct msm_gpu *gpu, + struct msm_file_private *ctx); + +u64 msm_gpu_counter_read(struct msm_gpu *gpu, + struct drm_msm_counter_read *data); + #endif /* __MSM_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 3c16222b8890..3af24646f4f1 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -16,6 +16,7 @@ */ #include <linux/of_platform.h> +#include <linux/of_address.h> #include "msm_drv.h" #include "msm_iommu.h" @@ -126,6 +127,9 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) if (ret) iommu->allow_dynamic = false; + /* Mark the GPU as I/O coherent if it is supported */ + iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node); + /* Attach the device to the domain */ ret = _attach_iommu_device(mmu, iommu->domain, names, cnt); if (ret) @@ -312,6 +316,7 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base) struct iommu_domain *domain; struct msm_mmu *mmu; int ret, val = 1; + struct msm_iommu *child_iommu; /* Don't continue if the base domain didn't have the support we need */ if (!base || base_iommu->allow_dynamic == false) @@ -339,5 +344,9 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base) iommu_domain_set_attr(domain, DOMAIN_ATTR_CONTEXT_BANK, &base_iommu->cb); + /* Mark the dynamic domain as I/O coherent if the base domain is */ + child_iommu = to_msm_iommu(mmu); + child_iommu->is_coherent = base_iommu->is_coherent; + return mmu; } diff --git a/drivers/gpu/drm/msm/msm_iommu.h b/drivers/gpu/drm/msm/msm_iommu.h index d005cfb9758f..3a67b60ad81d 100644 --- a/drivers/gpu/drm/msm/msm_iommu.h +++ b/drivers/gpu/drm/msm/msm_iommu.h @@ -25,6 +25,8 @@ struct msm_iommu { struct clk *clocks[5]; int nr_clocks; + + bool is_coherent; }; #define to_msm_iommu(x) container_of(x, struct msm_iommu, base) @@ -34,4 +36,11 @@ static inline bool msm_iommu_allow_dynamic(struct msm_mmu *mmu) return iommu->allow_dynamic; } + +static inline bool msm_iommu_coherent(struct msm_mmu *mmu) +{ + struct msm_iommu *iommu = to_msm_iommu(mmu); + + return iommu->is_coherent; +} #endif diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c index 956a833b8200..57c7389feee4 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/hw.c +++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c @@ -222,6 +222,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) uint32_t mpllP; pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP); + mpllP = (mpllP >> 8) & 0xf; if (!mpllP) mpllP = 4; @@ -232,7 +233,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) uint32_t clock; pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock); - return clock; + return clock / 1000; } ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals); diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 4dca65a63b92..af224fafa21f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -333,6 +333,9 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios) if (bios->major_version < 5 && bios->data[0x48] & 0x4) return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf; + if (drm->device.info.family >= NV_DEVICE_INFO_V0_MAXWELL) + return nvif_rd32(device, 0x001800) & 0x0000000f; + else if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf; else diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index bbc9824af6e0..ece9f4102c0e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -1833,7 +1833,7 @@ nvf1_chipset = { .fb = gk104_fb_new, .fuse = gf100_fuse_new, .gpio = gk104_gpio_new, - .i2c = gf119_i2c_new, + .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, @@ -1941,7 +1941,7 @@ nv117_chipset = { .fb = gm107_fb_new, .fuse = gm107_fuse_new, .gpio = gk104_gpio_new, - .i2c = gf119_i2c_new, + .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, .imem = nv50_instmem_new, .ltc = gm107_ltc_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c index c1590b746f13..eb58cd7bfbc9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c @@ -59,7 +59,7 @@ gt215_hda_eld(NV50_DISP_MTHD_V1) ); } for (i = 0; i < size; i++) - nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[0]); + nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]); for (; i < 0x60; i++) nvkm_wr32(device, 0x61c440 + soff, (i << 8)); nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c index e7cbc139c1d4..89976ff4b305 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c @@ -59,6 +59,7 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, struct nvkm_gpuobj *inst = chan->base.inst; int ret = 0; + mutex_lock(&subdev->mutex); nvkm_wr32(device, 0x002634, chan->base.chid); if (nvkm_msec(device, 2000, if (nvkm_rd32(device, 0x002634) == chan->base.chid) @@ -66,10 +67,12 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, ) < 0) { nvkm_error(subdev, "channel %d [%s] kick timeout\n", chan->base.chid, chan->base.object.client->name); - ret = -EBUSY; - if (suspend) - return ret; + ret = -ETIMEDOUT; } + mutex_unlock(&subdev->mutex); + + if (ret && suspend) + return ret; if (offset) { nvkm_kmap(inst); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c index 0b817540a9e4..aa1692e5669f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c @@ -39,7 +39,9 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan) struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_device *device = subdev->device; struct nvkm_client *client = chan->base.object.client; + int ret = 0; + mutex_lock(&subdev->mutex); nvkm_wr32(device, 0x002634, chan->base.chid); if (nvkm_msec(device, 2000, if (!(nvkm_rd32(device, 0x002634) & 0x00100000)) @@ -47,10 +49,10 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan) ) < 0) { nvkm_error(subdev, "channel %d [%s] kick timeout\n", chan->base.chid, client->name); - return -EBUSY; + ret = -ETIMEDOUT; } - - return 0; + mutex_unlock(&subdev->mutex); + return ret; } static u32 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h index 212800ecdce9..7d1d3c6b4b72 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h @@ -12,6 +12,7 @@ struct nvbios_source { bool rw; bool ignore_checksum; bool no_pcir; + bool require_checksum; }; int nvbios_extend(struct nvkm_bios *, u32 length); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c index b2557e87afdd..7deb81b6dbac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c @@ -86,9 +86,12 @@ shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd) nvbios_checksum(&bios->data[image.base], image.size)) { nvkm_debug(subdev, "%08x: checksum failed\n", image.base); - if (mthd->func->rw) + if (!mthd->func->require_checksum) { + if (mthd->func->rw) + score += 1; score += 1; - score += 1; + } else + return 0; } else { score += 3; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c index 8fecb5ff22a0..06572f8ce914 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c @@ -99,6 +99,7 @@ nvbios_acpi_fast = { .init = acpi_init, .read = acpi_read_fast, .rw = false, + .require_checksum = true, }; const struct nvbios_source diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c index 85b1464c0194..587c52f08d3f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c @@ -47,8 +47,10 @@ nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count) BUG_ON((first > limit) || (limit >= ltc->num_tags)); + mutex_lock(<c->subdev.mutex); ltc->func->cbc_clear(ltc, first, limit); ltc->func->cbc_wait(ltc); + mutex_unlock(<c->subdev.mutex); } int diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index afaf346bd50e..8901228b5d5d 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -90,6 +90,9 @@ static void radeon_show_cursor(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_device *rdev = crtc->dev->dev_private; + if (radeon_crtc->cursor_out_of_bounds) + return; + if (ASIC_IS_DCE4(rdev)) { WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, upper_32_bits(radeon_crtc->cursor_addr)); @@ -143,21 +146,25 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) int xorigin = 0, yorigin = 0; int w = radeon_crtc->cursor_width; + radeon_crtc->cursor_x = x; + radeon_crtc->cursor_y = y; + if (ASIC_IS_AVIVO(rdev)) { /* avivo cursor are offset into the total surface */ x += crtc->x; y += crtc->y; } - DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); - if (x < 0) { + if (x < 0) xorigin = min(-x, radeon_crtc->max_cursor_width - 1); - x = 0; - } - if (y < 0) { + if (y < 0) yorigin = min(-y, radeon_crtc->max_cursor_height - 1); - y = 0; + + if (!ASIC_IS_AVIVO(rdev)) { + x += crtc->x; + y += crtc->y; } + DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); /* fixed on DCE6 and newer */ if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) { @@ -180,27 +187,31 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) if (i > 1) { int cursor_end, frame_end; - cursor_end = x - xorigin + w; + cursor_end = x + w; frame_end = crtc->x + crtc->mode.crtc_hdisplay; if (cursor_end >= frame_end) { w = w - (cursor_end - frame_end); if (!(frame_end & 0x7f)) w--; - } else { - if (!(cursor_end & 0x7f)) - w--; + } else if (cursor_end <= 0) { + goto out_of_bounds; + } else if (!(cursor_end & 0x7f)) { + w--; } if (w <= 0) { - w = 1; - cursor_end = x - xorigin + w; - if (!(cursor_end & 0x7f)) { - x--; - WARN_ON_ONCE(x < 0); - } + goto out_of_bounds; } } } + if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) || + x >= (crtc->x + crtc->mode.hdisplay) || + y >= (crtc->y + crtc->mode.vdisplay)) + goto out_of_bounds; + + x += xorigin; + y += yorigin; + if (ASIC_IS_DCE4(rdev)) { WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); @@ -212,6 +223,9 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); } else { + x -= crtc->x; + y -= crtc->y; + if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) y *= 2; @@ -229,10 +243,20 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) yorigin * 256); } - radeon_crtc->cursor_x = x; - radeon_crtc->cursor_y = y; + if (radeon_crtc->cursor_out_of_bounds) { + radeon_crtc->cursor_out_of_bounds = false; + if (radeon_crtc->cursor_bo) + radeon_show_cursor(crtc); + } return 0; + + out_of_bounds: + if (!radeon_crtc->cursor_out_of_bounds) { + radeon_hide_cursor(crtc); + radeon_crtc->cursor_out_of_bounds = true; + } + return 0; } int radeon_crtc_cursor_move(struct drm_crtc *crtc, @@ -297,22 +321,23 @@ int radeon_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - radeon_crtc->cursor_width = width; - radeon_crtc->cursor_height = height; - radeon_lock_cursor(crtc, true); - if (hot_x != radeon_crtc->cursor_hot_x || + if (width != radeon_crtc->cursor_width || + height != radeon_crtc->cursor_height || + hot_x != radeon_crtc->cursor_hot_x || hot_y != radeon_crtc->cursor_hot_y) { int x, y; x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x; y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y; - radeon_cursor_move_locked(crtc, x, y); - + radeon_crtc->cursor_width = width; + radeon_crtc->cursor_height = height; radeon_crtc->cursor_hot_x = hot_x; radeon_crtc->cursor_hot_y = hot_y; + + radeon_cursor_move_locked(crtc, x, y); } radeon_show_cursor(crtc); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 7a0666ac4e23..d8f8be608c19 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -330,6 +330,7 @@ struct radeon_crtc { u16 lut_r[256], lut_g[256], lut_b[256]; bool enabled; bool can_tile; + bool cursor_out_of_bounds; uint32_t crtc_offset; struct drm_gem_object *cursor_bo; uint64_t cursor_addr; diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 10191b935937..b6f16804e73b 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -3008,24 +3008,12 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, (rdev->pdev->device == 0x6817) || (rdev->pdev->device == 0x6806)) max_mclk = 120000; - } else if (rdev->family == CHIP_VERDE) { - if ((rdev->pdev->revision == 0x81) || - (rdev->pdev->revision == 0x83) || - (rdev->pdev->revision == 0x87) || - (rdev->pdev->device == 0x6820) || - (rdev->pdev->device == 0x6821) || - (rdev->pdev->device == 0x6822) || - (rdev->pdev->device == 0x6823) || - (rdev->pdev->device == 0x682A) || - (rdev->pdev->device == 0x682B)) { - max_sclk = 75000; - max_mclk = 80000; - } } else if (rdev->family == CHIP_OLAND) { if ((rdev->pdev->revision == 0xC7) || (rdev->pdev->revision == 0x80) || (rdev->pdev->revision == 0x81) || (rdev->pdev->revision == 0x83) || + (rdev->pdev->revision == 0x87) || (rdev->pdev->device == 0x6604) || (rdev->pdev->device == 0x6605)) { max_sclk = 75000; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4ae8b56b1847..037c38bb5333 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1621,7 +1621,6 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) struct ttm_buffer_object *bo; int ret = -EBUSY; int put_count; - uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM); spin_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { @@ -1657,7 +1656,8 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) if (unlikely(ret != 0)) goto out; - if ((bo->mem.placement & swap_placement) != swap_placement) { + if (bo->mem.mem_type != TTM_PL_SYSTEM || + bo->ttm->caching_state != tt_cached) { struct ttm_mem_reg evict_mem; evict_mem = bo->mem; diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index bcefb9ebb026..88be56321610 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -148,26 +148,36 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) struct usb_interface *usbif = to_usb_interface(dev->parent); struct usb_device *usbdev = interface_to_usbdev(usbif); int brightness; - char data[8]; + char *data; + + data = kmalloc(8, GFP_KERNEL); + if (!data) + return -ENOMEM; ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), K90_REQUEST_STATUS, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, data, 8, USB_CTRL_SET_TIMEOUT); - if (ret < 0) { + if (ret < 5) { dev_warn(dev, "Failed to get K90 initial state (error %d).\n", ret); - return -EIO; + ret = -EIO; + goto out; } brightness = data[4]; if (brightness < 0 || brightness > 3) { dev_warn(dev, "Read invalid backlight brightness: %02hhx.\n", data[4]); - return -EIO; + ret = -EIO; + goto out; } - return brightness; + ret = brightness; +out: + kfree(data); + + return ret; } static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) @@ -253,17 +263,22 @@ static ssize_t k90_show_macro_mode(struct device *dev, struct usb_interface *usbif = to_usb_interface(dev->parent); struct usb_device *usbdev = interface_to_usbdev(usbif); const char *macro_mode; - char data[8]; + char *data; + + data = kmalloc(2, GFP_KERNEL); + if (!data) + return -ENOMEM; ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), K90_REQUEST_GET_MODE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, data, 2, USB_CTRL_SET_TIMEOUT); - if (ret < 0) { + if (ret < 1) { dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", ret); - return -EIO; + ret = -EIO; + goto out; } switch (data[0]) { @@ -277,10 +292,15 @@ static ssize_t k90_show_macro_mode(struct device *dev, default: dev_warn(dev, "K90 in unknown mode: %02hhx.\n", data[0]); - return -EIO; + ret = -EIO; + goto out; } - return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); + ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); +out: + kfree(data); + + return ret; } static ssize_t k90_store_macro_mode(struct device *dev, @@ -320,26 +340,36 @@ static ssize_t k90_show_current_profile(struct device *dev, struct usb_interface *usbif = to_usb_interface(dev->parent); struct usb_device *usbdev = interface_to_usbdev(usbif); int current_profile; - char data[8]; + char *data; + + data = kmalloc(8, GFP_KERNEL); + if (!data) + return -ENOMEM; ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), K90_REQUEST_STATUS, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, data, 8, USB_CTRL_SET_TIMEOUT); - if (ret < 0) { + if (ret < 8) { dev_warn(dev, "Failed to get K90 initial state (error %d).\n", ret); - return -EIO; + ret = -EIO; + goto out; } current_profile = data[7]; if (current_profile < 1 || current_profile > 3) { dev_warn(dev, "Read invalid current profile: %02hhx.\n", data[7]); - return -EIO; + ret = -EIO; + goto out; } - return snprintf(buf, PAGE_SIZE, "%d\n", current_profile); + ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile); +out: + kfree(data); + + return ret; } static ssize_t k90_store_current_profile(struct device *dev, diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 1b764d1745f3..1689568b597d 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -39,6 +39,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX)) return rdesc; + if (*rsize < 4) + return rdesc; + for (i = 0; i < *rsize - 4; i++) if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { rdesc[i] = 0x19; diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 5c02d7bbc7f2..35e3fd9fadf6 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -148,19 +148,21 @@ static int wacom_pl_irq(struct wacom_wac *wacom) wacom->id[0] = STYLUS_DEVICE_ID; } - pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); - if (features->pressure_max > 255) - pressure = (pressure << 1) | ((data[4] >> 6) & 1); - pressure += (features->pressure_max + 1) / 2; - - input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); - input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); - input_report_abs(input, ABS_PRESSURE, pressure); - - input_report_key(input, BTN_TOUCH, data[4] & 0x08); - input_report_key(input, BTN_STYLUS, data[4] & 0x10); - /* Only allow the stylus2 button to be reported for the pen tool. */ - input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20)); + if (prox) { + pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); + if (features->pressure_max > 255) + pressure = (pressure << 1) | ((data[4] >> 6) & 1); + pressure += (features->pressure_max + 1) / 2; + + input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); + input_report_abs(input, ABS_PRESSURE, pressure); + + input_report_key(input, BTN_TOUCH, data[4] & 0x08); + input_report_key(input, BTN_STYLUS, data[4] & 0x10); + /* Only allow the stylus2 button to be reported for the pen tool. */ + input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20)); + } if (!prox) wacom->id[0] = 0; diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 63194a9a7189..57c191798699 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -219,7 +219,7 @@ int hv_init(void) /* See if the hypercall page is already set */ rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); + virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX); if (!virtaddr) goto cleanup; @@ -422,7 +422,7 @@ int hv_synic_alloc(void) goto err; } - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC); if (hv_context.event_dpc[cpu] == NULL) { pr_err("Unable to allocate event dpc\n"); @@ -461,6 +461,8 @@ int hv_synic_alloc(void) pr_err("Unable to allocate post msg page\n"); goto err; } + + INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); } return 0; @@ -485,7 +487,7 @@ void hv_synic_free(void) int cpu; kfree(hv_context.hv_numa_map); - for_each_online_cpu(cpu) + for_each_present_cpu(cpu) hv_synic_free_cpu(cpu); } @@ -555,8 +557,6 @@ void hv_synic_init(void *arg) rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); hv_context.vp_index[cpu] = (u32)vp_index; - INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); - /* * Register the per-cpu clockevent source. */ diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index c37a71e13de0..1fb02dcbc500 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -61,6 +61,7 @@ static DECLARE_WORK(fcopy_send_work, fcopy_send_data); static const char fcopy_devname[] = "vmbus/hv_fcopy"; static u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; /* * This state maintains the version number registered by the daemon. */ @@ -312,12 +313,14 @@ static void fcopy_on_reset(void) if (cancel_delayed_work_sync(&fcopy_timeout_work)) fcopy_respond_to_host(HV_E_FAIL); + complete(&release_event); } int hv_fcopy_init(struct hv_util_service *srv) { recv_buffer = srv->recv_buffer; + init_completion(&release_event); /* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. @@ -339,4 +342,5 @@ void hv_fcopy_deinit(void) fcopy_transaction.state = HVUTIL_DEVICE_DYING; cancel_delayed_work_sync(&fcopy_timeout_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 2a3420c4ca59..ce4d3a935491 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -86,6 +86,7 @@ static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); static const char kvp_devname[] = "vmbus/hv_kvp"; static u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; /* * Register the kernel component with the user-level daemon. * As part of this registration, pass the LIC version number. @@ -682,6 +683,7 @@ static void kvp_on_reset(void) if (cancel_delayed_work_sync(&kvp_timeout_work)) kvp_respond_to_host(NULL, HV_E_FAIL); kvp_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); } int @@ -689,6 +691,7 @@ hv_kvp_init(struct hv_util_service *srv) { recv_buffer = srv->recv_buffer; + init_completion(&release_event); /* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. @@ -711,4 +714,5 @@ void hv_kvp_deinit(void) cancel_delayed_work_sync(&kvp_timeout_work); cancel_work_sync(&kvp_sendkey_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 81882d4848bd..faad79ae318a 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -66,6 +66,7 @@ static int dm_reg_value; static const char vss_devname[] = "vmbus/hv_vss"; static __u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; static void vss_send_op(struct work_struct *dummy); static void vss_timeout_func(struct work_struct *dummy); @@ -326,11 +327,13 @@ static void vss_on_reset(void) if (cancel_delayed_work_sync(&vss_timeout_work)) vss_respond_to_host(HV_E_FAIL); vss_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); } int hv_vss_init(struct hv_util_service *srv) { + init_completion(&release_event); if (vmbus_proto_version < VERSION_WIN8_1) { pr_warn("Integration service 'Backup (volume snapshot)'" " not supported on this host version.\n"); @@ -360,4 +363,5 @@ void hv_vss_deinit(void) cancel_delayed_work_sync(&vss_timeout_work); cancel_work_sync(&vss_send_op_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 12e851a5af48..46b4e35fd555 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) !data->valid) { for (i = 0; i < TEMP_IDX_LEN; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - temp_reg[i]); + data->temp[i] = (int8_t)i2c_smbus_read_byte_data( + client, temp_reg[i]); data->stat1 = i2c_smbus_read_byte_data(client, AMC6821_REG_STAT1); diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index edf550fc4eef..0043a4c02b85 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, if (res) return res; - val = (val * 10 / 625) * 8; + val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8; mutex_lock(&data->update_lock); data->temp[attr->index] = val; diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index b96a2a9e4df7..628be9c95ff9 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, * Convert fan RPM value from sysfs into count value for fan controller * register (FAN_SET_CNT). */ -static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p, +static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p, u8 clk_div, u8 gear_mult) { - if (!rpm) /* to stop the fan, set cnt to 255 */ + unsigned long f1 = clk_freq * 30 * gear_mult; + unsigned long f2 = p * clk_div; + + if (!rpm) /* to stop the fan, set cnt to 255 */ return 0xff; - return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)), - 0, 255); + rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2); + return DIV_ROUND_CLOSEST(f1, rpm * f2); } /* helper to grab and cache data, at most one time per second */ diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 3ce33d244cc0..12b94b094c0d 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, ret = 0; else if (ret) ret = DIV_ROUND_CLOSEST(1350000U, ret); + else + ret = 1350000U; abort: mutex_unlock(&data->access_lock); return ret; } static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, - u8 reg_fan_high, unsigned int limit) + u8 reg_fan_high, unsigned long limit) { int err; @@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; int err; + voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]); voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); - voltage = clamp_val(voltage, 0, 0x3ff); mutex_lock(&data->access_lock); err = regmap_write(data->regmap, @@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); err = regmap_write(data->regmap, nr, val & 0xff); return err ? : count; diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 7e20567bc369..6827169c82d4 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -272,6 +272,7 @@ static const struct of_device_id scpi_of_match[] = { {.compatible = "arm,scpi-sensors"}, {}, }; +MODULE_DEVICE_TABLE(of, scpi_of_match); static struct platform_driver scpi_hwmon_platdrv = { .driver = { diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index bf2a1dd7cf15..7f98d9f527b9 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -1151,12 +1151,20 @@ static void i2c_msm_dma_xfer_unprepare(struct i2c_msm_ctrl *ctrl) buf_itr->dma_dir); } -static void i2c_msm_dma_callback_xfer_complete(void *dma_async_param) +static void i2c_msm_dma_callback_tx_complete(void *dma_async_param) { struct i2c_msm_ctrl *ctrl = dma_async_param; + complete(&ctrl->xfer.complete); } +static void i2c_msm_dma_callback_rx_complete(void *dma_async_param) +{ + struct i2c_msm_ctrl *ctrl = dma_async_param; + + complete(&ctrl->xfer.rx_complete); +} + /* * i2c_msm_dma_xfer_process: Queue transfers to DMA * @pre 1)QUP is in run state. 2) i2c_msm_dma_xfer_prepare() was called. @@ -1269,14 +1277,16 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) } /* callback defined for tx dma desc */ - dma_desc_tx->callback = i2c_msm_dma_callback_xfer_complete; + dma_desc_tx->callback = i2c_msm_dma_callback_tx_complete; dma_desc_tx->callback_param = ctrl; dmaengine_submit(dma_desc_tx); dma_async_issue_pending(tx->dma_chan); /* queue the rx dma desc */ dma_desc_rx = dmaengine_prep_slave_sg(rx->dma_chan, sg_rx, - sg_rx_itr - sg_rx, rx->dir, 0); + sg_rx_itr - sg_rx, rx->dir, + (SPS_IOVEC_FLAG_EOT | + SPS_IOVEC_FLAG_NWD)); if (dma_desc_rx < 0) { dev_err(ctrl->dev, "error dmaengine_prep_slave_sg rx:%ld\n", @@ -1285,6 +1295,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) goto dma_xfer_end; } + dma_desc_rx->callback = i2c_msm_dma_callback_rx_complete; + dma_desc_rx->callback_param = ctrl; dmaengine_submit(dma_desc_rx); dma_async_issue_pending(rx->dma_chan); @@ -1297,6 +1309,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) } ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete); + if (!ret && ctrl->xfer.rx_cnt) + i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.rx_complete); dma_xfer_end: /* free scatter-gather lists */ @@ -2054,13 +2068,14 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, long time_left; int ret = 0; - time_left = wait_for_completion_timeout(complete, xfer->timeout); + time_left = wait_for_completion_timeout(complete, + xfer->timeout); if (!time_left) { xfer->err = I2C_MSM_ERR_TIMEOUT; i2c_msm_dbg_dump_diag(ctrl, false, 0, 0); ret = -EIO; i2c_msm_prof_evnt_add(ctrl, MSM_ERR, I2C_MSM_COMPLT_FL, - xfer->timeout, time_left, 0); + xfer->timeout, time_left, 0); } else { /* return an error if one detected by ISR */ if (xfer->err) @@ -2327,6 +2342,8 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) xfer->tx_ovrhd_cnt = 0; atomic_set(&xfer->event_cnt, 0); init_completion(&xfer->complete); + init_completion(&xfer->rx_complete); + xfer->cur_buf.is_init = false; xfer->cur_buf.msg_idx = 0; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d625167357cc..e4587411b447 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1400,7 +1400,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, if (i2c_check_addr_validity(addr, info.flags)) { dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", - info.addr, node->full_name); + addr, node->full_name); return ERR_PTR(-EINVAL); } diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 2413ec9f8207..94c837046786 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -329,7 +329,7 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, unsigned long arg) { struct i2c_smbus_ioctl_data data_arg; - union i2c_smbus_data temp; + union i2c_smbus_data temp = {}; int datasize, res; if (copy_from_user(&data_arg, diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index 57145ea72e90..537cca877f66 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -38,6 +38,7 @@ #define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B #define FG_ADC_RR_BATT_ID_CTRL 0x60 +#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0) #define FG_ADC_RR_BATT_ID_TRIGGER 0x61 #define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0) #define FG_ADC_RR_BATT_ID_STS 0x62 @@ -163,11 +164,6 @@ #define FG_ADC_RR_DIE_TEMP_SLOPE 2 #define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000 -#define FAB_ID_GF 0x30 -#define FAB_ID_SMIC 0x11 -#define FAB_ID_660_GF 0x0 -#define FAB_ID_660_TSMC 0x2 -#define FAB_ID_660_MX 0x3 #define FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV 1303168 #define FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C 3784 #define FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV 1338433 @@ -401,11 +397,11 @@ static int rradc_get_660_fab_coeff(struct rradc_chip *chip, int64_t *offset, int64_t *slope) { switch (chip->pmic_fab_id->fab_id) { - case FAB_ID_660_GF: + case PM660_FAB_ID_GF: *offset = FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV; *slope = FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C; break; - case FAB_ID_660_TSMC: + case PM660_FAB_ID_TSMC: *offset = FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV; *slope = FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C; break; @@ -421,11 +417,11 @@ static int rradc_get_8998_fab_coeff(struct rradc_chip *chip, int64_t *offset, int64_t *slope) { switch (chip->pmic_fab_id->fab_id) { - case FAB_ID_GF: + case PMI8998_FAB_ID_GF: *offset = FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV; *slope = FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C; break; - case FAB_ID_SMIC: + case PMI8998_FAB_ID_SMIC: *offset = FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV; *slope = FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C; break; @@ -753,6 +749,75 @@ static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip, return rc; } +static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable) +{ + int rc = 0; + + if (enable) { + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL, + FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, + FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV); + if (rc < 0) { + pr_err("Enabling BATT ID channel failed:%d\n", rc); + return rc; + } + } else { + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL, + FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0); + if (rc < 0) { + pr_err("Disabling BATT ID channel failed:%d\n", rc); + return rc; + } + } + + return rc; +} + +static int rradc_do_batt_id_conversion(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 *data, u8 *buf) +{ + int rc = 0, ret = 0; + + rc = rradc_enable_batt_id_channel(chip, true); + if (rc < 0) { + pr_err("Enabling BATT ID channel failed:%d\n", rc); + return rc; + } + + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, + FG_ADC_RR_BATT_ID_TRIGGER_CTL, + FG_ADC_RR_BATT_ID_TRIGGER_CTL); + if (rc < 0) { + pr_err("BATT_ID trigger set failed:%d\n", rc); + ret = rc; + rc = rradc_enable_batt_id_channel(chip, false); + if (rc < 0) + pr_err("Disabling BATT ID channel failed:%d\n", rc); + return ret; + } + + rc = rradc_read_channel_with_continuous_mode(chip, prop, buf); + if (rc < 0) { + pr_err("Error reading in continuous mode:%d\n", rc); + ret = rc; + } + + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, + FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0); + if (rc < 0) { + pr_err("BATT_ID trigger re-set failed:%d\n", rc); + ret = rc; + } + + rc = rradc_enable_batt_id_channel(chip, false); + if (rc < 0) { + pr_err("Disabling BATT ID channel failed:%d\n", rc); + ret = rc; + } + + return ret; +} + static int rradc_do_conversion(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 *data) { @@ -765,24 +830,9 @@ static int rradc_do_conversion(struct rradc_chip *chip, switch (prop->channel) { case RR_ADC_BATT_ID: - rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, - FG_ADC_RR_BATT_ID_TRIGGER_CTL, - FG_ADC_RR_BATT_ID_TRIGGER_CTL); - if (rc < 0) { - pr_err("BATT_ID trigger set failed:%d\n", rc); - goto fail; - } - - rc = rradc_read_channel_with_continuous_mode(chip, prop, buf); - if (rc < 0) { - pr_err("Error reading in continuous mode:%d\n", rc); - goto fail; - } - - rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, - FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0); + rc = rradc_do_batt_id_conversion(chip, prop, data, buf); if (rc < 0) { - pr_err("BATT_ID trigger re-set failed:%d\n", rc); + pr_err("Battery ID conversion failed:%d\n", rc); goto fail; } break; diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c index 9241288c1d43..054dfcc8556a 100644 --- a/drivers/iio/adc/qcom-tadc.c +++ b/drivers/iio/adc/qcom-tadc.c @@ -18,7 +18,12 @@ #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/power_supply.h> +#include <linux/pmic-voter.h> +#define USB_PRESENT_VOTER "USB_PRESENT_VOTER" +#define SLEEP_VOTER "SLEEP_VOTER" +#define SHUTDOWN_VOTER "SHUTDOWN_VOTER" #define TADC_REVISION1_REG 0x00 #define TADC_REVISION2_REG 0x01 #define TADC_REVISION3_REG 0x02 @@ -54,6 +59,7 @@ #define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73) #define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74) #define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75) +#define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7) /* TADC_CMP register definitions */ #define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51) @@ -217,6 +223,11 @@ struct tadc_chip { struct tadc_chan_data chans[TADC_NUM_CH]; struct completion eoc_complete; struct mutex write_lock; + struct mutex conv_lock; + struct power_supply *usb_psy; + struct votable *tadc_disable_votable; + struct work_struct status_change_work; + struct notifier_block nb; }; struct tadc_pt { @@ -274,7 +285,7 @@ static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg) if ((reg & 0xFF00) == chip->tadc_cmp_base) return true; - if (reg == TADC_HWTRIG_CONV_CH_EN_REG(chip)) + if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip)) return true; return false; @@ -480,12 +491,22 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) { unsigned long timeout, timeleft; u8 val[TADC_NUM_CH * 2]; - int rc, i; + int rc = 0, i; + mutex_lock(&chip->conv_lock); rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1); if (rc < 0) { pr_err("Couldn't read mbg error status rc=%d\n", rc); - return rc; + goto unlock; + } + + reinit_completion(&chip->eoc_complete); + + if (get_effective_result(chip->tadc_disable_votable)) { + /* leave it back in completed state */ + complete_all(&chip->eoc_complete); + rc = -ENODATA; + goto unlock; } if (val[0] != 0) { @@ -496,7 +517,7 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels); if (rc < 0) { pr_err("Couldn't write conversion request rc=%d\n", rc); - return rc; + goto unlock; } timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS); @@ -506,25 +527,34 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1); if (rc < 0) { pr_err("Couldn't read conversion status rc=%d\n", rc); - return rc; + goto unlock; } + /* + * check one last time if the channel we are requesting + * has completed conversion + */ if (val[0] != channels) { - pr_err("Conversion timed out\n"); - return -ETIMEDOUT; + rc = -ETIMEDOUT; + goto unlock; } } rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val)); if (rc < 0) { pr_err("Couldn't read adc channels rc=%d\n", rc); - return rc; + goto unlock; } for (i = 0; i < TADC_NUM_CH; i++) adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8); - return jiffies_to_msecs(timeout - timeleft); + pr_debug("Conversion time for channels 0x%x = %dms\n", channels, + jiffies_to_msecs(timeout - timeleft)); + +unlock: + mutex_unlock(&chip->conv_lock); + return rc; } static int tadc_read_raw(struct iio_dev *indio_dev, @@ -593,12 +623,17 @@ static int tadc_read_raw(struct iio_dev *indio_dev, break; default: rc = tadc_do_conversion(chip, BIT(chan->channel), adc); - if (rc >= 0) - *val = adc[chan->channel]; + if (rc < 0) { + if (rc != -ENODATA) + pr_err("Couldn't read battery current and voltage channels rc=%d\n", + rc); + return rc; + } + *val = adc[chan->channel]; break; } - if (rc < 0) { + if (rc < 0 && rc != -ENODATA) { pr_err("Couldn't read channel %d\n", chan->channel); return rc; } @@ -630,7 +665,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev, case TADC_BATT_P: rc = tadc_do_conversion(chip, BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc); - if (rc < 0) { + if (rc < 0 && rc != -ENODATA) { pr_err("Couldn't read battery current and voltage channels rc=%d\n", rc); return rc; @@ -641,7 +676,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev, case TADC_INPUT_P: rc = tadc_do_conversion(chip, BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc); - if (rc < 0) { + if (rc < 0 && rc != -ENODATA) { pr_err("Couldn't read input current and voltage channels rc=%d\n", rc); return rc; @@ -683,6 +718,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev, case TADC_DIE_TEMP: case TADC_DIE_TEMP_THR1: case TADC_DIE_TEMP_THR2: + case TADC_DIE_TEMP_THR3: *val = chan_data->scale; return IIO_VAL_INT; case TADC_BATT_I: @@ -821,15 +857,130 @@ static int tadc_write_raw(struct iio_dev *indio_dev, return 0; } - static irqreturn_t handle_eoc(int irq, void *dev_id) { struct tadc_chip *chip = dev_id; - complete(&chip->eoc_complete); + complete_all(&chip->eoc_complete); return IRQ_HANDLED; } +static int tadc_disable_vote_callback(struct votable *votable, + void *data, int disable, const char *client) +{ + struct tadc_chip *chip = data; + int rc; + int timeout; + unsigned long timeleft; + + if (disable) { + timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS); + timeleft = wait_for_completion_timeout(&chip->eoc_complete, + timeout); + if (timeleft == 0) + pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n"); + + rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00); + if (rc < 0) { + pr_err("Couldn't disable hw conversions rc=%d\n", rc); + return rc; + } + rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80); + if (rc < 0) { + pr_err("Couldn't enable direct test mode rc=%d\n", rc); + return rc; + } + } else { + rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00); + if (rc < 0) { + pr_err("Couldn't disable direct test mode rc=%d\n", rc); + return rc; + } + rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x07); + if (rc < 0) { + pr_err("Couldn't enable hw conversions rc=%d\n", rc); + return rc; + } + } + + pr_debug("client: %s disable: %d\n", client, disable); + return 0; +} + +static void status_change_work(struct work_struct *work) +{ + struct tadc_chip *chip = container_of(work, + struct tadc_chip, status_change_work); + union power_supply_propval pval = {0, }; + int rc; + + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + + if (!chip->usb_psy) { + /* treat usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); + return; + } + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) { + pr_err("Couldn't get present status rc=%d\n", rc); + /* treat usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); + return; + } + + /* disable if usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0); +} + +static int tadc_notifier_call(struct notifier_block *nb, + unsigned long ev, void *v) +{ + struct power_supply *psy = v; + struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb); + + if (ev != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if ((strcmp(psy->desc->name, "usb") == 0)) + schedule_work(&chip->status_change_work); + + return NOTIFY_OK; +} + +static int tadc_register_notifier(struct tadc_chip *chip) +{ + int rc; + + chip->nb.notifier_call = tadc_notifier_call; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + return rc; + } + + return 0; +} + +static int tadc_suspend(struct device *dev) +{ + struct tadc_chip *chip = dev_get_drvdata(dev); + + vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0); + return 0; +} + +static int tadc_resume(struct device *dev) +{ + struct tadc_chip *chip = dev_get_drvdata(dev); + + vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0); + return 0; +} + static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta, u32 rtherm) { @@ -1009,6 +1160,12 @@ static int tadc_probe(struct platform_device *pdev) chip->dev = &pdev->dev; init_completion(&chip->eoc_complete); + /* + * set the completion in "completed" state so disable of the tadc + * can progress + */ + complete_all(&chip->eoc_complete); + rc = of_property_read_u32(node, "reg", &chip->tadc_base); if (rc < 0) { pr_err("Couldn't read base address rc=%d\n", rc); @@ -1017,6 +1174,8 @@ static int tadc_probe(struct platform_device *pdev) chip->tadc_cmp_base = chip->tadc_base + 0x100; mutex_init(&chip->write_lock); + mutex_init(&chip->conv_lock); + INIT_WORK(&chip->status_change_work, status_change_work); chip->regmap = dev_get_regmap(chip->dev->parent, NULL); if (!chip->regmap) { pr_err("Couldn't get regmap\n"); @@ -1035,17 +1194,36 @@ static int tadc_probe(struct platform_device *pdev) return rc; } + chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE", + VOTE_SET_ANY, + tadc_disable_vote_callback, + chip); + if (IS_ERR(chip->tadc_disable_votable)) { + rc = PTR_ERR(chip->tadc_disable_votable); + return rc; + } + /* assume usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); + vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0); + vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0); + + rc = tadc_register_notifier(chip); + if (rc < 0) { + pr_err("Couldn't register notifier=%d\n", rc); + goto destroy_votable; + } + irq = of_irq_get_byname(node, "eoc"); if (irq < 0) { pr_err("Couldn't get eoc irq rc=%d\n", irq); - return irq; + goto destroy_votable; } rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc, IRQF_ONESHOT, "eoc", chip); if (rc < 0) { pr_err("Couldn't request irq %d rc=%d\n", irq, rc); - return rc; + goto destroy_votable; } indio_dev->dev.parent = chip->dev; @@ -1058,17 +1236,37 @@ static int tadc_probe(struct platform_device *pdev) rc = devm_iio_device_register(chip->dev, indio_dev); if (rc < 0) { pr_err("Couldn't register IIO device rc=%d\n", rc); - return rc; + goto destroy_votable; } + platform_set_drvdata(pdev, chip); return 0; + +destroy_votable: + destroy_votable(chip->tadc_disable_votable); + return rc; } static int tadc_remove(struct platform_device *pdev) { + struct tadc_chip *chip = platform_get_drvdata(pdev); + + destroy_votable(chip->tadc_disable_votable); return 0; } +static void tadc_shutdown(struct platform_device *pdev) +{ + struct tadc_chip *chip = platform_get_drvdata(pdev); + + vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0); +} + +static const struct dev_pm_ops tadc_pm_ops = { + .resume = tadc_resume, + .suspend = tadc_suspend, +}; + static const struct of_device_id tadc_match_table[] = { { .compatible = "qcom,tadc" }, { } @@ -1076,12 +1274,14 @@ static const struct of_device_id tadc_match_table[] = { MODULE_DEVICE_TABLE(of, tadc_match_table); static struct platform_driver tadc_driver = { - .driver = { + .driver = { .name = "qcom-tadc", .of_match_table = tadc_match_table, + .pm = &tadc_pm_ops, }, - .probe = tadc_probe, - .remove = tadc_remove, + .probe = tadc_probe, + .remove = tadc_remove, + .shutdown = tadc_shutdown, }; module_platform_driver(tadc_driver); diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index a0d7deeac62f..3f90985d545e 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -136,6 +136,7 @@ static const struct iio_chan_spec mpl115_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, }; diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 01b2e0b18878..0f5b8767ec2e 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -182,7 +182,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { { .type = IIO_PRESSURE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .scan_index = 0, .scan_type = { .sign = 'u', @@ -195,7 +195,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .scan_index = 1, .scan_type = { .sign = 's', diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 17a15c56028c..3f5741a3e728 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2578,7 +2578,8 @@ static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, if (!src_addr || !src_addr->sa_family) { src_addr = (struct sockaddr *) &id->route.addr.src_addr; src_addr->sa_family = dst_addr->sa_family; - if (dst_addr->sa_family == AF_INET6) { + if (IS_ENABLED(CONFIG_IPV6) && + dst_addr->sa_family == AF_INET6) { struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *) src_addr; struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *) dst_addr; src_addr6->sin6_scope_id = dst_addr6->sin6_scope_id; @@ -3348,6 +3349,9 @@ static int cma_accept_iw(struct rdma_id_private *id_priv, struct iw_cm_conn_param iw_param; int ret; + if (!conn_param) + return -EINVAL; + ret = cma_modify_qp_rtr(id_priv, conn_param); if (ret) return ret; diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 2281de122038..8d84c563ba75 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -1745,7 +1745,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv, if (!class) goto out; if (convert_mgmt_class(mad_hdr->mgmt_class) >= - IB_MGMT_MAX_METHODS) + ARRAY_SIZE(class->method_table)) goto out; method = class->method_table[convert_mgmt_class( mad_hdr->mgmt_class)]; diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c index 6aa648cb5381..2cd97977b988 100644 --- a/drivers/infiniband/core/multicast.c +++ b/drivers/infiniband/core/multicast.c @@ -517,8 +517,11 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec, process_join_error(group, status); else { int mgids_changed, is_mgid0; - ib_find_pkey(group->port->dev->device, group->port->port_num, - be16_to_cpu(rec->pkey), &pkey_index); + + if (ib_find_pkey(group->port->dev->device, + group->port->port_num, be16_to_cpu(rec->pkey), + &pkey_index)) + pkey_index = MCAST_INVALID_PKEY_INDEX; spin_lock_irq(&group->port->lock); if (group->state == MCAST_BUSY && diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 04f3c0db9126..0ae337bec4f2 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -134,6 +134,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND)); if (access & IB_ACCESS_ON_DEMAND) { + put_pid(umem->pid); ret = ib_umem_odp_get(context, umem); if (ret) { kfree(umem); @@ -149,6 +150,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, page_list = (struct page **) __get_free_page(GFP_KERNEL); if (!page_list) { + put_pid(umem->pid); kfree(umem); return ERR_PTR(-ENOMEM); } diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c index c007c766c61e..fc21bdbb8b32 100644 --- a/drivers/infiniband/hw/mlx4/ah.c +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -113,7 +113,9 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr !(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support)) --ah->av.eth.stat_rate; } - + ah->av.eth.sl_tclass_flowlabel |= + cpu_to_be32((ah_attr->grh.traffic_class << 20) | + ah_attr->grh.flow_label); /* * HW requires multicast LID so we just choose one. */ @@ -121,7 +123,7 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr ah->av.ib.dlid = cpu_to_be16(0xc000); memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16); - ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 29); + ah->av.eth.sl_tclass_flowlabel |= cpu_to_be32(ah_attr->sl << 29); return &ah->ibah; } diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 97d6878f9938..77ddf2fa8625 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -630,9 +630,11 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port, if (err) goto out; - props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ? - IB_WIDTH_4X : IB_WIDTH_1X; - props->active_speed = IB_SPEED_QDR; + props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) || + (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ? + IB_WIDTH_4X : IB_WIDTH_1X; + props->active_speed = (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ? + IB_SPEED_FDR : IB_SPEED_QDR; props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_IP_BASED_GIDS; props->gid_tbl_len = mdev->dev->caps.gid_table_len[port]; props->max_msg_sz = mdev->dev->caps.max_msg_sz; @@ -2401,14 +2403,19 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) goto err_steer_qp_release; } - bitmap_zero(ibdev->ib_uc_qpns_bitmap, ibdev->steer_qpn_count); - - err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE( - dev, ibdev->steer_qpn_base, - ibdev->steer_qpn_base + - ibdev->steer_qpn_count - 1); - if (err) - goto err_steer_free_bitmap; + if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_DMFS_IPOIB) { + bitmap_zero(ibdev->ib_uc_qpns_bitmap, + ibdev->steer_qpn_count); + err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE( + dev, ibdev->steer_qpn_base, + ibdev->steer_qpn_base + + ibdev->steer_qpn_count - 1); + if (err) + goto err_steer_free_bitmap; + } else { + bitmap_fill(ibdev->ib_uc_qpns_bitmap, + ibdev->steer_qpn_count); + } } for (j = 1; j <= ibdev->dev->caps.num_ports; j++) diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index f350f2d61c15..1c8b7c22c822 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1207,7 +1207,8 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp) if (is_qp0(dev, mqp)) mlx4_CLOSE_PORT(dev->dev, mqp->port); - if (dev->qp1_proxy[mqp->port - 1] == mqp) { + if (mqp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI && + dev->qp1_proxy[mqp->port - 1] == mqp) { mutex_lock(&dev->qp1_proxy_lock[mqp->port - 1]); dev->qp1_proxy[mqp->port - 1] = NULL; mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 6000f7aeede9..3399271c235b 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -614,6 +614,33 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) return 0; } +static void wait_for_async_commands(struct mlx5_ib_dev *dev) +{ + struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_cache_ent *ent; + int total = 0; + int i; + int j; + + for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + ent = &cache->ent[i]; + for (j = 0 ; j < 1000; j++) { + if (!ent->pending) + break; + msleep(50); + } + } + for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + ent = &cache->ent[i]; + total += ent->pending; + } + + if (total) + mlx5_ib_warn(dev, "aborted while there are %d pending mr requests\n", total); + else + mlx5_ib_warn(dev, "done with all pending requests\n"); +} + int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev) { int i; @@ -627,6 +654,7 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev) clean_keys(dev, i); destroy_workqueue(dev->cache.wq); + wait_for_async_commands(dev); del_timer_sync(&dev->delay_timer); return 0; diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 69a151ae8261..07cfcc326863 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -63,6 +63,8 @@ enum ipoib_flush_level { enum { IPOIB_ENCAP_LEN = 4, + IPOIB_PSEUDO_LEN = 20, + IPOIB_HARD_LEN = IPOIB_ENCAP_LEN + IPOIB_PSEUDO_LEN, IPOIB_UD_HEAD_SIZE = IB_GRH_BYTES + IPOIB_ENCAP_LEN, IPOIB_UD_RX_SG = 2, /* max buffer needed for 4K mtu */ @@ -131,15 +133,21 @@ struct ipoib_header { u16 reserved; }; -struct ipoib_cb { - struct qdisc_skb_cb qdisc_cb; - u8 hwaddr[INFINIBAND_ALEN]; +struct ipoib_pseudo_header { + u8 hwaddr[INFINIBAND_ALEN]; }; -static inline struct ipoib_cb *ipoib_skb_cb(const struct sk_buff *skb) +static inline void skb_add_pseudo_hdr(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct ipoib_cb)); - return (struct ipoib_cb *)skb->cb; + char *data = skb_push(skb, IPOIB_PSEUDO_LEN); + + /* + * only the ipoib header is present now, make room for a dummy + * pseudo header and set skb field accordingly + */ + memset(data, 0, IPOIB_PSEUDO_LEN); + skb_reset_mac_header(skb); + skb_pull(skb, IPOIB_HARD_LEN); } /* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 8ca75af0e6d1..2018d24344de 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -63,6 +63,8 @@ MODULE_PARM_DESC(cm_data_debug_level, #define IPOIB_CM_RX_DELAY (3 * 256 * HZ) #define IPOIB_CM_RX_UPDATE_MASK (0x3) +#define IPOIB_CM_RX_RESERVE (ALIGN(IPOIB_HARD_LEN, 16) - IPOIB_ENCAP_LEN) + static struct ib_qp_attr ipoib_cm_err_attr = { .qp_state = IB_QPS_ERR }; @@ -147,15 +149,15 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev, struct sk_buff *skb; int i; - skb = dev_alloc_skb(IPOIB_CM_HEAD_SIZE + 12); + skb = dev_alloc_skb(ALIGN(IPOIB_CM_HEAD_SIZE + IPOIB_PSEUDO_LEN, 16)); if (unlikely(!skb)) return NULL; /* - * IPoIB adds a 4 byte header. So we need 12 more bytes to align the + * IPoIB adds a IPOIB_ENCAP_LEN byte header, this will align the * IP header to a multiple of 16. */ - skb_reserve(skb, 12); + skb_reserve(skb, IPOIB_CM_RX_RESERVE); mapping[0] = ib_dma_map_single(priv->ca, skb->data, IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE); @@ -624,9 +626,9 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) if (wc->byte_len < IPOIB_CM_COPYBREAK) { int dlen = wc->byte_len; - small_skb = dev_alloc_skb(dlen + 12); + small_skb = dev_alloc_skb(dlen + IPOIB_CM_RX_RESERVE); if (small_skb) { - skb_reserve(small_skb, 12); + skb_reserve(small_skb, IPOIB_CM_RX_RESERVE); ib_dma_sync_single_for_cpu(priv->ca, rx_ring[wr_id].mapping[0], dlen, DMA_FROM_DEVICE); skb_copy_from_linear_data(skb, small_skb->data, dlen); @@ -663,8 +665,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) copied: skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb_reset_mac_header(skb); - skb_pull(skb, IPOIB_ENCAP_LEN); + skb_add_pseudo_hdr(skb); ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; @@ -1035,8 +1036,6 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ tx_qp = ib_create_qp(priv->pd, &attr); if (PTR_ERR(tx_qp) == -EINVAL) { - ipoib_warn(priv, "can't use GFP_NOIO for QPs on device %s, using GFP_KERNEL\n", - priv->ca->name); attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO; tx_qp = ib_create_qp(priv->pd, &attr); } @@ -1489,12 +1488,14 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr, ret = ipoib_set_mode(dev, buf); - rtnl_unlock(); - - if (!ret) - return count; + /* The assumption is that the function ipoib_set_mode returned + * with the rtnl held by it, if not the value -EBUSY returned, + * then no need to rtnl_unlock + */ + if (ret != -EBUSY) + rtnl_unlock(); - return ret; + return (!ret || ret == -EBUSY) ? count : ret; } static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 85de078fb0ce..8f8c3af9f4e8 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -130,16 +130,15 @@ static struct sk_buff *ipoib_alloc_rx_skb(struct net_device *dev, int id) buf_size = IPOIB_UD_BUF_SIZE(priv->max_ib_mtu); - skb = dev_alloc_skb(buf_size + IPOIB_ENCAP_LEN); + skb = dev_alloc_skb(buf_size + IPOIB_HARD_LEN); if (unlikely(!skb)) return NULL; /* - * IB will leave a 40 byte gap for a GRH and IPoIB adds a 4 byte - * header. So we need 4 more bytes to get to 48 and align the - * IP header to a multiple of 16. + * the IP header will be at IPOIP_HARD_LEN + IB_GRH_BYTES, that is + * 64 bytes aligned */ - skb_reserve(skb, 4); + skb_reserve(skb, sizeof(struct ipoib_pseudo_header)); mapping = priv->rx_ring[id].mapping; mapping[0] = ib_dma_map_single(priv->ca, skb->data, buf_size, @@ -242,8 +241,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) skb_pull(skb, IB_GRH_BYTES); skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb_reset_mac_header(skb); - skb_pull(skb, IPOIB_ENCAP_LEN); + skb_add_pseudo_hdr(skb); ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5f7681b975d0..8efcff1beb8f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -464,8 +464,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM; ipoib_flush_paths(dev); - rtnl_lock(); - return 0; + return (!rtnl_trylock()) ? -EBUSY : 0; } if (!strcmp(buf, "datagram\n")) { @@ -474,8 +473,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu)); rtnl_unlock(); ipoib_flush_paths(dev); - rtnl_lock(); - return 0; + return (!rtnl_trylock()) ? -EBUSY : 0; } return -EINVAL; @@ -628,6 +626,14 @@ void ipoib_mark_paths_invalid(struct net_device *dev) spin_unlock_irq(&priv->lock); } +static void push_pseudo_header(struct sk_buff *skb, const char *daddr) +{ + struct ipoib_pseudo_header *phdr; + + phdr = (struct ipoib_pseudo_header *)skb_push(skb, sizeof(*phdr)); + memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); +} + void ipoib_flush_paths(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -850,9 +856,11 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, ipoib_neigh_free(neigh); goto err_drop; } - if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) + if (skb_queue_len(&neigh->queue) < + IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, neigh->daddr); __skb_queue_tail(&neigh->queue, skb); - else { + } else { ipoib_warn(priv, "queue length limit %d. Packet drop.\n", skb_queue_len(&neigh->queue)); goto err_drop; @@ -868,10 +876,12 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, if (!path->query && path_rec_start(dev, path)) goto err_path; - if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) + if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, neigh->daddr); __skb_queue_tail(&neigh->queue, skb); - else + } else { goto err_drop; + } } spin_unlock_irqrestore(&priv->lock, flags); @@ -889,7 +899,7 @@ err_drop: } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, - struct ipoib_cb *cb) + struct ipoib_pseudo_header *phdr) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; @@ -897,16 +907,17 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, cb->hwaddr + 4); + path = __path_find(dev, phdr->hwaddr + 4); if (!path || !path->valid) { int new_path = 0; if (!path) { - path = path_rec_create(dev, cb->hwaddr + 4); + path = path_rec_create(dev, phdr->hwaddr + 4); new_path = 1; } if (path) { if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, phdr->hwaddr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -934,10 +945,11 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, be16_to_cpu(path->pathrec.dlid)); spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr)); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr)); return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, phdr->hwaddr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -951,13 +963,15 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_neigh *neigh; - struct ipoib_cb *cb = ipoib_skb_cb(skb); + struct ipoib_pseudo_header *phdr; struct ipoib_header *header; unsigned long flags; + phdr = (struct ipoib_pseudo_header *) skb->data; + skb_pull(skb, sizeof(*phdr)); header = (struct ipoib_header *) skb->data; - if (unlikely(cb->hwaddr[4] == 0xff)) { + if (unlikely(phdr->hwaddr[4] == 0xff)) { /* multicast, arrange "if" according to probability */ if ((header->proto != htons(ETH_P_IP)) && (header->proto != htons(ETH_P_IPV6)) && @@ -970,13 +984,13 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } /* Add in the P_Key for multicast*/ - cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; - cb->hwaddr[9] = priv->pkey & 0xff; + phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff; + phdr->hwaddr[9] = priv->pkey & 0xff; - neigh = ipoib_neigh_get(dev, cb->hwaddr); + neigh = ipoib_neigh_get(dev, phdr->hwaddr); if (likely(neigh)) goto send_using_neigh; - ipoib_mcast_send(dev, cb->hwaddr, skb); + ipoib_mcast_send(dev, phdr->hwaddr, skb); return NETDEV_TX_OK; } @@ -985,16 +999,16 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) case htons(ETH_P_IP): case htons(ETH_P_IPV6): case htons(ETH_P_TIPC): - neigh = ipoib_neigh_get(dev, cb->hwaddr); + neigh = ipoib_neigh_get(dev, phdr->hwaddr); if (unlikely(!neigh)) { - neigh_add_path(skb, cb->hwaddr, dev); + neigh_add_path(skb, phdr->hwaddr, dev); return NETDEV_TX_OK; } break; case htons(ETH_P_ARP): case htons(ETH_P_RARP): /* for unicast ARP and RARP should always perform path find */ - unicast_arp_send(skb, dev, cb); + unicast_arp_send(skb, dev, phdr); return NETDEV_TX_OK; default: /* ethertype not supported by IPoIB */ @@ -1011,11 +1025,12 @@ send_using_neigh: goto unref; } } else if (neigh->ah) { - ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr)); + ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(phdr->hwaddr)); goto unref; } if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, phdr->hwaddr); spin_lock_irqsave(&priv->lock, flags); __skb_queue_tail(&neigh->queue, skb); spin_unlock_irqrestore(&priv->lock, flags); @@ -1048,7 +1063,6 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; - struct ipoib_cb *cb = ipoib_skb_cb(skb); header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -1057,12 +1071,12 @@ static int ipoib_hard_header(struct sk_buff *skb, /* * we don't rely on dst_entry structure, always stuff the - * destination address into skb->cb so we can figure out where + * destination address into skb hard header so we can figure out where * to send the packet later. */ - memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); + push_pseudo_header(skb, daddr); - return sizeof *header; + return IPOIB_HARD_LEN; } static void ipoib_set_mcast_list(struct net_device *dev) @@ -1638,7 +1652,7 @@ void ipoib_setup(struct net_device *dev) dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - dev->hard_header_len = IPOIB_ENCAP_LEN; + dev->hard_header_len = IPOIB_HARD_LEN; dev->addr_len = INFINIBAND_ALEN; dev->type = ARPHRD_INFINIBAND; dev->tx_queue_len = ipoib_sendq_size * 2; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 87799de90a1d..5580ab0b5781 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -563,8 +563,11 @@ void ipoib_mcast_join_task(struct work_struct *work) if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) return; - if (ib_query_port(priv->ca, priv->port, &port_attr) || - port_attr.state != IB_PORT_ACTIVE) { + if (ib_query_port(priv->ca, priv->port, &port_attr)) { + ipoib_dbg(priv, "ib_query_port() failed\n"); + return; + } + if (port_attr.state != IB_PORT_ACTIVE) { ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n", port_attr.state); return; @@ -753,9 +756,11 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) __ipoib_mcast_add(dev, mcast); list_add_tail(&mcast->list, &priv->multicast_list); } - if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) + if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) { + /* put pseudoheader back on for next time */ + skb_push(skb, sizeof(struct ipoib_pseudo_header)); skb_queue_tail(&mcast->pkt_queue, skb); - else { + } else { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); } diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 5f0f4fc58f43..e397f1b0af09 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1787,17 +1787,24 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) { spin_lock_irqsave(&ch->lock, flags); ch->req_lim += be32_to_cpu(rsp->req_lim_delta); + if (rsp->tag == ch->tsk_mgmt_tag) { + ch->tsk_mgmt_status = -1; + if (be32_to_cpu(rsp->resp_data_len) >= 4) + ch->tsk_mgmt_status = rsp->data[3]; + complete(&ch->tsk_mgmt_done); + } else { + shost_printk(KERN_ERR, target->scsi_host, + "Received tsk mgmt response too late for tag %#llx\n", + rsp->tag); + } spin_unlock_irqrestore(&ch->lock, flags); - - ch->tsk_mgmt_status = -1; - if (be32_to_cpu(rsp->resp_data_len) >= 4) - ch->tsk_mgmt_status = rsp->data[3]; - complete(&ch->tsk_mgmt_done); } else { scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag); - if (scmnd) { + if (scmnd && scmnd->host_scribble) { req = (void *)scmnd->host_scribble; scmnd = srp_claim_req(ch, req, NULL, scmnd); + } else { + scmnd = NULL; } if (!scmnd) { shost_printk(KERN_ERR, target->scsi_host, @@ -2469,19 +2476,18 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth) } static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, - u8 func) + u8 func, u8 *status) { struct srp_target_port *target = ch->target; struct srp_rport *rport = target->rport; struct ib_device *dev = target->srp_host->srp_dev->dev; struct srp_iu *iu; struct srp_tsk_mgmt *tsk_mgmt; + int res; if (!ch->connected || target->qp_in_error) return -1; - init_completion(&ch->tsk_mgmt_done); - /* * Lock the rport mutex to avoid that srp_create_ch_ib() is * invoked while a task management function is being sent. @@ -2504,10 +2510,16 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, tsk_mgmt->opcode = SRP_TSK_MGMT; int_to_scsilun(lun, &tsk_mgmt->lun); - tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT; tsk_mgmt->tsk_mgmt_func = func; tsk_mgmt->task_tag = req_tag; + spin_lock_irq(&ch->lock); + ch->tsk_mgmt_tag = (ch->tsk_mgmt_tag + 1) | SRP_TAG_TSK_MGMT; + tsk_mgmt->tag = ch->tsk_mgmt_tag; + spin_unlock_irq(&ch->lock); + + init_completion(&ch->tsk_mgmt_done); + ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt, DMA_TO_DEVICE); if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) { @@ -2516,13 +2528,15 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, return -1; } + res = wait_for_completion_timeout(&ch->tsk_mgmt_done, + msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)); + if (res > 0 && status) + *status = ch->tsk_mgmt_status; mutex_unlock(&rport->mutex); - if (!wait_for_completion_timeout(&ch->tsk_mgmt_done, - msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS))) - return -1; + WARN_ON_ONCE(res < 0); - return 0; + return res > 0 ? 0 : -1; } static int srp_abort(struct scsi_cmnd *scmnd) @@ -2548,7 +2562,7 @@ static int srp_abort(struct scsi_cmnd *scmnd) shost_printk(KERN_ERR, target->scsi_host, "Sending SRP abort for tag %#x\n", tag); if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun, - SRP_TSK_ABORT_TASK) == 0) + SRP_TSK_ABORT_TASK, NULL) == 0) ret = SUCCESS; else if (target->rport->state == SRP_RPORT_LOST) ret = FAST_IO_FAIL; @@ -2566,14 +2580,15 @@ static int srp_reset_device(struct scsi_cmnd *scmnd) struct srp_target_port *target = host_to_target(scmnd->device->host); struct srp_rdma_ch *ch; int i; + u8 status; shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n"); ch = &target->ch[0]; if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun, - SRP_TSK_LUN_RESET)) + SRP_TSK_LUN_RESET, &status)) return FAILED; - if (ch->tsk_mgmt_status) + if (status) return FAILED; for (i = 0; i < target->ch_count; i++) { diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index f6af531f9f32..109eea94d0f9 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -168,6 +168,7 @@ struct srp_rdma_ch { int max_ti_iu_len; int comp_vector; + u64 tsk_mgmt_tag; struct completion tsk_mgmt_done; u8 tsk_mgmt_status; bool connected; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index aff42d5e2296..16f000a76de5 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1238,6 +1238,12 @@ static int xpad_init_input(struct usb_xpad *xpad) input_dev->name = xpad->name; input_dev->phys = xpad->phys; usb_to_input_id(xpad->udev, &input_dev->id); + + if (xpad->xtype == XTYPE_XBOX360W) { + /* x360w controllers and the receiver have different ids */ + input_dev->id.product = 0x02a1; + } + input_dev->dev.parent = &xpad->intf->dev; input_set_drvdata(input_dev, xpad); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index cf29f2756b84..c93dd193a496 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -3,6 +3,7 @@ * * Copyright 2005 Phil Blundell * Copyright 2010, 2011 David Jander <david@protonic.nl> + * Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -32,6 +33,7 @@ #include <linux/of_irq.h> #include <linux/spinlock.h> #include <linux/pinctrl/consumer.h> +#include <linux/syscore_ops.h> struct gpio_button_data { const struct gpio_keys_button *button; @@ -57,6 +59,11 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; +static struct device *global_dev; +static struct syscore_ops gpio_keys_syscore_pm_ops; + +static void gpio_keys_syscore_resume(void); + /* * SYSFS interface for enabling/disabling keys and switches: * @@ -343,14 +350,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = gpio_get_value_cansleep(button->gpio); + int state; + state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state\n"); return; } - state = (state ? 1 : 0) ^ button->active_low; if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); @@ -664,6 +671,8 @@ gpio_keys_get_devtree_pdata(struct device *dev) pdata->nbuttons = nbuttons; pdata->rep = !!of_get_property(node, "autorepeat", NULL); + pdata->name = of_get_property(node, "input-name", NULL); + pdata->use_syscore = of_property_read_bool(node, "use-syscore"); i = 0; for_each_child_of_node(node, pp) { @@ -710,7 +719,7 @@ gpio_keys_get_devtree_pdata(struct device *dev) button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); if (of_property_read_u32(pp, "debounce-interval", - &button->debounce_interval)) + &button->debounce_interval)) button->debounce_interval = 5; } @@ -767,6 +776,7 @@ static int gpio_keys_probe(struct platform_device *pdev) return -ENOMEM; } + global_dev = dev; ddata->pdata = pdata; ddata->input = input; mutex_init(&ddata->disable_lock); @@ -835,6 +845,11 @@ static int gpio_keys_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, wakeup); + if (pdata->use_syscore) + gpio_keys_syscore_pm_ops.resume = gpio_keys_syscore_resume; + + register_syscore_ops(&gpio_keys_syscore_pm_ops); + return 0; err_remove_group: @@ -857,6 +872,7 @@ err_setup_key: static int gpio_keys_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + unregister_syscore_ops(&gpio_keys_syscore_pm_ops); device_init_wakeup(&pdev->dev, 0); @@ -864,6 +880,41 @@ static int gpio_keys_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static void gpio_keys_syscore_resume(void) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(global_dev); + struct input_dev *input = ddata->input; + struct gpio_button_data *bdata = NULL; + int error = 0; + int i; + + if (ddata->key_pinctrl) { + error = gpio_keys_pinctrl_configure(ddata, true); + if (error) { + dev_err(global_dev, "failed to put the pin in resume state\n"); + return; + } + } + + if (device_may_wakeup(global_dev)) { + for (i = 0; i < ddata->pdata->nbuttons; i++) { + bdata = &ddata->data[i]; + if (bdata->button->wakeup) + disable_irq_wake(bdata->irq); + } + } else { + mutex_lock(&input->mutex); + if (input->users) + error = gpio_keys_open(input); + mutex_unlock(&input->mutex); + } + + if (error) + return; + + gpio_keys_report_state(ddata); +} + static int gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); @@ -901,6 +952,11 @@ static int gpio_keys_resume(struct device *dev) int error = 0; int i; + if (ddata->pdata->use_syscore == true) { + dev_dbg(global_dev, "Using syscore resume, no need of this resume.\n"); + return 0; + } + if (ddata->key_pinctrl) { error = gpio_keys_pinctrl_configure(ddata, true); if (error) { @@ -928,6 +984,21 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_report_state(ddata); return 0; } + +#else + +static void gpio_keys_syscore_resume(void){} + +static int gpio_keys_suspend(struct device *dev) +{ + return 0; +} + +static int gpio_keys_resume(struct device *dev) +{ + return 0; +} + #endif static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5cfa1848e37c..dd98d8c8fd1f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -834,4 +834,14 @@ config INPUT_DRV2667_HAPTICS source "drivers/input/misc/ots_pat9125/Kconfig" +config INPUT_STMVL53L0 + tristate "STM VL53L0 Proximity support" + depends on INPUT && I2C + help + Say Y here if you want to use STMicroelectronics's proximity sensor + through I2C interface. + + To compile this driver as a module, choose M here: the + module will be called stmvl53l0. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a5ab4b762d31..44c026abcb6f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -80,3 +80,4 @@ obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/ +obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/ diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 2adfd86c869a..930424e55439 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -592,7 +592,6 @@ static int drv260x_probe(struct i2c_client *client, } haptics->input_dev->name = "drv260x:haptics"; - haptics->input_dev->dev.parent = client->dev.parent; haptics->input_dev->close = drv260x_close; input_set_drvdata(haptics->input_dev, haptics); input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); diff --git a/drivers/input/misc/vl53L0/Makefile b/drivers/input/misc/vl53L0/Makefile index 4a6be55094b6..f105e1c3c60f 100644 --- a/drivers/input/misc/vl53L0/Makefile +++ b/drivers/input/misc/vl53L0/Makefile @@ -9,12 +9,12 @@ FEATURE_USE_CCI := true ifeq ($(FEATURE_USE_CCI), true) ccflags-y += -Idrivers/input/misc/vl53L0/inc -DCAMERA_CCI else -ccflags-y += -Idrivers/input/misc/vl53L0/inc +ccflags-y += -Idrivers/input/misc/vl53L0/inc endif ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io ccflags-y += -Idrivers/media/platform/msm/camera_v2 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci -obj-$(CONFIG_STMVL53L0) += stmvl53l0.o +obj-$(CONFIG_INPUT_STMVL53L0) += stmvl53l0.o stmvl53l0-objs := stmvl53l0_module.o stmvl53l0_module-i2c.o stmvl53l0_module-cci.o src/vl53l0_api_calibration.o src/vl53l0_api_core.o src/vl53l0_api_histogram.o src/vl53l0_api_ranging.o src/vl53l0_api_strings.o src/vl53l0_api.o src/vl53l0_platform.o src/vl53l0_i2c_platform.o src/vl53l0_port_i2c.o src/vl53l010_api.o src/vl53l010_tuning.o diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index d15b33813021..ed1935f300a7 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1232,6 +1232,7 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, { "ELAN0100", 0 }, { "ELAN0600", 0 }, + { "ELAN0605", 0 }, { "ELAN1000", 0 }, { } }; diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 073246c7d163..0cdd95801a25 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -211,6 +211,12 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), }, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), + DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), + }, + }, { } }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 075c18e0e4ae..1d4e8a4ce206 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1128,6 +1128,16 @@ config TOUCHSCREEN_FT5X06_GESTURE If unsure, say N. +config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + bool "Synaptics DSX firmware update extra sysfs attributes" + depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE + help + Say Y here to enable support for extra sysfs attributes + supporting firmware update in a development environment. + This does not affect the core or other subsystem attributes. + + If unsure, say N. + config TOUCHSCREEN_ROHM_BU21023 tristate "ROHM BU21023/24 Dual touch support resistive touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index ac09855fa435..486f8fe242da 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -905,9 +905,9 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) case QUEUE_HEADER_NORMAL: report_count = ts->buf[FW_HDR_COUNT]; - if (report_count > 3) { + if (report_count == 0 || report_count > 3) { dev_err(&client->dev, - "too large report count: %*ph\n", + "bad report count: %*ph\n", HEADER_SIZE, ts->buf); break; } diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c index 206941708141..3e85cb5e2ebc 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c @@ -3666,7 +3666,7 @@ static int synaptics_rmi4_probe(struct platform_device *pdev) return -EINVAL; } - rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + rmi4_data = devm_kzalloc(&pdev->dev, sizeof(*rmi4_data), GFP_KERNEL); if (!rmi4_data) { dev_err(&pdev->dev, "%s: Failed to alloc mem for rmi4_data\n", @@ -3684,6 +3684,12 @@ static int synaptics_rmi4_probe(struct platform_device *pdev) rmi4_data->fingers_on_2d = false; rmi4_data->update_coords = true; + rmi4_data->write_buf = devm_kzalloc(&pdev->dev, I2C_WRITE_BUF_MAX_LEN, + GFP_KERNEL); + if (!rmi4_data->write_buf) + return -ENOMEM; + rmi4_data->write_buf_len = I2C_WRITE_BUF_MAX_LEN; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; rmi4_data->reset_device = synaptics_rmi4_reset_device; diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h index 7d7e045d7917..a642092e2f63 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h @@ -101,6 +101,7 @@ #define PINCTRL_STATE_RELEASE "pmx_ts_release" #define SYNA_FW_NAME_MAX_LEN 50 +#define I2C_WRITE_BUF_MAX_LEN 32 enum exp_fn { RMI_DEV = 0, @@ -277,6 +278,8 @@ struct synaptics_rmi4_data { unsigned char no_sleep_setting; unsigned char intr_mask[MAX_INTR_REGISTERS]; unsigned char *button_txrx_mapping; + unsigned char *write_buf; + unsigned short write_buf_len; unsigned short num_of_intr_regs; unsigned short f01_query_base_addr; unsigned short f01_cmd_base_addr; diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c index 0ec16e606545..4787f2bcd768 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c @@ -102,6 +102,7 @@ (fwu->config_data[2] == config_id[2]) && \ (fwu->config_data[3] == config_id[3])) +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static ssize_t fwu_sysfs_show_image(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count); @@ -157,6 +158,7 @@ static ssize_t fwu_sysfs_config_id_show(struct device *dev, static ssize_t fwu_sysfs_package_id_show(struct device *dev, struct device_attribute *attr, char *buf); +#endif enum bl_version { V5 = 5, @@ -296,6 +298,7 @@ struct synaptics_rmi4_fwu_handle { struct synaptics_rmi4_data *rmi4_data; }; +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static struct bin_attribute dev_attr_data = { .attr = { .name = "data", @@ -305,9 +308,11 @@ static struct bin_attribute dev_attr_data = { .read = fwu_sysfs_show_image, .write = fwu_sysfs_store_image, }; +#endif static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS __ATTR(force_update_fw, S_IWUSR | S_IWGRP, NULL, fwu_sysfs_force_reflash_store), @@ -353,6 +358,7 @@ static struct device_attribute attrs[] = { __ATTR(package_id, S_IRUGO, fwu_sysfs_package_id_show, synaptics_rmi4_store_error), +#endif }; static struct synaptics_rmi4_fwu_handle *fwu; @@ -1220,6 +1226,7 @@ write_config: return retval; } +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static int fwu_start_write_config(void) { int retval; @@ -1395,6 +1402,7 @@ exit: return retval; } +#endif static int fwu_do_lockdown(void) { @@ -1585,6 +1593,7 @@ int synaptics_dsx_fw_updater(unsigned char *fw_data) } EXPORT_SYMBOL(synaptics_dsx_fw_updater); +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static ssize_t fwu_sysfs_show_image(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count) @@ -1972,6 +1981,7 @@ static ssize_t fwu_sysfs_package_id_show(struct device *dev, (package_id[1] << 8) | package_id[0], (package_id[3] << 8) | package_id[2]); } +#endif static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, unsigned char intr_mask) @@ -2045,6 +2055,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) fwu->do_lockdown = DO_LOCKDOWN; fwu->initialized = true; +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); if (retval < 0) { @@ -2053,6 +2064,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) __func__); goto exit_free_fwu; } +#endif for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, @@ -2074,7 +2086,9 @@ exit_remove_attrs: &attrs[attr_count].attr); } +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif exit_free_fwu: kfree(fwu); @@ -2096,7 +2110,9 @@ static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) &attrs[attr_count].attr); } +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif kfree(fwu->read_config_buf); kfree(fwu); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c index 0b3fbaf9f462..0be7e0bc9045 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c @@ -134,18 +134,33 @@ static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, { int retval; unsigned char retry; - unsigned char buf[length + 1]; struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); struct i2c_msg msg[] = { { .addr = i2c->addr, .flags = 0, .len = length + 1, - .buf = buf, } }; mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + /* + * Reassign memory for write_buf in case length is greater than 32 bytes + */ + if (rmi4_data->write_buf_len < length + 1) { + devm_kfree(rmi4_data->pdev->dev.parent, rmi4_data->write_buf); + rmi4_data->write_buf = devm_kzalloc(rmi4_data->pdev->dev.parent, + length + 1, GFP_KERNEL); + if (!rmi4_data->write_buf) { + rmi4_data->write_buf_len = 0; + retval = -ENOMEM; + goto exit; + } + rmi4_data->write_buf_len = length + 1; + } + + /* Assign the write_buf of driver structure to i2c_msg buf */ + msg[0].buf = rmi4_data->write_buf; retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); if (retval != PAGE_SELECT_LEN) { @@ -153,8 +168,8 @@ static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, goto exit; } - buf[0] = addr & MASK_8BIT; - memcpy(&buf[1], &data[0], length); + rmi4_data->write_buf[0] = addr & MASK_8BIT; + memcpy(&rmi4_data->write_buf[1], &data[0], length); for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { if (i2c_transfer(i2c->adapter, msg, 1) == 1) { diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 5975d76ce755..a0ef57483ebb 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -926,7 +926,7 @@ again: next_tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE; left = (head - next_tail) % CMD_BUFFER_SIZE; - if (left <= 2) { + if (left <= 0x20) { struct iommu_cmd sync_cmd; volatile u64 sem = 0; int ret; diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 7caf2fa237f2..4831eb910fc7 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -809,8 +809,10 @@ int amd_iommu_init_device(struct pci_dev *pdev, int pasids) goto out_free_domain; group = iommu_group_get(&pdev->dev); - if (!group) + if (!group) { + ret = -EINVAL; goto out_free_domain; + } ret = iommu_attach_group(dev_state->domain, group); if (ret != 0) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 03a691723349..51159711b1d8 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -2451,7 +2451,7 @@ static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain) int ret; int dest_vmids = VMID_HLOS; int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC; - int source_vmlist[2] = {VMID_HLOS, smmu_domain->secure_vmid}; + int source_vmlist[2] = {smmu_domain->secure_vmid, VMID_HLOS}; struct arm_smmu_pte_info *pte_info, *temp; if (!arm_smmu_is_master_side_secure(smmu_domain)) diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 2acb9242bcf8..8c6364f03eac 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -25,6 +25,13 @@ #define FAST_PAGE_SIZE (1UL << FAST_PAGE_SHIFT) #define FAST_PAGE_MASK (~(PAGE_SIZE - 1)) #define FAST_PTE_ADDR_MASK ((av8l_fast_iopte)0xfffffffff000) +#define FAST_MAIR_ATTR_IDX_CACHE 1 +#define FAST_PTE_ATTRINDX_SHIFT 2 +#define FAST_PTE_ATTRINDX_MASK 0x7 +#define FAST_PTE_SH_SHIFT 8 +#define FAST_PTE_SH_MASK (((av8l_fast_iopte)0x3) << FAST_PTE_SH_SHIFT) +#define FAST_PTE_SH_OS (((av8l_fast_iopte)2) << FAST_PTE_SH_SHIFT) +#define FAST_PTE_SH_IS (((av8l_fast_iopte)3) << FAST_PTE_SH_SHIFT) static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, bool coherent) @@ -56,6 +63,36 @@ static void fast_dmac_clean_range(struct dma_fast_smmu_mapping *mapping, dmac_clean_range(start, end); } +static bool __fast_is_pte_coherent(av8l_fast_iopte *ptep) +{ + int attr_idx = (*ptep & (FAST_PTE_ATTRINDX_MASK << + FAST_PTE_ATTRINDX_SHIFT)) >> + FAST_PTE_ATTRINDX_SHIFT; + + if ((attr_idx == FAST_MAIR_ATTR_IDX_CACHE) && + (((*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_IS) || + (*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_OS)) + return true; + + return false; +} + +static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs) +{ + bool is_coherent; + + if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs)) + is_coherent = true; + else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs)) + is_coherent = false; + else if (is_device_dma_coherent(dev)) + is_coherent = true; + else + is_coherent = false; + + return is_coherent; +} + /* * Checks if the allocated range (ending at @end) covered the upcoming * stale bit. We don't need to know exactly where the range starts since @@ -315,7 +352,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page, int nptes = len >> FAST_PAGE_SHIFT; bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs); int prot = __fast_dma_direction_to_prot(dir); - bool is_coherent = is_device_dma_coherent(dev); + bool is_coherent = is_dma_coherent(dev, attrs); prot = __get_iommu_pgprot(attrs, prot, is_coherent); @@ -360,7 +397,7 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, int nptes = len >> FAST_PAGE_SHIFT; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs); - bool is_coherent = is_device_dma_coherent(dev); + bool is_coherent = is_dma_coherent(dev, attrs); if (!skip_sync && !is_coherent) __fast_dma_page_dev_to_cpu(page, offset, size, dir); @@ -381,7 +418,7 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev, unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); - if (!is_device_dma_coherent(dev)) + if (!__fast_is_pte_coherent(pmd)) __fast_dma_page_dev_to_cpu(page, offset, size, dir); } @@ -394,7 +431,7 @@ static void fast_smmu_sync_single_for_device(struct device *dev, unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); - if (!is_device_dma_coherent(dev)) + if (!__fast_is_pte_coherent(pmd)) __fast_dma_page_cpu_to_dev(page, offset, size, dir); } @@ -472,7 +509,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, struct sg_mapping_iter miter; unsigned int count = ALIGN(size, SZ_4K) >> PAGE_SHIFT; int prot = IOMMU_READ | IOMMU_WRITE; /* TODO: extract from attrs */ - bool is_coherent = is_device_dma_coherent(dev); + bool is_coherent = is_dma_coherent(dev, attrs); pgprot_t remap_prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent); struct page **pages; @@ -596,7 +633,7 @@ static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, unsigned long uaddr = vma->vm_start; struct page **pages; int i, nr_pages, ret = 0; - bool coherent = is_device_dma_coherent(dev); + bool coherent = is_dma_coherent(dev, attrs); vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, coherent); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 59e9abd3345e..f0fc6f7b5d98 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1993,6 +1993,25 @@ static int domain_context_mapping_one(struct dmar_domain *domain, if (context_present(context)) goto out_unlock; + /* + * For kdump cases, old valid entries may be cached due to the + * in-flight DMA and copied pgtable, but there is no unmapping + * behaviour for them, thus we need an explicit cache flush for + * the newly-mapped device. For kdump, at this point, the device + * is supposed to finish reset at its driver probe stage, so no + * in-flight DMA will exist, and we don't need to worry anymore + * hereafter. + */ + if (context_copied(context)) { + u16 did_old = context_domain_id(context); + + if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) + iommu->flush.flush_context(iommu, did_old, + (((u16)bus) << 8) | devfn, + DMA_CCMD_MASK_NOBIT, + DMA_CCMD_DEVICE_INVL); + } + pgd = domain->pgd; context_clear_entry(context); @@ -3219,13 +3238,14 @@ static int __init init_dmars(void) iommu_identity_mapping |= IDENTMAP_GFX; #endif + check_tylersburg_isoch(); + if (iommu_identity_mapping) { ret = si_domain_init(hw_pass_through); if (ret) goto free_iommu; } - check_tylersburg_isoch(); /* * If we copied translations from a previous kernel in the kdump @@ -5020,6 +5040,25 @@ static void intel_iommu_remove_device(struct device *dev) } #ifdef CONFIG_INTEL_IOMMU_SVM +#define MAX_NR_PASID_BITS (20) +static inline unsigned long intel_iommu_get_pts(struct intel_iommu *iommu) +{ + /* + * Convert ecap_pss to extend context entry pts encoding, also + * respect the soft pasid_max value set by the iommu. + * - number of PASID bits = ecap_pss + 1 + * - number of PASID table entries = 2^(pts + 5) + * Therefore, pts = ecap_pss - 4 + * e.g. KBL ecap_pss = 0x13, PASID has 20 bits, pts = 15 + */ + if (ecap_pss(iommu->ecap) < 5) + return 0; + + /* pasid_max is encoded as actual number of entries not the bits */ + return find_first_bit((unsigned long *)&iommu->pasid_max, + MAX_NR_PASID_BITS) - 5; +} + int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev) { struct device_domain_info *info; @@ -5052,7 +5091,9 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd if (!(ctx_lo & CONTEXT_PASIDE)) { context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table); - context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | ecap_pss(iommu->ecap); + context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | + intel_iommu_get_pts(iommu); + wmb(); /* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both * extended to permit requests-with-PASID if the PASIDE bit diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 5f2b66286c0c..6a8a9492c771 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -89,6 +89,7 @@ #define ARM_LPAE_PTE_TYPE_TABLE 3 #define ARM_LPAE_PTE_TYPE_PAGE 3 +#define ARM_LPAE_PTE_SH_MASK (((arm_lpae_iopte)0x3) << 8) #define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63) #define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53) #define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10) @@ -928,8 +929,9 @@ static bool __arm_lpae_is_iova_coherent(struct arm_lpae_io_pgtable *data, ARM_LPAE_PTE_ATTRINDX_SHIFT)) >> ARM_LPAE_PTE_ATTRINDX_SHIFT; if ((attr_idx == ARM_LPAE_MAIR_ATTR_IDX_CACHE) && - ((*ptep & ARM_LPAE_PTE_SH_IS) || - (*ptep & ARM_LPAE_PTE_SH_OS))) + (((*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_IS) + || + (*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_OS)) return true; } else { if (*ptep & ARM_LPAE_PTE_MEMATTR_OIWB) diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 0fea985ef1dc..d7af88534971 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -216,6 +216,31 @@ static int bcm7038_l1_set_affinity(struct irq_data *d, return 0; } +static void bcm7038_l1_cpu_offline(struct irq_data *d) +{ + struct cpumask *mask = irq_data_get_affinity_mask(d); + int cpu = smp_processor_id(); + cpumask_t new_affinity; + + /* This CPU was not on the affinity mask */ + if (!cpumask_test_cpu(cpu, mask)) + return; + + if (cpumask_weight(mask) > 1) { + /* + * Multiple CPU affinity, remove this CPU from the affinity + * mask + */ + cpumask_copy(&new_affinity, mask); + cpumask_clear_cpu(cpu, &new_affinity); + } else { + /* Only CPU, put on the lowest online CPU */ + cpumask_clear(&new_affinity); + cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); + } + irq_set_affinity_locked(d, &new_affinity, false); +} + static int __init bcm7038_l1_init_one(struct device_node *dn, unsigned int idx, struct bcm7038_l1_chip *intc) @@ -267,6 +292,7 @@ static struct irq_chip bcm7038_l1_irq_chip = { .irq_mask = bcm7038_l1_mask, .irq_unmask = bcm7038_l1_unmask, .irq_set_affinity = bcm7038_l1_set_affinity, + .irq_cpu_offline = bcm7038_l1_cpu_offline, }; static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index c40a9f3b0724..4ff2ee2609ca 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -804,6 +804,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, gic_write_irouter(val, reg); /* + * It is possible that irq is disabled from SW perspective only, + * because kernel takes lazy disable approach. Therefore check irq + * descriptor if it should kept disabled. + */ + if (irqd_irq_disabled(d)) + enabled = 0; + + /* * If the interrupt was enabled, enabled it again. Otherwise, * just wait for the distributor to have digested our changes. */ diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 2a506fe0c8a4..74bf1a17ae7c 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -762,8 +762,10 @@ static int __init ser_gigaset_init(void) driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, GIGASET_MODULENAME, GIGASET_DEVNAME, &ops, THIS_MODULE); - if (!driver) + if (!driver) { + rc = -ENOMEM; goto error; + } rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc); if (rc != 0) { diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index d7c286656a25..7b4ddf0a39ec 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -11304,7 +11304,8 @@ static void mixer_notify_update(PLCI *plci, byte others) ((CAPI_MSG *) msg)->header.ncci = 0; ((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT; ((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3; - PUT_WORD(&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE); + ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff; + ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8; ((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0; w = api_put(notify_plci->appl, (CAPI_MSG *) msg); if (w != _QUEUE_FULL) diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index 85a6be824485..817dfa3b2f53 100644 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -897,9 +897,10 @@ static int qpnp_mpp_set(struct qpnp_led_data *led) } } - if (led->mpp_cfg->pwm_mode != MANUAL_MODE) + if (led->mpp_cfg->pwm_mode != MANUAL_MODE) { pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev); - else { + led->mpp_cfg->pwm_cfg->pwm_enabled = 1; + } else { if (led->cdev.brightness < LED_MPP_CURRENT_MIN) led->cdev.brightness = LED_MPP_CURRENT_MIN; else { @@ -950,6 +951,7 @@ static int qpnp_mpp_set(struct qpnp_led_data *led) led->mpp_cfg->pwm_mode = led->mpp_cfg->pwm_cfg->default_mode; pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev); + led->mpp_cfg->pwm_cfg->pwm_enabled = 0; } rc = qpnp_led_masked_write(led, LED_MPP_MODE_CTRL(led->base), @@ -1606,7 +1608,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) dev_err(&led->pdev->dev, "pwm enable failed\n"); return rc; } - + led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1; set_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use); /* is_kpdbl_master_turn_on will be set to true when GPLED1 @@ -1642,6 +1644,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) "pwm enable failed\n"); return rc; } + led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1; } else { if (kpdbl_master) { pwm_disable(kpdbl_master); @@ -1660,6 +1663,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) is_kpdbl_master_turn_on = false; } else { pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev); + led->kpdbl_cfg->pwm_cfg->pwm_enabled = 0; clear_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use); if (bitmap_weight(kpdbl_leds_in_use, NUM_KPDBL_LEDS) == 1 && kpdbl_master && @@ -1727,20 +1731,17 @@ static int qpnp_rgb_set(struct qpnp_led_data *led) "Failed to write led enable reg\n"); return rc; } - - if (led->rgb_cfg->pwm_cfg->pwm_enabled) { - pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); - led->rgb_cfg->pwm_cfg->pwm_enabled = 0; - } - - rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev); - if (!rc) + if (!led->rgb_cfg->pwm_cfg->pwm_enabled) { + pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev); led->rgb_cfg->pwm_cfg->pwm_enabled = 1; + } } else { led->rgb_cfg->pwm_cfg->mode = led->rgb_cfg->pwm_cfg->default_mode; - pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); - led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + if (led->rgb_cfg->pwm_cfg->pwm_enabled) { + pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); + led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + } rc = qpnp_led_masked_write(led, RGB_LED_EN_CTL(led->base), led->rgb_cfg->enable, RGB_LED_DISABLE); @@ -2183,11 +2184,17 @@ static ssize_t pwm_us_store(struct device *dev, previous_pwm_us = pwm_cfg->pwm_period_us; pwm_cfg->pwm_period_us = pwm_us; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->pwm_period_us = previous_pwm_us; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2237,12 +2244,18 @@ static ssize_t pause_lo_store(struct device *dev, previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.lut_pause_lo = pause_lo; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2292,12 +2305,18 @@ static ssize_t pause_hi_store(struct device *dev, previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.lut_pause_hi = pause_hi; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2348,12 +2367,18 @@ static ssize_t start_idx_store(struct device *dev, previous_start_idx = pwm_cfg->duty_cycles->start_idx; pwm_cfg->duty_cycles->start_idx = start_idx; pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->duty_cycles->start_idx = previous_start_idx; pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2403,12 +2428,18 @@ static ssize_t ramp_step_ms_store(struct device *dev, previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2458,12 +2489,18 @@ static ssize_t lut_flags_store(struct device *dev, previous_lut_flags = pwm_cfg->lut_params.flags; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.flags = lut_flags; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.flags = previous_lut_flags; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2543,7 +2580,11 @@ static ssize_t duty_pcts_store(struct device *dev, pwm_cfg->old_duty_pcts = previous_duty_pcts; pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } + ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) goto restore; @@ -2558,7 +2599,10 @@ restore: pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts; pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts; pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); return ret; @@ -2588,7 +2632,10 @@ static void led_blink(struct qpnp_led_data *led, led->kpdbl_cfg->pwm_mode = pwm_cfg->default_mode; } - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (led->id == QPNP_ID_RGB_RED || led->id == QPNP_ID_RGB_GREEN || led->id == QPNP_ID_RGB_BLUE) { @@ -3541,8 +3588,11 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led, } rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->pdev, node); - if (rc < 0) + if (rc < 0) { + if (led->kpdbl_cfg->pwm_cfg->pwm_dev) + pwm_put(led->kpdbl_cfg->pwm_cfg->pwm_dev); return rc; + } rc = of_property_read_u32(node, "qcom,row-id", &val); if (!rc) @@ -3605,8 +3655,11 @@ static int qpnp_get_config_rgb(struct qpnp_led_data *led, } rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node); - if (rc < 0) + if (rc < 0) { + if (led->rgb_cfg->pwm_cfg->pwm_dev) + pwm_put(led->rgb_cfg->pwm_cfg->pwm_dev); return rc; + } return 0; } @@ -3729,8 +3782,11 @@ static int qpnp_get_config_mpp(struct qpnp_led_data *led, } rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->pdev, node); - if (rc < 0) + if (rc < 0) { + if (led->mpp_cfg->pwm_cfg && led->mpp_cfg->pwm_cfg->pwm_dev) + pwm_put(led->mpp_cfg->pwm_cfg->pwm_dev); goto err_config_mpp; + } return 0; diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 7d5aa2c5c81d..36ca4e4cbfb7 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -475,6 +475,21 @@ config DM_VERITY If unsure, say N. +config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + bool "Prefetch size 128" + +config DM_VERITY_HASH_PREFETCH_MIN_SIZE + int "Verity hash prefetch minimum size" + depends on DM_VERITY + range 1 4096 + default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + default 1 + ---help--- + This sets minimum number of hash blocks to prefetch for dm-verity. + For devices like eMMC, having larger prefetch size like 128 can improve + performance with increased memory consumption for keeping more hashes + in RAM. + config DM_VERITY_FEC bool "Verity forward error correction support" depends on DM_VERITY @@ -527,6 +542,7 @@ config DM_ANDROID_VERITY depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE depends on MD_LINEAR + select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 ---help--- This device-mapper target is virtually a VERITY target. This target is setup by reading the metadata contents piggybacked diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 6b420a55c745..c3ea03c9a1a8 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -425,7 +425,7 @@ struct cache { * until a gc finishes - otherwise we could pointlessly burn a ton of * cpu */ - unsigned invalidate_needs_gc:1; + unsigned invalidate_needs_gc; bool discard; /* Get rid of? */ @@ -593,8 +593,8 @@ struct cache_set { /* Counts how many sectors bio_insert has added to the cache */ atomic_t sectors_to_gc; + wait_queue_head_t gc_wait; - wait_queue_head_t moving_gc_wait; struct keybuf moving_gc_keys; /* Number of moving GC bios in flight */ struct semaphore moving_in_flight; diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 22b9e34ceb75..5b815e64c1c9 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1762,33 +1762,34 @@ static void bch_btree_gc(struct cache_set *c) bch_moving_gc(c); } -static int bch_gc_thread(void *arg) +static bool gc_should_run(struct cache_set *c) { - struct cache_set *c = arg; struct cache *ca; unsigned i; - while (1) { -again: - bch_btree_gc(c); + for_each_cache(ca, c, i) + if (ca->invalidate_needs_gc) + return true; - set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) - break; + if (atomic_read(&c->sectors_to_gc) < 0) + return true; - mutex_lock(&c->bucket_lock); + return false; +} - for_each_cache(ca, c, i) - if (ca->invalidate_needs_gc) { - mutex_unlock(&c->bucket_lock); - set_current_state(TASK_RUNNING); - goto again; - } +static int bch_gc_thread(void *arg) +{ + struct cache_set *c = arg; - mutex_unlock(&c->bucket_lock); + while (1) { + wait_event_interruptible(c->gc_wait, + kthread_should_stop() || gc_should_run(c)); - try_to_freeze(); - schedule(); + if (kthread_should_stop()) + break; + + set_gc_sectors(c); + bch_btree_gc(c); } return 0; @@ -1796,11 +1797,10 @@ again: int bch_gc_thread_start(struct cache_set *c) { - c->gc_thread = kthread_create(bch_gc_thread, c, "bcache_gc"); + c->gc_thread = kthread_run(bch_gc_thread, c, "bcache_gc"); if (IS_ERR(c->gc_thread)) return PTR_ERR(c->gc_thread); - set_task_state(c->gc_thread, TASK_INTERRUPTIBLE); return 0; } diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h index 5c391fa01bed..9b80417cd547 100644 --- a/drivers/md/bcache/btree.h +++ b/drivers/md/bcache/btree.h @@ -260,8 +260,7 @@ void bch_initial_mark_key(struct cache_set *, int, struct bkey *); static inline void wake_up_gc(struct cache_set *c) { - if (c->gc_thread) - wake_up_process(c->gc_thread); + wake_up(&c->gc_wait); } #define MAP_DONE 0 diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 25fa8445bb24..2410df1c2a05 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -196,10 +196,8 @@ static void bch_data_insert_start(struct closure *cl) struct data_insert_op *op = container_of(cl, struct data_insert_op, cl); struct bio *bio = op->bio, *n; - if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) { - set_gc_sectors(op->c); + if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) wake_up_gc(op->c); - } if (op->bypass) return bch_data_invalidate(cl); diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 3d5c0ba13181..7b5880b8874c 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1489,6 +1489,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb) mutex_init(&c->bucket_lock); init_waitqueue_head(&c->btree_cache_wait); init_waitqueue_head(&c->bucket_wait); + init_waitqueue_head(&c->gc_wait); sema_init(&c->uuid_write_mutex, 1); spin_lock_init(&c->btree_gc_time.lock); @@ -1547,6 +1548,7 @@ static void run_cache_set(struct cache_set *c) for_each_cache(ca, c, i) c->nbuckets += ca->sb.nbuckets; + set_gc_sectors(c); if (CACHE_SYNC(&c->sb)) { LIST_HEAD(journal); diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index bb9b92ebbf8e..0da5efaad85c 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -248,7 +248,7 @@ struct cache { /* * Fields for converting from sectors to blocks. */ - uint32_t sectors_per_block; + sector_t sectors_per_block; int sectors_per_block_shift; spinlock_t lock; @@ -3544,11 +3544,11 @@ static void cache_status(struct dm_target *ti, status_type_t type, residency = policy_residency(cache->policy); - DMEMIT("%u %llu/%llu %u %llu/%llu %u %u %u %u %u %u %lu ", + DMEMIT("%u %llu/%llu %llu %llu/%llu %u %u %u %u %u %u %lu ", (unsigned)DM_CACHE_METADATA_BLOCK_SIZE, (unsigned long long)(nr_blocks_metadata - nr_free_blocks_metadata), (unsigned long long)nr_blocks_metadata, - cache->sectors_per_block, + (unsigned long long)cache->sectors_per_block, (unsigned long long) from_cblock(residency), (unsigned long long) from_cblock(cache->cache_size), (unsigned) atomic_read(&cache->stats.read_hit), diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index e540b7942eba..799b9a5ad4f5 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1500,12 +1500,15 @@ static int crypt_set_key(struct crypt_config *cc, char *key) if (!cc->key_size && strcmp(key, "-")) goto out; + /* clear the flag since following operations may invalidate previously valid key */ + clear_bit(DM_CRYPT_KEY_VALID, &cc->flags); + if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0) goto out; - set_bit(DM_CRYPT_KEY_VALID, &cc->flags); - r = crypt_setkey_allcpus(cc); + if (!r) + set_bit(DM_CRYPT_KEY_VALID, &cc->flags); out: /* Hex key string not needed after here, so wipe it. */ diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index 8e9e928dafba..78f403b45ab3 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c @@ -200,11 +200,13 @@ static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (!(fc->up_interval + fc->down_interval)) { ti->error = "Total (up + down) interval is zero"; + r = -EINVAL; goto bad; } if (fc->up_interval + fc->down_interval < fc->up_interval) { ti->error = "Interval overflow"; + r = -EINVAL; goto bad; } diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 8289804ccd99..d5ea9f28ae70 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -175,6 +175,7 @@ static void dm_stat_free(struct rcu_head *head) int cpu; struct dm_stat *s = container_of(head, struct dm_stat, rcu_head); + kfree(s->histogram_boundaries); kfree(s->program_id); kfree(s->aux_data); for_each_possible_cpu(cpu) { diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 9d3d4b297201..c7e97cf6e7fb 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -501,6 +501,7 @@ static void verity_prefetch_io(struct work_struct *work) container_of(work, struct dm_verity_prefetch_work, work); struct dm_verity *v = pw->v; int i; + sector_t prefetch_size; for (i = v->levels - 2; i >= 0; i--) { sector_t hash_block_start; @@ -523,8 +524,14 @@ static void verity_prefetch_io(struct work_struct *work) hash_block_end = v->hash_blocks - 1; } no_prefetch_cluster: + // for emmc, it is more efficient to send bigger read + prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE, + hash_block_end - hash_block_start + 1); + if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) { + prefetch_size = hash_block_end - hash_block_start + 1; + } dm_bufio_prefetch(v->bufio, hash_block_start, - hash_block_end - hash_block_start + 1); + prefetch_size); } kfree(pw); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index dd1cde5458b8..e9b34de2319e 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1467,11 +1467,62 @@ void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors) } EXPORT_SYMBOL_GPL(dm_accept_partial_bio); +/* + * Flush current->bio_list when the target map method blocks. + * This fixes deadlocks in snapshot and possibly in other targets. + */ +struct dm_offload { + struct blk_plug plug; + struct blk_plug_cb cb; +}; + +static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule) +{ + struct dm_offload *o = container_of(cb, struct dm_offload, cb); + struct bio_list list; + struct bio *bio; + + INIT_LIST_HEAD(&o->cb.list); + + if (unlikely(!current->bio_list)) + return; + + list = *current->bio_list; + bio_list_init(current->bio_list); + + while ((bio = bio_list_pop(&list))) { + struct bio_set *bs = bio->bi_pool; + if (unlikely(!bs) || bs == fs_bio_set) { + bio_list_add(current->bio_list, bio); + continue; + } + + spin_lock(&bs->rescue_lock); + bio_list_add(&bs->rescue_list, bio); + queue_work(bs->rescue_workqueue, &bs->rescue_work); + spin_unlock(&bs->rescue_lock); + } +} + +static void dm_offload_start(struct dm_offload *o) +{ + blk_start_plug(&o->plug); + o->cb.callback = flush_current_bio_list; + list_add(&o->cb.list, ¤t->plug->cb_list); +} + +static void dm_offload_end(struct dm_offload *o) +{ + list_del(&o->cb.list); + blk_finish_plug(&o->plug); +} + static void __map_bio(struct dm_target_io *tio) { int r; sector_t sector; struct mapped_device *md; + struct dm_offload o; struct bio *clone = &tio->clone; struct dm_target *ti = tio->ti; @@ -1484,7 +1535,11 @@ static void __map_bio(struct dm_target_io *tio) */ atomic_inc(&tio->io->io_count); sector = clone->bi_iter.bi_sector; + + dm_offload_start(&o); r = ti->type->map(ti, clone); + dm_offload_end(&o); + if (r == DM_MAPIO_REMAPPED) { /* the bio has been remapped so dispatch it */ diff --git a/drivers/md/linear.c b/drivers/md/linear.c index b7fe7e9fc777..6ba3227e29b2 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -52,18 +52,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) return conf->disks + lo; } +/* + * In linear_congested() conf->raid_disks is used as a copy of + * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks + * and conf->disks[] are created in linear_conf(), they are always + * consitent with each other, but mddev->raid_disks does not. + */ static int linear_congested(struct mddev *mddev, int bits) { struct linear_conf *conf; int i, ret = 0; - conf = mddev->private; + rcu_read_lock(); + conf = rcu_dereference(mddev->private); - for (i = 0; i < mddev->raid_disks && !ret ; i++) { + for (i = 0; i < conf->raid_disks && !ret ; i++) { struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); ret |= bdi_congested(&q->backing_dev_info, bits); } + rcu_read_unlock(); return ret; } @@ -143,6 +151,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) conf->disks[i-1].end_sector + conf->disks[i].rdev->sectors; + /* + * conf->raid_disks is copy of mddev->raid_disks. The reason to + * keep a copy of mddev->raid_disks in struct linear_conf is, + * mddev->raid_disks may not be consistent with pointers number of + * conf->disks[] when it is updated in linear_add() and used to + * iterate old conf->disks[] earray in linear_congested(). + * Here conf->raid_disks is always consitent with number of + * pointers in conf->disks[] array, and mddev->private is updated + * with rcu_assign_pointer() in linear_addr(), such race can be + * avoided. + */ + conf->raid_disks = raid_disks; + return conf; out: @@ -195,15 +216,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) if (!newconf) return -ENOMEM; + /* newconf->raid_disks already keeps a copy of * the increased + * value of mddev->raid_disks, WARN_ONCE() is just used to make + * sure of this. It is possible that oldconf is still referenced + * in linear_congested(), therefore kfree_rcu() is used to free + * oldconf until no one uses it anymore. + */ mddev_suspend(mddev); - oldconf = mddev->private; + oldconf = rcu_dereference(mddev->private); mddev->raid_disks++; - mddev->private = newconf; + WARN_ONCE(mddev->raid_disks != newconf->raid_disks, + "copied raid_disks doesn't match mddev->raid_disks"); + rcu_assign_pointer(mddev->private, newconf); md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); set_capacity(mddev->gendisk, mddev->array_sectors); mddev_resume(mddev); revalidate_disk(mddev->gendisk); - kfree(oldconf); + kfree_rcu(oldconf, rcu); return 0; } diff --git a/drivers/md/linear.h b/drivers/md/linear.h index b685ddd7d7f7..8d392e6098b3 100644 --- a/drivers/md/linear.h +++ b/drivers/md/linear.h @@ -10,6 +10,7 @@ struct linear_conf { struct rcu_head rcu; sector_t array_sectors; + int raid_disks; /* a copy of mddev->raid_disks */ struct dev_info disks[0]; }; #endif diff --git a/drivers/md/md.c b/drivers/md/md.c index c1c7d4fb4b77..eff554a12fb4 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6771,7 +6771,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, /* need to ensure recovery thread has run */ wait_event_interruptible_timeout(mddev->sb_wait, !test_bit(MD_RECOVERY_NEEDED, - &mddev->flags), + &mddev->recovery), msecs_to_jiffies(5000)); if (cmd == STOP_ARRAY || cmd == STOP_ARRAY_RO) { /* Need to flush page cache, and ensure no-one else opens diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index 7e44005595c1..20557e2c60c6 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c @@ -775,17 +775,15 @@ int dm_sm_metadata_create(struct dm_space_map *sm, memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm)); r = sm_ll_new_metadata(&smm->ll, tm); + if (!r) { + if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS) + nr_blocks = DM_SM_METADATA_MAX_BLOCKS; + r = sm_ll_extend(&smm->ll, nr_blocks); + } + memcpy(&smm->sm, &ops, sizeof(smm->sm)); if (r) return r; - if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS) - nr_blocks = DM_SM_METADATA_MAX_BLOCKS; - r = sm_ll_extend(&smm->ll, nr_blocks); - if (r) - return r; - - memcpy(&smm->sm, &ops, sizeof(smm->sm)); - /* * Now we need to update the newly created data structures with the * allocated blocks that they were built from. diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 10ce885445f6..7af976934441 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -6980,6 +6980,15 @@ static int run(struct mddev *mddev) stripe = (stripe | (stripe-1)) + 1; mddev->queue->limits.discard_alignment = stripe; mddev->queue->limits.discard_granularity = stripe; + + /* + * We use 16-bit counter of active stripes in bi_phys_segments + * (minus one for over-loaded initialization) + */ + blk_queue_max_hw_sectors(mddev->queue, 0xfffe * STRIPE_SECTORS); + blk_queue_max_discard_sectors(mddev->queue, + 0xfffe * STRIPE_SECTORS); + /* * unaligned part of discard request will be ignored, so can't * guarantee discard_zeroes_data diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1700447afe43..ba3287d176af 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -619,6 +619,7 @@ config VIDEO_S5K6A3 config VIDEO_S5K4ECGX tristate "Samsung S5K4ECGX sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select CRC32 ---help--- This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M camera sensor with an embedded SoC image signal processor. diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index c4307ad8594c..e543cbbf2ec4 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -2168,11 +2168,12 @@ static int dvb_register(struct cx23885_tsport *port) } port->i2c_client_tuner = client_tuner; break; - case CX23885_BOARD_HAUPPAUGE_HVR5525: - switch (port->nr) { + case CX23885_BOARD_HAUPPAUGE_HVR5525: { struct m88rs6000t_config m88rs6000t_config; struct a8293_platform_data a8293_pdata = {}; + switch (port->nr) { + /* port b - satellite */ case 1: /* attach frontend */ @@ -2267,6 +2268,7 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + } default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index 173daf0c0847..14fa7e40f2a6 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -1,6 +1,6 @@ config DVB_DM1105 tristate "SDMC DM1105 based PCI cards" - depends on DVB_CORE && PCI && I2C + depends on DVB_CORE && PCI && I2C && I2C_ALGOBIT select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 4ab6586c0467..f53e59e9c0ea 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -286,7 +286,10 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) { + u16 val; + writel(data, solo_dev->reg_base + reg); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); } static inline void solo_irq_on(struct solo_dev *dev, u32 mask) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index ba780c45f645..572bc043b62d 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1576,7 +1576,7 @@ static int vpfe_s_fmt(struct file *file, void *priv, return -EBUSY; } - ret = vpfe_try_fmt(file, priv, &format); + ret = __vpfe_get_format(vpfe, &format, &bpp); if (ret) return ret; diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index cff63e511e6d..b8f3d9fa66e9 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -214,6 +214,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) if (params->dlen > 24 || params->dlen <= 0) return -EINVAL; pctrl = devm_pinctrl_get(ppi->dev); + if (IS_ERR(pctrl)) + return PTR_ERR(pctrl); pstate = pinctrl_lookup_state(pctrl, pin_state[(params->dlen + 7) / 8 - 1]); if (pinctrl_select_state(pctrl, pstate)) diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index 3985df780216..df0664b496ba 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -486,6 +486,9 @@ static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh, if (WARN_ON(!k_ioctl || !pvdev)) return -EIO; + if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD) + return -EINVAL; + switch (k_ioctl->id) { case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { struct msm_camera_return_buf ptr, *tmp = NULL; diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c index 01b8e0878ea9..9cdcabb762c0 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c @@ -342,8 +342,14 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr) mapping->ion_fd); } } - pr_err("Cannot find vaddr:%pK in SMMU. %s uses invalid virtual address\n", - vaddr, iommu_cb_set.cb_info[idx].name); + if (!strcmp(iommu_cb_set.cb_info[idx].name, "vfe")) + pr_err_ratelimited("Cannot find vaddr:%pK in SMMU.\n" + " %s uses invalid virtual address\n", + vaddr, iommu_cb_set.cb_info[idx].name); + else + pr_err("Cannot find vaddr:%pK in SMMU.\n" + " %s uses invalid virtual address\n", + vaddr, iommu_cb_set.cb_info[idx].name); return; } diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c index 34fffa8dd7ce..a0c606ce9a29 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -376,18 +376,17 @@ 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) { + if (clk_rate <= 0) { pr_err("%s round rate failed\n", clk_info[i].clk_name); goto cam_clk_set_err; } - rc = clk_set_rate(clk_ptr[i], - clk_rate); - if (rc < 0) { - pr_err("%s set rate failed\n", - clk_info[i].clk_name); - goto cam_clk_set_err; - } + } + rc = clk_set_rate(clk_ptr[i], clk_rate); + if (rc < 0) { + pr_err("%s set rate failed\n", + clk_info[i].clk_name); + goto cam_clk_set_err; } } rc = clk_prepare_enable(clk_ptr[i]); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index 0325c5ded3cf..45c2dd588bc9 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -162,6 +162,8 @@ struct msm_vfe_irq_ops { void (*config_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, enum msm_isp_irq_operation); + void (*preprocess_camif_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0); }; struct msm_vfe_axi_ops { @@ -282,7 +284,7 @@ struct msm_vfe_stats_ops { void (*update_ping_pong_addr)(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr); + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size); uint32_t (*get_frame_id)(struct vfe_device *vfe_dev); uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1); @@ -494,6 +496,7 @@ struct msm_vfe_src_info { struct timeval time_stamp; enum msm_vfe_dual_hw_type dual_hw_type; struct msm_vfe_dual_hw_ms_info dual_hw_ms_info; + bool accept_frame; }; struct msm_vfe_fetch_engine_info { @@ -767,6 +770,7 @@ struct vfe_device { struct msm_cam_clk_info *hvx_clk_info; size_t num_hvx_clk; size_t num_norm_clk; + bool hvx_clk_state; enum cam_ahb_clk_vote ahb_vote; struct cx_ipeak_client *vfe_cx_ipeak; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c index bf18fc59585c..caf69af25601 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1305,7 +1305,7 @@ static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status, - dma_addr_t paddr) + dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index 8e549c338bdd..f5533fd9062e 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2114,7 +2114,7 @@ static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe40_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, @@ -2218,6 +2218,7 @@ struct msm_vfe_hardware_info vfe40_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe40_process_epoch_irq, .config_irq = msm_vfe40_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe40_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index 957cbc292be3..c85bf1655b8c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1720,7 +1720,7 @@ static void msm_vfe44_stats_update_cgc_override(struct vfe_device *vfe_dev, static void msm_vfe44_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, @@ -1825,6 +1825,7 @@ struct msm_vfe_hardware_info vfe44_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe44_process_epoch_irq, .config_irq = msm_vfe44_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe44_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index cc768db875db..72ce32940c29 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1798,7 +1798,7 @@ static void msm_vfe46_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe46_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, @@ -1902,6 +1902,7 @@ struct msm_vfe_hardware_info vfe46_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe46_process_epoch_irq, .config_irq = msm_vfe46_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe46_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index b704e84cc140..9c74695a6e32 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -386,6 +386,7 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( vfe_dev, 0); + msm_vfe47_configure_hvx(vfe_dev, 0); vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); } @@ -488,7 +489,7 @@ void msm_vfe47_process_violation_status( return; } - pr_err("%s: VFE pipeline violation status %d\n", __func__, + pr_err_ratelimited("%s: VFE pipeline violation status %d\n", __func__, violation_status); } @@ -685,6 +686,15 @@ void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev, } } +void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0) +{ + if (irq_status0 & BIT(1)) + vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false; + if (irq_status0 & BIT(0)) + vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true; +} + void msm_vfe47_reg_update(struct vfe_device *vfe_dev, enum msm_vfe_input_src frame_src) { @@ -1508,16 +1518,19 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev, pr_err("%s: no stream_clk\n", __func__); return; } - rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, vfe_dev->hvx_clk_info, - vfe_dev->hvx_clk, vfe_dev->num_hvx_clk, is_stream_on); - if (rc) { - pr_err("%s: stream_clk enable failed, enable: %u\n", - __func__, - is_stream_on); - return; - } - if (is_stream_on == 1) { + if (is_stream_on) { /* Enable HVX */ + if (!vfe_dev->hvx_clk_state) { + rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, + vfe_dev->hvx_clk_info, vfe_dev->hvx_clk, + vfe_dev->num_hvx_clk, is_stream_on); + if (rc) { + pr_err("%s: stream_clk enable failed\n", + __func__); + return; + } + vfe_dev->hvx_clk_state = true; + } val = msm_camera_io_r(vfe_dev->vfe_base + 0x50); val |= (1 << 3); msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50); @@ -1527,6 +1540,17 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev, msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50); } else { /* Disable HVX */ + if (!vfe_dev->hvx_clk_state) + return; + rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, + vfe_dev->hvx_clk_info, vfe_dev->hvx_clk, + vfe_dev->num_hvx_clk, is_stream_on); + if (rc) { + pr_err("%s: stream_clk disable failed\n", + __func__); + return; + } + vfe_dev->hvx_clk_state = false; val = msm_camera_io_r(vfe_dev->vfe_base + 0x50); val &= 0xFFFFFFF7; msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50); @@ -1895,7 +1919,7 @@ void msm_vfe47_update_ping_pong_addr( if (buf_size < 0) buf_size = 0; - paddr32_max = (paddr + buf_size) & 0xFFFFFFC0; + paddr32_max = (paddr + buf_size) & 0xFFFFFFE0; msm_camera_io_w(paddr32, vfe_base + VFE47_PING_PONG_BASE(wm_idx, pingpong_bit)); @@ -2383,18 +2407,23 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev, void msm_vfe47_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, stream_info); uint32_t paddr32 = (paddr & 0xFFFFFFFF); + uint32_t paddr32_max; int stats_idx; stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]); msm_camera_io_w(paddr32, vfe_base + VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status)); + + paddr32_max = (paddr + buf_size) & 0xFFFFFFE0; + msm_camera_io_w(paddr32_max, vfe_base + + VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status) + 0x4); } uint32_t msm_vfe47_stats_get_wm_mask( @@ -2528,6 +2557,7 @@ int msm_vfe47_get_clks(struct vfe_device *vfe_dev) vfe_dev->hvx_clk_info = &vfe_dev->vfe_clk_info[vfe_dev->num_clk-1]; vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1]; + vfe_dev->hvx_clk_state = false; } for (i = 0; i < vfe_dev->num_clk; i++) { @@ -2553,8 +2583,8 @@ void msm_vfe47_put_clks(struct vfe_device *vfe_dev) int msm_vfe47_enable_clks(struct vfe_device *vfe_dev, int enable) { return msm_camera_clk_enable(&vfe_dev->pdev->dev, - vfe_dev->vfe_clk_info, - vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable); + vfe_dev->vfe_clk_info, + vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable); } int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) @@ -2576,10 +2606,10 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) */ if ((vfe_dev->vfe_cx_ipeak) && (vfe_dev->msm_isp_vfe_clk_rate >= - vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO] + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] [vfe_dev->hw_info->vfe_clk_idx]) && prev_clk_rate < - vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO] + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] [vfe_dev->hw_info->vfe_clk_idx]) { ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true); if (ret) { @@ -2599,10 +2629,10 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) */ if ((vfe_dev->vfe_cx_ipeak) && (vfe_dev->msm_isp_vfe_clk_rate < - vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO] + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] [vfe_dev->hw_info->vfe_clk_idx]) && prev_clk_rate >= - vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO] + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] [vfe_dev->hw_info->vfe_clk_idx]) { ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false); if (ret) { @@ -2915,6 +2945,7 @@ struct msm_vfe_hardware_info vfe47_hw_info = { .process_epoch_irq = msm_vfe47_process_epoch_irq, .config_irq = msm_vfe47_config_irq, .read_irq_status = msm_vfe47_read_irq_status, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe47_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h index 22a1a21bce9a..60afe3a80091 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,6 +42,8 @@ void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev, void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, struct msm_isp_timestamp *ts); +void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0); void msm_vfe47_reg_update(struct vfe_device *vfe_dev, enum msm_vfe_input_src frame_src); long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev, @@ -148,7 +150,7 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev, uint32_t stats_mask, uint8_t enable); void msm_vfe47_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr); + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size); uint32_t msm_vfe47_stats_get_wm_mask( uint32_t irq_status0, uint32_t irq_status1); uint32_t msm_vfe47_stats_get_comp_mask( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c index 5b4d6aa63055..e3d8ecb410ff 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c @@ -237,6 +237,7 @@ static int msm_vfe48_get_clks(struct vfe_device *vfe_dev) vfe_dev->hvx_clk_info = &vfe_dev->vfe_clk_info[vfe_dev->num_clk-1]; vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1]; + vfe_dev->hvx_clk_state = false; } for (i = 0; i < vfe_dev->num_clk; i++) { @@ -368,6 +369,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe47_process_epoch_irq, .config_irq = msm_vfe47_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe47_axi_reload_wm, 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 8488405b561a..b44b7573e0e6 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 @@ -948,6 +948,8 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev, uint32_t pingpong_status = 0, pingpong_bit = 0; struct msm_isp_buffer *done_buf = NULL; int vfe_idx = -1; + /* initialize pd_buf_idx with an invalid index 0xF */ + vfe_dev->pd_buf_idx = 0xF; if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2) return; @@ -1171,7 +1173,7 @@ void msm_isp_get_avtimer_ts( rc = avcs_core_query_timer(&avtimer_tick); if (rc < 0) { - pr_err("%s: Error: Invalid AVTimer Tick, rc=%d\n", + pr_err_ratelimited("%s: Error: Invalid AVTimer Tick, rc=%d\n", __func__, rc); /* In case of error return zero AVTimer Tick Value */ time_stamp->vt_time.tv_sec = 0; @@ -2439,7 +2441,7 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) total_bandwidth += stream_info->bandwidth[ vfe_idx]; - stream_info->state = PAUSING; + stream_info->state = PAUSED; } spin_unlock_irqrestore(&stream_info->lock, flags); } @@ -2453,7 +2455,7 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) msm_isp_get_stream_common_data(vfe_dev, ab_ib_vote->stream_src[i]); spin_lock_irqsave(&stream_info->lock, flags); - if (stream_info->state == PAUSING) { + if (stream_info->state == PAUSED) { vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); @@ -2811,6 +2813,7 @@ static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info, case RESUMING: case RESUME_PENDING: case ACTIVE: + case PAUSED: if (cmd != 0) return -EALREADY; break; @@ -2877,9 +2880,11 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, * those state transitions instead of directly forcing stream to * be INACTIVE */ - while (stream_info->state != ACTIVE) - __msm_isp_axi_stream_update(stream_info, + if (stream_info->state != PAUSED) { + while (stream_info->state != ACTIVE) + __msm_isp_axi_stream_update(stream_info, ×tamp); + } msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG); msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); stream_info->undelivered_request_cnt = 0; @@ -2892,8 +2897,15 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, vfe_dev->hw_info->vfe_ops.axi_ops. clear_wm_irq_mask(vfe_dev, stream_info); } - init_completion(&stream_info->inactive_comp); - stream_info->state = STOP_PENDING; + if (stream_info->state == ACTIVE) { + init_completion(&stream_info->inactive_comp); + stream_info->state = STOP_PENDING; + } else if (stream_info->state == PAUSED) { + /* don't wait for reg update */ + stream_info->state = STOP_PENDING; + msm_isp_axi_stream_enable_cfg(stream_info); + stream_info->state = INACTIVE; + } spin_unlock_irqrestore(&stream_info->lock, flags); } @@ -3321,6 +3333,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, int k; uint32_t wm_mask = 0; int vfe_idx; + uint32_t pingpong_bit = 0; if (!vfe_dev || !stream_info) { pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__, @@ -3339,27 +3352,40 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } frame_src = SRC_TO_INTF(stream_info->stream_src); + pingpong_status = vfe_dev->hw_info-> + vfe_ops.axi_ops.get_pingpong_status(vfe_dev); /* * If PIX stream is active then RDI path uses SOF frame ID of PIX * In case of standalone RDI streaming, SOF are used from * individual intf. */ - if (((vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <= - vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id)) || - ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <= - vfe_dev->axi_data.src_info[frame_src].frame_id)) || - stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { - pr_debug("%s:%d invalid request_frame %d cur frame id %d pix %d\n", + /* + * If frame_id = 1 then no eof check is needed + */ + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active && + vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) { + pr_debug("%s:%d invalid time to request frame %d\n", + __func__, __LINE__, frame_id); + goto error; + } + if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id != + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev-> + axi_data.src_info[VFE_PIX_0].sof_counter_step)) || + ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id != + vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> + axi_data.src_info[frame_src].sof_counter_step))) { + pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n", __func__, __LINE__, frame_id, vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id, vfe_dev->axi_data.src_info[VFE_PIX_0].active); - - rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, - user_stream_id, frame_id, buf_index, frame_src); - if (rc < 0) - pr_err("%s:%d failed: return_empty_buffer src %d\n", - __func__, __LINE__, frame_src); - return 0; + goto error; + } + if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { + pr_debug("%s:%d invalid undelivered_request_cnt %d frame id %d\n", + __func__, __LINE__, + stream_info->undelivered_request_cnt, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + goto error; } if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt && MSM_VFE_STREAM_STOP_PERIOD != @@ -3381,6 +3407,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } spin_lock_irqsave(&stream_info->lock, flags); + vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); + if (stream_info->undelivered_request_cnt == 1) { + pingpong_status = + vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( + vfe_dev); + pingpong_bit = ((pingpong_status >> + stream_info->wm[vfe_idx][0]) & 0x1); + if (stream_info->sw_ping_pong_bit == !pingpong_bit) { + ISP_DBG("%s:Return Empty Buffer stream id 0x%X\n", + __func__, stream_info->stream_id); + rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, + user_stream_id, frame_id, buf_index, + frame_src); + spin_unlock_irqrestore(&stream_info->lock, + flags); + return 0; + } + } + queue_req = &stream_info->request_queue_cmd[stream_info->request_q_idx]; if (queue_req->cmd_used) { spin_unlock_irqrestore(&stream_info->lock, flags); @@ -3410,7 +3455,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, stream_info->request_q_cnt++; stream_info->undelivered_request_cnt++; - vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx]; stream_cfg_cmd.frame_skip_pattern = NO_SKIP; stream_cfg_cmd.init_frame_drop = 0; @@ -3439,9 +3483,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } stream_info->sw_ping_pong_bit = 0; } else if (stream_info->undelivered_request_cnt == 2) { - pingpong_status = - vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( - vfe_dev); rc = msm_isp_cfg_ping_pong_address( stream_info, pingpong_status); if (rc) { @@ -3467,6 +3508,14 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, spin_unlock_irqrestore(&stream_info->lock, flags); return rc; +error: + rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, + user_stream_id, frame_id, buf_index, frame_src); + if (rc < 0) + pr_err("%s:%d failed: return_empty_buffer src %d\n", + __func__, __LINE__, frame_src); + return 0; + } static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index 9ce2218d5e0d..f2cf4d477b3f 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -23,7 +23,8 @@ static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev, { vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr( vfe_dev, stream_info, - pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr); + pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr, + SZ_32M); } static inline void msm_isp_stats_cfg_stream_scratch( @@ -123,7 +124,8 @@ static int msm_isp_stats_cfg_ping_pong_address( vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr( vfe_dev, stream_info, pingpong_status, buf->mapped_info[0].paddr + - stream_info->buffer_offset[k]); + stream_info->buffer_offset[k], + buf->mapped_info[0].len); } stream_info->buf[pingpong_bit] = buf; buf->pingpong_bit = pingpong_bit; @@ -832,6 +834,12 @@ static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info; int k; + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); @@ -961,6 +969,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev, int vfe_idx; uint32_t stats_idx[MSM_ISP_STATS_MAX]; + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } memset(stats_idx, 0, sizeof(stats_idx)); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); @@ -1074,7 +1087,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0}; uint32_t num_stats_comp_mask = 0; struct msm_vfe_stats_stream *stream_info; - struct msm_vfe_stats_shared_data *stats_data; + struct msm_vfe_stats_shared_data *stats_data = NULL; int num_stream = 0; struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX]; struct msm_isp_timestamp timestamp; @@ -1136,10 +1149,12 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, comp_stats_mask[stream_info->composite_flag-1] |= 1 << idx; - ISP_DBG("%s: stats_mask %x %x active streams %d\n", + ISP_DBG("%s: stats_mask %x %x\n", __func__, comp_stats_mask[0], - comp_stats_mask[1], - stats_data->num_active_stream); + comp_stats_mask[1]); + if (stats_data) + ISP_DBG("%s: active_streams = %d\n", __func__, + stats_data->num_active_stream); streams[num_stream++] = stream_info; } diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 507198721ccc..f4e81c02208c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -2092,7 +2092,10 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data) __func__, vfe_dev->pdev->id); return IRQ_HANDLED; } - + if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) { + vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq( + vfe_dev, irq_status0); + } if (msm_isp_process_overflow_irq(vfe_dev, &irq_status0, &irq_status1, 0)) { /* if overflow initiated no need to handle the interrupts */ @@ -2139,7 +2142,7 @@ void msm_isp_do_tasklet(unsigned long data) while (atomic_read(&vfe_dev->irq_cnt)) { spin_lock_irqsave(&vfe_dev->tasklet_lock, flags); - queue_cmd = list_first_entry(&vfe_dev->tasklet_q, + queue_cmd = list_first_entry_or_null(&vfe_dev->tasklet_q, struct msm_vfe_tasklet_queue_cmd, list); if (!queue_cmd) { atomic_set(&vfe_dev->irq_cnt, 0); diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 1628c098622f..41d8ef577a27 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -73,7 +73,7 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif) static inline int msm_ispif_is_intf_valid(uint32_t csid_version, - uint8_t intf_type) + enum msm_ispif_vfe_intf intf_type) { return ((csid_version <= CSID_VERSION_V22 && intf_type != VFE0) || (intf_type >= VFE_MAX)) ? false : true; @@ -1662,7 +1662,7 @@ static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd, static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ispif_device *ispif = v4l2_get_subdevdata(sd); - int rc; + int rc = 0; mutex_lock(&ispif->mutex); if (0 == ispif->open_cnt) { diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c index de27e585f63d..06e3ee4c353b 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -261,7 +261,7 @@ static int msm_jpeg_init_dev(struct platform_device *pdev) goto fail_4; } - platform_set_drvdata(pdev, &msm_jpeg_device_p); + platform_set_drvdata(pdev, msm_jpeg_device_p); JPEG_DBG("%s %s%d: success\n", __func__, MSM_JPEG_NAME, pdev->id); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c index 88d90d0a7c08..994ba93907e3 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c @@ -1759,7 +1759,7 @@ void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma) */ static int msm_jpegdma_hw_attach_iommu(struct msm_jpegdma_device *dma) { - int ret; + int ret = -EINVAL; mutex_lock(&dma->lock); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index be266641a105..214dd6f3406d 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -1069,7 +1069,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s:%d csiphy config_vreg failed\n", __func__, __LINE__); - goto csiphy_resource_fail; + goto csiphy_vreg_config_fail; } rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, @@ -1089,7 +1089,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s: csiphy clk enable failed\n", __func__); csiphy_dev->ref_count--; - goto csiphy_resource_fail; + goto csiphy_enable_clk_fail; } CDBG("%s:%d called\n", __func__, __LINE__); @@ -1117,12 +1117,17 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) csiphy_dev->csiphy_state = CSIPHY_POWER_UP; return 0; +csiphy_enable_clk_fail: + msm_camera_enable_vreg(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_vreg, + csiphy_dev->regulator_count, NULL, 0, + &csiphy_dev->csiphy_reg_ptr[0], 0); top_vreg_enable_failed: msm_camera_config_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count, NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0); -csiphy_resource_fail: +csiphy_vreg_config_fail: if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); @@ -1169,7 +1174,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s:%d csiphy config_vreg failed\n", __func__, __LINE__); - goto csiphy_resource_fail; + goto csiphy_vreg_config_fail; } rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, @@ -1189,7 +1194,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s: csiphy clk enable failed\n", __func__); csiphy_dev->ref_count--; - goto csiphy_resource_fail; + goto csiphy_enable_clk_fail; } CDBG("%s:%d clk enable success\n", __func__, __LINE__); @@ -1213,12 +1218,18 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) csiphy_dev->hw_version); csiphy_dev->csiphy_state = CSIPHY_POWER_UP; return 0; + +csiphy_enable_clk_fail: + msm_camera_enable_vreg(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_vreg, + csiphy_dev->regulator_count, NULL, 0, + &csiphy_dev->csiphy_reg_ptr[0], 0); top_vreg_enable_failed: msm_camera_config_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count, NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0); -csiphy_resource_fail: +csiphy_vreg_config_fail: if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index ef0ef1512211..2fc3ce406c27 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -3969,6 +3969,18 @@ static int mpq_dmx_process_video_packet_framing( mpq_dmx_update_decoder_stat(mpq_feed); + if (video_b_frame_events == 1) { + if (non_predicted_video_frame == 0) { + struct dmx_pts_dts_info *pts_dts; + + pts_dts = + &meta_data.info.framing.pts_dts_info; + pts_dts->pts_exist = 0; + pts_dts->pts = 0; + pts_dts->dts_exist = 0; + pts_dts->dts = 0; + } + } /* * Write meta-data that includes the framing information */ @@ -3986,14 +3998,7 @@ static int mpq_dmx_process_video_packet_framing( stream_buffer, &data, ret); /* Trigger ES Data Event for VPTS */ - if (video_b_frame_events == 1) { - if (non_predicted_video_frame == 1) - feed->data_ready_cb.ts - (&feed->feed.ts, &data); - } else { - feed->data_ready_cb.ts(&feed->feed.ts, - &data); - } + feed->data_ready_cb.ts(&feed->feed.ts, &data); if (feed_data->video_buffer->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index e170c9ffafc7..0cd8e613c224 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -1924,8 +1924,13 @@ static long sde_rotator_private_ioctl(struct file *file, void *fh, static long sde_rotator_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { + struct video_device *vdev = video_devdata(file); + struct sde_rotator_ctx *ctx = + sde_rotator_ctx_from_fh(file->private_data); long ret; + mutex_lock(vdev->lock); + switch (cmd) { case VIDIOC_S_SDE_ROTATOR_FENCE: case VIDIOC_G_SDE_ROTATOR_FENCE: @@ -1934,14 +1939,14 @@ static long sde_rotator_compat_ioctl32(struct file *file, if (copy_from_user(&fence, (void __user *)arg, sizeof(struct msm_sde_rotator_fence))) - return -EFAULT; + goto ioctl32_error; ret = sde_rotator_private_ioctl(file, file->private_data, 0, cmd, (void *)&fence); if (copy_to_user((void __user *)arg, &fence, sizeof(struct msm_sde_rotator_fence))) - return -EFAULT; + goto ioctl32_error; break; } @@ -1952,24 +1957,31 @@ static long sde_rotator_compat_ioctl32(struct file *file, if (copy_from_user(&comp_ratio, (void __user *)arg, sizeof(struct msm_sde_rotator_comp_ratio))) - return -EFAULT; + goto ioctl32_error; ret = sde_rotator_private_ioctl(file, file->private_data, 0, cmd, (void *)&comp_ratio); if (copy_to_user((void __user *)arg, &comp_ratio, sizeof(struct msm_sde_rotator_comp_ratio))) - return -EFAULT; + goto ioctl32_error; break; } default: + SDEDEV_ERR(ctx->rot_dev->dev, "invalid ioctl32 type:%x\n", cmd); ret = -ENOIOCTLCMD; break; } + mutex_unlock(vdev->lock); return ret; + +ioctl32_error: + mutex_unlock(vdev->lock); + SDEDEV_ERR(ctx->rot_dev->dev, "error handling ioctl32 cmd:%x\n", cmd); + return -EFAULT; } #endif diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index b7e2c297a1ad..24eb8fff905b 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -528,7 +528,6 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { .step = 1, .menu_skip_mask = 0, .qmenu = NULL, - .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, }, { .id = V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2, diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 4739fc999c82..bc72c4a56c91 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -4472,7 +4472,7 @@ static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info) struct venus_hfi_device *device = dev; u32 smem_block_size = 0; u8 *smem_table_ptr; - char version[VENUS_VERSION_LENGTH]; + char version[VENUS_VERSION_LENGTH] = ""; const u32 smem_image_index_venus = 14 * 128; if (!device || !fw_info) { diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 0f301903aa6f..63165d324fff 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -263,6 +263,8 @@ static void ite_set_carrier_params(struct ite_dev *dev) if (allowance > ITE_RXDCR_MAX) allowance = ITE_RXDCR_MAX; + + use_demodulator = true; } } diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index c945e4c2fbd4..ec30a004f319 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -200,22 +200,30 @@ static int smsusb_start_streaming(struct smsusb_device_t *dev) static int smsusb_sendrequest(void *context, void *buffer, size_t size) { struct smsusb_device_t *dev = (struct smsusb_device_t *) context; - struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer; - int dummy; + struct sms_msg_hdr *phdr; + int dummy, ret; if (dev->state != SMSUSB_ACTIVE) { pr_debug("Device not active yet\n"); return -ENOENT; } + phdr = kmalloc(size, GFP_KERNEL); + if (!phdr) + return -ENOMEM; + memcpy(phdr, buffer, size); + pr_debug("sending %s(%d) size: %d\n", smscore_translate_msg(phdr->msg_type), phdr->msg_type, phdr->msg_length); smsendian_handle_tx_message((struct sms_msg_data *) phdr); - smsendian_handle_message_header((struct sms_msg_hdr *)buffer); - return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), - buffer, size, &dummy, 1000); + smsendian_handle_message_header((struct sms_msg_hdr *)phdr); + ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + phdr, size, &dummy, 1000); + + kfree(phdr); + return ret; } static char *smsusb1_fw_lkup[] = { diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index cfb868a48b5f..ff6feff21e94 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -416,7 +416,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, nextbuf = NULL; spin_unlock_irqrestore(&queue->irqlock, flags); - buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; + buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ca092ef75cfe..12ffa4192605 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -532,6 +532,14 @@ config HDCP_QSEECOM Environment (QSEE) via the QSEECOM Driver and also calls the APIs exposed by the HDMI driver to communicate with the Receiver. +config PROFILER + tristate "Qualcomm Technologies, Inc. trustzone Communicator driver" + help + Provides a communication interface between userspace and + trustzone using Secure Channel Manager (SCM) interface. + It exposes APIs for userspace to get system profiling + information. + config VEXPRESS_SYSCFG bool "Versatile Express System Configuration driver" depends on VEXPRESS_CONFIG diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 40b7e3b603e2..c2941afc961e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_SRAM) += sram.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_QSEECOM) += qseecom.o +obj-$(CONFIG_PROFILER) += profiler.o obj-$(CONFIG_HDCP_QSEECOM) += hdcp.o ifdef CONFIG_COMPAT obj-$(CONFIG_QSEECOM) += compat_qseecom.o diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 460ffc79f566..33ec0c15efa6 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -97,11 +97,6 @@ */ #define SLEEP_SET_HW_KEY_MS 220 -#define QSEECOM_ALIGN_SIZE 0x40 -#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1) -#define QSEECOM_ALIGN(x)\ - ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK)) - /* hdcp command status */ #define HDCP_SUCCESS 0 diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index e59838231703..be74a25708b2 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -399,7 +399,7 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev) EXPORT_SYMBOL_GPL(mei_cldev_enabled); /** - * mei_cldev_enable_device - enable me client device + * mei_cldev_enable - enable me client device * create connection with me client * * @cldev: me client device diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 958af84884b5..2ff39fbc70d1 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -698,7 +698,7 @@ void mei_host_client_init(struct work_struct *work) pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); - pm_runtime_autosuspend(dev->dev); + pm_request_autosuspend(dev->dev); } /** diff --git a/drivers/misc/profiler.c b/drivers/misc/profiler.c new file mode 100644 index 000000000000..a2887fcefbab --- /dev/null +++ b/drivers/misc/profiler.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "PROFILER: %s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/types.h> +#include <soc/qcom/scm.h> +#include <soc/qcom/socinfo.h> +#include <asm/cacheflush.h> +#include <linux/delay.h> +#include <soc/qcom/profiler.h> + +#include <linux/compat.h> + +#define PROFILER_DEV "profiler" + +static struct class *driver_class; +static dev_t profiler_device_no; + +struct profiler_control { + struct device *pdev; + struct cdev cdev; +}; + +static struct profiler_control profiler; + +struct profiler_dev_handle { + bool released; + int abort; + atomic_t ioctl_count; +}; + + +static int profiler_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, + const void *req_buf, void *resp_buf) +{ + int ret = 0; + uint32_t qseos_cmd_id = 0; + struct scm_desc desc = {0}; + + if (!req_buf || !resp_buf) { + pr_err("Invalid buffer pointer\n"); + return -EINVAL; + } + qseos_cmd_id = *(uint32_t *)req_buf; + + switch (svc_id) { + + case SCM_SVC_BW: + switch (qseos_cmd_id) { + case TZ_BW_SVC_START_ID: + case TZ_BW_SVC_GET_ID: + case TZ_BW_SVC_STOP_ID: + /* Send the command to TZ */ + desc.arginfo = SCM_ARGS(4, SCM_RW, SCM_VAL, + SCM_RW, SCM_VAL); + desc.args[0] = virt_to_phys(& + (((struct tz_bw_svc_buf *) + req_buf)->bwreq)); + desc.args[1] = ((struct tz_bw_svc_buf *) + req_buf)->req_size; + desc.args[2] = virt_to_phys(& + ((struct tz_bw_svc_buf *) + req_buf)->bwresp); + desc.args[3] = sizeof(struct tz_bw_svc_resp); + + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, + TZ_SVC_BW_PROF_ID), &desc); + break; + default: + pr_err("cmd_id %d is not supported by scm_call2.\n", + qseos_cmd_id); + ret = -EINVAL; + } /*end of switch (qsee_cmd_id) */ + break; + default: + pr_err("svc_id 0x%x is not supported by armv8 scm_call2.\n", + svc_id); + ret = -EINVAL; + break; + } /*end of switch svc_id */ + return ret; +} + + +static int profiler_scm_call(u32 svc_id, u32 tz_cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len) +{ + if (!is_scm_armv8()) + return scm_call(svc_id, tz_cmd_id, cmd_buf, cmd_len, + resp_buf, resp_len); + else + return profiler_scm_call2(svc_id, tz_cmd_id, cmd_buf, resp_buf); +} + +static int bw_profiling_command(void *req) +{ + struct tz_bw_svc_resp *bw_resp = NULL; + uint32_t cmd_id = 0; + int ret; + + cmd_id = *(uint32_t *)req; + bw_resp = &((struct tz_bw_svc_buf *)req)->bwresp; + /* Flush buffers from cache to memory. */ + dmac_flush_range(req, req + + PAGE_ALIGN(sizeof(union tz_bw_svc_req))); + dmac_flush_range((void *)bw_resp, ((void *)bw_resp) + + sizeof(struct tz_bw_svc_resp)); + ret = profiler_scm_call(SCM_SVC_BW, TZ_SVC_BW_PROF_ID, req, + sizeof(struct tz_bw_svc_buf), + bw_resp, sizeof(struct tz_bw_svc_resp)); + if (ret) { + pr_err("profiler_scm_call failed with err: %d\n", ret); + return -EINVAL; + } + /* Invalidate cache. */ + dmac_inv_range((void *)bw_resp, ((void *)bw_resp) + + sizeof(struct tz_bw_svc_resp)); + /* Verify cmd id and Check that request succeeded.*/ + if ((bw_resp->status != E_BW_SUCCESS) || + (cmd_id != bw_resp->cmd_id)) { + ret = -1; + pr_err("Status: %d,Cmd: %d\n", + bw_resp->status, + bw_resp->cmd_id); + } + return ret; +} + +static int bw_profiling_start(struct tz_bw_svc_buf *bwbuf) +{ + struct tz_bw_svc_start_req *bwstartreq = NULL; + + bwstartreq = (struct tz_bw_svc_start_req *) &bwbuf->bwreq; + /* Populate request data */ + bwstartreq->cmd_id = TZ_BW_SVC_START_ID; + bwstartreq->version = TZ_BW_SVC_VERSION; + bwbuf->req_size = sizeof(struct tz_bw_svc_start_req); + return bw_profiling_command(bwbuf); +} + +static int bw_profiling_get(void __user *argp, struct tz_bw_svc_buf *bwbuf) +{ + struct tz_bw_svc_get_req *bwgetreq = NULL; + int ret; + char *buf = NULL; + const int numberofregs = 3; + struct profiler_bw_cntrs_req cnt_buf; + + bwgetreq = (struct tz_bw_svc_get_req *) &bwbuf->bwreq; + /* Allocate memory for get buffer */ + buf = kzalloc(PAGE_ALIGN(numberofregs * sizeof(uint32_t)), GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + pr_err(" Failed to allocate memory\n"); + return ret; + } + /* Populate request data */ + bwgetreq->cmd_id = TZ_BW_SVC_GET_ID; + bwgetreq->buf_ptr = (uint64_t) virt_to_phys(buf); + bwgetreq->buf_size = numberofregs * sizeof(uint32_t); + bwbuf->req_size = sizeof(struct tz_bw_svc_get_req); + dmac_flush_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size)); + ret = bw_profiling_command(bwbuf); + if (ret) { + pr_err("bw_profiling_command failed\n"); + return ret; + } + dmac_inv_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size)); + cnt_buf.total = *(uint32_t *) (buf + 0 * sizeof(uint32_t)); + cnt_buf.cpu = *(uint32_t *) (buf + 1 * sizeof(uint32_t)); + cnt_buf.gpu = *(uint32_t *) (buf + 2 * sizeof(uint32_t)); + if (copy_to_user(argp, &cnt_buf, sizeof(struct profiler_bw_cntrs_req))) + pr_err("copy_to_user failed\n"); + /* Free memory for response */ + if (buf != NULL) { + kfree(buf); + buf = NULL; + } + return ret; +} + +static int bw_profiling_stop(struct tz_bw_svc_buf *bwbuf) +{ + struct tz_bw_svc_stop_req *bwstopreq = NULL; + + bwstopreq = (struct tz_bw_svc_stop_req *) &bwbuf->bwreq; + /* Populate request data */ + bwstopreq->cmd_id = TZ_BW_SVC_STOP_ID; + bwbuf->req_size = sizeof(struct tz_bw_svc_stop_req); + return bw_profiling_command(bwbuf); +} + + +static int profiler_get_bw_info(void __user *argp) +{ + int ret = 0; + struct tz_bw_svc_buf *bwbuf = NULL; + struct profiler_bw_cntrs_req cnt_buf; + + ret = copy_from_user(&cnt_buf, argp, + sizeof(struct profiler_bw_cntrs_req)); + if (ret) + return ret; + /* Allocate memory for request */ + bwbuf = kzalloc(PAGE_ALIGN(sizeof(struct tz_bw_svc_buf)), GFP_KERNEL); + if (bwbuf == NULL) + return -ENOMEM; + switch (cnt_buf.cmd) { + case TZ_BW_SVC_START_ID: + ret = bw_profiling_start(bwbuf); + if (ret) + pr_err("bw_profiling_start Failed with ret: %d\n", ret); + break; + case TZ_BW_SVC_GET_ID: + ret = bw_profiling_get(argp, bwbuf); + if (ret) + pr_err("bw_profiling_get Failed with ret: %d\n", ret); + break; + case TZ_BW_SVC_STOP_ID: + ret = bw_profiling_stop(bwbuf); + if (ret) + pr_err("bw_profiling_stop Failed with ret: %d\n", ret); + break; + default: + pr_err("Invalid IOCTL: 0x%x\n", cnt_buf.cmd); + ret = -EINVAL; + } + /* Free memory for command */ + if (bwbuf != NULL) { + kfree(bwbuf); + bwbuf = NULL; + } + return ret; +} + +static int profiler_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct profiler_dev_handle *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + file->private_data = data; + data->abort = 0; + data->released = false; + atomic_set(&data->ioctl_count, 0); + return ret; +} + +static int compat_get_profiler_bw_info( + struct compat_profiler_bw_cntrs_req __user *data32, + struct profiler_bw_cntrs_req __user *data) +{ + compat_uint_t total; + compat_uint_t cpu; + compat_uint_t gpu; + compat_uint_t cmd; + int err; + + err = get_user(total, &data32->total); + err |= put_user(total, &data->total); + err |= get_user(gpu, &data32->gpu); + err |= put_user(gpu, &data->gpu); + err |= get_user(cpu, &data32->cpu); + err |= put_user(cpu, &data->cpu); + err |= get_user(cmd, &data32->cmd); + err |= put_user(cmd, &data->cmd); + return err; +} + +static int compat_put_profiler_bw_info( + struct compat_profiler_bw_cntrs_req __user *data32, + struct profiler_bw_cntrs_req __user *data) +{ + compat_uint_t total; + compat_uint_t cpu; + compat_uint_t gpu; + compat_uint_t cmd; + int err; + + err = get_user(total, &data->total); + err |= put_user(total, &data32->total); + err |= get_user(gpu, &data->gpu); + err |= put_user(gpu, &data32->gpu); + err |= get_user(cpu, &data->cpu); + err |= put_user(cpu, &data32->cpu); + err |= get_user(cmd, &data->cmd); + err |= put_user(cmd, &data32->cmd); + return err; +} + +static unsigned int convert_cmd(unsigned int cmd) +{ + switch (cmd) { + case COMPAT_PROFILER_IOCTL_GET_BW_INFO: + return PROFILER_IOCTL_GET_BW_INFO; + + default: + return cmd; + } +} + + +long profiler_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + int ret = 0; + struct profiler_dev_handle *data = file->private_data; + void __user *argp = (void __user *) arg; + + if (!data) { + pr_err("Invalid/uninitialized device handle\n"); + return -EINVAL; + } + + if (data->abort) { + pr_err("Aborting profiler driver\n"); + return -ENODEV; + } + + switch (cmd) { + case PROFILER_IOCTL_GET_BW_INFO: + atomic_inc(&data->ioctl_count); + ret = profiler_get_bw_info(argp); + if (ret) + pr_err("failed get system bandwidth info: %d\n", ret); + atomic_dec(&data->ioctl_count); + break; + default: + pr_err("Invalid IOCTL: 0x%x\n", cmd); + return -EINVAL; + } + return ret; +} + +long compat_profiler_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + + switch (cmd) { + case COMPAT_PROFILER_IOCTL_GET_BW_INFO:{ + struct compat_profiler_bw_cntrs_req __user *data32; + struct profiler_bw_cntrs_req __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (data == NULL) + return -EFAULT; + err = compat_get_profiler_bw_info(data32, data); + if (err) + return err; + ret = profiler_ioctl(file, convert_cmd(cmd), + (unsigned long)data); + err = compat_put_profiler_bw_info(data32, data); + return ret ? ret : err; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static int profiler_release(struct inode *inode, struct file *file) +{ + pr_info("profiler release\n"); + return 0; +} + +static const struct file_operations profiler_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = profiler_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_profiler_ioctl, +#endif + .open = profiler_open, + .release = profiler_release +}; + +static int profiler_init(void) +{ + int rc; + struct device *class_dev; + + rc = alloc_chrdev_region(&profiler_device_no, 0, 1, PROFILER_DEV); + if (rc < 0) { + pr_err("alloc_chrdev_region failed %d\n", rc); + return rc; + } + + driver_class = class_create(THIS_MODULE, PROFILER_DEV); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("class_create failed %d\n", rc); + goto exit_unreg_chrdev_region; + } + + class_dev = device_create(driver_class, NULL, profiler_device_no, NULL, + PROFILER_DEV); + if (IS_ERR(class_dev)) { + pr_err("class_device_create failed %d\n", rc); + rc = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&profiler.cdev, &profiler_fops); + profiler.cdev.owner = THIS_MODULE; + + rc = cdev_add(&profiler.cdev, MKDEV(MAJOR(profiler_device_no), 0), 1); + if (rc < 0) { + pr_err("%s: cdev_add failed %d\n", __func__, rc); + goto exit_destroy_device; + } + + profiler.pdev = class_dev; + return 0; + +exit_destroy_device: + device_destroy(driver_class, profiler_device_no); +exit_destroy_class: + class_destroy(driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(profiler_device_no, 1); + return rc; +} + +static void profiler_exit(void) +{ + pr_info("Exiting from profiler\n"); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. trustzone Communicator"); + +module_init(profiler_init); +module_exit(profiler_exit); diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 78f03fc75761..9855bee67627 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -2333,7 +2333,13 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp) ret); goto loadapp_err; } - + if (load_img_req.mdt_len > len || load_img_req.img_len > len) { + pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n", + len, load_img_req.mdt_len, + load_img_req.img_len); + ret = -EINVAL; + goto loadapp_err; + } /* Populate the structure for sending scm call to load image */ if (qseecom.qsee_version < QSEE_VERSION_40) { load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND; @@ -5149,6 +5155,12 @@ static int qseecom_load_external_elf(struct qseecom_dev_handle *data, ret); return ret; } + if (load_img_req.mdt_len > len || load_img_req.img_len > len) { + pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n", + len, load_img_req.mdt_len, + load_img_req.img_len); + return ret; + } /* Populate the structure for sending scm call to load image */ if (qseecom.qsee_version < QSEE_VERSION_40) { load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND; diff --git a/drivers/misc/qseecom_kernel.h b/drivers/misc/qseecom_kernel.h index 8f981903c3a1..40426b749f60 100644 --- a/drivers/misc/qseecom_kernel.h +++ b/drivers/misc/qseecom_kernel.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, 2016-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -19,7 +19,7 @@ #define QSEECOM_ALIGN_SIZE 0x40 #define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1) #define QSEECOM_ALIGN(x) \ - ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK)) + ((x + QSEECOM_ALIGN_MASK) & (~QSEECOM_ALIGN_MASK)) /* * struct qseecom_handle - diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 9766dcc95602..12c66919f06f 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -791,7 +791,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test, struct mmc_async_req *cur_areq = &test_areq[0].areq; struct mmc_async_req *other_areq = &test_areq[1].areq; int i; - int ret; + int ret = RESULT_OK; test_areq[0].test = test; test_areq[1].test = test; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4567b7526469..542f1733d0dd 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2032,10 +2032,10 @@ reinit: err = mmc_select_hs400(card); if (err) goto free_card; - } else if (mmc_card_hs(card)) { + } else { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); - if (!IS_ERR_VALUE(err)) { + if (!IS_ERR_VALUE(err) && mmc_card_hs(card)) { err = mmc_select_hs_ddr(card); if (err) goto free_card; diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 44ecebd1ea8c..c8b8ac66ff7e 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -309,6 +309,9 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) cmd0 = BF_SSP(cmd->opcode, CMD0_CMD); cmd1 = cmd->arg; + if (cmd->opcode == MMC_STOP_TRANSMISSION) + cmd0 |= BM_SSP_CMD0_APPEND_8CYC; + if (host->sdio_irq_en) { ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN; @@ -417,8 +420,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) ssp->base + HW_SSP_BLOCK_SIZE); } - if ((cmd->opcode == MMC_STOP_TRANSMISSION) || - (cmd->opcode == SD_IO_RW_EXTENDED)) + if (cmd->opcode == SD_IO_RW_EXTENDED) cmd0 |= BM_SSP_CMD0_APPEND_8CYC; cmd1 = cmd->arg; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 7274a6d2cce0..2eaac11ec8ba 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2736,14 +2736,15 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) msm_host->offset; unsigned long flags; bool done = false; - u32 io_sig_sts; + u32 io_sig_sts = SWITCHABLE_SIGNALLING_VOL; spin_lock_irqsave(&host->lock, flags); pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", mmc_hostname(host->mmc), __func__, req_type, msm_host->curr_pwr_state, msm_host->curr_io_level); - io_sig_sts = sdhci_msm_readl_relaxed(host, - msm_host_offset->CORE_GENERICS); + if (!msm_host->mci_removed) + io_sig_sts = sdhci_msm_readl_relaxed(host, + msm_host_offset->CORE_GENERICS); /* * The IRQ for request type IO High/Low will be generated when - @@ -3304,6 +3305,21 @@ static void sdhci_msm_cmdq_dump_debug_ram(struct sdhci_host *host) pr_err("-------------------------\n"); } +static void sdhci_msm_cache_debug_data(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + struct sdhci_msm_debug_data *cached_data = &msm_host->cached_data; + + memcpy(&cached_data->copy_mmc, msm_host->mmc, + sizeof(struct mmc_host)); + if (msm_host->mmc->card) + memcpy(&cached_data->copy_card, msm_host->mmc->card, + sizeof(struct mmc_card)); + memcpy(&cached_data->copy_host, host, + sizeof(struct sdhci_host)); +} + void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -3316,6 +3332,7 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) u32 debug_reg[MAX_TEST_BUS] = {0}; u32 sts = 0; + sdhci_msm_cache_debug_data(host); pr_info("----------- VENDOR REGISTER DUMP -----------\n"); if (host->cq_host) sdhci_msm_cmdq_dump_debug_ram(host); @@ -3897,8 +3914,8 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host, group->req.type = PM_QOS_REQ_AFFINE_CORES; cpumask_copy(&group->req.cpus_affine, &msm_host->pdata->pm_qos_data.cpu_group_map.mask[i]); - /* For initialization phase, set the performance mode latency */ - group->latency = latency[i].latency[SDHCI_PERFORMANCE_MODE]; + /* We set default latency here for all pm_qos cpu groups. */ + group->latency = PM_QOS_DEFAULT_VALUE; pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY, group->latency); pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n", diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h index c26636198a22..2e4f2179378e 100644 --- a/drivers/mmc/host/sdhci-msm.h +++ b/drivers/mmc/host/sdhci-msm.h @@ -171,6 +171,12 @@ struct sdhci_msm_ice_data { int state; }; +struct sdhci_msm_debug_data { + struct mmc_host copy_mmc; + struct mmc_card copy_card; + struct sdhci_host copy_host; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -186,6 +192,7 @@ struct sdhci_msm_host { atomic_t clks_on; /* Set if clocks are enabled */ struct sdhci_msm_pltfm_data *pdata; struct mmc_host *mmc; + struct sdhci_msm_debug_data cached_data; struct sdhci_pltfm_data sdhci_msm_pdata; u32 curr_pwr_state; u32 curr_io_level; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 083cb8cdf1ca..44633dc5d2be 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2507,7 +2507,27 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) ctrl &= ~SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); + err = -EIO; + + if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200) + goto out; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + + spin_unlock_irqrestore(&host->lock, flags); + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = MMC_STOP_TRANSMISSION; + cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + cmd.busy_timeout = 50; + mmc_wait_for_cmd(mmc, &cmd, 0); + + spin_lock_irqsave(&host->lock, flags); + goto out; } @@ -3283,7 +3303,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) pr_err("%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); - if (intmask & SDHCI_INT_CARD_INT) { + if ((intmask & SDHCI_INT_CARD_INT) && + (host->ier & SDHCI_INT_CARD_INT)) { sdhci_enable_sdio_irq_nolock(host, false); host->thread_isr |= SDHCI_INT_CARD_INT; result = IRQ_WAKE_THREAD; diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index f9fa3fad728e..2051f28ddac6 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -139,15 +139,13 @@ static int __init init_msp_flash(void) } msp_maps[i].bankwidth = 1; - msp_maps[i].name = kmalloc(7, GFP_KERNEL); + msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL); if (!msp_maps[i].name) { iounmap(msp_maps[i].virt); kfree(msp_parts[i]); goto cleanup_loop; } - msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7); - for (j = 0; j < pcnt; j++) { part_name[5] = '0' + i; part_name[7] = '0' + j; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 093fd917274f..132d0d04726c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -531,7 +531,7 @@ config MTD_NAND_FSMC Flexible Static Memory Controller (FSMC) config MTD_NAND_XWAY - tristate "Support for NAND on Lantiq XWAY SoC" + bool "Support for NAND on Lantiq XWAY SoC" depends on LANTIQ && SOC_TYPE_XWAY select MTD_NAND_PLATFORM help diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index 7be393c96b1a..cf7c18947189 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -161,6 +161,7 @@ static int c_can_pci_probe(struct pci_dev *pdev, dev->irq = pdev->irq; priv->base = addr; + priv->device = &pdev->dev; if (!c_can_pci_data->freq) { dev_err(&pdev->dev, "no clock frequency defined\n"); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 680d1ff07a55..6749b1829469 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -948,7 +948,12 @@ static int ti_hecc_probe(struct platform_device *pdev) netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, HECC_DEF_NAPI_WEIGHT); - clk_enable(priv->clk); + err = clk_prepare_enable(priv->clk); + if (err) { + dev_err(&pdev->dev, "clk_prepare_enable() failed\n"); + goto probe_exit_clk; + } + err = register_candev(ndev); if (err) { dev_err(&pdev->dev, "register_candev() failed\n"); @@ -981,7 +986,7 @@ static int ti_hecc_remove(struct platform_device *pdev) struct ti_hecc_priv *priv = netdev_priv(ndev); unregister_candev(ndev); - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); clk_put(priv->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(priv->base); @@ -1006,7 +1011,7 @@ static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); priv->can.state = CAN_STATE_SLEEPING; - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); return 0; } @@ -1015,8 +1020,11 @@ static int ti_hecc_resume(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(dev); + int err; - clk_enable(priv->clk); + err = clk_prepare_enable(priv->clk); + if (err) + return err; hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); priv->can.state = CAN_STATE_ERROR_ACTIVE; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 5a2e341a6d1e..91be4575b524 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -872,23 +872,25 @@ lbl_free_candev: static void peak_usb_disconnect(struct usb_interface *intf) { struct peak_usb_device *dev; + struct peak_usb_device *dev_prev_siblings; /* unregister as many netdev devices as siblings */ - for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) { struct net_device *netdev = dev->netdev; char name[IFNAMSIZ]; + dev_prev_siblings = dev->prev_siblings; dev->state &= ~PCAN_USB_STATE_CONNECTED; strncpy(name, netdev->name, IFNAMSIZ); unregister_netdev(netdev); - free_candev(netdev); kfree(dev->cmd_buf); dev->next_siblings = NULL; if (dev->adapter->dev_free) dev->adapter->dev_free(dev); + free_candev(netdev); dev_info(&intf->dev, "%s removed\n", name); } diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c index a731720f1d13..449b2a47f9a8 100644 --- a/drivers/net/can/usb/usb_8dev.c +++ b/drivers/net/can/usb/usb_8dev.c @@ -954,8 +954,8 @@ static int usb_8dev_probe(struct usb_interface *intf, for (i = 0; i < MAX_TX_URBS; i++) priv->tx_contexts[i].echo_index = MAX_TX_URBS; - priv->cmd_msg_buffer = kzalloc(sizeof(struct usb_8dev_cmd_msg), - GFP_KERNEL); + priv->cmd_msg_buffer = devm_kzalloc(&intf->dev, sizeof(struct usb_8dev_cmd_msg), + GFP_KERNEL); if (!priv->cmd_msg_buffer) goto cleanup_candev; @@ -969,7 +969,7 @@ static int usb_8dev_probe(struct usb_interface *intf, if (err) { netdev_err(netdev, "couldn't register CAN device: %d\n", err); - goto cleanup_cmd_msg_buffer; + goto cleanup_candev; } err = usb_8dev_cmd_version(priv, &version); @@ -990,9 +990,6 @@ static int usb_8dev_probe(struct usb_interface *intf, cleanup_unregister_candev: unregister_netdev(priv->netdev); -cleanup_cmd_msg_buffer: - kfree(priv->cmd_msg_buffer); - cleanup_candev: free_candev(netdev); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 858106352ce9..8860e74aa28f 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -732,11 +732,8 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs; unsigned int pkts_compl = 0, bytes_compl = 0; struct bcm_sysport_cb *cb; - struct netdev_queue *txq; u32 hw_ind; - txq = netdev_get_tx_queue(ndev, ring->index); - /* Compute how many descriptors have been processed since last call */ hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index)); c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK; @@ -767,9 +764,6 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, ring->c_index = c_index; - if (netif_tx_queue_stopped(txq) && pkts_compl) - netif_tx_wake_queue(txq); - netif_dbg(priv, tx_done, ndev, "ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n", ring->index, ring->c_index, pkts_compl, bytes_compl); @@ -781,16 +775,33 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, struct bcm_sysport_tx_ring *ring) { + struct netdev_queue *txq; unsigned int released; unsigned long flags; + txq = netdev_get_tx_queue(priv->netdev, ring->index); + spin_lock_irqsave(&ring->lock, flags); released = __bcm_sysport_tx_reclaim(priv, ring); + if (released) + netif_tx_wake_queue(txq); + spin_unlock_irqrestore(&ring->lock, flags); return released; } +/* Locked version of the per-ring TX reclaim, but does not wake the queue */ +static void bcm_sysport_tx_clean(struct bcm_sysport_priv *priv, + struct bcm_sysport_tx_ring *ring) +{ + unsigned long flags; + + spin_lock_irqsave(&ring->lock, flags); + __bcm_sysport_tx_reclaim(priv, ring); + spin_unlock_irqrestore(&ring->lock, flags); +} + static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget) { struct bcm_sysport_tx_ring *ring = @@ -1275,7 +1286,7 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv, napi_disable(&ring->napi); netif_napi_del(&ring->napi); - bcm_sysport_tx_reclaim(priv, ring); + bcm_sysport_tx_clean(priv, ring); kfree(ring->cbs); ring->cbs = NULL; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index bdbd80423b17..9ff2881f933d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -900,9 +900,7 @@ static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value) { - u8 __iomem *reg_addr = ACCESS_ONCE(base); - - writel(value, reg_addr + reg); + writel(value, base + reg); } #define dsaf_write_dev(a, reg, value) \ @@ -910,9 +908,7 @@ static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value) static inline u32 dsaf_read_reg(u8 __iomem *base, u32 reg) { - u8 __iomem *reg_addr = ACCESS_ONCE(base); - - return readl(reg_addr + reg); + return readl(base + reg); } #define dsaf_read_dev(a, reg) \ diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index a4beccf1fd46..0e67145bc418 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -772,6 +772,17 @@ struct mvpp2_rx_desc { u32 reserved8; }; +struct mvpp2_txq_pcpu_buf { + /* Transmitted SKB */ + struct sk_buff *skb; + + /* Physical address of transmitted buffer */ + dma_addr_t phys; + + /* Size transmitted */ + size_t size; +}; + /* Per-CPU Tx queue control */ struct mvpp2_txq_pcpu { int cpu; @@ -787,11 +798,8 @@ struct mvpp2_txq_pcpu { /* Number of Tx DMA descriptors reserved for each CPU */ int reserved_num; - /* Array of transmitted skb */ - struct sk_buff **tx_skb; - - /* Array of transmitted buffers' physical addresses */ - dma_addr_t *tx_buffs; + /* Infos about transmitted buffers */ + struct mvpp2_txq_pcpu_buf *buffs; /* Index of last TX DMA descriptor that was inserted */ int txq_put_index; @@ -981,10 +989,11 @@ static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu, struct sk_buff *skb, struct mvpp2_tx_desc *tx_desc) { - txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb; - if (skb) - txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] = - tx_desc->buf_phys_addr; + struct mvpp2_txq_pcpu_buf *tx_buf = + txq_pcpu->buffs + txq_pcpu->txq_put_index; + tx_buf->skb = skb; + tx_buf->size = tx_desc->data_size; + tx_buf->phys = tx_desc->buf_phys_addr + tx_desc->packet_offset; txq_pcpu->txq_put_index++; if (txq_pcpu->txq_put_index == txq_pcpu->size) txq_pcpu->txq_put_index = 0; @@ -4403,17 +4412,16 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, int i; for (i = 0; i < num; i++) { - dma_addr_t buf_phys_addr = - txq_pcpu->tx_buffs[txq_pcpu->txq_get_index]; - struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index]; + struct mvpp2_txq_pcpu_buf *tx_buf = + txq_pcpu->buffs + txq_pcpu->txq_get_index; mvpp2_txq_inc_get(txq_pcpu); - dma_unmap_single(port->dev->dev.parent, buf_phys_addr, - skb_headlen(skb), DMA_TO_DEVICE); - if (!skb) + dma_unmap_single(port->dev->dev.parent, tx_buf->phys, + tx_buf->size, DMA_TO_DEVICE); + if (!tx_buf->skb) continue; - dev_kfree_skb_any(skb); + dev_kfree_skb_any(tx_buf->skb); } } @@ -4664,15 +4672,10 @@ static int mvpp2_txq_init(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); txq_pcpu->size = txq->size; - txq_pcpu->tx_skb = kmalloc(txq_pcpu->size * - sizeof(*txq_pcpu->tx_skb), - GFP_KERNEL); - if (!txq_pcpu->tx_skb) - goto error; - - txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size * - sizeof(dma_addr_t), GFP_KERNEL); - if (!txq_pcpu->tx_buffs) + txq_pcpu->buffs = kmalloc(txq_pcpu->size * + sizeof(struct mvpp2_txq_pcpu_buf), + GFP_KERNEL); + if (!txq_pcpu->buffs) goto error; txq_pcpu->count = 0; @@ -4686,8 +4689,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port, error: for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); - kfree(txq_pcpu->tx_skb); - kfree(txq_pcpu->tx_buffs); + kfree(txq_pcpu->buffs); } dma_free_coherent(port->dev->dev.parent, @@ -4706,8 +4708,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); - kfree(txq_pcpu->tx_skb); - kfree(txq_pcpu->tx_buffs); + kfree(txq_pcpu->buffs); } if (txq->descs) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index bbff8ec6713e..28a4b34310b2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -502,8 +502,11 @@ void mlx4_en_recover_from_oom(struct mlx4_en_priv *priv) return; for (ring = 0; ring < priv->rx_ring_num; ring++) { - if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) + if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) { + local_bh_disable(); napi_reschedule(&priv->rx_cq[ring]->napi); + local_bh_enable(); + } } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 6cf6d93d8831..ba115ec7aa92 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -432,6 +432,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev) MLX5_SET(cmd_hca_cap, set_hca_cap, pkey_table_size, to_fw_pkey_sz(128)); + /* Check log_max_qp from HCA caps to set in current profile */ + if (MLX5_CAP_GEN_MAX(dev, log_max_qp) < profile[prof_sel].log_max_qp) { + mlx5_core_warn(dev, "log_max_qp value in current profile is %d, changing it to HCA capability limit (%d)\n", + profile[prof_sel].log_max_qp, + MLX5_CAP_GEN_MAX(dev, log_max_qp)); + profile[prof_sel].log_max_qp = MLX5_CAP_GEN_MAX(dev, log_max_qp); + } if (prof->mask & MLX5_PROF_MASK_QP_SIZE) MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_qp, prof->log_max_qp); @@ -505,7 +512,6 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) struct mlx5_priv *priv = &mdev->priv; struct msix_entry *msix = priv->msix_arr; int irq = msix[i + MLX5_EQ_VEC_COMP_BASE].vector; - int numa_node = priv->numa_node; int err; if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) { @@ -513,7 +519,7 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) return -ENOMEM; } - cpumask_set_cpu(cpumask_local_spread(i, numa_node), + cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node), priv->irq_info[i].mask); err = irq_set_affinity_hint(irq, priv->irq_info[i].mask); diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 142f33d978c5..a0fbe00dd690 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -206,21 +206,21 @@ MLXSW_ITEM32(pci, eqe, owner, 0x0C, 0, 1); /* pci_eqe_cmd_token * Command completion event - token */ -MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16); +MLXSW_ITEM32(pci, eqe, cmd_token, 0x00, 16, 16); /* pci_eqe_cmd_status * Command completion event - status */ -MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8); +MLXSW_ITEM32(pci, eqe, cmd_status, 0x00, 0, 8); /* pci_eqe_cmd_out_param_h * Command completion event - output parameter - higher part */ -MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32); +MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x04, 0, 32); /* pci_eqe_cmd_out_param_l * Command completion event - output parameter - lower part */ -MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32); +MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x08, 0, 32); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3be4a2355ead..cb165c2d4803 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -390,6 +390,7 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb_orig); return NETDEV_TX_OK; } + dev_consume_skb_any(skb_orig); } if (eth_skb_pad(skb)) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index d85960cfb694..fb2d9a82ce3d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -313,6 +313,7 @@ static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb_orig); return NETDEV_TX_OK; } + dev_consume_skb_any(skb_orig); } mlxsw_sx_txhdr_construct(skb, &tx_info); len = skb->len; diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index 6b23a8f61f35..bf6502e27bdd 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -30,7 +30,6 @@ #include <linux/rtnetlink.h> #define RMNET_MHI_DRIVER_NAME "rmnet_mhi" -#define RMNET_MHI_DEV_NAME "rmnet_mhi%d" #define MHI_DEFAULT_MTU 8000 #define MHI_MAX_MRU 0xFFFF #define MHI_NAPI_WEIGHT_VALUE 12 @@ -80,6 +79,7 @@ struct __packed mhi_skb_priv { struct rmnet_mhi_private { struct list_head node; u32 dev_id; + const char *interface_name; struct mhi_client_handle *tx_client_handle; struct mhi_client_handle *rx_client_handle; enum MHI_CLIENT_CHANNEL tx_channel; @@ -113,6 +113,7 @@ struct rmnet_mhi_private { }; static LIST_HEAD(rmnet_mhi_ctxt_list); +static struct platform_driver rmnet_mhi_driver; static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, struct sk_buff *skb, int frag) @@ -693,7 +694,7 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) ext_cmd.u.data = 0; break; case RMNET_IOCTL_GET_DRIVER_NAME: - strlcpy(ext_cmd.u.if_name, RMNET_MHI_DRIVER_NAME, + strlcpy(ext_cmd.u.if_name, rmnet_mhi_ptr->interface_name, sizeof(ext_cmd.u.if_name)); break; case RMNET_IOCTL_SET_SLEEP_STATE: @@ -799,6 +800,7 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) struct rmnet_mhi_private **rmnet_mhi_ctxt = NULL; int r = 0; char ifalias[IFALIASZ]; + char ifname[IFNAMSIZ]; struct mhi_client_handle *client_handle = NULL; rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered.\n"); @@ -861,18 +863,20 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) snprintf(ifalias, sizeof(ifalias), "%s_%04x_%02u.%02u.%02u_%u", - RMNET_MHI_DRIVER_NAME, + rmnet_mhi_ptr->interface_name, client_handle->dev_id, client_handle->domain, client_handle->bus, client_handle->slot, rmnet_mhi_ptr->dev_id); + snprintf(ifname, sizeof(ifname), "%s%%d", + rmnet_mhi_ptr->interface_name); + rtnl_lock(); rmnet_mhi_ptr->dev = alloc_netdev(sizeof(struct rmnet_mhi_private *), - RMNET_MHI_DEV_NAME, - NET_NAME_PREDICTABLE, rmnet_mhi_setup); + ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup); if (!rmnet_mhi_ptr->dev) { rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, @@ -1083,7 +1087,7 @@ static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr) snprintf(node_name, sizeof(node_name), "%s_%04x_%02u.%02u.%02u_%u", - RMNET_MHI_DRIVER_NAME, + rmnet_mhi_ptr->interface_name, client_handle->dev_id, client_handle->domain, client_handle->bus, @@ -1209,6 +1213,15 @@ static int rmnet_mhi_probe(struct platform_device *pdev) rmnet_mhi_ptr->max_mtu = MHI_MAX_MTU; } + rc = of_property_read_string(pdev->dev.of_node, "qcom,interface-name", + &rmnet_mhi_ptr->interface_name); + if (likely(rc)) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "interface-name not defined, setting to default name %s\n", + RMNET_MHI_DRIVER_NAME); + rmnet_mhi_ptr->interface_name = rmnet_mhi_driver.driver.name; + } + client_info.dev = &pdev->dev; client_info.node_name = "qcom,mhi"; client_info.mhi_client_cb = rmnet_mhi_cb; @@ -1271,7 +1284,7 @@ static int rmnet_mhi_probe(struct platform_device *pdev) snprintf(node_name, sizeof(node_name), "%s_%04x_%02u.%02u.%02u_%u", - RMNET_MHI_DRIVER_NAME, + rmnet_mhi_ptr->interface_name, client_handle->dev_id, client_handle->domain, client_handle->bus, diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index 50d5604833ed..e0993eba5df3 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -2223,8 +2223,6 @@ static irqreturn_t vxge_isr_napi(int irq, void *dev_id) return IRQ_NONE; } -#ifdef CONFIG_PCI_MSI - static irqreturn_t vxge_tx_msix_handle(int irq, void *dev_id) { struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id; @@ -2442,16 +2440,13 @@ static void vxge_rem_msix_isr(struct vxgedev *vdev) if (vdev->config.intr_type == MSI_X) pci_disable_msix(vdev->pdev); } -#endif static void vxge_rem_isr(struct vxgedev *vdev) { -#ifdef CONFIG_PCI_MSI - if (vdev->config.intr_type == MSI_X) { + if (IS_ENABLED(CONFIG_PCI_MSI) && + vdev->config.intr_type == MSI_X) { vxge_rem_msix_isr(vdev); - } else -#endif - if (vdev->config.intr_type == INTA) { + } else if (vdev->config.intr_type == INTA) { synchronize_irq(vdev->pdev->irq); free_irq(vdev->pdev->irq, vdev); } @@ -2460,11 +2455,10 @@ static void vxge_rem_isr(struct vxgedev *vdev) static int vxge_add_isr(struct vxgedev *vdev) { int ret = 0; -#ifdef CONFIG_PCI_MSI int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0; int pci_fun = PCI_FUNC(vdev->pdev->devfn); - if (vdev->config.intr_type == MSI_X) + if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) ret = vxge_enable_msix(vdev); if (ret) { @@ -2475,7 +2469,7 @@ static int vxge_add_isr(struct vxgedev *vdev) vdev->config.intr_type = INTA; } - if (vdev->config.intr_type == MSI_X) { + if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) { for (intr_idx = 0; intr_idx < (vdev->no_of_vpath * VXGE_HW_VPATH_MSIX_ACTIVE); intr_idx++) { @@ -2576,9 +2570,8 @@ static int vxge_add_isr(struct vxgedev *vdev) vdev->vxge_entries[intr_cnt].in_use = 1; vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[0]; } -INTA_MODE: -#endif +INTA_MODE: if (vdev->config.intr_type == INTA) { snprintf(vdev->desc[0], VXGE_INTR_STRLEN, "%s:vxge:INTA", vdev->ndev->name); @@ -3889,12 +3882,12 @@ static void vxge_device_config_init(struct vxge_hw_device_config *device_config, if (max_mac_vpath > VXGE_MAX_MAC_ADDR_COUNT) max_mac_vpath = VXGE_MAX_MAC_ADDR_COUNT; -#ifndef CONFIG_PCI_MSI - vxge_debug_init(VXGE_ERR, - "%s: This Kernel does not support " - "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME); - *intr_type = INTA; -#endif + if (!IS_ENABLED(CONFIG_PCI_MSI)) { + vxge_debug_init(VXGE_ERR, + "%s: This Kernel does not support " + "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME); + *intr_type = INTA; + } /* Configure whether MSI-X or IRQL. */ switch (*intr_type) { diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 467d41698fd5..549ad2018e7f 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1330,6 +1330,19 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) + entry / NUM_TX_DESC * DPTR_ALIGN; len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data; + /* Zero length DMA descriptors are problematic as they seem to + * terminate DMA transfers. Avoid them by simply using a length of + * DPTR_ALIGN (4) when skb data is aligned to DPTR_ALIGN. + * + * As skb is guaranteed to have at least ETH_ZLEN (60) bytes of + * data by the call to skb_put_padto() above this is safe with + * respect to both the length of the first DMA descriptor (len) + * overflowing the available data and the length of the second DMA + * descriptor (skb->len - len) being negative. + */ + if (len == 0) + len = DPTR_ALIGN; + memcpy(buffer, skb->data, len); dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE); if (dma_mapping_error(ndev->dev.parent, dma_addr)) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index a5b869eb4678..4b100ef4af9f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2939,12 +2939,6 @@ int stmmac_dvr_probe(struct device *device, spin_lock_init(&priv->lock); spin_lock_init(&priv->tx_lock); - ret = register_netdev(ndev); - if (ret) { - pr_err("%s: ERROR %i registering the device\n", __func__, ret); - goto error_netdev_register; - } - /* If a specific clk_csr value is passed from the platform * this means that the CSR Clock Range selection cannot be * changed at run-time and it is fixed. Viceversa the driver'll try to @@ -2969,11 +2963,21 @@ int stmmac_dvr_probe(struct device *device, } } - return 0; + ret = register_netdev(ndev); + if (ret) { + netdev_err(priv->dev, "%s: ERROR %i registering the device\n", + __func__, ret); + goto error_netdev_register; + } + + return ret; -error_mdio_register: - unregister_netdev(ndev); error_netdev_register: + if (priv->pcs != STMMAC_PCS_RGMII && + priv->pcs != STMMAC_PCS_TBI && + priv->pcs != STMMAC_PCS_RTBI) + stmmac_mdio_unregister(ndev); +error_mdio_register: netif_napi_del(&priv->napi); error_hw_init: clk_disable_unprepare(priv->pclk); diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 77d26fe286c0..7e8bce46e6b4 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -549,7 +549,8 @@ fatal_error: static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) { - int queue, len; + int queue; + unsigned int len; struct cpmac_desc *desc; struct cpmac_priv *priv = netdev_priv(dev); @@ -559,7 +560,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb_padto(skb, ETH_ZLEN))) return NETDEV_TX_OK; - len = max(skb->len, ETH_ZLEN); + len = max_t(unsigned int, skb->len, ETH_ZLEN); queue = skb_get_queue_mapping(skb); netif_stop_subqueue(dev, queue); @@ -1236,7 +1237,7 @@ int cpmac_init(void) goto fail_alloc; } -#warning FIXME: unhardcode gpio&reset bits + /* FIXME: unhardcode gpio&reset bits */ ar7_gpio_disable(26); ar7_gpio_disable(27); ar7_device_reset(AR7_RESET_BIT_CPMAC_LO); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 409b48e1e589..e8a09ff9e724 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -40,6 +40,8 @@ #include "hyperv_net.h" +/* Restrict GSO size to account for NVGRE */ +#define NETVSC_GSO_MAX_SIZE 62768 #define RING_SIZE_MIN 64 static int ring_size = 128; @@ -1139,6 +1141,7 @@ static int netvsc_probe(struct hv_device *dev, nvdev = hv_get_drvdata(dev); netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); + netif_set_gso_max_size(net, NETVSC_GSO_MAX_SIZE); ret = register_netdev(net); if (ret != 0) { diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 199a94a9c8bc..3a429f1a8002 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -110,13 +110,26 @@ static int atusb_read_reg(struct atusb *atusb, uint8_t reg) { struct usb_device *usb_dev = atusb->usb_dev; int ret; + uint8_t *buffer; uint8_t value; + buffer = kmalloc(1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + dev_dbg(&usb_dev->dev, "atusb: reg = 0x%x\n", reg); ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, - 0, reg, &value, 1, 1000); - return ret >= 0 ? value : ret; + 0, reg, buffer, 1, 1000); + + if (ret >= 0) { + value = buffer[0]; + kfree(buffer); + return value; + } else { + kfree(buffer); + return ret; + } } static int atusb_write_subreg(struct atusb *atusb, uint8_t reg, uint8_t mask, @@ -517,9 +530,13 @@ static struct ieee802154_ops atusb_ops = { static int atusb_get_and_show_revision(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; - unsigned char buffer[3]; + unsigned char *buffer; int ret; + buffer = kmalloc(3, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + /* Get a couple of the ATMega Firmware values */ ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0, @@ -535,15 +552,20 @@ static int atusb_get_and_show_revision(struct atusb *atusb) dev_info(&usb_dev->dev, "Please update to version 0.2 or newer"); } + kfree(buffer); return ret; } static int atusb_get_and_show_build(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; - char build[ATUSB_BUILD_SIZE + 1]; + char *build; int ret; + build = kmalloc(ATUSB_BUILD_SIZE + 1, GFP_KERNEL); + if (!build) + return -ENOMEM; + ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_BUILD, ATUSB_REQ_FROM_DEV, 0, 0, build, ATUSB_BUILD_SIZE, 1000); @@ -552,6 +574,7 @@ static int atusb_get_and_show_build(struct atusb *atusb) dev_info(&usb_dev->dev, "Firmware: build %s\n", build); } + kfree(build); return ret; } diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c index 860d4aed8274..43617ded3773 100644 --- a/drivers/net/ieee802154/fakelb.c +++ b/drivers/net/ieee802154/fakelb.c @@ -30,7 +30,7 @@ static int numlbs = 2; static LIST_HEAD(fakelb_phys); -static DEFINE_SPINLOCK(fakelb_phys_lock); +static DEFINE_MUTEX(fakelb_phys_lock); static LIST_HEAD(fakelb_ifup_phys); static DEFINE_RWLOCK(fakelb_ifup_phys_lock); @@ -180,9 +180,9 @@ static int fakelb_add_one(struct device *dev) if (err) goto err_reg; - spin_lock(&fakelb_phys_lock); + mutex_lock(&fakelb_phys_lock); list_add_tail(&phy->list, &fakelb_phys); - spin_unlock(&fakelb_phys_lock); + mutex_unlock(&fakelb_phys_lock); return 0; @@ -214,10 +214,10 @@ static int fakelb_probe(struct platform_device *pdev) return 0; err_slave: - spin_lock(&fakelb_phys_lock); + mutex_lock(&fakelb_phys_lock); list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) fakelb_del(phy); - spin_unlock(&fakelb_phys_lock); + mutex_unlock(&fakelb_phys_lock); return err; } @@ -225,10 +225,10 @@ static int fakelb_remove(struct platform_device *pdev) { struct fakelb_phy *phy, *tmp; - spin_lock(&fakelb_phys_lock); + mutex_lock(&fakelb_phys_lock); list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) fakelb_del(phy); - spin_unlock(&fakelb_phys_lock); + mutex_unlock(&fakelb_phys_lock); return 0; } diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index dc7d970bd1c0..effcdbfb06e9 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -164,6 +164,7 @@ static void loopback_setup(struct net_device *dev) { dev->mtu = 64 * 1024; dev->hard_header_len = ETH_HLEN; /* 14 */ + dev->min_header_len = ETH_HLEN; /* 14 */ dev->addr_len = ETH_ALEN; /* 6 */ dev->type = ARPHRD_LOOPBACK; /* 0x0001*/ dev->flags = IFF_LOOPBACK; diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 159a68782bec..79de9608ac48 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -725,7 +725,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, ssize_t n; if (q->flags & IFF_VNET_HDR) { - vnet_hdr_len = q->vnet_hdr_sz; + vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz); err = -EINVAL; if (len < vnet_hdr_len) @@ -865,7 +865,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, if (q->flags & IFF_VNET_HDR) { struct virtio_net_hdr vnet_hdr; - vnet_hdr_len = q->vnet_hdr_sz; + vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz); if (iov_iter_count(iter) < vnet_hdr_len) return -EINVAL; diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 86b28052bf06..9b709f78bb03 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -21,6 +21,23 @@ MODULE_DESCRIPTION("Broadcom 63xx internal PHY driver"); MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); MODULE_LICENSE("GPL"); +static int bcm63xx_config_intr(struct phy_device *phydev) +{ + int reg, err; + + reg = phy_read(phydev, MII_BCM63XX_IR); + if (reg < 0) + return reg; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + reg &= ~MII_BCM63XX_IR_GMASK; + else + reg |= MII_BCM63XX_IR_GMASK; + + err = phy_write(phydev, MII_BCM63XX_IR, reg); + return err; +} + static int bcm63xx_config_init(struct phy_device *phydev) { int reg, err; @@ -55,7 +72,7 @@ static struct phy_driver bcm63xx_driver[] = { .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, - .config_intr = bcm_phy_config_intr, + .config_intr = bcm63xx_config_intr, .driver = { .owner = THIS_MODULE }, }, { /* same phy as above, with just a different OUI */ @@ -68,7 +85,7 @@ static struct phy_driver bcm63xx_driver[] = { .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, - .config_intr = bcm_phy_config_intr, + .config_intr = bcm63xx_config_intr, .driver = { .owner = THIS_MODULE }, } }; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index dd7b7d64c90a..a444294fb555 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1105,9 +1105,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (tun->flags & IFF_VNET_HDR) { - if (len < tun->vnet_hdr_sz) + int vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz); + + if (len < vnet_hdr_sz) return -EINVAL; - len -= tun->vnet_hdr_sz; + len -= vnet_hdr_sz; n = copy_from_iter(&gso, sizeof(gso), from); if (n != sizeof(gso)) @@ -1119,7 +1121,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (tun16_to_cpu(tun, gso.hdr_len) > len) return -EINVAL; - iov_iter_advance(from, tun->vnet_hdr_sz - sizeof(gso)); + iov_iter_advance(from, vnet_hdr_sz - sizeof(gso)); } if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) { @@ -1302,7 +1304,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, vlan_hlen = VLAN_HLEN; if (tun->flags & IFF_VNET_HDR) - vnet_hdr_sz = tun->vnet_hdr_sz; + vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz); total = skb->len + vlan_hlen + vnet_hdr_sz; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 7cba2c3759df..8c408aa2f208 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -462,6 +462,7 @@ static const struct driver_info wwan_info = { #define SAMSUNG_VENDOR_ID 0x04e8 #define LENOVO_VENDOR_ID 0x17ef #define NVIDIA_VENDOR_ID 0x0955 +#define HP_VENDOR_ID 0x03f0 static const struct usb_device_id products[] = { /* BLACKLIST !! @@ -608,6 +609,13 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* HP lt2523 (Novatel E371) - handled by qmi_wwan */ +{ + USB_DEVICE_AND_INTERFACE_INFO(HP_VENDOR_ID, 0x421d, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* AnyDATA ADU960S - handled by qmi_wwan */ { USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index a34f491224c1..09052f9e324f 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -485,6 +485,13 @@ static const struct usb_device_id products[] = { USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&qmi_wwan_info, }, + { /* HP lt2523 (Novatel E371) */ + USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, + USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&qmi_wwan_info, + }, { /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), .driver_info = (unsigned long)&qmi_wwan_info, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2fb637ad594a..fbb1867ff25c 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1645,7 +1645,7 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) u8 checksum = CHECKSUM_NONE; u32 opts2, opts3; - if (tp->version == RTL_VER_01) + if (!(tp->netdev->features & NETIF_F_RXCSUM)) goto return_result; opts2 = le32_to_cpu(rx_desc->opts2); @@ -3442,43 +3442,93 @@ static bool delay_autosuspend(struct r8152 *tp) */ if (!sw_linking && tp->rtl_ops.in_nway(tp)) return true; + else if (!skb_queue_empty(&tp->tx_queue)) + return true; else return false; } -static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) +static int rtl8152_rumtime_suspend(struct r8152 *tp) { - struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev = tp->netdev; int ret = 0; - mutex_lock(&tp->control); + if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { + u32 rcr = 0; - if (PMSG_IS_AUTO(message)) { - if (netif_running(netdev) && delay_autosuspend(tp)) { + if (delay_autosuspend(tp)) { ret = -EBUSY; goto out1; } - set_bit(SELECTIVE_SUSPEND, &tp->flags); - } else { - netif_device_detach(netdev); + if (netif_carrier_ok(netdev)) { + u32 ocp_data; + + rcr = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data = rcr & ~RCR_ACPT_ALL; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + rxdy_gated_en(tp, true); + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, + PLA_OOB_CTRL); + if (!(ocp_data & RXFIFO_EMPTY)) { + rxdy_gated_en(tp, false); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); + ret = -EBUSY; + goto out1; + } + } + + clear_bit(WORK_ENABLE, &tp->flags); + usb_kill_urb(tp->intr_urb); + + rtl_runtime_suspend_enable(tp, true); + + if (netif_carrier_ok(netdev)) { + napi_disable(&tp->napi); + rtl_stop_rx(tp); + rxdy_gated_en(tp, false); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); + napi_enable(&tp->napi); + } } + set_bit(SELECTIVE_SUSPEND, &tp->flags); + +out1: + return ret; +} + +static int rtl8152_system_suspend(struct r8152 *tp) +{ + struct net_device *netdev = tp->netdev; + int ret = 0; + + netif_device_detach(netdev); + if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); napi_disable(&tp->napi); - if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { - rtl_stop_rx(tp); - rtl_runtime_suspend_enable(tp, true); - } else { - cancel_delayed_work_sync(&tp->schedule); - tp->rtl_ops.down(tp); - } + cancel_delayed_work_sync(&tp->schedule); + tp->rtl_ops.down(tp); napi_enable(&tp->napi); } -out1: + + return ret; +} + +static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct r8152 *tp = usb_get_intfdata(intf); + int ret; + + mutex_lock(&tp->control); + + if (PMSG_IS_AUTO(message)) + ret = rtl8152_rumtime_suspend(tp); + else + ret = rtl8152_system_suspend(tp); + mutex_unlock(&tp->control); return ret; @@ -4173,6 +4223,11 @@ static int rtl8152_probe(struct usb_interface *intf, NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; + if (tp->version == RTL_VER_01) { + netdev->features &= ~NETIF_F_RXCSUM; + netdev->hw_features &= ~NETIF_F_RXCSUM; + } + netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 903bda437839..d6b619667f1a 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -301,7 +301,9 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, .flowi4_tos = RT_TOS(ip4h->tos), .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF, + .flowi4_proto = ip4h->protocol, .daddr = ip4h->daddr, + .saddr = ip4h->saddr, }; if (vrf_send_v4_prep(skb, &fl4, vrf_dev)) @@ -410,6 +412,8 @@ static int vrf_finish_output6(struct net *net, struct sock *sk, struct in6_addr *nexthop; int ret; + nf_reset(skb); + skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; @@ -521,6 +525,8 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s u32 nexthop; int ret = -EINVAL; + nf_reset(skb); + /* Be paranoid, rather than too clever. */ if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { struct sk_buff *skb2; @@ -919,6 +925,8 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, return -EINVAL; vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]); + if (vrf->tb_id == RT_TABLE_UNSPEC) + return -EINVAL; dev->priv_flags |= IFF_L3MDEV_MASTER; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 284caf81e808..486af5dac5df 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -332,5 +332,6 @@ source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/cw1200/Kconfig" source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/cnss/Kconfig" +source "drivers/net/wireless/cnss_genl/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 818fa279b25d..0204fc00f0c5 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -66,3 +66,4 @@ obj-$(CONFIG_WCNSS_CORE) += wcnss/ obj-$(CONFIG_CNSS) += cnss/ obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/ obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/ +obj-$(CONFIG_CNSS_GENL) += cnss_genl/ diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 25b23bf2c8e6..5fe8bc184868 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -26,6 +26,7 @@ ath10k_pci-y += pci.o \ ce.o obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o ath10k_snoc-y += snoc.o \ + qmi.o \ ce.o ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index b8a3a1ecabaa..9cda1303c9e1 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -455,6 +455,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, u32 desc_flags = 0; int ret = 0; + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return -ESHUTDOWN; + if (nbytes > ce_state->src_sz_max) ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n", __func__, nbytes, ce_state->src_sz_max); @@ -942,6 +945,9 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) struct ath10k_ce_pipe *ce_state; struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return; + if (ar->target_version == ATH10K_HW_WCN3990) intr_summary = 0xFFF; else diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index d37ed66d767b..9acaffa51516 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1536,7 +1536,6 @@ static void ath10k_core_restart(struct work_struct *work) struct ath10k *ar = container_of(work, struct ath10k, restart_work); set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags); - ath10k_gen_set_base_mac_addr(ar, ar->base_mac_addr); /* Place a barrier to make sure the compiler doesn't reorder * CRASH_FLUSH and calling other functions. @@ -2390,6 +2389,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, mutex_init(&ar->conf_mutex); spin_lock_init(&ar->data_lock); spin_lock_init(&ar->txqs_lock); + spin_lock_init(&ar->datapath_rx_stat_lock); INIT_LIST_HEAD(&ar->txqs); INIT_LIST_HEAD(&ar->peers); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 21c63d5d3ead..01d5ecc4f6b8 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -70,6 +70,20 @@ #define ATH10K_NAPI_BUDGET 64 #define ATH10K_NAPI_QUOTA_LIMIT 60 +#define ATH10K_RX_MCS_MIN 0 +#define ATH10K_RX_HT_MCS_MAX 32 +#define ATH10K_RX_VHT_RATEIDX_MAX 9 +#define ATH10K_RX_VHT_MCS_MAX 20 /* For 2x2 */ +#define ATH10K_RX_NSS_MIN 0 +#define ATH10K_RX_NSS_MAX 5 + +enum ath10k_datapath_rx_band { + ATH10K_BAND_MIN, + ATH10K_BAND_2GHZ = ATH10K_BAND_MIN, + ATH10K_BAND_5GHZ, + ATH10K_BAND_MAX, +}; + struct ath10k; enum ath10k_bus { @@ -703,6 +717,20 @@ struct ath10k_fw_components { struct ath10k_fw_file fw_file; }; +struct datapath_rx_stats { + u32 no_of_packets; + u32 short_gi_pkts; + u32 ht_rate_indx[ATH10K_RX_HT_MCS_MAX + 1]; + u32 vht_rate_indx[ATH10K_RX_VHT_MCS_MAX + 1]; + u32 ht_rate_packets; + u32 vht_rate_packets; + u32 legacy_pkt; + u32 nss[ATH10K_RX_NSS_MAX + 1]; + u32 num_pkts_40Mhz; + u32 num_pkts_80Mhz; + u32 band[ATH10K_BAND_MAX + 1]; +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -729,7 +757,7 @@ struct ath10k { u32 max_spatial_stream; /* protected by conf_mutex */ bool ani_enabled; - + bool sifs_burst_enabled; bool p2p; struct { @@ -900,7 +928,10 @@ struct ath10k { enum ath10k_spectral_mode mode; struct ath10k_spec_scan config; } spectral; + struct datapath_rx_stats *rx_stats; #endif + /* prevent concurrency histogram for receiving data packet */ + spinlock_t datapath_rx_stat_lock; struct { /* protected by conf_mutex */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index c36c2481856b..ec8063e7986a 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -537,6 +537,230 @@ static const struct file_operations fops_fw_stats = { .llseek = default_llseek, }; +static inline int is_vht_rate_valid(u32 rate_indx) +{ + if ((rate_indx >= ATH10K_RX_MCS_MIN) && + (rate_indx <= ATH10K_RX_VHT_RATEIDX_MAX)) + return 1; + else + return 0; +} + +void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status) +{ + struct datapath_rx_stats *stat_cnt = ar->rx_stats; + + spin_lock_bh(&ar->datapath_rx_stat_lock); + + stat_cnt->no_of_packets += 1; + if (!(stat_cnt->no_of_packets)) { + memset(stat_cnt, 0, sizeof(*stat_cnt)); + stat_cnt->no_of_packets += 1; + } + + if (status->flag & RX_FLAG_SHORT_GI) + stat_cnt->short_gi_pkts += 1; + + if ((status->vht_nss >= ATH10K_RX_NSS_MIN) && + (status->vht_nss < ATH10K_RX_NSS_MAX)) { + stat_cnt->nss[status->vht_nss] += 1; + if (status->flag & RX_FLAG_VHT) { + stat_cnt->vht_rate_packets += 1; + if (is_vht_rate_valid(status->rate_idx)) { + stat_cnt->vht_rate_indx[((status->vht_nss - 1) * + 10) + status->rate_idx] += 1; + } else { + /*if we get index other than (>=0 and <=9)*/ + stat_cnt->vht_rate_indx[ATH10K_RX_VHT_MCS_MAX] += 1; + } + } else if (status->flag & RX_FLAG_HT) { + stat_cnt->ht_rate_packets += 1; + if ((status->rate_idx >= ATH10K_RX_MCS_MIN) && + (status->rate_idx < ATH10K_RX_HT_MCS_MAX)) + stat_cnt->ht_rate_indx[status->rate_idx] += 1; + else { + /*if we get index other than (>=0 and <=31)*/ + stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX] += 1; + } + } else { + /* if pkt is other than HT and VHT */ + stat_cnt->legacy_pkt += 1; + } + } else { + stat_cnt->nss[ATH10K_RX_NSS_MAX] += 1; + } + + if (status->flag & RX_FLAG_40MHZ) + stat_cnt->num_pkts_40Mhz += 1; + if (status->vht_flag & RX_VHT_FLAG_80MHZ) + stat_cnt->num_pkts_80Mhz += 1; + if ((status->band >= ATH10K_BAND_MIN) && + (status->band < ATH10K_BAND_MAX)) { + stat_cnt->band[status->band] += 1; + } else { + /*if band is other than 0,1 */ + stat_cnt->band[ATH10K_BAND_MAX] += 1; + } + + spin_unlock_bh(&ar->datapath_rx_stat_lock); +} + +size_t get_datapath_stat(char *buf, struct ath10k *ar) +{ + u8 i; + struct datapath_rx_stats *stat_cnt = ar->rx_stats; + size_t j = 0; + + spin_lock(&ar->datapath_rx_stat_lock); + + j = snprintf(buf, ATH10K_DATAPATH_BUF_SIZE, "\nNo of packets: %u\t" + "No of short_gi packets: %u\n" + "\nHT Packets: %u \t VHT Packets: %u\n" + "\n40Mhz Packets: %u \t 80Mhz Packets: %u\n" + "\n2.4GHz: %u \t 5GHz: %u \t band-error: %u\n\n", + stat_cnt->no_of_packets, + stat_cnt->short_gi_pkts, + stat_cnt->ht_rate_packets, + stat_cnt->vht_rate_packets, + stat_cnt->num_pkts_40Mhz, + stat_cnt->num_pkts_80Mhz, + stat_cnt->band[ATH10K_BAND_2GHZ], + stat_cnt->band[ATH10K_BAND_5GHZ], + stat_cnt->band[ATH10K_BAND_MAX]); + + for (i = 0; i <= ATH10K_RX_NSS_MAX; i++) { + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "NSS-%u: %u\t", i, stat_cnt->nss[i]); + } + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "\n\n----HT Rate index------\n"); + + for (i = ATH10K_RX_MCS_MIN; i < ATH10K_RX_HT_MCS_MAX; + i += 4) { + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\t" + "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\n", + i, stat_cnt->ht_rate_indx[i], + i + 1, stat_cnt->ht_rate_indx[i + 1], + i + 2, stat_cnt->ht_rate_indx[i + 2], + i + 3, stat_cnt->ht_rate_indx[i + 3]); + } + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "ht_rate_indx[OOB]: %10u\n", + stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX]); + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "\n----VHT Rate index------\n"); + + for (i = ATH10K_RX_MCS_MIN; + i <= ATH10K_RX_VHT_RATEIDX_MAX; i++) { + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "vht_rate_indx[%02u]: %10u\tvht_rate_indx[%02u]: %10u\n", + i, stat_cnt->vht_rate_indx[i], + i + 10, stat_cnt->vht_rate_indx[i + 10]); + } + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "vht_rate_indx[%02u]: %10u\n", + i + 10, stat_cnt->vht_rate_indx[i + 10]); + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "\nnumber of pkt other than HT and VHT(legacy) : %u\n" + "----------------------\n", + stat_cnt->legacy_pkt); + + spin_unlock(&ar->datapath_rx_stat_lock); + + return j; +} + +static int ath10k_datapath_stats_open(struct inode *inode, struct file *file) +{ + struct ath10k *ar = inode->i_private; + int ret; + + spin_lock(&ar->datapath_rx_stat_lock); + + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + file->private_data = ar; + + spin_unlock(&ar->datapath_rx_stat_lock); + return 0; + +err_unlock: + spin_unlock(&ar->datapath_rx_stat_lock); + return ret; +} + +static ssize_t ath10k_datapath_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + size_t buf_len; + unsigned int ret; + void *buf = NULL; + + buf = vmalloc(ATH10K_DATAPATH_BUF_SIZE); + if (!buf) + return 0; + + buf_len = get_datapath_stat(buf, ar); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, buf_len); + vfree(buf); + + return ret; +} + +static ssize_t ath10k_datapath_stats_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + u32 filter; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &filter)) + return -EINVAL; + + spin_lock(&ar->datapath_rx_stat_lock); + + if (ar->state != ATH10K_STATE_ON) { + ret = count; + goto err_unlock; + } + + if (!filter) + memset(ar->rx_stats, 0, sizeof(*ar->rx_stats)); + + ret = count; + +err_unlock: + spin_unlock(&ar->datapath_rx_stat_lock); + return ret; +} + +static int ath10k_datapath_stats_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations fops_datapath_stats = { + .open = ath10k_datapath_stats_open, + .read = ath10k_datapath_stats_read, + .write = ath10k_datapath_stats_write, + .release = ath10k_datapath_stats_release, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1572,6 +1796,64 @@ static const struct file_operations fops_ani_enable = { .llseek = default_llseek, }; +static ssize_t ath10k_write_sifs_burst_enable(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + int ret; + u8 enable; + + if (kstrtou8_from_user(user_buf, count, 0, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->sifs_burst_enabled == enable) { + ret = count; + goto exit; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->burst_enable, + enable); + if (ret) { + ath10k_warn(ar, "sifs_burst_enable failed: %d\n", ret); + goto exit; + } + ar->sifs_burst_enabled = enable; + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static ssize_t ath10k_read_sifs_burst_enable(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + int len; + bool ret = false; + + if (ar->sifs_burst_enabled) + ret = true; + + len = scnprintf(buf, sizeof(buf), "%d\n", ret); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_sifs_burst_enable = { + .read = ath10k_read_sifs_burst_enable, + .write = ath10k_write_sifs_burst_enable, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static const struct file_operations fops_cal_data = { .open = ath10k_debug_cal_data_open, .read = ath10k_debug_cal_data_read, @@ -2343,7 +2625,11 @@ int ath10k_debug_create(struct ath10k *ar) ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); if (!ar->debug.cal_data) - return -ENOMEM; + goto err_cal_data; + + ar->rx_stats = vzalloc(sizeof(*ar->rx_stats)); + if (!ar->rx_stats) + goto err_rx_stats; INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs); INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs); @@ -2351,6 +2637,13 @@ int ath10k_debug_create(struct ath10k *ar) INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd); return 0; + +err_rx_stats: + vfree(ar->debug.cal_data); + +err_cal_data: + vfree(ar->debug.fw_crash_data); + return -ENOMEM; } void ath10k_debug_destroy(struct ath10k *ar) @@ -2361,6 +2654,9 @@ void ath10k_debug_destroy(struct ath10k *ar) vfree(ar->debug.cal_data); ar->debug.cal_data = NULL; + vfree(ar->rx_stats); + ar->rx_stats = NULL; + ath10k_debug_fw_stats_reset(ar); kfree(ar->debug.tpc_stats); @@ -2383,6 +2679,9 @@ int ath10k_debug_register(struct ath10k *ar) init_completion(&ar->debug.tpc_complete); init_completion(&ar->debug.fw_stats_complete); + debugfs_create_file("datapath_rx_stats", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_datapath_stats); + debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_fw_stats); @@ -2432,6 +2731,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_ani_enable); + debugfs_create_file("sifs_burst_enable", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_sifs_burst_enable); + if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) { debugfs_create_file("dfs_simulate_radar", S_IWUSR, ar->debug.debugfs_phy, ar, diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index b1db01a167ac..f963391e3544 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -38,7 +38,7 @@ enum ath10k_debug_mask { ATH10K_DBG_WMI_PRINT = 0x00002000, ATH10K_DBG_PCI_PS = 0x00004000, ATH10K_DBG_AHB = 0x00008000, - ATH10K_DBG_SNOC = 0x00009000, + ATH10K_DBG_SNOC = 0x00010000, ATH10K_DBG_ANY = 0xffffffff, }; @@ -59,6 +59,7 @@ enum ath10k_dbg_aggr_mode { /* FIXME: How to calculate the buffer size sanely? */ #define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024) +#define ATH10K_DATAPATH_BUF_SIZE (1024 * 1024) extern unsigned int ath10k_debug_mask; @@ -95,6 +96,8 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw, void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ethtool_stats *stats, u64 *data); +void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status); +size_t get_datapath_stat(char *buf, struct ath10k *ar); #else static inline int ath10k_debug_start(struct ath10k *ar) { @@ -145,6 +148,16 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) return NULL; } +static inline void fill_datapath_stats(struct ath10k *ar, + struct ieee80211_rx_status *status) +{ +} + +static inline size_t get_datapath_stat(char *buf, struct ath10k *ar) +{ + return 0; +} + #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) #define ath10k_debug_get_et_strings NULL diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index ddf097e3a143..437ea2c192b3 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -940,7 +940,7 @@ static void ath10k_process_rx(struct ath10k *ar, status = IEEE80211_SKB_RXCB(skb); *status = *rx_status; - + fill_datapath_stats(ar, status); ath10k_dbg(ar, ATH10K_DBG_DATA, "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8d49acd3c9f5..35e5d980ed49 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4450,7 +4450,8 @@ static int ath10k_start(struct ieee80211_hw *hw) ar->state = ATH10K_STATE_ON; break; case ATH10K_STATE_RESTARTING: - ath10k_halt(ar); + if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + ath10k_halt(ar); ar->state = ATH10K_STATE_RESTARTED; break; case ATH10K_STATE_ON: @@ -4538,6 +4539,7 @@ static int ath10k_start(struct ieee80211_hw *hw) ret); goto err_core_stop; } + ar->sifs_burst_enabled = false; } param = ar->wmi.pdev_param->ani_enable; diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c new file mode 100644 index 000000000000..7b5fc52d269a --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <soc/qcom/subsystem_notif.h> +#include <soc/qcom/subsystem_restart.h> +#include <soc/qcom/service-notifier.h> +#include "core.h" +#include "qmi.h" +#include "snoc.h" +#include <soc/qcom/icnss.h> + +static int +ath10k_snoc_service_notifier_notify(struct notifier_block *nb, + unsigned long notification, void *data) +{ + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, + service_notifier_nb); + enum pd_subsys_state *state = data; + struct ath10k *ar = ar_snoc->ar; + + switch (notification) { + case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service down, data: 0x%pK\n", + data); + + if (!state || *state != ROOT_PD_SHUTDOWN) + atomic_set(&ar_snoc->fw_crashed, 1); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD went down %d\n", + atomic_read(&ar_snoc->fw_crashed)); + break; + case SERVREG_NOTIF_SERVICE_STATE_UP_V01: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service up\n"); + queue_work(ar->workqueue, &ar->restart_work); + break; + default: + ath10k_dbg(ar, ATH10K_DBG_SNOC, + "Service state Unknown, notification: 0x%lx\n", + notification); + return NOTIFY_DONE; + } + return NOTIFY_OK; +} + +static int ath10k_snoc_get_service_location_notify(struct notifier_block *nb, + unsigned long opcode, + void *data) +{ + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, + get_service_nb); + struct ath10k *ar = ar_snoc->ar; + struct pd_qmi_client_data *pd = data; + int curr_state; + int ret; + int i; + struct ath10k_service_notifier_context *notifier; + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service notify opcode: %lu\n", + opcode); + + if (opcode != LOCATOR_UP) + return NOTIFY_DONE; + + if (!pd->total_domains) { + ath10k_err(ar, "Did not find any domains\n"); + ret = -ENOENT; + goto out; + } + + notifier = kcalloc(pd->total_domains, + sizeof(struct ath10k_service_notifier_context), + GFP_KERNEL); + if (!notifier) { + ret = -ENOMEM; + goto out; + } + + ar_snoc->service_notifier_nb.notifier_call = + ath10k_snoc_service_notifier_notify; + + for (i = 0; i < pd->total_domains; i++) { + ath10k_dbg(ar, ATH10K_DBG_SNOC, + "%d: domain_name: %s, instance_id: %d\n", i, + pd->domain_list[i].name, + pd->domain_list[i].instance_id); + + notifier[i].handle = + service_notif_register_notifier( + pd->domain_list[i].name, + pd->domain_list[i].instance_id, + &ar_snoc->service_notifier_nb, + &curr_state); + notifier[i].instance_id = pd->domain_list[i].instance_id; + strlcpy(notifier[i].name, pd->domain_list[i].name, + QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1); + + if (IS_ERR(notifier[i].handle)) { + ath10k_err(ar, "%d: Unable to register notifier for %s(0x%x)\n", + i, pd->domain_list->name, + pd->domain_list->instance_id); + ret = PTR_ERR(notifier[i].handle); + goto free_handle; + } + } + + ar_snoc->service_notifier = notifier; + ar_snoc->total_domains = pd->total_domains; + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD restart enabled\n"); + + return NOTIFY_OK; + +free_handle: + for (i = 0; i < pd->total_domains; i++) { + if (notifier[i].handle) { + service_notif_unregister_notifier( + notifier[i].handle, + &ar_snoc->service_notifier_nb); + } + } + kfree(notifier); + +out: + ath10k_err(ar, "PD restart not enabled: %d\n", ret); + + return NOTIFY_OK; +} + +int ath10k_snoc_pd_restart_enable(struct ath10k *ar) +{ + int ret; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service location\n"); + + ar_snoc->get_service_nb.notifier_call = + ath10k_snoc_get_service_location_notify; + ret = get_service_location(ATH10K_SERVICE_LOCATION_CLIENT_NAME, + ATH10K_WLAN_SERVICE_NAME, + &ar_snoc->get_service_nb); + if (ret) { + ath10k_err(ar, "Get service location failed: %d\n", ret); + goto out; + } + + return 0; +out: + ath10k_err(ar, "PD restart not enabled: %d\n", ret); + return ret; +} + +int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar) +{ + int i; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + for (i = 0; i < ar_snoc->total_domains; i++) { + if (ar_snoc->service_notifier[i].handle) + service_notif_unregister_notifier( + ar_snoc->service_notifier[i].handle, + &ar_snoc->service_notifier_nb); + } + + kfree(ar_snoc->service_notifier); + + ar_snoc->service_notifier = NULL; + + return 0; +} + +static int ath10k_snoc_modem_notifier_nb(struct notifier_block *nb, + unsigned long code, + void *data) +{ + struct notif_data *notif = data; + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, + modem_ssr_nb); + struct ath10k *ar = ar_snoc->ar; + + if (code != SUBSYS_BEFORE_SHUTDOWN) + return NOTIFY_OK; + + if (notif->crashed) + atomic_set(&ar_snoc->fw_crashed, 1); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Modem went down %d\n", + atomic_read(&ar_snoc->fw_crashed)); + if (notif->crashed) + queue_work(ar->workqueue, &ar->restart_work); + + return NOTIFY_OK; +} + +int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar) +{ + int ret = 0; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + ar_snoc->modem_ssr_nb.notifier_call = ath10k_snoc_modem_notifier_nb; + + ar_snoc->modem_notify_handler = + subsys_notif_register_notifier("modem", &ar_snoc->modem_ssr_nb); + + if (IS_ERR(ar_snoc->modem_notify_handler)) { + ret = PTR_ERR(ar_snoc->modem_notify_handler); + ath10k_err(ar, "Modem register notifier failed: %d\n", ret); + } + + return ret; +} + +int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + subsys_notif_unregister_notifier(ar_snoc->modem_notify_handler, + &ar_snoc->modem_ssr_nb); + ar_snoc->modem_notify_handler = NULL; + + return 0; +} + diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h new file mode 100644 index 000000000000..f8ba3288753b --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/qmi.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _QMI_H_ +#define _QMI_H_ +int ath10k_snoc_pd_restart_enable(struct ath10k *ar); +int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar); +int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar); +int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index f6eb131e1e1b..89042dcf70a0 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -24,6 +24,7 @@ #include "htc.h" #include "ce.h" #include "snoc.h" +#include "qmi.h" #include <soc/qcom/icnss.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -413,6 +414,20 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = { { 11, WCN3990_DST_WR_INDEX_OFFSET}, }; +static bool ath10k_snoc_has_fw_crashed(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + return atomic_read(&ar_snoc->fw_crashed); +} + +static void ath10k_snoc_fw_crashed_clear(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + atomic_set(&ar_snoc->fw_crashed, 0); +} + void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); @@ -655,6 +670,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, "snoc tx item %d paddr %pad len %d n_items %d\n", i, &items[i].paddr, items[i].len, n_items); + if (ath10k_snoc_has_fw_crashed(ar)) + return -EINVAL; + err = ath10k_ce_send_nolock(ce_pipe, items[i].transfer_context, items[i].paddr, @@ -867,11 +885,17 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar) { if (!ar) return; - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); - ath10k_snoc_irq_disable(ar); + if (ath10k_snoc_has_fw_crashed(ar) || + test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { + ath10k_snoc_free_irq(ar); + } else { + ath10k_snoc_irq_disable(ar); + } + ath10k_snoc_flush(ar); napi_synchronize(&ar->napi); napi_disable(&ar->napi); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); } static int ath10k_snoc_alloc_pipes(struct ath10k *ar) @@ -1002,24 +1026,28 @@ static void ath10k_snoc_free_irq(struct ath10k *ar) static int ath10k_snoc_get_soc_info(struct ath10k *ar) { - int ret; - struct icnss_soc_info soc_info; + struct resource *res; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + struct platform_device *pdev; - memset(&soc_info, 0, sizeof(soc_info)); - - ret = icnss_get_soc_info(&soc_info); - if (ret < 0) { - ath10k_err(ar, "%s: icnss_get_soc_info error = %d", - __func__, ret); - return ret; + pdev = ar_snoc->dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase"); + if (!res) { + ath10k_err(ar, "Memory base not found in DT\n"); + return -EINVAL; } - ar_snoc->mem = soc_info.v_addr; - ar_snoc->mem_pa = soc_info.p_addr; + ar_snoc->mem_pa = res->start; + ar_snoc->mem = devm_ioremap(&pdev->dev, ar_snoc->mem_pa, + resource_size(res)); + if (!ar_snoc->mem) { + ath10k_err(ar, "Memory base ioremap failed: phy addr: %pa\n", + &ar_snoc->mem_pa); + return -EINVAL; + } - ar_snoc->target_info.soc_version = soc_info.soc_id; - ar_snoc->target_info.target_version = soc_info.soc_id; + ar_snoc->target_info.soc_version = ATH10K_HW_WCN3990; + ar_snoc->target_info.target_version = ATH10K_HW_WCN3990; ar_snoc->target_info.target_revision = 0; ath10k_dbg(ar, ATH10K_DBG_SNOC, @@ -1034,10 +1062,8 @@ static int ath10k_snoc_get_soc_info(struct ath10k *ar) static int ath10k_snoc_wlan_enable(struct ath10k *ar) { struct icnss_wlan_enable_cfg cfg; - int pipe_num, i; + int pipe_num; struct ath10k_ce_tgt_pipe_cfg tgt_cfg[CE_COUNT_MAX]; - struct ce_tgt_pipe_cfg *tmp_tgt_cfg; - struct ce_svc_pipe_cfg *tmp_svc_cfg; for (pipe_num = 0; pipe_num < CE_COUNT_MAX; pipe_num++) { tgt_cfg[pipe_num].pipe_num = @@ -1066,12 +1092,6 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar) cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *) &target_shadow_reg_cfg_map; - for (i = 0; i < cfg.num_ce_tgt_cfg; i++) - tmp_tgt_cfg = cfg.ce_tgt_cfg + i; - - for (i = 0; i < cfg.num_ce_svc_pipe_cfg; i++) - tmp_svc_cfg = cfg.ce_svc_cfg + i; - return icnss_wlan_enable(&cfg, ICNSS_MISSION, "5.1.0.26N"); } @@ -1091,9 +1111,14 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar) static int ath10k_snoc_hif_start(struct ath10k *ar) { - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); + if (ath10k_snoc_has_fw_crashed(ar)) { + ath10k_snoc_request_irq(ar); + ath10k_snoc_fw_crashed_clear(ar); + } ath10k_snoc_irq_enable(ar); ath10k_snoc_rx_post(ar); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); return 0; } @@ -1114,7 +1139,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n", __func__, ar->state); - if (ar->state == ATH10K_STATE_ON) { + if (ar->state == ATH10K_STATE_ON || + test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { ret = ath10k_snoc_bus_configure(ar); if (ret) ath10k_err(ar, "failed to configure bus: %d\n", ret); @@ -1137,6 +1163,10 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget) struct ath10k *ar = container_of(ctx, struct ath10k, napi); int done = 0; + if (ath10k_snoc_has_fw_crashed(ar)) { + napi_complete(ctx); + return done; + } ath10k_ce_per_engine_service_any(ar); done = ath10k_htt_txrx_compl_task(ar, budget); @@ -1258,6 +1288,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_free_irq; } + + ath10k_snoc_modem_ssr_register_notifier(ar); + ath10k_snoc_pd_restart_enable(ar); + ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 probed\n", __func__); return 0; @@ -1286,6 +1320,8 @@ static int ath10k_snoc_remove(struct platform_device *pdev) return -EINVAL; ath10k_core_unregister(ar); + ath10k_snoc_pdr_unregister_notifier(ar); + ath10k_snoc_modem_ssr_unregister_notifier(ar); ath10k_snoc_free_irq(ar); ath10k_snoc_release_resource(ar); ath10k_snoc_free_pipes(ar); diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 0a5f5bff37ec..c62519b2a340 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -16,8 +16,11 @@ #include "hw.h" #include "ce.h" #include "pci.h" +#include <soc/qcom/service-locator.h> #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 +#define ATH10K_SERVICE_LOCATION_CLIENT_NAME "ATH10K-WLAN" +#define ATH10K_WLAN_SERVICE_NAME "wlan/fw" /* struct snoc_state: SNOC target state * @pipe_cfg_addr: pipe configuration address @@ -88,6 +91,17 @@ struct ath10k_target_info { u32 soc_version; }; +/* struct ath10k_service_notifier_context: service notification context + * @handle: notifier handle + * @instance_id: domain instance id + * @name: domain name + */ +struct ath10k_service_notifier_context { + void *handle; + u32 instance_id; + char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1]; +}; + /* struct ath10k_snoc: SNOC info struct * @dev: device structure * @ar:ath10k base structure @@ -101,6 +115,13 @@ struct ath10k_target_info { * @rx_post_retry: rx buffer post processing timer * @vaddr_rri_on_ddr: virtual address for RRI * @is_driver_probed: flag to indicate driver state + * @modem_ssr_nb: notifier callback for modem notification + * @modem_notify_handler: modem notification handler + * @service_notifier: notifier context for service notification + * @service_notifier_nb: notifier callback for service notification + * @total_domains: no of service domains + * @get_service_nb: notifier callback for service discovery + * @fw_crashed: fw state flag */ struct ath10k_snoc { struct bus_opaque opaque_ctx; @@ -115,6 +136,13 @@ struct ath10k_snoc { u32 ce_irqs[CE_COUNT_MAX]; u32 *vaddr_rri_on_ddr; bool is_driver_probed; + struct notifier_block modem_ssr_nb; + void *modem_notify_handler; + struct ath10k_service_notifier_context *service_notifier; + struct notifier_block service_notifier_nb; + int total_domains; + struct notifier_block get_service_nb; + atomic_t fw_crashed; }; /* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration @@ -171,6 +199,11 @@ struct ath10k_wlan_enable_cfg { struct ath10k_shadow_reg_cfg *shadow_reg_cfg; }; +struct ath10k_event_pd_down_data { + bool crashed; + bool fw_rejuvenate; +}; + /* enum ath10k_driver_mode: ath10k driver mode * @ATH10K_MISSION: mission mode * @ATH10K_FTM: ftm mode diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index 7d9b0da1b010..2ffc1fe4923b 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -338,7 +338,7 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, } else { res = -EINVAL; } - } else if (strncmp("background", buf, 9) == 0) { + } else if (strncmp("background", buf, 10) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND); } else if (strncmp("manual", buf, 6) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL); diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 3fef967ca1ad..75f2528b8b84 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -553,7 +553,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_tlv_event_tx_pause(ar, skb); break; default: - ath10k_warn(ar, "Unknown eventid: %d\n", id); + ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id); break; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4dd60b74853d..cbbba8d79e6b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1799,6 +1799,9 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) { int ret = -EOPNOTSUPP; + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return -ESHUTDOWN; + might_sleep(); if (cmd_id == WMI_CMD_UNSUPPORTED) { diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index dc44cfef7517..16e052d02c94 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -502,8 +502,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, break; return -EOPNOTSUPP; default: - WARN_ON(1); - return -EINVAL; + return -EOPNOTSUPP; } mutex_lock(&ah->lock); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 694ca2e680e5..74670e08e6da 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -73,13 +73,13 @@ #define AR9300_OTP_BASE \ ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30000 : 0x14000) #define AR9300_OTP_STATUS \ - ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30018 : 0x15f18) + ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x31018 : 0x15f18) #define AR9300_OTP_STATUS_TYPE 0x7 #define AR9300_OTP_STATUS_VALID 0x4 #define AR9300_OTP_STATUS_ACCESS_BUSY 0x2 #define AR9300_OTP_STATUS_SM_BUSY 0x1 #define AR9300_OTP_READ_DATA \ - ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3001c : 0x15f1c) + ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3101c : 0x15f1c) enum targetPowerHTRates { HT_TARGET_RATE_0_8_16, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b42f4a963ef4..a660e40f2df1 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -959,6 +959,7 @@ struct ath_softc { struct survey_info *cur_survey; struct survey_info survey[ATH9K_NUM_CHANNELS]; + spinlock_t intr_lock; struct tasklet_struct intr_tq; struct tasklet_struct bcon_tasklet; struct ath_hw *sc_ah; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index bc70ce62bc03..0f5672f5c9ba 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -619,6 +619,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, common->bt_ant_diversity = 1; spin_lock_init(&common->cc_lock); + spin_lock_init(&sc->intr_lock); spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); spin_lock_init(&sc->chan_lock); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index bba85d1a6cd1..d937c39b3a0b 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -805,21 +805,12 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_disable_interrupts); -void ath9k_hw_enable_interrupts(struct ath_hw *ah) +static void __ath9k_hw_enable_interrupts(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 sync_default = AR_INTR_SYNC_DEFAULT; u32 async_mask; - if (!(ah->imask & ATH9K_INT_GLOBAL)) - return; - - if (!atomic_inc_and_test(&ah->intr_ref_cnt)) { - ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", - atomic_read(&ah->intr_ref_cnt)); - return; - } - if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; @@ -841,6 +832,39 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); } + +void ath9k_hw_resume_interrupts(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (!(ah->imask & ATH9K_INT_GLOBAL)) + return; + + if (atomic_read(&ah->intr_ref_cnt) != 0) { + ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", + atomic_read(&ah->intr_ref_cnt)); + return; + } + + __ath9k_hw_enable_interrupts(ah); +} +EXPORT_SYMBOL(ath9k_hw_resume_interrupts); + +void ath9k_hw_enable_interrupts(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (!(ah->imask & ATH9K_INT_GLOBAL)) + return; + + if (!atomic_inc_and_test(&ah->intr_ref_cnt)) { + ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", + atomic_read(&ah->intr_ref_cnt)); + return; + } + + __ath9k_hw_enable_interrupts(ah); +} EXPORT_SYMBOL(ath9k_hw_enable_interrupts); void ath9k_hw_set_interrupts(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 7fbf7f965f61..1b63d26f30ce 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -748,6 +748,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah); void ath9k_hw_enable_interrupts(struct ath_hw *ah); void ath9k_hw_disable_interrupts(struct ath_hw *ah); void ath9k_hw_kill_interrupts(struct ath_hw *ah); +void ath9k_hw_resume_interrupts(struct ath_hw *ah); void ar9002_hw_attach_mac_ops(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 7be31f27266c..3abc64574116 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -373,21 +373,20 @@ void ath9k_tasklet(unsigned long data) struct ath_common *common = ath9k_hw_common(ah); enum ath_reset_type type; unsigned long flags; - u32 status = sc->intrstatus; + u32 status; u32 rxmask; + spin_lock_irqsave(&sc->intr_lock, flags); + status = sc->intrstatus; + sc->intrstatus = 0; + spin_unlock_irqrestore(&sc->intr_lock, flags); + ath9k_ps_wakeup(sc); spin_lock(&sc->sc_pcu_lock); if (status & ATH9K_INT_FATAL) { type = RESET_TYPE_FATAL_INT; ath9k_queue_reset(sc, type); - - /* - * Increment the ref. counter here so that - * interrupts are enabled in the reset routine. - */ - atomic_inc(&ah->intr_ref_cnt); ath_dbg(common, RESET, "FATAL: Skipping interrupts\n"); goto out; } @@ -403,11 +402,6 @@ void ath9k_tasklet(unsigned long data) type = RESET_TYPE_BB_WATCHDOG; ath9k_queue_reset(sc, type); - /* - * Increment the ref. counter here so that - * interrupts are enabled in the reset routine. - */ - atomic_inc(&ah->intr_ref_cnt); ath_dbg(common, RESET, "BB_WATCHDOG: Skipping interrupts\n"); goto out; @@ -420,7 +414,6 @@ void ath9k_tasklet(unsigned long data) if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) { type = RESET_TYPE_TX_GTT; ath9k_queue_reset(sc, type); - atomic_inc(&ah->intr_ref_cnt); ath_dbg(common, RESET, "GTT: Skipping interrupts\n"); goto out; @@ -477,7 +470,7 @@ void ath9k_tasklet(unsigned long data) ath9k_btcoex_handle_interrupt(sc, status); /* re-enable hardware interrupt */ - ath9k_hw_enable_interrupts(ah); + ath9k_hw_resume_interrupts(ah); out: spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); @@ -541,7 +534,9 @@ irqreturn_t ath_isr(int irq, void *dev) return IRQ_NONE; /* Cache the status */ - sc->intrstatus = status; + spin_lock(&sc->intr_lock); + sc->intrstatus |= status; + spin_unlock(&sc->intr_lock); if (status & SCHED_INTR) sched = true; @@ -587,7 +582,7 @@ chip_reset: if (sched) { /* turn off every interrupt */ - ath9k_hw_disable_interrupts(ah); + ath9k_hw_kill_interrupts(ah); tasklet_schedule(&sc->intr_tq); } diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 7cdaf40c3057..ea7b8c25955f 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -27,7 +27,6 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ - { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ #ifdef CONFIG_ATH9K_PCOEM /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */ @@ -38,7 +37,7 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_LED_ACT_HI }, #endif - { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ #ifdef CONFIG_ATH9K_PCOEM { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, @@ -86,7 +85,11 @@ static const struct pci_device_id ath_pci_id_table[] = { 0x10CF, /* Fujitsu */ 0x1536), .driver_data = ATH9K_PCI_D3_L1_WAR }, +#endif + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + +#ifdef CONFIG_ATH9K_PCOEM /* AR9285 card for Asus */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x002B, diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index acd5347f2cae..923fe470360c 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -475,22 +475,23 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } mutex_unlock(&wil->p2p_wdev_mutex); - /* social scan on P2P_DEVICE is handled as p2p search */ - if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE && - wil_p2p_is_social_scan(request)) { + if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { if (!wil->p2p.p2p_dev_started) { wil_err(wil, "P2P search requested on stopped P2P device\n"); rc = -EIO; goto out; } - wil->scan_request = request; - wil->radio_wdev = wdev; - rc = wil_p2p_search(wil, request); - if (rc) { - wil->radio_wdev = wil_to_wdev(wil); - wil->scan_request = NULL; + /* social scan on P2P_DEVICE is handled as p2p search */ + if (wil_p2p_is_social_scan(request)) { + wil->scan_request = request; + wil->radio_wdev = wdev; + rc = wil_p2p_search(wil, request); + if (rc) { + wil->radio_wdev = wil_to_wdev(wil); + wil->scan_request = NULL; + } + goto out; } - goto out; } (void)wil_p2p_stop_discovery(wil); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index fe6d5ab7f95b..01a27335ec34 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -130,9 +130,15 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, u32 *d = dst; const volatile u32 __iomem *s = src; - /* size_t is unsigned, if (count%4 != 0) it will wrap */ - for (count += 4; count > 4; count -= 4) + for (; count >= 4; count -= 4) *d++ = __raw_readl(s++); + + if (unlikely(count)) { + /* count can be 1..3 */ + u32 tmp = __raw_readl(s); + + memcpy(d, &tmp, count); + } } void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst, @@ -149,8 +155,16 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, volatile u32 __iomem *d = dst; const u32 *s = src; - for (count += 4; count > 4; count -= 4) + for (; count >= 4; count -= 4) __raw_writel(*s++, d++); + + if (unlikely(count)) { + /* count can be 1..3 */ + u32 tmp = 0; + + memcpy(&tmp, s, count); + __raw_writel(tmp, d); + } } void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil, @@ -891,6 +905,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (wil->hw_version == HW_VER_UNKNOWN) return -ENODEV; + wil_dbg_misc(wil, "Prevent DS in BL & mark FW to set T_POWER_ON=0\n"); + wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP | + BIT_USER_SUPPORT_T_POWER_ON_0); + if (wil->platform_ops.notify) { rc = wil->platform_ops.notify(wil->platform_handle, WIL_PLATFORM_EVT_PRE_RESET); @@ -993,6 +1011,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + if (wil->tt_data_set) + wmi_set_tt_cfg(wil, &wil->tt_data); + wil_collect_fw_info(wil); if (wil->platform_ops.notify) { diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c index 0faa26ca41c9..a3689738a070 100644 --- a/drivers/net/wireless/ath/wil6210/sysfs.c +++ b/drivers/net/wireless/ath/wil6210/sysfs.c @@ -94,8 +94,119 @@ static DEVICE_ATTR(ftm_txrx_offset, 0644, wil_ftm_txrx_offset_sysfs_show, wil_ftm_txrx_offset_sysfs_store); +static ssize_t +wil_tt_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + ssize_t len; + struct wmi_tt_data tt_data; + int i, rc; + + rc = wmi_get_tt_cfg(wil, &tt_data); + if (rc) + return rc; + + len = snprintf(buf, PAGE_SIZE, " high max critical\n"); + + len += snprintf(buf + len, PAGE_SIZE - len, "bb: "); + if (tt_data.bb_enabled) + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) + len += snprintf(buf + len, PAGE_SIZE - len, + "%03d-%03d ", + tt_data.bb_zones[i].temperature_high, + tt_data.bb_zones[i].temperature_low); + else + len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *"); + len += snprintf(buf + len, PAGE_SIZE - len, "\nrf: "); + if (tt_data.rf_enabled) + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) + len += snprintf(buf + len, PAGE_SIZE - len, + "%03d-%03d ", + tt_data.rf_zones[i].temperature_high, + tt_data.rf_zones[i].temperature_low); + else + len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *"); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + + return len; +} + +static ssize_t +wil_tt_sysfs_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + int i, rc = -EINVAL; + char *token, *dupbuf, *tmp; + struct wmi_tt_data tt_data = { + .bb_enabled = 0, + .rf_enabled = 0, + }; + + tmp = kmemdup(buf, count + 1, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + tmp[count] = '\0'; + dupbuf = tmp; + + /* Format for writing is 12 unsigned bytes separated by spaces: + * <bb_z1_h> <bb_z1_l> <bb_z2_h> <bb_z2_l> <bb_z3_h> <bb_z3_l> \ + * <rf_z1_h> <rf_z1_l> <rf_z2_h> <rf_z2_l> <rf_z3_h> <rf_z3_l> + * To disable thermal throttling for bb or for rf, use 0 for all + * its six set points. + */ + + /* bb */ + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) { + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_high)) + goto out; + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_low)) + goto out; + + if (tt_data.bb_zones[i].temperature_high > 0 || + tt_data.bb_zones[i].temperature_low > 0) + tt_data.bb_enabled = 1; + } + /* rf */ + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) { + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_high)) + goto out; + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_low)) + goto out; + + if (tt_data.rf_zones[i].temperature_high > 0 || + tt_data.rf_zones[i].temperature_low > 0) + tt_data.rf_enabled = 1; + } + + rc = wmi_set_tt_cfg(wil, &tt_data); + if (rc) + goto out; + + rc = count; +out: + kfree(tmp); + return rc; +} + +static DEVICE_ATTR(thermal_throttling, 0644, + wil_tt_sysfs_show, wil_tt_sysfs_store); + static struct attribute *wil6210_sysfs_entries[] = { &dev_attr_ftm_txrx_offset.attr, + &dev_attr_thermal_throttling.attr, NULL }; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index fcfbcf7fbd7d..f64323b03a3b 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -141,6 +141,9 @@ struct RGF_ICR { #define RGF_USER_USAGE_1 (0x880004) #define RGF_USER_USAGE_6 (0x880018) #define BIT_USER_OOB_MODE BIT(31) +#define RGF_USER_USAGE_8 (0x880020) + #define BIT_USER_PREVENT_DEEP_SLEEP BIT(0) + #define BIT_USER_SUPPORT_T_POWER_ON_0 BIT(1) #define RGF_USER_HW_MACHINE_STATE (0x8801dc) #define HW_MACHINE_BOOT_DONE (0x3fffffd) #define RGF_USER_USER_CPU_0 (0x8801e0) @@ -693,6 +696,8 @@ struct wil6210_priv { struct wil_halp halp; struct wil_ftm_priv ftm; + bool tt_data_set; + struct wmi_tt_data tt_data; #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP @@ -851,6 +856,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short); int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short); int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid); +int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data); +int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data); int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index bccd27dcb42b..6a6ba02beba0 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1763,6 +1763,67 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid) return rc; } +int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data) +{ + int rc; + struct wmi_set_thermal_throttling_cfg_cmd cmd = { + .tt_data = *tt_data, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_set_thermal_throttling_cfg_event evt; + } __packed reply; + + if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING, + wil->fw_capabilities)) + return -EOPNOTSUPP; + + memset(&reply, 0, sizeof(reply)); + rc = wmi_call(wil, WMI_SET_THERMAL_THROTTLING_CFG_CMDID, &cmd, + sizeof(cmd), WMI_SET_THERMAL_THROTTLING_CFG_EVENTID, + &reply, sizeof(reply), 100); + if (rc) { + wil_err(wil, "failed to set thermal throttling\n"); + return rc; + } + if (reply.evt.status) { + wil_err(wil, "set thermal throttling failed, error %d\n", + reply.evt.status); + return -EIO; + } + + wil->tt_data = *tt_data; + wil->tt_data_set = true; + + return 0; +} + +int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data) +{ + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_get_thermal_throttling_cfg_event evt; + } __packed reply; + + if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING, + wil->fw_capabilities)) + return -EOPNOTSUPP; + + rc = wmi_call(wil, WMI_GET_THERMAL_THROTTLING_CFG_CMDID, NULL, 0, + WMI_GET_THERMAL_THROTTLING_CFG_EVENTID, &reply, + sizeof(reply), 100); + if (rc) { + wil_err(wil, "failed to get thermal throttling\n"); + return rc; + } + + if (tt_data) + *tt_data = reply.evt.tt_data; + + return 0; +} + void wmi_event_flush(struct wil6210_priv *wil) { ulong flags; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 7c9fee57aa91..f7f5f4f801e3 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -58,6 +58,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3, WMI_FW_CAPABILITY_DISABLE_AP_SME = 4, WMI_FW_CAPABILITY_WMI_ONLY = 5, + WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, WMI_FW_CAPABILITY_MAX, }; @@ -142,8 +143,6 @@ enum wmi_command_id { WMI_MAINTAIN_RESUME_CMDID = 0x851, WMI_RS_MGMT_CMDID = 0x852, WMI_RF_MGMT_CMDID = 0x853, - WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854, - WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855, WMI_OTP_READ_CMDID = 0x856, WMI_OTP_WRITE_CMDID = 0x857, WMI_LED_CFG_CMDID = 0x858, @@ -192,6 +191,8 @@ enum wmi_command_id { WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931, WMI_NEW_STA_CMDID = 0x935, WMI_DEL_STA_CMDID = 0x936, + WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940, + WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941, WMI_TOF_SESSION_START_CMDID = 0x991, WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, WMI_TOF_SET_LCR_CMDID = 0x993, @@ -438,16 +439,6 @@ struct wmi_rf_mgmt_cmd { __le32 rf_mgmt_type; } __packed; -/* WMI_THERMAL_THROTTLING_CTRL_CMDID */ -#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF) - -/* WMI_THERMAL_THROTTLING_CTRL_CMDID */ -struct wmi_thermal_throttling_ctrl_cmd { - __le32 time_on_usec; - __le32 time_off_usec; - __le32 max_txop_length_usec; -} __packed; - /* WMI_RF_RX_TEST_CMDID */ struct wmi_rf_rx_test_cmd { __le32 sector; @@ -549,7 +540,7 @@ struct wmi_pcp_start_cmd { u8 hidden_ssid; u8 is_go; u8 reserved0[5]; - /* abft_len override if non-0 */ + /* A-BFT length override if non-0 */ u8 abft_len; u8 disable_ap_sme; u8 network_type; @@ -910,6 +901,39 @@ struct wmi_set_mgmt_retry_limit_cmd { u8 reserved[3]; } __packed; +/* Zones: HIGH, MAX, CRITICAL */ +#define WMI_NUM_OF_TT_ZONES (3) + +struct wmi_tt_zone_limits { + /* Above this temperature this zone is active */ + u8 temperature_high; + /* Below this temperature the adjacent lower zone is active */ + u8 temperature_low; + u8 reserved[2]; +} __packed; + +/* Struct used for both configuration and status commands of thermal + * throttling + */ +struct wmi_tt_data { + /* Enable/Disable TT algorithm for baseband */ + u8 bb_enabled; + u8 reserved0[3]; + /* Define zones for baseband */ + struct wmi_tt_zone_limits bb_zones[WMI_NUM_OF_TT_ZONES]; + /* Enable/Disable TT algorithm for radio */ + u8 rf_enabled; + u8 reserved1[3]; + /* Define zones for all radio chips */ + struct wmi_tt_zone_limits rf_zones[WMI_NUM_OF_TT_ZONES]; +} __packed; + +/* WMI_SET_THERMAL_THROTTLING_CFG_CMDID */ +struct wmi_set_thermal_throttling_cfg_cmd { + /* Command data */ + struct wmi_tt_data tt_data; +} __packed; + /* WMI_NEW_STA_CMDID */ struct wmi_new_sta_cmd { u8 dst_mac[WMI_MAC_LEN]; @@ -1040,7 +1064,6 @@ enum wmi_event_id { WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, WMI_RS_MGMT_DONE_EVENTID = 0x1852, WMI_RF_MGMT_STATUS_EVENTID = 0x1853, - WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855, WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, WMI_RX_MGMT_PACKET_EVENTID = 0x1840, WMI_TX_MGMT_PACKET_EVENTID = 0x1841, @@ -1090,6 +1113,8 @@ enum wmi_event_id { WMI_BRP_SET_ANT_LIMIT_EVENTID = 0x1924, WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930, WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931, + WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940, + WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941, WMI_TOF_SESSION_END_EVENTID = 0x1991, WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992, WMI_TOF_SET_LCR_EVENTID = 0x1993, @@ -1133,13 +1158,6 @@ struct wmi_rf_mgmt_status_event { __le32 rf_status; } __packed; -/* WMI_THERMAL_THROTTLING_STATUS_EVENTID */ -struct wmi_thermal_throttling_status_event { - __le32 time_on_usec; - __le32 time_off_usec; - __le32 max_txop_length_usec; -} __packed; - /* WMI_GET_STATUS_DONE_EVENTID */ struct wmi_get_status_done_event { __le32 is_associated; @@ -2206,6 +2224,19 @@ struct wmi_tof_get_capabilities_event { __le32 aoa_supported_types; } __packed; +/* WMI_SET_THERMAL_THROTTLING_CFG_EVENTID */ +struct wmi_set_thermal_throttling_cfg_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_GET_THERMAL_THROTTLING_CFG_EVENTID */ +struct wmi_get_thermal_throttling_cfg_event { + /* Status data */ + struct wmi_tt_data tt_data; +} __packed; + enum wmi_tof_session_end_status { WMI_TOF_SESSION_END_NO_ERROR = 0x00, WMI_TOF_SESSION_END_FAIL = 0x01, diff --git a/drivers/net/wireless/cnss_genl/Kconfig b/drivers/net/wireless/cnss_genl/Kconfig new file mode 100644 index 000000000000..f1b8a586ec90 --- /dev/null +++ b/drivers/net/wireless/cnss_genl/Kconfig @@ -0,0 +1,7 @@ +config CNSS_GENL + tristate "CNSS Generic Netlink Socket Driver" + ---help--- + This module creates generic netlink family "CLD80211". This can be + used by cld driver and userspace utilities to communicate over + netlink sockets. This module creates different multicast groups to + facilitate the same. diff --git a/drivers/net/wireless/cnss_genl/Makefile b/drivers/net/wireless/cnss_genl/Makefile new file mode 100644 index 000000000000..9431c9e596bb --- /dev/null +++ b/drivers/net/wireless/cnss_genl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CNSS_GENL) := cnss_nl.o diff --git a/drivers/net/wireless/cnss_genl/cnss_nl.c b/drivers/net/wireless/cnss_genl/cnss_nl.c new file mode 100644 index 000000000000..fafd9ce4b4c4 --- /dev/null +++ b/drivers/net/wireless/cnss_genl/cnss_nl.c @@ -0,0 +1,204 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <net/genetlink.h> +#include <net/cnss_nl.h> +#include <linux/module.h> + +#define CLD80211_GENL_NAME "cld80211" + +#define CLD80211_MULTICAST_GROUP_SVC_MSGS "svc_msgs" +#define CLD80211_MULTICAST_GROUP_HOST_LOGS "host_logs" +#define CLD80211_MULTICAST_GROUP_FW_LOGS "fw_logs" +#define CLD80211_MULTICAST_GROUP_PER_PKT_STATS "per_pkt_stats" +#define CLD80211_MULTICAST_GROUP_DIAG_EVENTS "diag_events" +#define CLD80211_MULTICAST_GROUP_FATAL_EVENTS "fatal_events" +#define CLD80211_MULTICAST_GROUP_OEM_MSGS "oem_msgs" + +static const struct genl_multicast_group nl_mcgrps[] = { + [CLD80211_MCGRP_SVC_MSGS] = { .name = + CLD80211_MULTICAST_GROUP_SVC_MSGS}, + [CLD80211_MCGRP_HOST_LOGS] = { .name = + CLD80211_MULTICAST_GROUP_HOST_LOGS}, + [CLD80211_MCGRP_FW_LOGS] = { .name = + CLD80211_MULTICAST_GROUP_FW_LOGS}, + [CLD80211_MCGRP_PER_PKT_STATS] = { .name = + CLD80211_MULTICAST_GROUP_PER_PKT_STATS}, + [CLD80211_MCGRP_DIAG_EVENTS] = { .name = + CLD80211_MULTICAST_GROUP_DIAG_EVENTS}, + [CLD80211_MCGRP_FATAL_EVENTS] = { .name = + CLD80211_MULTICAST_GROUP_FATAL_EVENTS}, + [CLD80211_MCGRP_OEM_MSGS] = { .name = + CLD80211_MULTICAST_GROUP_OEM_MSGS}, +}; + +struct cld_ops { + cld80211_cb cb; + void *cb_ctx; +}; + +struct cld80211_nl_data { + struct cld_ops cld_ops[CLD80211_MAX_COMMANDS]; +}; + +static struct cld80211_nl_data nl_data; + +static inline struct cld80211_nl_data *get_local_ctx(void) +{ + return &nl_data; +} + +static struct genl_ops nl_ops[CLD80211_MAX_COMMANDS]; + +/* policy for the attributes */ +static const struct nla_policy cld80211_policy[CLD80211_ATTR_MAX + 1] = { + [CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED }, + [CLD80211_ATTR_DATA] = { .type = NLA_BINARY, + .len = CLD80211_MAX_NL_DATA }, +}; + +static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + u8 cmd_id = ops->cmd; + struct cld80211_nl_data *nl = get_local_ctx(); + + if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) { + pr_err("CLD80211: Command Not supported: %u\n", cmd_id); + return -EOPNOTSUPP; + } + info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb; + info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx; + + return 0; +} + +/* The netlink family */ +static struct genl_family cld80211_fam = { + .id = GENL_ID_GENERATE, + .name = CLD80211_GENL_NAME, + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ + .maxattr = CLD80211_ATTR_MAX, + .netnsok = true, + .pre_doit = cld80211_pre_doit, + .post_doit = NULL, +}; + +int register_cld_cmd_cb(u8 cmd_id, cld80211_cb func, void *cb_ctx) +{ + struct cld80211_nl_data *nl = get_local_ctx(); + + pr_debug("CLD80211: Registering command: %d\n", cmd_id); + if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) { + pr_debug("CLD80211: invalid command: %d\n", cmd_id); + return -EINVAL; + } + + nl->cld_ops[cmd_id - 1].cb = func; + nl->cld_ops[cmd_id - 1].cb_ctx = cb_ctx; + + return 0; +} +EXPORT_SYMBOL(register_cld_cmd_cb); + +int deregister_cld_cmd_cb(u8 cmd_id) +{ + struct cld80211_nl_data *nl = get_local_ctx(); + + pr_debug("CLD80211: De-registering command: %d\n", cmd_id); + if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) { + pr_debug("CLD80211: invalid command: %d\n", cmd_id); + return -EINVAL; + } + + nl->cld_ops[cmd_id - 1].cb = NULL; + nl->cld_ops[cmd_id - 1].cb_ctx = NULL; + + return 0; +} +EXPORT_SYMBOL(deregister_cld_cmd_cb); + +struct genl_family *cld80211_get_genl_family(void) +{ + return &cld80211_fam; +} +EXPORT_SYMBOL(cld80211_get_genl_family); + +static int cld80211_doit(struct sk_buff *skb, struct genl_info *info) +{ + cld80211_cb cld_cb; + void *cld_ctx; + + cld_cb = info->user_ptr[0]; + + if (!cld_cb) { + pr_err("CLD80211: Not supported\n"); + return -EOPNOTSUPP; + } + cld_ctx = info->user_ptr[1]; + + if (info->attrs[CLD80211_ATTR_VENDOR_DATA]) { + cld_cb(nla_data(info->attrs[CLD80211_ATTR_VENDOR_DATA]), + nla_len(info->attrs[CLD80211_ATTR_VENDOR_DATA]), + cld_ctx, info->snd_portid); + } else { + pr_err("CLD80211: No CLD80211_ATTR_VENDOR_DATA\n"); + return -EINVAL; + } + return 0; +} + +static int __cld80211_init(void) +{ + int err, i; + + memset(&nl_ops[0], 0, sizeof(nl_ops)); + + pr_info("CLD80211: Initializing\n"); + for (i = 0; i < CLD80211_MAX_COMMANDS; i++) { + nl_ops[i].cmd = i + 1; + nl_ops[i].doit = cld80211_doit; + nl_ops[i].flags = GENL_ADMIN_PERM; + nl_ops[i].policy = cld80211_policy; + } + + err = genl_register_family_with_ops_groups(&cld80211_fam, nl_ops, + nl_mcgrps); + if (err) { + pr_err("CLD80211: Failed to register cld80211 family: %d\n", + err); + } + + return err; +} + +static void __cld80211_exit(void) +{ + genl_unregister_family(&cld80211_fam); +} + +static int __init cld80211_init(void) +{ + return __cld80211_init(); +} + +static void __exit cld80211_exit(void) +{ + __cld80211_exit(); +} + +module_init(cld80211_init); +module_exit(cld80211_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CNSS generic netlink module"); diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 7a40d8dffa36..aab752328c26 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc); static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc) { + struct ieee80211_hw *hw = rtlpriv->hw; + rtlpriv->ra.is_special_data = true; if (rtlpriv->cfg->ops->get_btc_status()) rtlpriv->btcoexist.btc_ops->btc_special_packet_notify( rtlpriv, 1); - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } @@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, if (is_tx) { rtlpriv->ra.is_special_data = true; - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index a4527880b2b7..8b537a5a4b01 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1153,10 +1153,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, } else { mstatus = RT_MEDIA_DISCONNECT; - if (mac->link_state == MAC80211_LINKED) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + if (mac->link_state == MAC80211_LINKED) + rtl_lps_leave(hw); if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); mac->link_state = MAC80211_NOLINK; @@ -1434,8 +1432,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw, } if (mac->link_state == MAC80211_LINKED) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); mac->link_state = MAC80211_LINKED_SCANNING; } else { rtl_ips_nic_on(hw); diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 5b4048041147..a52230377e2c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -664,11 +664,9 @@ tx_status_ok: } if (((rtlpriv->link_info.num_rx_inperiod + - rtlpriv->link_info.num_tx_inperiod) > 8) || - (rtlpriv->link_info.num_rx_inperiod > 2)) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + rtlpriv->link_info.num_tx_inperiod) > 8) || + (rtlpriv->link_info.num_rx_inperiod > 2)) + rtl_lps_leave(hw); } static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, @@ -919,10 +917,8 @@ new_trx_end: } if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || - (rtlpriv->link_info.num_rx_inperiod > 2)) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + (rtlpriv->link_info.num_rx_inperiod > 2)) + rtl_lps_leave(hw); skb = new_skb; no_new: if (rtlpriv->use_new_trx_flow) { diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h index 5da6703942d9..672f81ea02d0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.h +++ b/drivers/net/wireless/realtek/rtlwifi/pci.h @@ -275,10 +275,10 @@ struct mp_adapter { }; struct rtl_pci_priv { + struct bt_coexist_info bt_coexist; + struct rtl_led_ctl ledctl; struct rtl_pci dev; struct mp_adapter ndis_adapter; - struct rtl_led_ctl ledctl; - struct bt_coexist_info bt_coexist; }; #define rtl_pcipriv(hw) (((struct rtl_pci_priv *)(rtl_priv(hw))->priv)) diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c index b69321d45f04..626ff300352b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/ps.c +++ b/drivers/net/wireless/realtek/rtlwifi/ps.c @@ -414,8 +414,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) } } -/*Enter the leisure power save mode.*/ -void rtl_lps_enter(struct ieee80211_hw *hw) +/* Interrupt safe routine to enter the leisure power save mode.*/ +static void rtl_lps_enter_core(struct ieee80211_hw *hw) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -455,10 +455,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw) spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); } -EXPORT_SYMBOL(rtl_lps_enter); -/*Leave the leisure power save mode.*/ -void rtl_lps_leave(struct ieee80211_hw *hw) +/* Interrupt safe routine to leave the leisure power save mode.*/ +static void rtl_lps_leave_core(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -488,7 +487,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw) } spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); } -EXPORT_SYMBOL(rtl_lps_leave); /* For sw LPS*/ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) @@ -681,12 +679,34 @@ void rtl_lps_change_work_callback(struct work_struct *work) struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->enter_ps) - rtl_lps_enter(hw); + rtl_lps_enter_core(hw); else - rtl_lps_leave(hw); + rtl_lps_leave_core(hw); } EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); +void rtl_lps_enter(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (!in_interrupt()) + return rtl_lps_enter_core(hw); + rtlpriv->enter_ps = true; + schedule_work(&rtlpriv->works.lps_change_work); +} +EXPORT_SYMBOL_GPL(rtl_lps_enter); + +void rtl_lps_leave(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (!in_interrupt()) + return rtl_lps_leave_core(hw); + rtlpriv->enter_ps = false; + schedule_work(&rtlpriv->works.lps_change_work); +} +EXPORT_SYMBOL_GPL(rtl_lps_leave); + void rtl_swlps_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c index 5f14308e8eb3..b1601441991d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c @@ -1003,7 +1003,7 @@ static void _rtl92ee_hw_configure(struct ieee80211_hw *hw) rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x100a); /* Note Data sheet don't define */ - rtl_write_word(rtlpriv, 0x4C7, 0x80); + rtl_write_byte(rtlpriv, 0x4C7, 0x80); rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x20); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index bbb789f8990b..c2103e7a8132 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -1127,7 +1127,7 @@ static u8 _rtl8821ae_dbi_read(struct rtl_priv *rtlpriv, u16 addr) } if (0 == tmp) { read_addr = REG_DBI_RDATA + addr % 4; - ret = rtl_read_word(rtlpriv, read_addr); + ret = rtl_read_byte(rtlpriv, read_addr); } return ret; } diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index aac1ed3f7bb4..ad8390d2997b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -834,12 +834,30 @@ static void rtl_usb_stop(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); + struct urb *urb; /* should after adapter start and interrupt enable. */ set_hal_stop(rtlhal); cancel_work_sync(&rtlpriv->works.fill_h2c_cmd); /* Enable software */ SET_USB_STOP(rtlusb); + + /* free pre-allocated URBs from rtl_usb_start() */ + usb_kill_anchored_urbs(&rtlusb->rx_submitted); + + tasklet_kill(&rtlusb->rx_work_tasklet); + cancel_work_sync(&rtlpriv->works.lps_change_work); + + flush_workqueue(rtlpriv->works.rtl_wq); + + skb_queue_purge(&rtlusb->rx_queue); + + while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) { + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + } + rtlpriv->cfg->ops->hw_disable(hw); } @@ -1073,6 +1091,7 @@ int rtl_usb_probe(struct usb_interface *intf, return -ENOMEM; } rtlpriv = hw->priv; + rtlpriv->hw = hw; rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32), GFP_KERNEL); if (!rtlpriv->usb_data) diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h index 685273ca9561..441c4412130c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.h +++ b/drivers/net/wireless/realtek/rtlwifi/usb.h @@ -150,8 +150,9 @@ struct rtl_usb { }; struct rtl_usb_priv { - struct rtl_usb dev; + struct bt_coexist_info bt_coexist; struct rtl_led_ctl ledctl; + struct rtl_usb dev; }; #define rtl_usbpriv(hw) (((struct rtl_usb_priv *)(rtl_priv(hw))->priv)) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index d6abf191122a..1f445f357da1 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1391,6 +1391,8 @@ static void xennet_disconnect_backend(struct netfront_info *info) for (i = 0; i < num_queues && info->queues; ++i) { struct netfront_queue *queue = &info->queues[i]; + del_timer_sync(&queue->rx_refill_timer); + if (queue->tx_irq && (queue->tx_irq == queue->rx_irq)) unbind_from_irqhandler(queue->tx_irq, queue); if (queue->tx_irq && (queue->tx_irq != queue->rx_irq)) { @@ -1745,7 +1747,6 @@ static void xennet_destroy_queues(struct netfront_info *info) if (netif_running(info->netdev)) napi_disable(&queue->napi); - del_timer_sync(&queue->rx_refill_timer); netif_napi_del(&queue->napi); } diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c index 5b58475ae489..f4ad5bde06a8 100644 --- a/drivers/nfc/nq-nci.c +++ b/drivers/nfc/nq-nci.c @@ -267,7 +267,7 @@ static ssize_t nfc_write(struct file *filp, const char __user *buf, ret = i2c_master_send(nqx_dev->client, tmp, count); if (ret != count) { - dev_err(&nqx_dev->client->dev, + dev_dbg(&nqx_dev->client->dev, "%s: failed to write %d\n", __func__, ret); ret = -EIO; goto out_free; @@ -304,7 +304,7 @@ static int nqx_standby_write(struct nqx_dev *nqx_dev, for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) { ret = i2c_master_send(nqx_dev->client, buf, len); if (ret < 0) { - dev_err(&nqx_dev->client->dev, + dev_dbg(&nqx_dev->client->dev, "%s: write failed, Maybe in Standby Mode - Retry(%d)\n", __func__, retry_cnt); usleep_range(1000, 1100); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 60654d524858..ecc6fb9ca92f 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -1623,7 +1623,7 @@ ntb_transport_create_queue(void *data, struct device *client_dev, node = dev_to_node(&ndev->dev); - free_queue = ffs(nt->qp_bitmap); + free_queue = ffs(nt->qp_bitmap_free); if (!free_queue) goto err; @@ -2082,9 +2082,8 @@ module_init(ntb_transport_init); static void __exit ntb_transport_exit(void) { - debugfs_remove_recursive(nt_debugfs_dir); - ntb_unregister_client(&ntb_transport_client); bus_unregister(&ntb_transport_bus); + debugfs_remove_recursive(nt_debugfs_dir); } module_exit(ntb_transport_exit); diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 62120c38d56b..aae7379af4e4 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1534,6 +1534,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) static int find_pmem_label_set(struct nd_region *nd_region, struct nd_namespace_pmem *nspm) { + u64 altcookie = nd_region_interleave_set_altcookie(nd_region); u64 cookie = nd_region_interleave_set_cookie(nd_region); struct nd_namespace_label *nd_label; u8 select_id[NSLABEL_UUID_LEN]; @@ -1542,8 +1543,10 @@ static int find_pmem_label_set(struct nd_region *nd_region, int rc = -ENODEV, l; u16 i; - if (cookie == 0) + if (cookie == 0) { + dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n"); return -ENXIO; + } /* * Find a complete set of labels by uuid. By definition we can start @@ -1552,13 +1555,24 @@ static int find_pmem_label_set(struct nd_region *nd_region, for_each_label(l, nd_label, nd_region->mapping[0].labels) { u64 isetcookie = __le64_to_cpu(nd_label->isetcookie); - if (isetcookie != cookie) - continue; + if (isetcookie != cookie) { + dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", + nd_label->uuid); + if (isetcookie != altcookie) + continue; + + dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n", + nd_label->uuid); + } + + for (i = 0; nd_region->ndr_mappings; i++) { + if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) + continue; + if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i)) + continue; + break; + } - for (i = 0; nd_region->ndr_mappings; i++) - if (!has_uuid_at_pos(nd_region, nd_label->uuid, - cookie, i)) - break; if (i < nd_region->ndr_mappings) { /* * Give up if we don't find an instance of a diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 417e521d299c..fc870e55bb66 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -245,6 +245,7 @@ struct nd_region *to_nd_region(struct device *dev); int nd_region_to_nstype(struct nd_region *nd_region); int nd_region_register_namespaces(struct nd_region *nd_region, int *err); u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region); void nvdimm_bus_lock(struct device *dev); void nvdimm_bus_unlock(struct device *dev); bool is_nvdimm_bus_locked(struct device *dev); diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 9521696c9385..dc2e919daa39 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -379,6 +379,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region) return 0; } +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (nd_set) + return nd_set->altcookie; + return 0; +} + /* * Upon successful probe/remove, take/release a reference on the * associated interleave set (if present), and plant new btt + namespace diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 584ad96c703f..eade4f85632a 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -2511,6 +2511,48 @@ int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base, } EXPORT_SYMBOL(msm_pcie_debug_info); +#ifdef CONFIG_SYSFS +static ssize_t msm_pcie_enumerate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *) + dev_get_drvdata(dev); + + if (pcie_dev) + msm_pcie_enumerate(pcie_dev->rc_idx); + + return count; +} + +static DEVICE_ATTR(enumerate, S_IWUSR, NULL, msm_pcie_enumerate_store); + +static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev) +{ + int ret; + + ret = device_create_file(&dev->pdev->dev, &dev_attr_enumerate); + if (ret) + PCIE_DBG_FS(dev, + "RC%d: failed to create sysfs enumerate node\n", + dev->rc_idx); +} + +static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev) +{ + if (dev->pdev) + device_remove_file(&dev->pdev->dev, &dev_attr_enumerate); +} +#else +static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev) +{ +} + +static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev) +{ +} +#endif + #ifdef CONFIG_DEBUG_FS static struct dentry *dent_msm_pcie; static struct dentry *dfile_rc_sel; @@ -4600,6 +4642,8 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) do { usleep_range(LINK_UP_TIMEOUT_US_MIN, LINK_UP_TIMEOUT_US_MAX); val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); + PCIE_DBG(dev, "PCIe RC%d: LTSSM_STATE:0x%x\n", + dev->rc_idx, (val >> 12) & 0x3f); } while ((!(val & XMLH_LINK_UP) || !msm_pcie_confirm_linkup(dev, false, false, NULL)) && (link_check_count++ < LINK_UP_CHECK_MAX_COUNT)); @@ -6277,6 +6321,9 @@ static int msm_pcie_probe(struct platform_device *pdev) msm_pcie_dev[rc_idx].pcidev_table[i].registered = true; } + dev_set_drvdata(&msm_pcie_dev[rc_idx].pdev->dev, &msm_pcie_dev[rc_idx]); + msm_pcie_sysfs_init(&msm_pcie_dev[rc_idx]); + ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx], msm_pcie_dev[rc_idx].pdev); @@ -6490,11 +6537,16 @@ int __init pcie_init(void) static void __exit pcie_exit(void) { + int i; + PCIE_GEN_DBG("pcie:%s.\n", __func__); platform_driver_unregister(&msm_pcie_driver); msm_pcie_debugfs_exit(); + + for (i = 0; i < MAX_RC_NUM; i++) + msm_pcie_sysfs_exit(&msm_pcie_dev[i]); } subsys_initcall_sync(pcie_init); diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index e12bafdc42e0..f2fcbe944d94 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -258,8 +258,13 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn) static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn) { - if (vio_find_node(dn)) + struct vio_dev *vio_dev; + + vio_dev = vio_find_node(dn); + if (vio_dev) { + put_device(&vio_dev->dev); return -EINVAL; + } if (!vio_register_device_node(dn)) { printk(KERN_ERR @@ -335,6 +340,9 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn) return -EINVAL; vio_unregister_device(vio_dev); + + put_device(&vio_dev->dev); + return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 42d8617352ae..e311a9bf2c90 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2043,6 +2043,10 @@ bool pci_dev_run_wake(struct pci_dev *dev) if (!dev->pme_support) return false; + /* PME-capable in principle, but not from the intended sleep state */ + if (!pci_pme_capable(dev, pci_target_state(dev))) + return false; + while (bus->parent) { struct pci_dev *bridge = bus->self; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 317e3558a35e..c6a012b5ba39 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -518,25 +518,32 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) return NULL; + INIT_LIST_HEAD(&link->sibling); INIT_LIST_HEAD(&link->children); INIT_LIST_HEAD(&link->link); link->pdev = pdev; - if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) { + + /* + * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe + * hierarchies. + */ + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) { + link->root = link; + } else { struct pcie_link_state *parent; + parent = pdev->bus->parent->self->link_state; if (!parent) { kfree(link); return NULL; } + link->parent = parent; + link->root = link->parent->root; list_add(&link->link, &parent->children); } - /* Setup a pointer to the root port link */ - if (!link->parent) - link->root = link; - else - link->root = link->parent->root; list_add(&link->sibling, &link_list); pdev->link_state = link; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b5843c255263..71d9a6d1bd56 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1019,6 +1019,7 @@ void set_pcie_port_type(struct pci_dev *pdev) pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); if (!pos) return; + pdev->pcie_cap = pos; pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); pdev->pcie_flags_reg = reg16; @@ -1026,13 +1027,14 @@ void set_pcie_port_type(struct pci_dev *pdev) pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; /* - * A Root Port is always the upstream end of a Link. No PCIe - * component has two Links. Two Links are connected by a Switch - * that has a Port on each Link and internal logic to connect the - * two Ports. + * A Root Port or a PCI-to-PCIe bridge is always the upstream end + * of a Link. No PCIe component has two Links. Two Links are + * connected by a Switch that has a Port on each Link and internal + * logic to connect the two Ports. */ type = pci_pcie_type(pdev); - if (type == PCI_EXP_TYPE_ROOT_PORT) + if (type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_PCIE_BRIDGE) pdev->has_secondary_link = 1; else if (type == PCI_EXP_TYPE_UPSTREAM || type == PCI_EXP_TYPE_DOWNSTREAM) { diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index c3cb57ed6083..1e3e175d3e8d 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -113,6 +113,14 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, goto out; } + /* + * UFS PHY power management is managed by its parent (UFS host + * controller) hence set the no the no runtime PM callbacks flag + * on UFS PHY device to avoid any accidental attempt to call the + * PM callbacks for PHY device. + */ + pm_runtime_no_callbacks(&generic_phy->dev); + common_cfg->phy_spec_ops = phy_spec_ops; common_cfg->dev = dev; diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c index 5979d38c46b2..7329500943a3 100644 --- a/drivers/pinctrl/intel/pinctrl-broxton.c +++ b/drivers/pinctrl/intel/pinctrl-broxton.c @@ -19,7 +19,7 @@ #define BXT_PAD_OWN 0x020 #define BXT_HOSTSW_OWN 0x080 -#define BXT_PADCFGLOCK 0x090 +#define BXT_PADCFGLOCK 0x060 #define BXT_GPI_IE 0x110 #define BXT_COMMUNITY(s, e) \ diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 84943e4cff09..13730ca151ad 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -246,7 +246,7 @@ static int meson_pmx_request_gpio(struct pinctrl_dev *pcdev, { struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev); - meson_pmx_disable_other_groups(pc, range->pin_base + offset, -1); + meson_pmx_disable_other_groups(pc, offset, -1); return 0; } diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpi.c index 67cac25689ef..4ca5d5fa0531 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpi.c @@ -117,12 +117,12 @@ static const u32 lpi_offset[] = { 0x00005010, 0x00005020, 0x00005030, - 0x00005040, - 0x00005050, 0x00006000, 0x00006010, 0x00007000, 0x00007010, + 0x00005040, + 0x00005050, 0x00008000, 0x00008010, 0x00008020, diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 863c3e30ce05..50f2014fed55 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -483,7 +483,8 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin, switch (param) { case PIN_CONFIG_BIAS_DISABLE: - return true; + return pin->configs & + (SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN); case PIN_CONFIG_BIAS_PULL_UP: return pin->configs & SH_PFC_PIN_CFG_PULL_UP; diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c index 1f52462f4cdd..dd9ea463c2a4 100644 --- a/drivers/platform/goldfish/pdev_bus.c +++ b/drivers/platform/goldfish/pdev_bus.c @@ -157,23 +157,26 @@ static int goldfish_new_pdev(void) static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id) { irqreturn_t ret = IRQ_NONE; + while (1) { u32 op = readl(pdev_bus_base + PDEV_BUS_OP); - switch (op) { - case PDEV_BUS_OP_DONE: - return IRQ_NONE; + switch (op) { case PDEV_BUS_OP_REMOVE_DEV: goldfish_pdev_remove(); + ret = IRQ_HANDLED; break; case PDEV_BUS_OP_ADD_DEV: goldfish_new_pdev(); + ret = IRQ_HANDLED; break; + + case PDEV_BUS_OP_DONE: + default: + return ret; } - ret = IRQ_HANDLED; } - return ret; } static int goldfish_pdev_bus_probe(struct platform_device *pdev) diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index dff565d9e206..c2902beaa0b8 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -561,7 +561,7 @@ static void gsi_handle_irq(void) if (!type) break; - GSIDBG("type %x\n", type); + GSIDBG_LOW("type %x\n", type); if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK) gsi_handle_ch_ctrl(ee); @@ -2743,9 +2743,10 @@ void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset, unsigned long *size) { if (base_offset) - *base_offset = GSI_GSI_INST_RAM_BASE_OFFS; + *base_offset = GSI_GSI_INST_RAM_n_OFFS(0); if (size) - *size = GSI_GSI_INST_RAM_SIZE; + *size = GSI_GSI_INST_RAM_n_WORD_SZ * + (GSI_GSI_INST_RAM_n_MAXn + 1); } EXPORT_SYMBOL(gsi_get_inst_ram_offset_and_size); @@ -2821,6 +2822,13 @@ static int msm_gsi_probe(struct platform_device *pdev) return -ENOMEM; } + gsi_ctx->ipc_logbuf = ipc_log_context_create(GSI_IPC_LOG_PAGES, + "gsi", 0); + if (gsi_ctx->ipc_logbuf == NULL) { + GSIERR("failed to get ipc_logbuf\n"); + return -ENOMEM; + } + gsi_ctx->dev = dev; init_completion(&gsi_ctx->gen_ee_cmd_compl); gsi_debugfs_init(); diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h index d0eb162c49cf..f53a4bd8fa6b 100644 --- a/drivers/platform/msm/gsi/gsi.h +++ b/drivers/platform/msm/gsi/gsi.h @@ -18,6 +18,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/msm_gsi.h> +#include <linux/ipc_logging.h> #define GSI_CHAN_MAX 31 #define GSI_EVT_RING_MAX 23 @@ -26,10 +27,48 @@ #define gsi_readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) #define gsi_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); }) -#define GSIERR(fmt, args...) \ - dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args) +#define GSI_IPC_LOGGING(buf, fmt, args...) \ + do { \ + if (buf) \ + ipc_log_string((buf), fmt, __func__, __LINE__, \ + ## args); \ + } while (0) + #define GSIDBG(fmt, args...) \ - dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args) + do { \ + dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \ + ## args);\ + if (gsi_ctx) { \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \ + "%s:%d " fmt, ## args); \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \ + "%s:%d " fmt, ## args); \ + } \ + } while (0) + +#define GSIDBG_LOW(fmt, args...) \ + do { \ + dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \ + ## args);\ + if (gsi_ctx) { \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \ + "%s:%d " fmt, ## args); \ + } \ + } while (0) + +#define GSIERR(fmt, args...) \ + do { \ + dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \ + ## args);\ + if (gsi_ctx) { \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \ + "%s:%d " fmt, ## args); \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \ + "%s:%d " fmt, ## args); \ + } \ + } while (0) + +#define GSI_IPC_LOG_PAGES 50 enum gsi_evt_ring_state { GSI_EVT_RING_STATE_NOT_ALLOCATED = 0x0, @@ -163,6 +202,8 @@ struct gsi_ctx { u32 max_ch; u32 max_ev; struct completion gen_ee_cmd_compl; + void *ipc_logbuf; + void *ipc_logbuf_low; }; enum gsi_re_type { diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c index 5eb9084292a4..717c891788f2 100644 --- a/drivers/platform/msm/gsi/gsi_dbg.c +++ b/drivers/platform/msm/gsi/gsi_dbg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -747,6 +747,45 @@ error: return -EFAULT; } +static ssize_t gsi_enable_ipc_low(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long missing; + s8 option = 0; + + if (sizeof(dbg_buff) < count + 1) + return -EFAULT; + + missing = copy_from_user(dbg_buff, ubuf, count); + if (missing) + return -EFAULT; + + dbg_buff[count] = '\0'; + if (kstrtos8(dbg_buff, 0, &option)) + return -EFAULT; + + if (option) { + if (!gsi_ctx->ipc_logbuf_low) { + gsi_ctx->ipc_logbuf_low = + ipc_log_context_create(GSI_IPC_LOG_PAGES, + "gsi_low", 0); + } + + if (gsi_ctx->ipc_logbuf_low == NULL) { + TERR("failed to get ipc_logbuf_low\n"); + return -EFAULT; + } + } else { + if (gsi_ctx->ipc_logbuf_low) + ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low); + gsi_ctx->ipc_logbuf_low = NULL; + } + + return count; +} + + + const struct file_operations gsi_ev_dump_ops = { .write = gsi_dump_evt, }; @@ -783,6 +822,10 @@ const struct file_operations gsi_print_dp_stats_ops = { .write = gsi_print_dp_stats, }; +const struct file_operations gsi_ipc_low_ops = { + .write = gsi_enable_ipc_low, +}; + void gsi_debugfs_init(void) { static struct dentry *dfile; @@ -858,6 +901,13 @@ void gsi_debugfs_init(void) goto fail; } + dfile = debugfs_create_file("ipc_low", write_only_mode, + dent, 0, &gsi_ipc_low_ops); + if (!dfile || IS_ERR(dfile)) { + TERR("could not create ipc_low\n"); + goto fail; + } + return; fail: debugfs_remove_recursive(dent); diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h index d0462aad72d2..653cdd4823c6 100644 --- a/drivers/platform/msm/gsi/gsi_reg.h +++ b/drivers/platform/msm/gsi/gsi_reg.h @@ -688,8 +688,9 @@ #define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_BMSK 0xfff #define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_SHFT 0x0 +#define GSI_GSI_INST_RAM_n_WORD_SZ 0x4 #define GSI_GSI_INST_RAM_n_OFFS(n) \ - (GSI_GSI_REG_BASE_OFFS + 0x00004000 + 0x4 * (n)) + (GSI_GSI_REG_BASE_OFFS + 0x00004000 + GSI_GSI_INST_RAM_n_WORD_SZ * (n)) #define GSI_GSI_INST_RAM_n_RMSK 0xffffffff #define GSI_GSI_INST_RAM_n_MAXn 4095 #define GSI_GSI_INST_RAM_n_INST_BYTE_3_BMSK 0xff000000 @@ -1842,7 +1843,5 @@ #define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_BMSK 0xffffffff #define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_SHFT 0x0 -#define GSI_GSI_INST_RAM_BASE_OFFS 0x4000 -#define GSI_GSI_INST_RAM_SIZE 0x4000 #endif /* __GSI_REG_H__ */ diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c index 069f0a2e3fee..51c930a81c8d 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c @@ -74,6 +74,10 @@ struct ipa_uc_offload_ctx { static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE]; +static int ipa_uc_ntn_cons_release(void); +static int ipa_uc_ntn_cons_request(void); +static void ipa_uc_offload_rm_notify(void *, enum ipa_rm_event, unsigned long); + static int ipa_commit_partial_hdr( struct ipa_ioc_add_hdr *hdr, const char *netdev_name, @@ -115,16 +119,37 @@ static int ipa_uc_offload_ntn_reg_intf( struct ipa_uc_offload_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { - struct ipa_ioc_add_hdr *hdr; + struct ipa_ioc_add_hdr *hdr = NULL; struct ipa_tx_intf tx; struct ipa_rx_intf rx; struct ipa_ioc_tx_intf_prop tx_prop[2]; struct ipa_ioc_rx_intf_prop rx_prop[2]; + struct ipa_rm_create_params param; u32 len; int ret = 0; IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n", inp->netdev_name); + memset(¶m, 0, sizeof(param)); + param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; + param.reg_params.user_data = ntn_ctx; + param.reg_params.notify_cb = ipa_uc_offload_rm_notify; + param.floor_voltage = IPA_VOLTAGE_SVS; + ret = ipa_rm_create_resource(¶m); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n"); + return -EFAULT; + } + + memset(¶m, 0, sizeof(param)); + param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; + param.request_resource = ipa_uc_ntn_cons_request; + param.release_resource = ipa_uc_ntn_cons_release; + ret = ipa_rm_create_resource(¶m); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n"); + goto fail_create_rm_cons; + } memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX); ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len; @@ -136,7 +161,8 @@ static int ipa_uc_offload_ntn_reg_intf( hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len); - return -ENOMEM; + ret = -ENOMEM; + goto fail_alloc; } if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) { @@ -197,8 +223,15 @@ static int ipa_uc_offload_ntn_reg_intf( init_completion(&ntn_ctx->ntn_completion); ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED; + kfree(hdr); + return ret; + fail: kfree(hdr); +fail_alloc: + ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); +fail_create_rm_cons: + ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); return ret; } @@ -301,9 +334,10 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, struct ipa_ntn_conn_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { - struct ipa_rm_create_params param; int result = 0; + enum ipa_uc_offload_state prev_state; + prev_state = ntn_ctx->state; if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT || inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) { IPA_UC_OFFLOAD_ERR("alignment failure on TX\n"); @@ -315,42 +349,13 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, return -EINVAL; } - memset(¶m, 0, sizeof(param)); - param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; - param.reg_params.user_data = ntn_ctx; - param.reg_params.notify_cb = ipa_uc_offload_rm_notify; - param.floor_voltage = IPA_VOLTAGE_SVS; - result = ipa_rm_create_resource(¶m); - if (result) { - IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n"); - return -EFAULT; - } - - memset(¶m, 0, sizeof(param)); - param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; - param.request_resource = ipa_uc_ntn_cons_request; - param.release_resource = ipa_uc_ntn_cons_release; - result = ipa_rm_create_resource(¶m); + result = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, + IPA_RM_RESOURCE_APPS_CONS); if (result) { - IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n"); - goto fail_create_rm_cons; - } - - if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, - IPA_RM_RESOURCE_APPS_CONS)) { - IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n"); - result = -EFAULT; - goto fail; - } - - if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify, - ntn_ctx->priv, ntn_ctx->hdr_len, outp)) { - IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n"); - result = -EFAULT; - goto fail; + IPA_UC_OFFLOAD_ERR("fail to add rm dependency: %d\n", result); + return result; } - ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP; result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); if (result == -EINPROGRESS) { if (wait_for_completion_timeout(&ntn_ctx->ntn_completion, @@ -365,13 +370,22 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, goto fail; } + ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP; + result = ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify, + ntn_ctx->priv, ntn_ctx->hdr_len, outp); + if (result) { + IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes: %d\n", + result); + ntn_ctx->state = prev_state; + result = -EFAULT; + goto fail; + } + return 0; fail: - ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); -fail_create_rm_cons: - ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); - + ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, + IPA_RM_RESOURCE_APPS_CONS); return result; } @@ -399,7 +413,8 @@ int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp, return -EINVAL; } - if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) { + if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED && + offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) { IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state); return -EPERM; } @@ -454,32 +469,34 @@ EXPORT_SYMBOL(ipa_set_perf_profile); static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx) { int ipa_ep_idx_ul, ipa_ep_idx_dl; + int ret = 0; ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN; - if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, - IPA_RM_RESOURCE_APPS_CONS)) { - IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n"); - return -EFAULT; - } - if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) { - IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n"); + ret = ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to release ODU_ADAPT_PROD res: %d\n", + ret); return -EFAULT; } - if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) { - IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n"); + ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, + IPA_RM_RESOURCE_APPS_CONS); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to del dep ODU->APPS, %d\n", ret); return -EFAULT; } ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD); ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS); - if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) { - IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n"); + ret = ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to tear down ntn offload pipes, %d\n", + ret); return -EFAULT; } - return 0; + return ret; } int ipa_uc_offload_disconn_pipes(u32 clnt_hdl) @@ -524,6 +541,16 @@ static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx) int len, result = 0; struct ipa_ioc_del_hdr *hdr; + if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) { + IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n"); + return -EFAULT; + } + + if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) { + IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n"); + return -EFAULT; + } + len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c index 54cad888cb7f..e10c75a473ce 100644 --- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c +++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c @@ -227,7 +227,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph, if (ipa_rm_dep_graph_get_resource(graph, resource_name, &dependent)) { - IPA_RM_ERR("%s does not exist\n", + IPA_RM_DBG("%s does not exist\n", ipa_rm_resource_str(resource_name)); result = -EINVAL; goto bail; @@ -236,7 +236,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph, if (ipa_rm_dep_graph_get_resource(graph, depends_on_name, &dependency)) { - IPA_RM_ERR("%s does not exist\n", + IPA_RM_DBG("%s does not exist\n", ipa_rm_resource_str(depends_on_name)); result = -EINVAL; goto bail; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index 7ca2314d5839..d94e8f9f0e12 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1465,34 +1465,41 @@ void ipa_install_dflt_flt_rules(u32 ipa_ep_idx) mutex_lock(&ipa_ctx->lock); tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4]; - tbl->sticky_rear = true; rule.action = IPA_PASS_TO_EXCEPTION; - __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, false, + __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, true, &ep->dflt_flt4_rule_hdl); ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4); + tbl->sticky_rear = true; tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6]; - tbl->sticky_rear = true; rule.action = IPA_PASS_TO_EXCEPTION; - __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, false, + __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, true, &ep->dflt_flt6_rule_hdl); ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6); + tbl->sticky_rear = true; mutex_unlock(&ipa_ctx->lock); } void ipa_delete_dflt_flt_rules(u32 ipa_ep_idx) { + struct ipa_flt_tbl *tbl; struct ipa_ep_context *ep = &ipa_ctx->ep[ipa_ep_idx]; mutex_lock(&ipa_ctx->lock); if (ep->dflt_flt4_rule_hdl) { + tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4]; __ipa_del_flt_rule(ep->dflt_flt4_rule_hdl); ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4); + /* Reset the sticky flag. */ + tbl->sticky_rear = false; ep->dflt_flt4_rule_hdl = 0; } if (ep->dflt_flt6_rule_hdl) { + tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6]; __ipa_del_flt_rule(ep->dflt_flt6_rule_hdl); ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6); + /* Reset the sticky flag. */ + tbl->sticky_rear = false; ep->dflt_flt6_rule_hdl = 0; } mutex_unlock(&ipa_ctx->lock); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c index e2ac9bfceed7..119d17cae9f5 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c @@ -655,9 +655,8 @@ int qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01 *req) /* check if the filter rules from IPACM is valid */ if (req->filter_index_list_len == 0) { - IPAWANERR(" delete UL filter rule for pipe %d\n", + IPAWANDBG(" delete UL filter rule for pipe %d\n", req->source_pipe_index); - return -EINVAL; } else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) { IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n", req->source_pipe_index, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c index 00d52d0d9115..0b46ab2a8439 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c @@ -299,12 +299,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup ul ep cfg */ ep_ul->valid = 1; ep_ul->client = in->ul.client; - result = ipa_enable_data_path(ipa_ep_idx_ul); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_ul); - return -EFAULT; - } ep_ul->client_notify = notify; ep_ul->priv = priv; @@ -333,14 +327,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup dl ep cfg */ ep_dl->valid = 1; ep_dl->client = in->dl.client; - result = ipa_enable_data_path(ipa_ep_idx_dl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_dl); - result = -EFAULT; - goto fail; - } - memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg)); ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT; ep_dl->cfg.hdr.hdr_len = hdr_len; @@ -359,9 +345,16 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, } outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX; ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED; + + result = ipa_enable_data_path(ipa_ep_idx_dl); + if (result) { + IPAERR("Enable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_dl); + result = -EFAULT; + goto fail; + } IPAERR("client %d (ep: %d) connected\n", in->dl.client, ipa_ep_idx_dl); - ipa_inc_acquire_wakelock(IPA_WAKELOCK_REF_CLIENT_ODU_RX); fail: IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); @@ -403,28 +396,32 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); - /* teardown the UL pipe */ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_NTN; - tear = &cmd_data->CommonCh_params.NtnCommonCh_params; - tear->params.ipa_pipe_number = ipa_ep_idx_ul; + + /* teardown the DL pipe */ + ipa_disable_data_path(ipa_ep_idx_dl); + /* + * Reset ep before sending cmd otherwise disconnect + * during data transfer will result into + * enormous suspend interrupts + */ + memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context)); + IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + tear->params.ipa_pipe_number = ipa_ep_idx_dl; result = ipa_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { - IPAERR("fail to tear down ul pipe\n"); + IPAERR("fail to tear down dl pipe\n"); result = -EFAULT; goto fail; } - ipa_disable_data_path(ipa_ep_idx_ul); - ipa_delete_dflt_flt_rules(ipa_ep_idx_ul); - memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context)); - IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); - /* teardown the DL pipe */ - tear->params.ipa_pipe_number = ipa_ep_idx_dl; + /* teardown the UL pipe */ + tear->params.ipa_pipe_number = ipa_ep_idx_ul; result = ipa_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, @@ -434,10 +431,10 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, result = -EFAULT; goto fail; } - ipa_disable_data_path(ipa_ep_idx_dl); - memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context)); - IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); - ipa_dec_release_wakelock(IPA_WAKELOCK_REF_CLIENT_ODU_RX); + + ipa_delete_dflt_flt_rules(ipa_ep_idx_ul); + memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context)); + IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); fail: dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 362294b0f695..41b29335d23b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -1389,16 +1389,23 @@ void ipa3_install_dflt_flt_rules(u32 ipa_ep_idx) void ipa3_delete_dflt_flt_rules(u32 ipa_ep_idx) { struct ipa3_ep_context *ep = &ipa3_ctx->ep[ipa_ep_idx]; + struct ipa3_flt_tbl *tbl; mutex_lock(&ipa3_ctx->lock); if (ep->dflt_flt4_rule_hdl) { + tbl = &ipa3_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4]; __ipa_del_flt_rule(ep->dflt_flt4_rule_hdl); ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v4); + /* Reset the sticky flag. */ + tbl->sticky_rear = false; ep->dflt_flt4_rule_hdl = 0; } if (ep->dflt_flt6_rule_hdl) { + tbl = &ipa3_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6]; __ipa_del_flt_rule(ep->dflt_flt6_rule_hdl); ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v6); + /* Reset the sticky flag. */ + tbl->sticky_rear = false; ep->dflt_flt6_rule_hdl = 0; } mutex_unlock(&ipa3_ctx->lock); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index 3086311b5c2a..335e5283cc29 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -786,9 +786,8 @@ int ipa3_qmi_filter_notify_send( /* check if the filter rules from IPACM is valid */ if (req->rule_id_len == 0) { - IPAWANERR(" delete UL filter rule for pipe %d\n", + IPAWANDBG(" delete UL filter rule for pipe %d\n", req->source_pipe_index); - return -EINVAL; } else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) { IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n", req->source_pipe_index, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c index 7b891843028d..fdb6d05f683d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c @@ -265,12 +265,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup ul ep cfg */ ep_ul->valid = 1; ep_ul->client = in->ul.client; - result = ipa3_enable_data_path(ipa_ep_idx_ul); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_ul); - return -EFAULT; - } ep_ul->client_notify = notify; ep_ul->priv = priv; @@ -299,14 +293,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup dl ep cfg */ ep_dl->valid = 1; ep_dl->client = in->dl.client; - result = ipa3_enable_data_path(ipa_ep_idx_dl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_dl); - result = -EFAULT; - goto fail; - } - memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg)); ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT; ep_dl->cfg.hdr.hdr_len = hdr_len; @@ -325,6 +311,14 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, } outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX; ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED; + + result = ipa3_enable_data_path(ipa_ep_idx_dl); + if (result) { + IPAERR("Enable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_dl); + result = -EFAULT; + goto fail; + } IPADBG("client %d (ep: %d) connected\n", in->dl.client, ipa_ep_idx_dl); @@ -368,28 +362,32 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); - /* teardown the UL pipe */ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_NTN; - tear = &cmd_data->CommonCh_params.NtnCommonCh_params; - tear->params.ipa_pipe_number = ipa_ep_idx_ul; + + /* teardown the DL pipe */ + ipa3_disable_data_path(ipa_ep_idx_dl); + /* + * Reset ep before sending cmd otherwise disconnect + * during data transfer will result into + * enormous suspend interrupts + */ + memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context)); + IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + tear->params.ipa_pipe_number = ipa_ep_idx_dl; result = ipa3_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { - IPAERR("fail to tear down ul pipe\n"); + IPAERR("fail to tear down dl pipe\n"); result = -EFAULT; goto fail; } - ipa3_disable_data_path(ipa_ep_idx_ul); - ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul); - memset(&ipa3_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa3_ep_context)); - IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); - /* teardown the DL pipe */ - tear->params.ipa_pipe_number = ipa_ep_idx_dl; + /* teardown the UL pipe */ + tear->params.ipa_pipe_number = ipa_ep_idx_ul; result = ipa3_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, @@ -399,9 +397,9 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, result = -EFAULT; goto fail; } - ipa3_disable_data_path(ipa_ep_idx_dl); + ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul); memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context)); - IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); fail: dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 2f28ba673d5a..c8ff06ddda87 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3565,11 +3565,6 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) memset(&mem, 0, sizeof(mem)); - if (IPA_CLIENT_IS_PROD(ep->client)) { - res = gsi_stop_channel(ep->gsi_chan_hdl); - goto end_sequence; - } - for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) { IPADBG("Calling gsi_stop_channel\n"); res = gsi_stop_channel(ep->gsi_chan_hdl); @@ -3577,12 +3572,14 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT) goto end_sequence; - IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n"); - /* Send a 1B packet DMA_TASK to IPA and try again */ - res = ipa3_inject_dma_task_for_gsi(); - if (res) { - IPAERR("Failed to inject DMA TASk for GSI\n"); - goto end_sequence; + if (IPA_CLIENT_IS_CONS(ep->client)) { + IPADBG("Inject a DMA_TASK with 1B packet to IPA\n"); + /* Send a 1B packet DMA_TASK to IPA and try again */ + res = ipa3_inject_dma_task_for_gsi(); + if (res) { + IPAERR("Failed to inject DMA TASk for GSI\n"); + goto end_sequence; + } } /* sleep for short period to flush IPA */ diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index 7f97d7c6bab7..61a870c9928b 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -36,7 +36,7 @@ #define WIGIG_VENDOR (0x1ae9) #define WIGIG_DEVICE (0x0310) -#define SMMU_BASE 0x10000000 /* Device address range base */ +#define SMMU_BASE 0x20000000 /* Device address range base */ #define SMMU_SIZE ((SZ_1G * 4ULL) - SMMU_BASE) #define WIGIG_ENABLE_DELAY 50 @@ -55,7 +55,6 @@ #define VDDIO_MAX_UV 2040000 #define VDDIO_MAX_UA 70300 -#define DISABLE_PCIE_L1_MASK 0xFFFFFFFD #define PCIE20_CAP_LINKCTRLSTATUS 0x80 #define WIGIG_MIN_CPU_BOOST_KBPS 150000 @@ -90,12 +89,16 @@ struct msm11ad_ctx { u32 rc_index; /* PCIE root complex index */ struct pci_dev *pcidev; struct pci_saved_state *pristine_state; + bool l1_enabled_in_enum; /* SMMU */ bool use_smmu; /* have SMMU enabled? */ - int smmu_bypass; + int smmu_s1_en; int smmu_fast_map; + int smmu_coherent; struct dma_iommu_mapping *mapping; + u32 smmu_base; + u32 smmu_size; /* bus frequency scaling */ struct msm_bus_scale_pdata *bus_scale; @@ -479,6 +482,47 @@ static void msm_11ad_disable_clocks(struct msm11ad_ctx *ctx) msm_11ad_disable_clk(ctx, &ctx->rf_clk3); } +int msm_11ad_ctrl_aspm_l1(struct msm11ad_ctx *ctx, bool enable) +{ + int rc; + u32 val; + struct pci_dev *pdev = ctx->pcidev; + bool l1_enabled; + + /* Read current state */ + rc = pci_read_config_dword(pdev, + PCIE20_CAP_LINKCTRLSTATUS, &val); + if (rc) { + dev_err(ctx->dev, + "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", rc); + return rc; + } + dev_dbg(ctx->dev, "PCIE20_CAP_LINKCTRLSTATUS read returns 0x%x\n", val); + + l1_enabled = val & PCI_EXP_LNKCTL_ASPM_L1; + if (l1_enabled == enable) { + dev_dbg(ctx->dev, "ASPM_L1 is already %s\n", + l1_enabled ? "enabled" : "disabled"); + return 0; + } + + if (enable) + val |= PCI_EXP_LNKCTL_ASPM_L1; /* enable bit 1 */ + else + val &= ~PCI_EXP_LNKCTL_ASPM_L1; /* disable bit 1 */ + + dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n", + val); + rc = pci_write_config_dword(pdev, + PCIE20_CAP_LINKCTRLSTATUS, val); + if (rc) + dev_err(ctx->dev, + "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n", + val, rc); + + return rc; +} + static int ops_suspend(void *handle) { int rc; @@ -521,7 +565,6 @@ static int ops_resume(void *handle) int rc; struct msm11ad_ctx *ctx = handle; struct pci_dev *pcidev; - u32 val; pr_info("%s(%p)\n", __func__, handle); if (!ctx) { @@ -565,25 +608,14 @@ static int ops_resume(void *handle) goto err_suspend_rc; } - /* Disable L1 */ - rc = pci_read_config_dword(ctx->pcidev, - PCIE20_CAP_LINKCTRLSTATUS, &val); - if (rc) { - dev_err(ctx->dev, - "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", - rc); - goto err_suspend_rc; - } - val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */ - dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n", - val); - rc = pci_write_config_dword(ctx->pcidev, - PCIE20_CAP_LINKCTRLSTATUS, val); - if (rc) { - dev_err(ctx->dev, - "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n", - val, rc); - goto err_suspend_rc; + /* Disable L1, in case it is enabled */ + if (ctx->l1_enabled_in_enum) { + rc = msm_11ad_ctrl_aspm_l1(ctx, false); + if (rc) { + dev_err(ctx->dev, + "failed to disable L1, rc %d\n", rc); + goto err_suspend_rc; + } } return 0; @@ -609,15 +641,17 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx) { int atomic_ctx = 1; int rc; + int force_pt_coherent = 1; + int smmu_bypass = !ctx->smmu_s1_en; if (!ctx->use_smmu) return 0; - dev_info(ctx->dev, "Initialize SMMU, bypass = %d, fastmap = %d\n", - ctx->smmu_bypass, ctx->smmu_fast_map); + dev_info(ctx->dev, "Initialize SMMU, bypass=%d, fastmap=%d, coherent=%d\n", + smmu_bypass, ctx->smmu_fast_map, ctx->smmu_coherent); ctx->mapping = arm_iommu_create_mapping(&platform_bus_type, - SMMU_BASE, SMMU_SIZE); + ctx->smmu_base, ctx->smmu_size); if (IS_ERR_OR_NULL(ctx->mapping)) { rc = PTR_ERR(ctx->mapping) ?: -ENODEV; dev_err(ctx->dev, "Failed to create IOMMU mapping (%d)\n", rc); @@ -633,23 +667,39 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx) goto release_mapping; } - if (ctx->smmu_bypass) { + if (smmu_bypass) { rc = iommu_domain_set_attr(ctx->mapping->domain, DOMAIN_ATTR_S1_BYPASS, - &ctx->smmu_bypass); + &smmu_bypass); if (rc) { dev_err(ctx->dev, "Set bypass attribute to SMMU failed (%d)\n", rc); goto release_mapping; } - } else if (ctx->smmu_fast_map) { - rc = iommu_domain_set_attr(ctx->mapping->domain, - DOMAIN_ATTR_FAST, - &ctx->smmu_fast_map); - if (rc) { - dev_err(ctx->dev, "Set fast attribute to SMMU failed (%d)\n", - rc); - goto release_mapping; + } else { + /* Set dma-coherent and page table coherency */ + if (ctx->smmu_coherent) { + arch_setup_dma_ops(&ctx->pcidev->dev, 0, 0, NULL, true); + rc = iommu_domain_set_attr(ctx->mapping->domain, + DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT, + &force_pt_coherent); + if (rc) { + dev_err(ctx->dev, + "Set SMMU PAGE_TABLE_FORCE_COHERENT attr failed (%d)\n", + rc); + goto release_mapping; + } + } + + if (ctx->smmu_fast_map) { + rc = iommu_domain_set_attr(ctx->mapping->domain, + DOMAIN_ATTR_FAST, + &ctx->smmu_fast_map); + if (rc) { + dev_err(ctx->dev, "Set fast attribute to SMMU failed (%d)\n", + rc); + goto release_mapping; + } } } @@ -871,6 +921,7 @@ static int msm_11ad_probe(struct platform_device *pdev) struct device_node *of_node = dev->of_node; struct device_node *rc_node; struct pci_dev *pcidev = NULL; + u32 smmu_mapping[2]; int rc; u32 val; @@ -925,8 +976,27 @@ static int msm_11ad_probe(struct platform_device *pdev) ctx->use_smmu = of_property_read_bool(of_node, "qcom,smmu-support"); ctx->bus_scale = msm_bus_cl_get_pdata(pdev); - ctx->smmu_bypass = 1; - ctx->smmu_fast_map = 0; + ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en"); + if (ctx->smmu_s1_en) { + ctx->smmu_fast_map = of_property_read_bool( + of_node, "qcom,smmu-fast-map"); + ctx->smmu_coherent = of_property_read_bool( + of_node, "qcom,smmu-coherent"); + } + rc = of_property_read_u32_array(dev->of_node, "qcom,smmu-mapping", + smmu_mapping, 2); + if (rc) { + dev_err(ctx->dev, + "Failed to read base/size smmu addresses %d, fallback to default\n", + rc); + ctx->smmu_base = SMMU_BASE; + ctx->smmu_size = SMMU_SIZE; + } else { + ctx->smmu_base = smmu_mapping[0]; + ctx->smmu_size = smmu_mapping[1]; + } + dev_dbg(ctx->dev, "smmu_base=0x%x smmu_sise=0x%x\n", + ctx->smmu_base, ctx->smmu_size); /*== execute ==*/ /* turn device on */ @@ -992,8 +1062,8 @@ static int msm_11ad_probe(struct platform_device *pdev) } ctx->pcidev = pcidev; - /* Disable L1 */ - rc = pci_read_config_dword(ctx->pcidev, + /* Read current state */ + rc = pci_read_config_dword(pcidev, PCIE20_CAP_LINKCTRLSTATUS, &val); if (rc) { dev_err(ctx->dev, @@ -1001,16 +1071,19 @@ static int msm_11ad_probe(struct platform_device *pdev) rc); goto out_rc; } - val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */ - dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n", - val); - rc = pci_write_config_dword(ctx->pcidev, - PCIE20_CAP_LINKCTRLSTATUS, val); - if (rc) { - dev_err(ctx->dev, - "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n", - val, rc); - goto out_rc; + + ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1; + dev_dbg(ctx->dev, "L1 is %s in enumeration\n", + ctx->l1_enabled_in_enum ? "enabled" : "disabled"); + + /* Disable L1, in case it is enabled */ + if (ctx->l1_enabled_in_enum) { + rc = msm_11ad_ctrl_aspm_l1(ctx, false); + if (rc) { + dev_err(ctx->dev, + "failed to disable L1, rc %d\n", rc); + goto out_rc; + } } rc = pci_save_state(pcidev); @@ -1258,6 +1331,13 @@ static int ops_notify(void *handle, enum wil_platform_event evt) * TODO: Enable rf_clk3 clock before resetting the device to * ensure stable ref clock during the device reset */ + /* Re-enable L1 in case it was enabled in enumeration */ + if (ctx->l1_enabled_in_enum) { + rc = msm_11ad_ctrl_aspm_l1(ctx, true); + if (rc) + dev_err(ctx->dev, + "failed to enable L1, rc %d\n", rc); + } break; case WIL_PLATFORM_EVT_FW_RDY: /* diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c index a35ed1afc720..6f9a13040cd5 100644 --- a/drivers/platform/msm/msm_ext_display.c +++ b/drivers/platform/msm/msm_ext_display.c @@ -215,7 +215,8 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp, { int ret = 0; - if (!(flags & MSM_EXT_DISP_HPD_VIDEO)) { + if (!(flags & (MSM_EXT_DISP_HPD_VIDEO + | MSM_EXT_DISP_HPD_ASYNC_VIDEO))) { pr_debug("skipping video setup for display (%s)\n", msm_ext_disp_name(type)); goto end; @@ -224,7 +225,8 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp, ret = msm_ext_disp_send_cable_notification(ext_disp, state); /* positive ret value means audio node was switched */ - if (IS_ERR_VALUE(ret) || !ret) { + if ((ret <= 0) || + (flags & MSM_EXT_DISP_HPD_ASYNC_VIDEO)) { pr_debug("not waiting for display\n"); goto end; } @@ -237,9 +239,8 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp, goto end; } - ret = 0; end: - return ret; + return (ret >= 0) ? 0 : -EINVAL; } static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp, @@ -248,7 +249,8 @@ static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp, { int ret = 0; - if (!(flags & MSM_EXT_DISP_HPD_AUDIO)) { + if (!(flags & (MSM_EXT_DISP_HPD_AUDIO + | MSM_EXT_DISP_HPD_ASYNC_AUDIO))) { pr_debug("skipping audio setup for display (%s)\n", msm_ext_disp_name(type)); goto end; @@ -257,7 +259,8 @@ static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp, ret = msm_ext_disp_send_audio_notification(ext_disp, state); /* positive ret value means audio node was switched */ - if (IS_ERR_VALUE(ret) || !ret || !ext_disp->ack_enabled) { + if ((ret <= 0) || !ext_disp->ack_enabled || + (flags & MSM_EXT_DISP_HPD_ASYNC_AUDIO)) { pr_debug("not waiting for audio\n"); goto end; } @@ -270,9 +273,8 @@ static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp, goto end; } - ret = 0; end: - return ret; + return (ret >= 0) ? 0 : -EINVAL; } static bool msm_ext_disp_validate_connect(struct msm_ext_disp *ext_disp, diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 131fee2b093e..a3661cc44f86 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -128,6 +128,15 @@ static const struct dmi_system_id asus_quirks[] = { }, { .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X45U", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X45U"), + }, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, .ident = "ASUSTeK COMPUTER INC. X456UA", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 1fc0de870ff8..361770568ad0 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -77,7 +77,7 @@ static int mfld_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0, + error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT, DRIVER_NAME, input); if (error) { dev_err(&pdev->dev, "Unable to request irq %d for mfld power" diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 201a53e66ef0..438da2c51dd6 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -264,6 +264,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(dp_dm), POWER_SUPPLY_ATTR(input_current_limited), POWER_SUPPLY_ATTR(input_current_now), + POWER_SUPPLY_ATTR(charge_qnovo_enable), POWER_SUPPLY_ATTR(current_qnovo), POWER_SUPPLY_ATTR(voltage_qnovo), POWER_SUPPLY_ATTR(rerun_aicl), diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c index b5ae7f4ade03..9455468f1734 100644 --- a/drivers/power/qcom/apm.c +++ b/drivers/power/qcom/apm.c @@ -629,9 +629,15 @@ done: #define MSM8953_APCC_APM_MODE 0x000002a8 #define MSM8953_APCC_APM_CTL_STS 0x000002b0 +/* 8953 constants */ +#define MSM8953_APM_SWITCH_TIMEOUT_US 500 + +/* Register bit mask definitions */ +#define MSM8953_APM_CTL_STS_MASK 0x1f + static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) { - int timeout = MSM_APM_SWITCH_TIMEOUT_US; + int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; u32 regval; int ret = 0; unsigned long flags; @@ -648,7 +654,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) while (timeout > 0) { regval = readl_relaxed(ctrl_dev->reg_base + MSM8953_APCC_APM_CTL_STS); - if ((regval & MSM_APM_CTL_STS_MASK) == + if ((regval & MSM8953_APM_CTL_STS_MASK) == MSM8953_APM_MX_DONE_VAL) break; @@ -672,7 +678,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) { - int timeout = MSM_APM_SWITCH_TIMEOUT_US; + int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; u32 regval; int ret = 0; unsigned long flags; @@ -689,7 +695,7 @@ static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) while (timeout > 0) { regval = readl_relaxed(ctrl_dev->reg_base + MSM8953_APCC_APM_CTL_STS); - if ((regval & MSM_APM_CTL_STS_MASK) == + if ((regval & MSM8953_APM_CTL_STS_MASK) == MSM8953_APM_APCC_DONE_VAL) break; diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 5b320ca8e74e..0c80c8be7909 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -24,7 +24,7 @@ #include <linux/printk.h> #include <linux/pm_wakeup.h> #include <linux/slab.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define DRV_MAJOR_VERSION 1 #define DRV_MINOR_VERSION 0 diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 32a25b4e2c7b..f2047592a94b 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -29,7 +29,7 @@ #include <linux/string_helpers.h> #include <linux/types.h> #include <linux/uaccess.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define fg_dbg(chip, reason, fmt, ...) \ do { \ @@ -46,10 +46,13 @@ && (value) <= (right))) /* Awake votable reasons */ -#define SRAM_READ "fg_sram_read" -#define SRAM_WRITE "fg_sram_write" -#define PROFILE_LOAD "fg_profile_load" -#define DELTA_SOC "fg_delta_soc" +#define SRAM_READ "fg_sram_read" +#define SRAM_WRITE "fg_sram_write" +#define PROFILE_LOAD "fg_profile_load" +#define DELTA_SOC "fg_delta_soc" + +/* Delta BSOC votable reasons */ +#define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq" #define DEBUG_PRINT_BUFFER_SIZE 64 /* 3 byte address + 1 space character */ @@ -330,6 +333,7 @@ struct fg_chip { struct fg_memif *sram; struct fg_irq_info *irqs; struct votable *awake_votable; + struct votable *delta_bsoc_irq_en_votable; struct fg_sram_param *sp; struct fg_alg_flag *alg_flags; int *debug_mask; @@ -370,8 +374,8 @@ struct fg_chip { bool esr_fcc_ctrl_en; bool soc_reporting_ready; bool esr_flt_cold_temp_en; - bool bsoc_delta_irq_en; bool slope_limit_en; + bool use_ima_single_mode; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c index 2dc76182ed15..8a949bfe61d0 100644 --- a/drivers/power/supply/qcom/fg-memif.c +++ b/drivers/power/supply/qcom/fg-memif.c @@ -48,6 +48,10 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst) int rc; u8 intf_ctl = 0; + fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "access: %d burst: %d\n", + access, burst); + + WARN_ON(burst && chip->use_ima_single_mode); intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) | (burst ? MEM_ACS_BURST_BIT : 0); @@ -175,6 +179,7 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip) { int rc; u8 dma_sts; + bool error_present; rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1); if (rc < 0) { @@ -184,14 +189,13 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip) } fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts); - if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) { - rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), - DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT); - if (rc < 0) { - pr_err("failed to write addr=0x%04x, rc=%d\n", - MEM_IF_DMA_CTL(chip), rc); - return rc; - } + error_present = dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT); + rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), DMA_CLEAR_LOG_BIT, + error_present ? DMA_CLEAR_LOG_BIT : 0); + if (rc < 0) { + pr_err("failed to write addr=0x%04x, rc=%d\n", + MEM_IF_DMA_CTL(chip), rc); + return rc; } return 0; @@ -293,7 +297,9 @@ static int fg_check_iacs_ready(struct fg_chip *chip) /* check for error condition */ rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { - pr_err("Failed to check for ima errors rc=%d\n", rc); + if (rc != -EAGAIN) + pr_err("Failed to check for ima errors rc=%d\n", + rc); return rc; } @@ -357,7 +363,12 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, /* check for error condition */ rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { - pr_err("Failed to check for ima errors rc=%d\n", rc); + if (rc == -EAGAIN) + pr_err("IMA error cleared, address [%d %d] len %d\n", + address, offset, len); + else + pr_err("Failed to check for ima errors rc=%d\n", + rc); return rc; } @@ -365,6 +376,15 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, len -= num_bytes; offset = byte_enable = 0; + if (chip->use_ima_single_mode && len) { + address++; + rc = fg_set_address(chip, address); + if (rc < 0) { + pr_err("failed to set address rc = %d\n", rc); + return rc; + } + } + rc = fg_check_iacs_ready(chip); if (rc < 0) { pr_debug("IACS_RDY failed rc=%d\n", rc); @@ -403,22 +423,40 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address, /* check for error condition */ rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { - pr_err("Failed to check for ima errors rc=%d\n", rc); + if (rc == -EAGAIN) + pr_err("IMA error cleared, address [%d %d] len %d\n", + address, offset, len); + else + pr_err("Failed to check for ima errors rc=%d\n", + rc); return rc; } - if (len && len < BYTES_PER_SRAM_WORD) { - /* - * Move to single mode. Changing address is not - * required here as it must be in burst mode. Address - * will get incremented internally by FG HW once the MSB - * of RD_DATA is read. - */ - rc = fg_config_access_mode(chip, FG_READ, 0); - if (rc < 0) { - pr_err("failed to move to single mode rc=%d\n", - rc); - return -EIO; + if (chip->use_ima_single_mode) { + if (len) { + address++; + rc = fg_set_address(chip, address); + if (rc < 0) { + pr_err("failed to set address rc = %d\n", + rc); + return rc; + } + } + } else { + if (len && len < BYTES_PER_SRAM_WORD) { + /* + * Move to single mode. Changing address is not + * required here as it must be in burst mode. + * Address will get incremented internally by FG + * HW once the MSB of RD_DATA is read. + */ + rc = fg_config_access_mode(chip, FG_READ, + false); + if (rc < 0) { + pr_err("failed to move to single mode rc=%d\n", + rc); + return -EIO; + } } } @@ -489,6 +527,7 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, u16 address, int offset, int len, bool access) { int rc = 0; + bool burst_mode = false; if (!is_mem_access_available(chip, access)) return -EBUSY; @@ -503,7 +542,8 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, } /* configure for the read/write, single/burst mode */ - rc = fg_config_access_mode(chip, access, (offset + len) > 4); + burst_mode = chip->use_ima_single_mode ? false : ((offset + len) > 4); + rc = fg_config_access_mode(chip, access, burst_mode); if (rc < 0) { pr_err("failed to set memory access rc = %d\n", rc); return rc; @@ -583,7 +623,7 @@ retry: if (rc < 0) { count++; if (rc == -EAGAIN) { - pr_err("IMA access failed retry_count = %d\n", count); + pr_err("IMA read failed retry_count = %d\n", count); goto retry; } pr_err("failed to read SRAM address rc = %d\n", rc); @@ -667,8 +707,8 @@ retry: rc = __fg_interleaved_mem_write(chip, address, offset, val, len); if (rc < 0) { count++; - if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { - pr_err("IMA access failed retry_count = %d\n", count); + if (rc == -EAGAIN) { + pr_err("IMA write failed retry_count = %d\n", count); goto retry; } pr_err("failed to write SRAM address rc = %d\n", rc); diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index c07e9f083204..b99558ed2100 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -18,9 +18,9 @@ #include <linux/slab.h> #include <linux/string.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> -#define NUM_MAX_CLIENTS 8 +#define NUM_MAX_CLIENTS 16 #define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT" static DEFINE_SPINLOCK(votable_list_slock); diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 5dcd4c36675a..59216a567662 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -525,7 +525,7 @@ static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id, } #define CC_SOC_30BIT GENMASK(29, 0) -static int fg_get_cc_soc(struct fg_chip *chip, int *val) +static int fg_get_charge_raw(struct fg_chip *chip, int *val) { int rc, cc_soc; @@ -539,7 +539,7 @@ static int fg_get_cc_soc(struct fg_chip *chip, int *val) return 0; } -static int fg_get_cc_soc_sw(struct fg_chip *chip, int *val) +static int fg_get_charge_counter(struct fg_chip *chip, int *val) { int rc, cc_soc; @@ -1054,6 +1054,25 @@ static void fg_notify_charger(struct fg_chip *chip) fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n"); } +static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data, + int enable, const char *client) +{ + struct fg_chip *chip = data; + + if (!chip->irqs[BSOC_DELTA_IRQ].irq) + return 0; + + if (enable) { + enable_irq(chip->irqs[BSOC_DELTA_IRQ].irq); + enable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq); + } else { + disable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq); + disable_irq(chip->irqs[BSOC_DELTA_IRQ].irq); + } + + return 0; +} + static int fg_awake_cb(struct votable *votable, void *data, int awake, const char *client) { @@ -1241,7 +1260,7 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) chip->cl.final_cc_uah, old_cap, chip->cl.learned_cc_uah); } -static int fg_cap_learning_process_full_data(struct fg_chip *chip) +static int fg_cap_learning_process_full_data(struct fg_chip *chip) { int rc, cc_soc_sw, cc_soc_delta_pct; int64_t delta_cc_uah; @@ -1263,30 +1282,39 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) return 0; } -static int fg_cap_learning_begin(struct fg_chip *chip, int batt_soc) +#define BATT_SOC_32BIT GENMASK(31, 0) +static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc) { - int rc, cc_soc_sw; + int rc, cc_soc_sw, batt_soc_msb; - if (DIV_ROUND_CLOSEST(batt_soc * 100, FULL_SOC_RAW) > + batt_soc_msb = batt_soc >> 24; + if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) > chip->dt.cl_start_soc) { fg_dbg(chip, FG_CAP_LEARN, "Battery SOC %d is high!, not starting\n", - batt_soc); + batt_soc_msb); return -EINVAL; } - chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc, + chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc_msb, FULL_SOC_RAW); - rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw); + + /* Prime cc_soc_sw with battery SOC when capacity learning begins */ + cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT, + BATT_SOC_32BIT); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word, + chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, + chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) { - pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); - return rc; + pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); + goto out; } chip->cl.init_cc_soc_sw = cc_soc_sw; chip->cl.active = true; fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n", - batt_soc, chip->cl.init_cc_soc_sw); - return 0; + batt_soc_msb, chip->cl.init_cc_soc_sw); +out: + return rc; } static int fg_cap_learning_done(struct fg_chip *chip) @@ -1318,7 +1346,7 @@ out: #define FULL_SOC_RAW 255 static void fg_cap_learning_update(struct fg_chip *chip) { - int rc, batt_soc; + int rc, batt_soc, batt_soc_msb; mutex_lock(&chip->cl.lock); @@ -1337,11 +1365,9 @@ static void fg_cap_learning_update(struct fg_chip *chip) goto out; } - /* We need only the most significant byte here */ - batt_soc = (u32)batt_soc >> 24; - + batt_soc_msb = (u32)batt_soc >> 24; fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n", - chip->charge_status, chip->cl.active, batt_soc); + chip->charge_status, chip->cl.active, batt_soc_msb); /* Initialize the starting point of learning capacity */ if (!chip->cl.active) { @@ -1363,7 +1389,7 @@ static void fg_cap_learning_update(struct fg_chip *chip) if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n", - batt_soc); + batt_soc_msb); chip->cl.active = false; chip->cl.init_cc_uah = 0; } @@ -1470,16 +1496,8 @@ static int fg_charge_full_update(struct fg_chip *chip) return 0; mutex_lock(&chip->charge_full_lock); - if (!chip->charge_done && chip->bsoc_delta_irq_en) { - disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq); - disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq); - chip->bsoc_delta_irq_en = false; - } else if (chip->charge_done && !chip->bsoc_delta_irq_en) { - enable_irq(fg_irqs[BSOC_DELTA_IRQ].irq); - enable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq); - chip->bsoc_delta_irq_en = true; - } - + vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, + chip->charge_done, 0); rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH, &prop); if (rc < 0) { @@ -1598,6 +1616,9 @@ static int fg_rconn_config(struct fg_chip *chip) u64 scaling_factor; u32 val = 0; + if (!chip->dt.rconn_mohms) + return 0; + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); if (rc < 0) { @@ -2188,6 +2209,17 @@ static bool is_profile_load_required(struct fg_chip *chip) /* Check if integrity bit is set */ if (val & PROFILE_LOAD_BIT) { fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); + + /* Whitelist the values */ + val &= ~PROFILE_LOAD_BIT; + if (val != HLOS_RESTART_BIT && val != BOOTLOADER_LOAD_BIT && + val != (BOOTLOADER_LOAD_BIT | BOOTLOADER_RESTART_BIT)) { + val |= PROFILE_LOAD_BIT; + pr_warn("Garbage value in profile integrity word: 0x%x\n", + val); + return true; + } + rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); if (rc < 0) { @@ -2818,7 +2850,7 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = chip->cyc_ctr.id; break; case POWER_SUPPLY_PROP_CHARGE_NOW_RAW: - rc = fg_get_cc_soc(chip, &pval->intval); + rc = fg_get_charge_raw(chip, &pval->intval); break; case POWER_SUPPLY_PROP_CHARGE_NOW: pval->intval = chip->cl.init_cc_uah; @@ -2827,7 +2859,7 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = chip->cl.learned_cc_uah; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: - rc = fg_get_cc_soc_sw(chip, &pval->intval); + rc = fg_get_charge_counter(chip, &pval->intval); break; case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: rc = fg_get_time_to_full(chip, &pval->intval); @@ -3176,12 +3208,10 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } - if (chip->dt.rconn_mohms > 0) { - rc = fg_rconn_config(chip); - if (rc < 0) { - pr_err("Error in configuring Rconn, rc=%d\n", rc); - return rc; - } + rc = fg_rconn_config(chip); + if (rc < 0) { + pr_err("Error in configuring Rconn, rc=%d\n", rc); + return rc; } fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER, @@ -3228,20 +3258,19 @@ static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data) } fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status); - if (status & MEM_XCP_BIT) { - rc = fg_clear_dma_errors_if_any(chip); - if (rc < 0) { - pr_err("Error in clearing DMA error, rc=%d\n", rc); - return IRQ_HANDLED; - } - mutex_lock(&chip->sram_rw_lock); + mutex_lock(&chip->sram_rw_lock); + rc = fg_clear_dma_errors_if_any(chip); + if (rc < 0) + pr_err("Error in clearing DMA error, rc=%d\n", rc); + + if (status & MEM_XCP_BIT) { rc = fg_clear_ima_errors_if_any(chip, true); if (rc < 0 && rc != -EAGAIN) pr_err("Error in checking IMA errors rc:%d\n", rc); - mutex_unlock(&chip->sram_rw_lock); } + mutex_unlock(&chip->sram_rw_lock); return IRQ_HANDLED; } @@ -3737,6 +3766,7 @@ static int fg_parse_dt(struct fg_chip *chip) case PM660_SUBTYPE: chip->sp = pmi8998_v2_sram_params; chip->alg_flags = pmi8998_v2_alg_flags; + chip->use_ima_single_mode = true; break; default: return -EINVAL; @@ -3957,9 +3987,7 @@ static int fg_parse_dt(struct fg_chip *chip) pr_err("Error in parsing Ki coefficients, rc=%d\n", rc); rc = of_property_read_u32(node, "qcom,fg-rconn-mohms", &temp); - if (rc < 0) - chip->dt.rconn_mohms = -EINVAL; - else + if (!rc) chip->dt.rconn_mohms = temp; rc = of_property_read_u32(node, "qcom,fg-esr-filter-switch-temp", @@ -4017,6 +4045,9 @@ static void fg_cleanup(struct fg_chip *chip) if (chip->awake_votable) destroy_votable(chip->awake_votable); + if (chip->delta_bsoc_irq_en_votable) + destroy_votable(chip->delta_bsoc_irq_en_votable); + if (chip->batt_id_chan) iio_channel_release(chip->batt_id_chan); @@ -4058,7 +4089,15 @@ static int fg_gen3_probe(struct platform_device *pdev) chip); if (IS_ERR(chip->awake_votable)) { rc = PTR_ERR(chip->awake_votable); - return rc; + goto exit; + } + + chip->delta_bsoc_irq_en_votable = create_votable("FG_DELTA_BSOC_IRQ", + VOTE_SET_ANY, + fg_delta_bsoc_irq_en_cb, chip); + if (IS_ERR(chip->delta_bsoc_irq_en_votable)) { + rc = PTR_ERR(chip->delta_bsoc_irq_en_votable); + goto exit; } rc = fg_parse_dt(chip); @@ -4085,7 +4124,7 @@ static int fg_gen3_probe(struct platform_device *pdev) rc = fg_get_batt_id(chip); if (rc < 0) { pr_err("Error in getting battery id, rc:%d\n", rc); - return rc; + goto exit; } rc = fg_get_batt_profile(chip); @@ -4143,11 +4182,7 @@ static int fg_gen3_probe(struct platform_device *pdev) disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq); /* Keep BSOC_DELTA_IRQ irq disabled until we require it */ - if (fg_irqs[BSOC_DELTA_IRQ].irq) { - disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq); - disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq); - chip->bsoc_delta_irq_en = false; - } + rerun_election(chip->delta_bsoc_irq_en_votable); rc = fg_debugfs_create(chip); if (rc < 0) { diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c index d36db8d8f3f1..759f483031d2 100644 --- a/drivers/power/supply/qcom/qpnp-qnovo.c +++ b/drivers/power/supply/qcom/qpnp-qnovo.c @@ -19,7 +19,7 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/qpnp/qpnp-revid.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define QNOVO_REVISION1 0x00 #define QNOVO_REVISION2 0x01 @@ -29,6 +29,8 @@ #define QNOVO_PTRAIN_STS 0x08 #define QNOVO_ERROR_STS 0x09 #define QNOVO_ERROR_BIT BIT(0) +#define QNOVO_ERROR_STS2 0x0A +#define QNOVO_ERROR_CHARGING_DISABLED BIT(1) #define QNOVO_INT_RT_STS 0x10 #define QNOVO_INT_SET_TYPE 0x11 #define QNOVO_INT_POLARITY_HIGH 0x12 @@ -109,20 +111,6 @@ struct qnovo_dt_props { struct device_node *revid_dev_node; }; -enum { - QNOVO_NO_ERR_STS_BIT = BIT(0), -}; - -struct chg_props { - bool charging; - bool usb_online; - bool dc_online; -}; - -struct chg_status { - bool ok_to_qnovo; -}; - struct qnovo { int base; struct mutex write_lock; @@ -141,17 +129,14 @@ struct qnovo { s64 v_gain_mega; struct notifier_block nb; struct power_supply *batt_psy; - struct power_supply *usb_psy; - struct power_supply *dc_psy; - struct chg_props cp; - struct chg_status cs; struct work_struct status_change_work; int fv_uV_request; int fcc_uA_request; + bool ok_to_qnovo; }; static int debug_mask; -module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR); +module_param_named(debug_mask, debug_mask, int, 0600); #define qnovo_dbg(chip, reason, fmt, ...) \ do { \ @@ -272,28 +257,22 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, const char *client) { struct qnovo *chip = data; - int rc = 0; + union power_supply_propval pval = {0}; + int rc; - if (disable) { - rc = qnovo_batt_psy_update(chip, true); - if (rc < 0) - return rc; - } + if (!is_batt_available(chip)) + return -EINVAL; - rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, - disable ? 0 : QNOVO_PTRAIN_EN_BIT); + pval.intval = !disable; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, + &pval); if (rc < 0) { - dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", - disable ? "disable" : "enable", rc); - return rc; - } - - if (!disable) { - rc = qnovo_batt_psy_update(chip, false); - if (rc < 0) - return rc; + pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc); + return -EINVAL; } + rc = qnovo_batt_psy_update(chip, disable); return rc; } @@ -325,36 +304,18 @@ static int qnovo_parse_dt(struct qnovo *chip) return 0; } -static int qnovo_check_chg_version(struct qnovo *chip) -{ - int rc; - - chip->pmic_rev_id = get_revid_data(chip->dt.revid_dev_node); - if (IS_ERR(chip->pmic_rev_id)) { - rc = PTR_ERR(chip->pmic_rev_id); - if (rc != -EPROBE_DEFER) - pr_err("Unable to get pmic_revid rc=%d\n", rc); - return rc; - } - - if ((chip->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) - && (chip->pmic_rev_id->rev4 < PMI8998_V2P0_REV4)) { - chip->wa_flags |= QNOVO_NO_ERR_STS_BIT; - } - - return 0; -} - enum { VER = 0, OK_TO_QNOVO, - ENABLE, + QNOVO_ENABLE, + PT_ENABLE, FV_REQUEST, FCC_REQUEST, PE_CTRL_REG, PE_CTRL2_REG, PTRAIN_STS_REG, INT_RT_STS_REG, + ERR_STS2_REG, PREST1, PPULS1, NREST1, @@ -394,6 +355,12 @@ struct param_info { }; static struct param_info params[] = { + [PT_ENABLE] = { + .name = "PT_ENABLE", + .start_addr = QNOVO_PTRAIN_EN, + .num_regs = 1, + .units_str = "", + }, [FV_REQUEST] = { .units_str = "uV", }, @@ -424,6 +391,12 @@ static struct param_info params[] = { .num_regs = 1, .units_str = "", }, + [ERR_STS2_REG] = { + .name = "RAW_CHGR_ERR", + .start_addr = QNOVO_ERROR_STS2, + .num_regs = 1, + .units_str = "", + }, [PREST1] = { .name = "PREST1", .start_addr = QNOVO_PREST1_CTRL, @@ -431,7 +404,7 @@ static struct param_info params[] = { .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, .min_val = 5, - .max_val = 1275, + .max_val = 255, .units_str = "mS", }, [PPULS1] = { @@ -440,8 +413,8 @@ static struct param_info params[] = { .num_regs = 2, .reg_to_unit_multiplier = 1600, /* converts to uC */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 104856000, + .min_val = 30000, + .max_val = 65535000, .units_str = "uC", }, [NREST1] = { @@ -451,7 +424,7 @@ static struct param_info params[] = { .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, .min_val = 5, - .max_val = 1275, + .max_val = 255, .units_str = "mS", }, [NPULS1] = { @@ -460,8 +433,8 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, - .min_val = 5, - .max_val = 1275, + .min_val = 0, + .max_val = 255, .units_str = "mS", }, [PPCNT] = { @@ -470,7 +443,7 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 1, .reg_to_unit_divider = 1, - .min_val = 0, + .min_val = 1, .max_val = 255, .units_str = "pulses", }, @@ -480,8 +453,8 @@ static struct param_info params[] = { .num_regs = 2, .reg_to_unit_multiplier = 610350, /* converts to nV */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 5000000, + .min_val = 2200000, + .max_val = 4500000, .units_str = "uV", }, [PVOLT1] = { @@ -506,8 +479,6 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 2, .reg_to_unit_divider = 1, - .min_val = 5, - .max_val = 1275, .units_str = "S", }, [PREST2] = { @@ -517,7 +488,7 @@ static struct param_info params[] = { .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, .min_val = 5, - .max_val = 327675, + .max_val = 65535, .units_str = "mS", }, [PPULS2] = { @@ -526,8 +497,8 @@ static struct param_info params[] = { .num_regs = 2, .reg_to_unit_multiplier = 1600, /* converts to uC */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 104856000, + .min_val = 30000, + .max_val = 65535000, .units_str = "uC", }, [NREST2] = { @@ -538,7 +509,7 @@ static struct param_info params[] = { .reg_to_unit_divider = 1, .reg_to_unit_offset = -5, .min_val = 5, - .max_val = 1280, + .max_val = 255, .units_str = "mS", }, [NPULS2] = { @@ -547,18 +518,18 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, - .min_val = 5, - .max_val = 1275, + .min_val = 0, + .max_val = 255, .units_str = "mS", }, [VLIM2] = { - .name = "VLIM1", + .name = "VLIM2", .start_addr = QNOVO_VLIM2_LSB_CTRL, .num_regs = 2, .reg_to_unit_multiplier = 610350, /* converts to nV */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 5000000, + .min_val = 2200000, + .max_val = 4500000, .units_str = "uV", }, [PVOLT2] = { @@ -591,6 +562,8 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 1, .reg_to_unit_divider = 1, + .min_val = 0, + .max_val = 255, .units_str = "pulses", }, [VMAX] = { @@ -645,33 +618,73 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr, { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); - return snprintf(buf, PAGE_SIZE, "%d\n", chip->cs.ok_to_qnovo); + return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo); } -static ssize_t enable_show(struct class *c, struct class_attribute *attr, +static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr, char *ubuf) { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); - int val; + int val = get_effective_result(chip->disable_votable); - val = get_client_vote(chip->disable_votable, USER_VOTER); - val = !val; - return snprintf(ubuf, PAGE_SIZE, "%d\n", val); + return snprintf(ubuf, PAGE_SIZE, "%d\n", !val); +} + +static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + unsigned long val; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + vote(chip->disable_votable, USER_VOTER, !val, 0); + + return count; +} + +static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = attr - qnovo_attributes; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + u16 regval; + int rc; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + regval = buf[1] << 8 | buf[0]; + + return snprintf(ubuf, PAGE_SIZE, "%d\n", + (int)(regval & QNOVO_PTRAIN_EN_BIT)); } -static ssize_t enable_store(struct class *c, struct class_attribute *attr, +static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr, const char *ubuf, size_t count) { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); unsigned long val; - bool disable; + int rc = 0; - if (kstrtoul(ubuf, 10, &val)) + if (get_effective_result(chip->disable_votable)) return -EINVAL; - disable = !val; + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, + (bool)val ? QNOVO_PTRAIN_EN_BIT : 0); + if (rc < 0) { + dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", + (bool)val ? "enable" : "disable", rc); + return rc; + } - vote(chip->disable_votable, USER_VOTER, disable, 0); return count; } @@ -688,7 +701,7 @@ static ssize_t val_show(struct class *c, struct class_attribute *attr, if (i == FCC_REQUEST) val = chip->fcc_uA_request; - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", val); } static ssize_t val_store(struct class *c, struct class_attribute *attr, @@ -698,7 +711,7 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr, int i = attr - qnovo_attributes; unsigned long val; - if (kstrtoul(ubuf, 10, &val)) + if (kstrtoul(ubuf, 0, &val)) return -EINVAL; if (i == FV_REQUEST) @@ -707,6 +720,9 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr, if (i == FCC_REQUEST) chip->fcc_uA_request = val; + if (!get_effective_result(chip->disable_votable)) + qnovo_batt_psy_update(chip, false); + return count; } @@ -726,8 +742,7 @@ static ssize_t reg_show(struct class *c, struct class_attribute *attr, } regval = buf[1] << 8 | buf[0]; - return snprintf(ubuf, PAGE_SIZE, "0x%04x%s\n", - regval, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval); } static ssize_t reg_store(struct class *c, struct class_attribute *attr, @@ -739,7 +754,7 @@ static ssize_t reg_store(struct class *c, struct class_attribute *attr, unsigned long val; int rc; - if (kstrtoul(ubuf, 16, &val)) + if (kstrtoul(ubuf, 0, &val)) return -EINVAL; buf[0] = val & 0xFF; @@ -774,7 +789,7 @@ static ssize_t time_show(struct class *c, struct class_attribute *attr, / params[i].reg_to_unit_divider) - params[i].reg_to_unit_offset; - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", val); } static ssize_t time_store(struct class *c, struct class_attribute *attr, @@ -787,7 +802,7 @@ static ssize_t time_store(struct class *c, struct class_attribute *attr, unsigned long val; int rc; - if (kstrtoul(ubuf, 10, &val)) + if (kstrtoul(ubuf, 0, &val)) return -EINVAL; if (val < params[i].min_val || val > params[i].max_val) { @@ -841,11 +856,10 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr, gain = chip->internal_i_gain_mega; } - comp_val_nA = div_s64(regval_nA * gain, 1000000) + offset_nA; + comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA; comp_val_uA = div_s64(comp_val_nA, 1000); - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - comp_val_uA, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA); } static ssize_t voltage_show(struct class *c, struct class_attribute *attr, @@ -875,8 +889,7 @@ static ssize_t voltage_show(struct class *c, struct class_attribute *attr, comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV; comp_val_uV = div_s64(comp_val_nV, 1000); - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - comp_val_uV, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV); } static ssize_t voltage_store(struct class *c, struct class_attribute *attr, @@ -890,7 +903,7 @@ static ssize_t voltage_store(struct class *c, struct class_attribute *attr, s64 regval_nV; s64 gain, offset_nV; - if (kstrtoul(ubuf, 10, &val_uV)) + if (kstrtoul(ubuf, 0, &val_uV)) return -EINVAL; if (val_uV < params[i].min_val || val_uV > params[i].max_val) { @@ -947,8 +960,7 @@ static ssize_t coulomb_show(struct class *c, struct class_attribute *attr, gain = chip->internal_i_gain_mega; comp_val_uC = div_s64(regval_uC * gain, 1000000); - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - comp_val_uC, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC); } static ssize_t coulomb_store(struct class *c, struct class_attribute *attr, @@ -962,7 +974,7 @@ static ssize_t coulomb_store(struct class *c, struct class_attribute *attr, s64 regval; s64 gain; - if (kstrtoul(ubuf, 10, &val_uC)) + if (kstrtoul(ubuf, 0, &val_uC)) return -EINVAL; if (val_uC < params[i].min_val || val_uC > params[i].max_val) { @@ -1014,167 +1026,113 @@ static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr, return -EINVAL; } - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - pval.intval, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval); } static struct class_attribute qnovo_attributes[] = { [VER] = __ATTR_RO(version), [OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo), - [ENABLE] = __ATTR(enable, S_IRUGO | S_IWUSR, - enable_show, enable_store), - [FV_REQUEST] = __ATTR(fv_uV_request, S_IRUGO | S_IWUSR, + [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable), + [PT_ENABLE] = __ATTR_RW(pt_enable), + [FV_REQUEST] = __ATTR(fv_uV_request, 0644, val_show, val_store), - [FCC_REQUEST] = __ATTR(fcc_uA_request, S_IRUGO | S_IWUSR, + [FCC_REQUEST] = __ATTR(fcc_uA_request, 0644, val_show, val_store), - [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, S_IRUGO | S_IWUSR, + [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, 0644, reg_show, reg_store), - [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, S_IRUGO | S_IWUSR, + [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644, reg_show, reg_store), - [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, S_IRUGO | S_IWUSR, - reg_show, reg_store), - [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, S_IRUGO | S_IWUSR, - reg_show, reg_store), - [PREST1] = __ATTR(PREST1_mS, S_IRUGO | S_IWUSR, + [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444, + reg_show, NULL), + [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444, + reg_show, NULL), + [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444, + reg_show, NULL), + [PREST1] = __ATTR(PREST1_mS, 0644, time_show, time_store), - [PPULS1] = __ATTR(PPULS1_uC, S_IRUGO | S_IWUSR, + [PPULS1] = __ATTR(PPULS1_uC, 0644, coulomb_show, coulomb_store), - [NREST1] = __ATTR(NREST1_mS, S_IRUGO | S_IWUSR, + [NREST1] = __ATTR(NREST1_mS, 0644, time_show, time_store), - [NPULS1] = __ATTR(NPULS1_mS, S_IRUGO | S_IWUSR, + [NPULS1] = __ATTR(NPULS1_mS, 0644, time_show, time_store), - [PPCNT] = __ATTR(PPCNT, S_IRUGO | S_IWUSR, + [PPCNT] = __ATTR(PPCNT, 0644, time_show, time_store), - [VLIM1] = __ATTR(VLIM1_uV, S_IRUGO | S_IWUSR, + [VLIM1] = __ATTR(VLIM1_uV, 0644, voltage_show, voltage_store), - [PVOLT1] = __ATTR(PVOLT1_uV, S_IRUGO, + [PVOLT1] = __ATTR(PVOLT1_uV, 0444, voltage_show, NULL), - [PCUR1] = __ATTR(PCUR1_uA, S_IRUGO, + [PCUR1] = __ATTR(PCUR1_uA, 0444, current_show, NULL), - [PTTIME] = __ATTR(PTTIME_S, S_IRUGO, + [PTTIME] = __ATTR(PTTIME_S, 0444, time_show, NULL), - [PREST2] = __ATTR(PREST2_mS, S_IRUGO | S_IWUSR, + [PREST2] = __ATTR(PREST2_mS, 0644, time_show, time_store), - [PPULS2] = __ATTR(PPULS2_mS, S_IRUGO | S_IWUSR, + [PPULS2] = __ATTR(PPULS2_uC, 0644, coulomb_show, coulomb_store), - [NREST2] = __ATTR(NREST2_mS, S_IRUGO | S_IWUSR, + [NREST2] = __ATTR(NREST2_mS, 0644, time_show, time_store), - [NPULS2] = __ATTR(NPULS2_mS, S_IRUGO | S_IWUSR, + [NPULS2] = __ATTR(NPULS2_mS, 0644, time_show, time_store), - [VLIM2] = __ATTR(VLIM2_uV, S_IRUGO | S_IWUSR, + [VLIM2] = __ATTR(VLIM2_uV, 0644, voltage_show, voltage_store), - [PVOLT2] = __ATTR(PVOLT2_uV, S_IRUGO, + [PVOLT2] = __ATTR(PVOLT2_uV, 0444, voltage_show, NULL), - [RVOLT2] = __ATTR(RVOLT2_uV, S_IRUGO, + [RVOLT2] = __ATTR(RVOLT2_uV, 0444, voltage_show, NULL), - [PCUR2] = __ATTR(PCUR2_uA, S_IRUGO, + [PCUR2] = __ATTR(PCUR2_uA, 0444, current_show, NULL), - [SCNT] = __ATTR(SCNT, S_IRUGO | S_IWUSR, + [SCNT] = __ATTR(SCNT, 0644, time_show, time_store), - [VMAX] = __ATTR(VMAX_uV, S_IRUGO, + [VMAX] = __ATTR(VMAX_uV, 0444, voltage_show, NULL), - [SNUM] = __ATTR(SNUM, S_IRUGO | S_IWUSR, - time_show, time_store), - [VBATT] = __ATTR(VBATT_uV, S_IRUGO, + [SNUM] = __ATTR(SNUM, 0444, + time_show, NULL), + [VBATT] = __ATTR(VBATT_uV, 0444, batt_prop_show, NULL), - [IBATT] = __ATTR(IBATT_uA, S_IRUGO, + [IBATT] = __ATTR(IBATT_uA, 0444, batt_prop_show, NULL), - [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, S_IRUGO, + [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, 0444, batt_prop_show, NULL), - [BATTSOC] = __ATTR(BATTSOC, S_IRUGO, + [BATTSOC] = __ATTR(BATTSOC, 0444, batt_prop_show, NULL), __ATTR_NULL, }; -static void get_chg_props(struct qnovo *chip, struct chg_props *cp) +static int qnovo_update_status(struct qnovo *chip) { - union power_supply_propval pval; u8 val = 0; int rc; + bool charging; + bool changed = false; - cp->charging = true; - rc = qnovo_read(chip, QNOVO_ERROR_STS, &val, 1); + rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1); if (rc < 0) { pr_err("Couldn't read error sts rc = %d\n", rc); - cp->charging = false; + charging = false; } else { - cp->charging = (!(val & QNOVO_ERROR_BIT)); + charging = !(val & QNOVO_ERROR_CHARGING_DISABLED); } - if (chip->wa_flags & QNOVO_NO_ERR_STS_BIT) { - /* - * on v1.0 and v1.1 pmic's force charging to true - * if things are not good to charge s/w gets a PTRAIN_DONE - * interrupt - */ - cp->charging = true; - } + if (chip->ok_to_qnovo ^ charging) { - cp->usb_online = false; - if (!chip->usb_psy) - chip->usb_psy = power_supply_get_by_name("usb"); - if (chip->usb_psy) { - rc = power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_ONLINE, &pval); - if (rc < 0) - pr_err("Couldn't read usb online rc = %d\n", rc); - else - cp->usb_online = (bool)pval.intval; - } + vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !charging, 0); + if (!charging) + vote(chip->disable_votable, USER_VOTER, true, 0); - cp->dc_online = false; - if (!chip->dc_psy) - chip->dc_psy = power_supply_get_by_name("dc"); - if (chip->dc_psy) { - rc = power_supply_get_property(chip->dc_psy, - POWER_SUPPLY_PROP_ONLINE, &pval); - if (rc < 0) - pr_err("Couldn't read dc online rc = %d\n", rc); - else - cp->dc_online = (bool)pval.intval; + chip->ok_to_qnovo = charging; + changed = true; } -} - -static void get_chg_status(struct qnovo *chip, const struct chg_props *cp, - struct chg_status *cs) -{ - cs->ok_to_qnovo = false; - if (cp->charging && - (cp->usb_online || cp->dc_online)) - cs->ok_to_qnovo = true; + return changed; } static void status_change_work(struct work_struct *work) { struct qnovo *chip = container_of(work, struct qnovo, status_change_work); - bool notify_uevent = false; - struct chg_props cp; - struct chg_status cs; - - get_chg_props(chip, &cp); - get_chg_status(chip, &cp, &cs); - - if (cs.ok_to_qnovo ^ chip->cs.ok_to_qnovo) { - /* - * when it is not okay to Qnovo charge, disable both voters, - * so that when it becomes okay to Qnovo charge the user voter - * has to specifically enable its vote to being Qnovo charging - */ - if (!cs.ok_to_qnovo) { - vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 1, 0); - vote(chip->disable_votable, USER_VOTER, 1, 0); - } else { - vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 0, 0); - } - notify_uevent = true; - } - memcpy(&chip->cp, &cp, sizeof(struct chg_props)); - memcpy(&chip->cs, &cs, sizeof(struct chg_status)); - - if (notify_uevent) + if (qnovo_update_status(chip)) kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); } @@ -1186,8 +1144,8 @@ static int qnovo_notifier_call(struct notifier_block *nb, if (ev != PSY_EVENT_PROP_CHANGED) return NOTIFY_OK; - if ((strcmp(psy->desc->name, "battery") == 0) - || (strcmp(psy->desc->name, "usb") == 0)) + + if (strcmp(psy->desc->name, "battery") == 0) schedule_work(&chip->status_change_work); return NOTIFY_OK; @@ -1197,8 +1155,7 @@ static irqreturn_t handle_ptrain_done(int irq, void *data) { struct qnovo *chip = data; - /* disable user voter here */ - vote(chip->disable_votable, USER_VOTER, 0, 0); + qnovo_update_status(chip); kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); return IRQ_HANDLED; } @@ -1211,7 +1168,14 @@ static int qnovo_hw_init(struct qnovo *chip) u8 vadc_offset, vadc_gain; u8 val; - vote(chip->disable_votable, USER_VOTER, 1, 0); + vote(chip->disable_votable, USER_VOTER, true, 0); + + val = 0; + rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc bitstream control rc = %d\n", rc); + return rc; + } rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1); if (rc < 0) { @@ -1219,12 +1183,28 @@ static int qnovo_hw_init(struct qnovo *chip) return rc; } + /* stored as an 8 bit 2's complement signed integer */ + val = -1 * iadc_offset_external; + rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc offset rc = %d\n", rc); + return rc; + } + rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1); if (rc < 0) { pr_err("Couldn't read iadc internal offset rc = %d\n", rc); return rc; } + /* stored as an 8 bit 2's complement signed integer */ + val = -1 * iadc_offset_internal; + rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc offset rc = %d\n", rc); + return rc; + } + rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1); if (rc < 0) { pr_err("Couldn't read iadc external gain rc = %d\n", rc); @@ -1249,53 +1229,20 @@ static int qnovo_hw_init(struct qnovo *chip) return rc; } - chip->external_offset_nA = (s64)iadc_offset_external * IADC_LSB_NA; - chip->internal_offset_nA = (s64)iadc_offset_internal * IADC_LSB_NA; - chip->offset_nV = (s64)vadc_offset * VADC_LSB_NA; + chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA; + chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA; + chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA; chip->external_i_gain_mega - = 1000000000 + (s64)iadc_gain_external * GAIN_LSB_FACTOR; + = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR; chip->external_i_gain_mega = div_s64(chip->external_i_gain_mega, 1000); chip->internal_i_gain_mega - = 1000000000 + (s64)iadc_gain_internal * GAIN_LSB_FACTOR; + = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR; chip->internal_i_gain_mega = div_s64(chip->internal_i_gain_mega, 1000); - chip->v_gain_mega = 1000000000 + (s64)vadc_gain * GAIN_LSB_FACTOR; + chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR; chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000); - val = 0; - rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1); - if (rc < 0) { - pr_err("Couldn't write iadc bitsteam control rc = %d\n", rc); - return rc; - } - - rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); - if (rc < 0) { - pr_err("Couldn't read iadc offset rc = %d\n", rc); - return rc; - } - - val *= -1; - rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); - if (rc < 0) { - pr_err("Couldn't write iadc offset rc = %d\n", rc); - return rc; - } - - rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); - if (rc < 0) { - pr_err("Couldn't read iadc offset rc = %d\n", rc); - return rc; - } - - val *= -1; - rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); - if (rc < 0) { - pr_err("Couldn't write iadc offset rc = %d\n", rc); - return rc; - } - return 0; } @@ -1333,6 +1280,9 @@ static int qnovo_request_interrupts(struct qnovo *chip) irq_ptrain_done, rc); return rc; } + + enable_irq_wake(irq_ptrain_done); + return rc; } @@ -1362,13 +1312,6 @@ static int qnovo_probe(struct platform_device *pdev) return rc; } - rc = qnovo_check_chg_version(chip); - if (rc < 0) { - if (rc != -EPROBE_DEFER) - pr_err("Couldn't check version rc=%d\n", rc); - return rc; - } - /* set driver data before resources request it */ platform_set_drvdata(pdev, chip); @@ -1414,6 +1357,8 @@ static int qnovo_probe(struct platform_device *pdev) goto unreg_notifier; } + device_init_wakeup(chip->dev, true); + return rc; unreg_notifier: diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 771d1cd32714..e8249163e948 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -11,6 +11,7 @@ */ #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -25,7 +26,7 @@ #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define SMB2_DEFAULT_WPWR_UW 8000000 @@ -837,7 +838,9 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, @@ -909,6 +912,9 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER); break; + case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: + rc = smblib_get_prop_charge_qnovo_enable(chg, val); + break; case POWER_SUPPLY_PROP_VOLTAGE_QNOVO: val->intval = chg->qnovo_fv_uv; break; @@ -984,12 +990,17 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval); break; + case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: + rc = smblib_set_prop_charge_qnovo_enable(chg, val); + break; case POWER_SUPPLY_PROP_VOLTAGE_QNOVO: chg->qnovo_fv_uv = val->intval; rc = rerun_election(chg->fv_votable); break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: chg->qnovo_fcc_ua = val->intval; + vote(chg->pl_disable_votable, PL_QNOVO_VOTER, + val->intval != -EINVAL && val->intval < 2000000, 0); rc = rerun_election(chg->fcc_votable); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: @@ -1332,6 +1343,38 @@ static int smb2_disable_typec(struct smb_charger *chg) { int rc; + /* Move to typeC mode */ + /* configure FSM in idle state */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't put FSM in idle rc=%d\n", rc); + return rc; + } + + /* wait for FSM to enter idle state */ + msleep(200); + /* configure TypeC mode */ + rc = smblib_masked_write(chg, TYPE_C_CFG_REG, + TYPE_C_OR_U_USB_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc); + return rc; + } + + /* wait for mode change before enabling FSM */ + usleep_range(10000, 11000); + /* release FSM from idle state */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't release FSM rc=%d\n", rc); + return rc; + } + + /* wait for FSM to start */ + msleep(100); + /* move to uUSB mode */ /* configure FSM in idle state */ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT); @@ -1340,6 +1383,8 @@ static int smb2_disable_typec(struct smb_charger *chg) return rc; } + /* wait for FSM to enter idle state */ + msleep(200); /* configure micro USB mode */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, TYPE_C_OR_U_USB_BIT, TYPE_C_OR_U_USB_BIT); @@ -1348,6 +1393,8 @@ static int smb2_disable_typec(struct smb_charger *chg) return rc; } + /* wait for mode change before enabling FSM */ + usleep_range(10000, 11000); /* release FSM from idle state */ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_DISABLE_CMD_BIT, 0); @@ -1428,10 +1475,10 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, true, chip->dt.fv_uv); vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); - vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, - chip->dt.hvdcp_disable, 0); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, + chip->dt.hvdcp_disable, 0); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, @@ -1497,13 +1544,6 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } - rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG, - QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT); - if (rc < 0) { - dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc); - return rc; - } - /* configure step charging */ rc = smb2_config_step_charging(chip); if (rc < 0) { @@ -1616,6 +1656,15 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +static int smb2_post_init(struct smb2 *chip) +{ + struct smb_charger *chg = &chip->chg; + + rerun_election(chg->usb_irq_enable_votable); + + return 0; +} + static int smb2_chg_config_init(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; @@ -2145,6 +2194,8 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } + smb2_post_init(chip); + smb2_create_debugfs(chip); rc = smblib_get_prop_usb_present(chg, &val); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 7d2e00dc934b..51c87f963307 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -22,7 +22,7 @@ #include "smb-lib.h" #include "smb-reg.h" #include "storm-watch.h" -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ @@ -725,7 +725,6 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg) int smblib_rerun_apsd_if_required(struct smb_charger *chg) { - const struct apsd_result *apsd_result; union power_supply_propval val; int rc; @@ -738,12 +737,27 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) if (!val.intval) return 0; - apsd_result = smblib_get_apsd_result(chg); - if ((apsd_result->pst == POWER_SUPPLY_TYPE_UNKNOWN) - || (apsd_result->pst == POWER_SUPPLY_TYPE_USB)) { - smblib_rerun_apsd(chg); + /* fetch the DPDM regulator */ + if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, + "dpdm-supply", NULL)) { + chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm"); + if (IS_ERR(chg->dpdm_reg)) { + smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n", + PTR_ERR(chg->dpdm_reg)); + chg->dpdm_reg = NULL; + } } + if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); + rc = regulator_enable(chg->dpdm_reg); + if (rc < 0) + smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n", + rc); + } + + smblib_rerun_apsd(chg); + return 0; } @@ -1109,6 +1123,26 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable, return rc; } +static int smblib_usb_irq_enable_vote_callback(struct votable *votable, + void *data, int enable, const char *client) +{ + struct smb_charger *chg = data; + + if (!chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq || + !chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq) + return 0; + + if (enable) { + enable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq); + enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); + } else { + disable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq); + disable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); + } + + return 0; +} + /******************* * VCONN REGULATOR * * *****************/ @@ -1305,6 +1339,14 @@ int smblib_vbus_regulator_enable(struct regulator_dev *rdev) if (chg->otg_en) goto unlock; + if (!chg->usb_icl_votable) { + chg->usb_icl_votable = find_votable("USB_ICL"); + + if (!chg->usb_icl_votable) + return -EINVAL; + } + vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, true, 0); + rc = _smblib_vbus_regulator_enable(rdev); if (rc >= 0) chg->otg_en = true; @@ -1368,6 +1410,8 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev) if (rc >= 0) chg->otg_en = false; + if (chg->usb_icl_votable) + vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0); unlock: mutex_unlock(&chg->otg_oc_lock); return rc; @@ -1681,6 +1725,23 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, return 0; } +int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD rc=%d\n", + rc); + return rc; + } + + val->intval = (bool)(stat & QNOVO_PT_ENABLE_CMD_BIT); + return 0; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -1745,6 +1806,22 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, return 0; } +int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG, + QNOVO_PT_ENABLE_CMD_BIT, + val->intval ? QNOVO_PT_ENABLE_CMD_BIT : 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc); + return rc; + } + + return rc; +} + int smblib_rerun_aicl(struct smb_charger *chg) { int rc, settled_icl_ua; @@ -2403,6 +2480,22 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, return -EINVAL; } + if (power_role == UFP_EN_CMD_BIT) { + /* disable PBS workaround when forcing sink mode */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n", + rc); + } + } else { + /* restore it back to 0xA5 */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n", + rc); + } + } + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_POWER_ROLE_CMD_MASK, power_role); if (rc < 0) { @@ -2470,6 +2563,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0); vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0); /* * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line @@ -3304,6 +3398,10 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, /* could be a legacy cable, try doing hvdcp */ try_rerun_apsd_for_hvdcp(chg); + /* enable HDC and ICL irq for QC2/3 charger */ + if (qc_charger) + vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0); + /* * HVDCP detection timeout done * If adapter is not QC2.0/QC3.0 - it is a plain old DCP. @@ -3554,6 +3652,8 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); + vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0); /* reset votes from vbus_cc_short */ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, @@ -4236,6 +4336,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->usb_irq_enable_votable = create_votable("USB_IRQ_DISABLE", + VOTE_SET_ANY, + smblib_usb_irq_enable_vote_callback, + chg); + if (IS_ERR(chg->usb_irq_enable_votable)) { + rc = PTR_ERR(chg->usb_irq_enable_votable); + return rc; + } + return rc; } diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index de236164e6b2..048e7c2b4091 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -32,10 +32,12 @@ enum print_reason { #define USER_VOTER "USER_VOTER" #define PD_VOTER "PD_VOTER" #define DCP_VOTER "DCP_VOTER" +#define QC_VOTER "QC_VOTER" #define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER" #define USB_PSY_VOTER "USB_PSY_VOTER" #define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER" #define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER" +#define PL_QNOVO_VOTER "PL_QNOVO_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define USBIN_V_VOTER "USBIN_V_VOTER" #define CHG_STATE_VOTER "CHG_STATE_VOTER" @@ -49,6 +51,7 @@ enum print_reason { #define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER" #define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER" #define BOOST_BACK_VOTER "BOOST_BACK_VOTER" +#define USBIN_USBIN_BOOST_VOTER "USBIN_USBIN_BOOST_VOTER" #define HVDCP_INDIRECT_VOTER "HVDCP_INDIRECT_VOTER" #define MICRO_USB_VOTER "MICRO_USB_VOTER" #define DEBUG_BOARD_VOTER "DEBUG_BOARD_VOTER" @@ -272,6 +275,7 @@ struct smb_charger { struct votable *hvdcp_enable_votable; struct votable *apsd_disable_votable; struct votable *hvdcp_hw_inov_dis_votable; + struct votable *usb_irq_enable_votable; /* work */ struct work_struct bms_update_work; @@ -455,6 +459,8 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_die_health(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_pd_current_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_usb_current_max(struct smb_charger *chg, @@ -475,6 +481,8 @@ int smblib_get_prop_slave_current_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_ship_mode(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, + const union power_supply_propval *val); void smblib_suspend_on_debug_battery(struct smb_charger *chg); int smblib_rerun_apsd_if_required(struct smb_charger *chg); int smblib_get_prop_fcc_delta(struct smb_charger *chg, diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index f7c13390d477..b79060094cf6 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -1019,6 +1019,8 @@ enum { #define CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG (MISC_BASE + 0xA0) #define CFG_BUCKBOOST_FREQ_SELECT_BOOST_REG (MISC_BASE + 0xA1) +#define TM_IO_DTEST4_SEL (MISC_BASE + 0xE9) + /* CHGR FREQ Peripheral registers */ #define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50) diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 739d80cd8801..4916c87aced8 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -28,7 +28,7 @@ #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define SMB138X_DEFAULT_FCC_UA 1000000 #define SMB138X_DEFAULT_ICL_UA 1500000 @@ -96,6 +96,7 @@ struct smb_dt_props { int dc_icl_ua; int chg_temp_max_mdegc; int connector_temp_max_mdegc; + int pl_mode; }; struct smb138x { @@ -161,6 +162,11 @@ static int smb138x_parse_dt(struct smb138x *chip) return -EINVAL; } + rc = of_property_read_u32(node, + "qcom,parallel-mode", &chip->dt.pl_mode); + if (rc < 0) + chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID; + chip->dt.suspend_input = of_property_read_bool(node, "qcom,suspend-input"); @@ -529,6 +535,8 @@ static enum power_supply_property smb138x_parallel_props[] = { POWER_SUPPLY_PROP_CHARGING_ENABLED, POWER_SUPPLY_PROP_PIN_ENABLED, POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, @@ -568,6 +576,19 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_SUSPEND: rc = smblib_get_usb_suspend(chg, &val->intval); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + rc = smblib_get_prop_input_current_limited(chg, val); + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + rc = smblib_get_charge_param(chg, &chg->param.usb_icl, + &val->intval); + else + val->intval = 0; + break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval); break; @@ -588,7 +609,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, val->strval = "smb138x"; break; case POWER_SUPPLY_PROP_PARALLEL_MODE: - val->intval = POWER_SUPPLY_PL_USBMID_USBMID; + val->intval = chip->dt.pl_mode; break; case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: val->intval = smb138x_get_prop_connector_health(chip); @@ -647,6 +668,11 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_SUSPEND: rc = smb138x_set_parallel_suspend(chip, (bool)val->intval); break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + val->intval); + break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval); break; @@ -1458,6 +1484,15 @@ static int smb138x_slave_probe(struct smb138x *chip) goto cleanup; } + if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) { + rc = smb138x_init_vbus_regulator(chip); + if (rc < 0) { + pr_err("Couldn't initialize vbus regulator rc=%d\n", + rc); + return rc; + } + } + rc = smb138x_init_parallel_psy(chip); if (rc < 0) { pr_err("Couldn't initialize parallel psy rc=%d\n", rc); @@ -1482,6 +1517,8 @@ cleanup: smblib_deinit(chg); if (chip->parallel_psy) power_supply_unregister(chip->parallel_psy); + if (chg->vbus_vreg && chg->vbus_vreg->rdev) + regulator_unregister(chg->vbus_vreg->rdev); return rc; } diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 117fccf7934a..01a6a83f625d 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -65,7 +65,6 @@ #define PCA9685_MAXCHAN 0x10 #define LED_FULL (1 << 4) -#define MODE1_RESTART (1 << 7) #define MODE1_SLEEP (1 << 4) #define MODE2_INVRT (1 << 4) #define MODE2_OUTDRV (1 << 2) @@ -117,16 +116,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, udelay(500); pca->period_ns = period_ns; - - /* - * If the duty cycle did not change, restart PWM with - * the same duty cycle to period ratio and return. - */ - if (duty_ns == pca->duty_ns) { - regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_RESTART, 0x1); - return 0; - } } else { dev_err(chip->dev, "prescaler not set: period out of bounds!\n"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 88a5c497d5ed..4f9de7fda612 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4705,12 +4705,13 @@ static void regulator_summary_show_subtree(struct seq_file *s, seq_puts(s, "\n"); list_for_each_entry(consumer, &rdev->consumer_list, list) { - if (consumer->dev->class == ®ulator_class) + if (consumer->dev && consumer->dev->class == ®ulator_class) continue; seq_printf(s, "%*s%-*s ", (level + 1) * 3 + 1, "", - 30 - (level + 1) * 3, dev_name(consumer->dev)); + 30 - (level + 1) * 3, + consumer->dev ? dev_name(consumer->dev) : "deviceless"); switch (rdev->desc->type) { case REGULATOR_VOLTAGE: diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index 77e8bf4b9895..9a20b6925877 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -87,8 +87,9 @@ struct cpr3_msm8996_hmss_fuses { /* * Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0. * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1. + * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2. */ -#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 16 +#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 24 /* * Constants which define the name of each fuse corner. Note that no actual @@ -420,6 +421,9 @@ static const int msm8996_vdd_mx_fuse_ret_volt[] = { #define MSM8996_HMSS_AGING_SENSOR_ID 11 #define MSM8996_HMSS_AGING_BYPASS_MASK0 (GENMASK(7, 0) & ~BIT(3)) +/* Use scaled gate count (GCNT) for aging measurements */ +#define MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR 1500 + /** * cpr3_msm8996_hmss_use_voltage_offset_fuse() - return if this part utilizes * voltage offset fuses or not @@ -1541,6 +1545,8 @@ static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl) ctrl->aging_sensor->sensor_id = MSM8996_HMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSM8996_HMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->ro_scale = aging_ro_scale; + ctrl->aging_gcnt_scaling_factor + = MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR; ctrl->aging_sensor->init_quot_diff = cpr3_convert_open_loop_voltage_fuse(0, diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c index 41032dd3c15a..5a031f503b51 100644 --- a/drivers/regulator/cpr3-mmss-regulator.c +++ b/drivers/regulator/cpr3-mmss-regulator.c @@ -72,8 +72,9 @@ struct cpr3_msm8996_mmss_fuses { /* * Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0. * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1. + * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2. */ -#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16 +#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 24 /* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */ #define CPR3_MSM8998_MMSS_FUSE_COMBO_COUNT 8 @@ -245,6 +246,9 @@ msm8998_v2_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { #define MSM8996_MMSS_AGING_SENSOR_ID 29 #define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0)) +/* Use scaled gate count (GCNT) for aging measurements */ +#define MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR 1500 + #define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SCALE 1 #define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SIZE 8 @@ -808,6 +812,8 @@ static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl) return -ENOMEM; ctrl->aging_sensor->ro_scale = aging_ro_scale; + ctrl->aging_gcnt_scaling_factor + = MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR; if (cpr3_ctrl_is_msm8998(ctrl)) { ctrl->aging_sensor->sensor_id = MSM8998_MMSS_AGING_SENSOR_ID; diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index 3ffe094fe53c..d131a8ea4144 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -317,6 +317,12 @@ */ #define CPRH_DELTA_QUOT_STEP_FACTOR 4 +/* + * The multiplier applied to scaling factor value used to derive GCNT + * for aging measurements. + */ +#define CPR3_AGING_GCNT_SCALING_UNITY 1000 + static DEFINE_MUTEX(cpr3_controller_list_mutex); static LIST_HEAD(cpr3_controller_list); static struct dentry *cpr3_debugfs_base; @@ -1011,7 +1017,8 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) - | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) + | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop + && sdelta->allow_core_count_adj) ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) || !ctrl->supports_hw_closed_loop) @@ -3563,7 +3570,12 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl, gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0)); gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1)); gcnt_ref = cpr3_regulator_get_gcnt(ctrl); - gcnt = gcnt_ref * 3 / 2; + + gcnt = gcnt_ref; + if (ctrl->aging_gcnt_scaling_factor) + gcnt = gcnt_ref * ctrl->aging_gcnt_scaling_factor + / CPR3_AGING_GCNT_SCALING_UNITY; + cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt); cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt); diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index 1ac0f7b816b3..99c86e7b70c9 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -702,6 +702,13 @@ struct cpr3_panic_regs_info { * @aging_possible_val: Optional value that the masked aging_possible_reg * register must have in order for a CPR aging measurement * to be possible. + * @aging_gcnt_scaling_factor: The scaling factor used to derive the gate count + * used for aging measurements. This value is divided by + * 1000 when used as shown in the below equation: + * Aging_GCNT = GCNT_REF * scaling_factor / 1000. + * For example, a value of 1500 specifies that the gate + * count (GCNT) used for aging measurement should be 1.5 + * times of reference gate count (GCNT_REF). * @step_quot_fixed: Fixed step quotient value used for target quotient * adjustment if use_dynamic_step_quot is not set. * This parameter is only relevant for CPR4 controllers @@ -827,6 +834,7 @@ struct cpr3_controller { int aging_sensor_count; u32 aging_possible_mask; u32 aging_possible_val; + u32 aging_gcnt_scaling_factor; u32 step_quot_fixed; u32 initial_temp_band; diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index c75beec59a79..484f0412addf 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -118,6 +118,12 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_FTS2_STEP_MARGIN_NUM 4 #define QPNP_FTS2_STEP_MARGIN_DEN 5 +/* + * Settling delay for FTS2.5 + * Warm-up=20uS, 0-10% & 90-100% non-linear V-ramp delay = 50uS + */ +#define FTS2P5_SETTLING_DELAY_US 70 + /* VSET value to decide the range of ULT SMPS */ #define ULT_SMPS_RANGE_SPLIT 0x60 @@ -268,6 +274,7 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) unsigned vlevel = spm_regulator_uv_to_vlevel(vreg, uV); bool spm_failed = false; int rc = 0; + u32 slew_delay; u8 reg; if (likely(!vreg->bypass_spm)) { @@ -295,7 +302,16 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) if (uV > vreg->last_set_uV) { /* Wait for voltage stepping to complete. */ - udelay(DIV_ROUND_UP(uV - vreg->last_set_uV, vreg->step_rate)); + slew_delay = DIV_ROUND_UP(uV - vreg->last_set_uV, + vreg->step_rate); + if (vreg->regulator_type == QPNP_TYPE_FTS2p5) + slew_delay += FTS2P5_SETTLING_DELAY_US; + udelay(slew_delay); + } else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) { + /* add the ramp-down delay */ + slew_delay = DIV_ROUND_UP(vreg->last_set_uV - uV, + vreg->step_rate) + FTS2P5_SETTLING_DELAY_US; + udelay(slew_delay); } vreg->last_set_uV = uV; diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 7d2ae3e9e942..342f5da79975 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -47,7 +47,8 @@ static struct regulator_desc vmmc_regulator = { .volt_table = stw481x_vmmc_voltages, .enable_time = 200, /* FIXME: look this up */ .enable_reg = STW_CONF1, - .enable_mask = STW_CONF1_PDN_VMMC, + .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS, + .enable_val = STW_CONF1_PDN_VMMC, .vsel_reg = STW_CONF1, .vsel_mask = STW_CONF1_VMMC_MASK, }; diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 5836751b8203..9bb934ed2a7a 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -748,9 +748,23 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq); */ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) { + struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); + struct rtc_time tm; + ktime_t now; + timer->enabled = 1; + __rtc_read_time(rtc, &tm); + now = rtc_tm_to_ktime(tm); + + /* Skip over expired timers */ + while (next) { + if (next->expires.tv64 >= now.tv64) + break; + next = timerqueue_iterate_next(next); + } + timerqueue_add(&rtc->timerqueue, &timer->node); - if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { + if (!next) { struct rtc_wkalrm alarm; int err; alarm.time = rtc_ktime_to_tm(timer->node.expires); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index c169a2cd4727..e29cc9fca0bf 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -37,9 +37,11 @@ /* Control register */ #define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) #define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) #define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) +#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) /* RTC */ @@ -114,13 +116,17 @@ struct sun6i_rtc_dev { void __iomem *base; int irq; unsigned long alarm; + + spinlock_t lock; }; static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) { struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + irqreturn_t ret = IRQ_NONE; u32 val; + spin_lock(&chip->lock); val = readl(chip->base + SUN6I_ALRM_IRQ_STA); if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { @@ -129,10 +135,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); - return IRQ_HANDLED; + ret = IRQ_HANDLED; } + spin_unlock(&chip->lock); - return IRQ_NONE; + return ret; } static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) @@ -140,6 +147,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) u32 alrm_val = 0; u32 alrm_irq_val = 0; u32 alrm_wake_val = 0; + unsigned long flags; if (to) { alrm_val = SUN6I_ALRM_EN_CNT_EN; @@ -150,9 +158,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) chip->base + SUN6I_ALRM_IRQ_STA); } + spin_lock_irqsave(&chip->lock, flags); writel(alrm_val, chip->base + SUN6I_ALRM_EN); writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); + spin_unlock_irqrestore(&chip->lock, flags); } static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) @@ -191,11 +201,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) { struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + unsigned long flags; u32 alrm_st; u32 alrm_en; + spin_lock_irqsave(&chip->lock, flags); alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + spin_unlock_irqrestore(&chip->lock, flags); + wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); rtc_time_to_tm(chip->alarm, &wkalrm->time); @@ -356,6 +370,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev) chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; + spin_lock_init(&chip->lock); platform_set_drvdata(pdev, chip); chip->dev = &pdev->dev; @@ -404,6 +419,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev) /* disable alarm wakeup */ writel(0, chip->base + SUN6I_ALARM_CONFIG); + /* switch to the external, more precise, oscillator */ + writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, + chip->base + SUN6I_LOSC_CTRL); + chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, &sun6i_rtc_ops, THIS_MODULE); if (IS_ERR(chip->rtc)) { diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 94a8f4ab57bc..ae1dc37e4068 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -892,7 +892,7 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum, dev_info = bdev->bd_disk->private_data; if (!dev_info) return -ENODEV; - dev_sz = dev_info->end - dev_info->start; + dev_sz = dev_info->end - dev_info->start + 1; offset = secnum * 512; addr = (void *) (dev_info->start + offset); *pfn = virt_to_phys(addr) >> PAGE_SHIFT; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 799c1524c779..4b8de3e70cf2 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -872,7 +872,7 @@ static int __init vmlogrdr_init(void) goto cleanup; for (i=0; i < MAXMINOR; ++i ) { - sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL); + sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sys_ser[i].buffer) { rc = -ENOMEM; break; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5d06253c2a7a..30e9fbbff051 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) struct qdio_q *q; int i; - for_each_input_queue(irq, q, i) { - if (!references_shared_dsci(irq) && - has_multiple_inq_on_dsci(irq)) - xchg(q->irq_ptr->dsci, 0); + if (!references_shared_dsci(irq) && + has_multiple_inq_on_dsci(irq)) + xchg(irq->dsci, 0); + for_each_input_queue(irq, q, i) { if (q->u.in.queue_start_poll) { /* skip if polling is enabled or already in work */ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 581001989937..d5bf36ec8a75 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -289,11 +289,12 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter, /** - * zfcp_dbf_rec_run - trace event related to running recovery + * zfcp_dbf_rec_run_lvl - trace event related to running recovery + * @level: trace level to be used for event * @tag: identifier for event * @erp: erp_action running */ -void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) +void zfcp_dbf_rec_run_lvl(int level, char *tag, struct zfcp_erp_action *erp) { struct zfcp_dbf *dbf = erp->adapter->dbf; struct zfcp_dbf_rec *rec = &dbf->rec_buf; @@ -319,11 +320,21 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) else rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter); - debug_event(dbf->rec, 1, rec, sizeof(*rec)); + debug_event(dbf->rec, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->rec_lock, flags); } /** + * zfcp_dbf_rec_run - trace event related to running recovery + * @tag: identifier for event + * @erp: erp_action running + */ +void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) +{ + zfcp_dbf_rec_run_lvl(1, tag, erp); +} + +/** * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery * @tag: identifier for event * @wka_port: well known address port diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 36d07584271d..db186d44cfaf 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -2,7 +2,7 @@ * zfcp device driver * debug feature declarations * - * Copyright IBM Corp. 2008, 2015 + * Copyright IBM Corp. 2008, 2016 */ #ifndef ZFCP_DBF_H @@ -283,6 +283,30 @@ struct zfcp_dbf { struct zfcp_dbf_scsi scsi_buf; }; +/** + * zfcp_dbf_hba_fsf_resp_suppress - true if we should not trace by default + * @req: request that has been completed + * + * Returns true if FCP response with only benign residual under count. + */ +static inline +bool zfcp_dbf_hba_fsf_resp_suppress(struct zfcp_fsf_req *req) +{ + struct fsf_qtcb *qtcb = req->qtcb; + u32 fsf_stat = qtcb->header.fsf_status; + struct fcp_resp *fcp_rsp; + u8 rsp_flags, fr_status; + + if (qtcb->prefix.qtcb_type != FSF_IO_COMMAND) + return false; /* not an FCP response */ + fcp_rsp = (struct fcp_resp *)&qtcb->bottom.io.fcp_rsp; + rsp_flags = fcp_rsp->fr_flags; + fr_status = fcp_rsp->fr_status; + return (fsf_stat == FSF_FCP_RSP_AVAILABLE) && + (rsp_flags == FCP_RESID_UNDER) && + (fr_status == SAM_STAT_GOOD); +} + static inline void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req) { @@ -304,7 +328,9 @@ void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req) zfcp_dbf_hba_fsf_resp("fs_perr", 1, req); } else if (qtcb->header.fsf_status != FSF_GOOD) { - zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req); + zfcp_dbf_hba_fsf_resp("fs_ferr", + zfcp_dbf_hba_fsf_resp_suppress(req) + ? 5 : 1, req); } else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) || (req->fsf_command == FSF_QTCB_OPEN_LUN)) { @@ -388,4 +414,15 @@ void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag) _zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL); } +/** + * zfcp_dbf_scsi_nullcmnd() - trace NULLify of SCSI command in dev/tgt-reset. + * @scmnd: SCSI command that was NULLified. + * @fsf_req: request that owned @scmnd. + */ +static inline void zfcp_dbf_scsi_nullcmnd(struct scsi_cmnd *scmnd, + struct zfcp_fsf_req *fsf_req) +{ + _zfcp_dbf_scsi("scfc__1", 3, scmnd, fsf_req); +} + #endif /* ZFCP_DBF_H */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index a59d678125bd..7ccfce559034 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -3,7 +3,7 @@ * * Error Recovery Procedures (ERP). * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #define KMSG_COMPONENT "zfcp" @@ -1204,6 +1204,62 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) } } +/** + * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery + * @port: zfcp_port whose fc_rport we should try to unblock + */ +static void zfcp_erp_try_rport_unblock(struct zfcp_port *port) +{ + unsigned long flags; + struct zfcp_adapter *adapter = port->adapter; + int port_status; + struct Scsi_Host *shost = adapter->scsi_host; + struct scsi_device *sdev; + + write_lock_irqsave(&adapter->erp_lock, flags); + port_status = atomic_read(&port->status); + if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || + (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE | + ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) { + /* new ERP of severity >= port triggered elsewhere meanwhile or + * local link down (adapter erp_failed but not clear unblock) + */ + zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action); + write_unlock_irqrestore(&adapter->erp_lock, flags); + return; + } + spin_lock(shost->host_lock); + __shost_for_each_device(sdev, shost) { + struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev); + int lun_status; + + if (zsdev->port != port) + continue; + /* LUN under port of interest */ + lun_status = atomic_read(&zsdev->status); + if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0) + continue; /* unblock rport despite failed LUNs */ + /* LUN recovery not given up yet [maybe follow-up pending] */ + if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || + (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) { + /* LUN blocked: + * not yet unblocked [LUN recovery pending] + * or meanwhile blocked [new LUN recovery triggered] + */ + zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action); + spin_unlock(shost->host_lock); + write_unlock_irqrestore(&adapter->erp_lock, flags); + return; + } + } + /* now port has no child or all children have completed recovery, + * and no ERP of severity >= port was meanwhile triggered elsewhere + */ + zfcp_scsi_schedule_rport_register(port); + spin_unlock(shost->host_lock); + write_unlock_irqrestore(&adapter->erp_lock, flags); +} + static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) { struct zfcp_adapter *adapter = act->adapter; @@ -1214,6 +1270,7 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) case ZFCP_ERP_ACTION_REOPEN_LUN: if (!(act->status & ZFCP_STATUS_ERP_NO_REF)) scsi_device_put(sdev); + zfcp_erp_try_rport_unblock(port); break; case ZFCP_ERP_ACTION_REOPEN_PORT: @@ -1224,7 +1281,7 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) */ if (act->step != ZFCP_ERP_STEP_UNINITIALIZED) if (result == ZFCP_ERP_SUCCEEDED) - zfcp_scsi_schedule_rport_register(port); + zfcp_erp_try_rport_unblock(port); /* fall through */ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: put_device(&port->dev); diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index c8fed9fa1cca..21c8c689b02b 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -3,7 +3,7 @@ * * External function declarations. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #ifndef ZFCP_EXT_H @@ -35,6 +35,8 @@ extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *); extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *, struct zfcp_port *, struct scsi_device *, u8, u8); extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *); +extern void zfcp_dbf_rec_run_lvl(int level, char *tag, + struct zfcp_erp_action *erp); extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64); extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 75f820ca17b7..27ff38f839fc 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1583,7 +1583,7 @@ out: int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req = NULL; + struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1612,7 +1612,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); - if (req && !IS_ERR(req)) + if (!retval) zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id); return retval; } @@ -1638,7 +1638,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req = NULL; + struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1667,7 +1667,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); - if (req && !IS_ERR(req)) + if (!retval) zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id); return retval; } diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index be1c04b334c5..ea3c76ac0de1 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -3,7 +3,7 @@ * * Interface to the FSF support functions. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #ifndef FSF_H @@ -78,6 +78,7 @@ #define FSF_APP_TAG_CHECK_FAILURE 0x00000082 #define FSF_REF_TAG_CHECK_FAILURE 0x00000083 #define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD +#define FSF_FCP_RSP_AVAILABLE 0x000000AF #define FSF_UNKNOWN_COMMAND 0x000000E2 #define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3 #define FSF_INVALID_COMMAND_OPTION 0x000000E5 diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h index 7c2c6194dfca..703fce59befe 100644 --- a/drivers/s390/scsi/zfcp_reqlist.h +++ b/drivers/s390/scsi/zfcp_reqlist.h @@ -4,7 +4,7 @@ * Data structure and helper functions for tracking pending FSF * requests. * - * Copyright IBM Corp. 2009 + * Copyright IBM Corp. 2009, 2016 */ #ifndef ZFCP_REQLIST_H @@ -180,4 +180,32 @@ static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl, spin_unlock_irqrestore(&rl->lock, flags); } +/** + * zfcp_reqlist_apply_for_all() - apply a function to every request. + * @rl: the requestlist that contains the target requests. + * @f: the function to apply to each request; the first parameter of the + * function will be the target-request; the second parameter is the same + * pointer as given with the argument @data. + * @data: freely chosen argument; passed through to @f as second parameter. + * + * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash- + * table (not a 'safe' variant, so don't modify the list). + * + * Holds @rl->lock over the entire request-iteration. + */ +static inline void +zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl, + void (*f)(struct zfcp_fsf_req *, void *), void *data) +{ + struct zfcp_fsf_req *req; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&rl->lock, flags); + for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) + list_for_each_entry(req, &rl->buckets[i], list) + f(req, data); + spin_unlock_irqrestore(&rl->lock, flags); +} + #endif /* ZFCP_REQLIST_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 9069f98a1817..07ffdbb5107f 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -3,7 +3,7 @@ * * Interface to Linux SCSI midlayer. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #define KMSG_COMPONENT "zfcp" @@ -88,9 +88,7 @@ int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt) } if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) { - /* This could be either - * open LUN pending: this is temporary, will result in - * open LUN or ERP_FAILED, so retry command + /* This could be * call to rport_delete pending: mimic retry from * fc_remote_port_chkready until rport is BLOCKED */ @@ -209,6 +207,57 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) return retval; } +struct zfcp_scsi_req_filter { + u8 tmf_scope; + u32 lun_handle; + u32 port_handle; +}; + +static void zfcp_scsi_forget_cmnd(struct zfcp_fsf_req *old_req, void *data) +{ + struct zfcp_scsi_req_filter *filter = + (struct zfcp_scsi_req_filter *)data; + + /* already aborted - prevent side-effects - or not a SCSI command */ + if (old_req->data == NULL || old_req->fsf_command != FSF_QTCB_FCP_CMND) + return; + + /* (tmf_scope == FCP_TMF_TGT_RESET || tmf_scope == FCP_TMF_LUN_RESET) */ + if (old_req->qtcb->header.port_handle != filter->port_handle) + return; + + if (filter->tmf_scope == FCP_TMF_LUN_RESET && + old_req->qtcb->header.lun_handle != filter->lun_handle) + return; + + zfcp_dbf_scsi_nullcmnd((struct scsi_cmnd *)old_req->data, old_req); + old_req->data = NULL; +} + +static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags) +{ + struct zfcp_adapter *adapter = zsdev->port->adapter; + struct zfcp_scsi_req_filter filter = { + .tmf_scope = FCP_TMF_TGT_RESET, + .port_handle = zsdev->port->handle, + }; + unsigned long flags; + + if (tm_flags == FCP_TMF_LUN_RESET) { + filter.tmf_scope = FCP_TMF_LUN_RESET; + filter.lun_handle = zsdev->lun_handle; + } + + /* + * abort_lock secures against other processings - in the abort-function + * and normal cmnd-handler - of (struct zfcp_fsf_req *)->data + */ + write_lock_irqsave(&adapter->abort_lock, flags); + zfcp_reqlist_apply_for_all(adapter->req_list, zfcp_scsi_forget_cmnd, + &filter); + write_unlock_irqrestore(&adapter->abort_lock, flags); +} + static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); @@ -241,8 +290,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags); retval = FAILED; - } else + } else { zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags); + zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags); + } zfcp_fsf_req_free(fsf_req); return retval; diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 0d351cd3191b..26d38b1a45ab 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -50,9 +50,13 @@ struct aac_common aac_config = { static inline int aac_is_msix_mode(struct aac_dev *dev) { - u32 status; + u32 status = 0; - status = src_readl(dev, MUnit.OMR); + if (dev->pdev->device == PMC_DEVICE_S6 || + dev->pdev->device == PMC_DEVICE_S7 || + dev->pdev->device == PMC_DEVICE_S8) { + status = src_readl(dev, MUnit.OMR); + } return (status & AAC_INT_MODE_MSIX); } diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index bc0203f3d243..e415e1c58eb5 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -413,16 +413,23 @@ static int aac_src_check_health(struct aac_dev *dev) u32 status = src_readl(dev, MUnit.OMR); /* + * Check to see if the board panic'd. + */ + if (unlikely(status & KERNEL_PANIC)) + goto err_blink; + + /* * Check to see if the board failed any self tests. */ if (unlikely(status & SELF_TEST_FAILED)) - return -1; + goto err_out; /* - * Check to see if the board panic'd. + * Check to see if the board failed any self tests. */ - if (unlikely(status & KERNEL_PANIC)) - return (status >> 16) & 0xFF; + if (unlikely(status & MONITOR_PANIC)) + goto err_out; + /* * Wait for the adapter to be up and running. */ @@ -432,6 +439,12 @@ static int aac_src_check_health(struct aac_dev *dev) * Everything is OK */ return 0; + +err_out: + return -1; + +err_blink: + return (status > 16) & 0xFF; } /** diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 33ec4fa39ccb..f224cdb2fce4 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1182,6 +1182,7 @@ struct lpfc_mbx_wq_create { #define lpfc_mbx_wq_create_page_size_SHIFT 0 #define lpfc_mbx_wq_create_page_size_MASK 0x000000FF #define lpfc_mbx_wq_create_page_size_WORD word1 +#define LPFC_WQ_PAGE_SIZE_4096 0x1 #define lpfc_mbx_wq_create_wqe_size_SHIFT 8 #define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F #define lpfc_mbx_wq_create_wqe_size_WORD word1 @@ -1253,6 +1254,7 @@ struct rq_context { #define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */ #define lpfc_rq_context_page_size_MASK 0x000000FF #define lpfc_rq_context_page_size_WORD word0 +#define LPFC_RQ_PAGE_SIZE_4096 0x1 uint32_t reserved1; uint32_t word2; #define lpfc_rq_context_cq_id_SHIFT 16 diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 92dfd6a5178c..f5aeda8f014f 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -13475,7 +13475,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_128); bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + LPFC_WQ_PAGE_SIZE_4096); page = wq_create->u.request_1.page; break; } @@ -13501,8 +13501,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_128); break; } - bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + bf_set(lpfc_mbx_wq_create_page_size, + &wq_create->u.request_1, + LPFC_WQ_PAGE_SIZE_4096); page = wq_create->u.request_1.page; break; default: @@ -13688,7 +13689,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, LPFC_RQE_SIZE_8); bf_set(lpfc_rq_context_page_size, &rq_create->u.request.context, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + LPFC_RQ_PAGE_SIZE_4096); } else { switch (hrq->entry_count) { default: diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 021b994fdae8..96007633ad39 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1856,6 +1856,8 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, io_request->DevHandle = pd_sync->seq[pd_index].devHandle; pRAID_Context->regLockFlags |= (MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA); + pRAID_Context->Type = MPI2_TYPE_CUDA; + pRAID_Context->nseg = 0x1; } else if (fusion->fast_path_io) { pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); pRAID_Context->configSeqNum = 0; @@ -1891,12 +1893,10 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, pRAID_Context->timeoutValue = cpu_to_le16((os_timeout_value > timeout_limit) ? timeout_limit : os_timeout_value); - if (fusion->adapter_type == INVADER_SERIES) { - pRAID_Context->Type = MPI2_TYPE_CUDA; - pRAID_Context->nseg = 0x1; + if (fusion->adapter_type == INVADER_SERIES) io_request->IoFlags |= cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); - } + cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); @@ -2648,6 +2648,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, dev_err(&instance->pdev->dev, "pending commands remain after waiting, " "will reset adapter scsi%d.\n", instance->host->host_no); + *convert = 1; retval = 1; } out: diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 8cead04f26d6..f6a8e9958e75 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -51,6 +51,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/pci.h> +#include <linux/pci-aspm.h> #include <linux/interrupt.h> #include <linux/aer.h> #include <linux/raid_class.h> @@ -8483,6 +8484,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) switch (hba_mpi_version) { case MPI2_VERSION: + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); /* Use mpt2sas driver host template for SAS 2.0 HBA's */ shost = scsi_host_alloc(&mpt2sas_driver_template, sizeof(struct MPT3SAS_ADAPTER)); diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index 9270d15ff1a4..7353ac8d0d39 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -621,7 +621,7 @@ static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx) { u32 tmp; tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3)); - if (tmp && 1 << (slot_idx % 32)) { + if (tmp & 1 << (slot_idx % 32)) { mv_printk("command active %08X, slot [%x].\n", tmp, slot_idx); mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3), 1 << (slot_idx % 32)); diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 9c780740fb82..e712fe745955 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -737,8 +737,8 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf mv_dprintk("device %016llx not ready.\n", SAS_ADDR(dev->sas_addr)); - rc = SAS_PHY_DOWN; - return rc; + rc = SAS_PHY_DOWN; + return rc; } tei.port = dev->port->lldd_port; if (tei.port && !tei.port->port_attached && !tmf) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c44cbf46221c..3588a56aabb4 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3365,7 +3365,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, sizeof(struct ct6_dsd), 0, SLAB_HWCACHE_ALIGN, NULL); if (!ctx_cachep) - goto fail_free_gid_list; + goto fail_free_srb_mempool; } ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ, ctx_cachep); @@ -3518,7 +3518,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long), GFP_KERNEL); if (!ha->loop_id_map) - goto fail_async_pd; + goto fail_loop_id_map; else { qla2x00_set_reserved_loop_ids(ha); ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123, @@ -3527,6 +3527,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, return 0; +fail_loop_id_map: + dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma); fail_async_pd: dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); fail_ex_init_cb: @@ -3554,6 +3556,10 @@ fail_free_ms_iocb: dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); ha->ms_iocb = NULL; ha->ms_iocb_dma = 0; + + if (ha->sns_cmd) + dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt), + ha->sns_cmd, ha->sns_cmd_dma); fail_dma_pool: if (IS_QLA82XX(ha) || ql2xenabledif) { dma_pool_destroy(ha->fcp_cmnd_dma_pool); @@ -3571,10 +3577,12 @@ fail_free_nvram: kfree(ha->nvram); ha->nvram = NULL; fail_free_ctx_mempool: - mempool_destroy(ha->ctx_mempool); + if (ha->ctx_mempool) + mempool_destroy(ha->ctx_mempool); ha->ctx_mempool = NULL; fail_free_srb_mempool: - mempool_destroy(ha->srb_mempool); + if (ha->srb_mempool) + mempool_destroy(ha->srb_mempool); ha->srb_mempool = NULL; fail_free_gid_list: dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c index e7649ed3f667..4d655b568269 100644 --- a/drivers/scsi/scsi_dh.c +++ b/drivers/scsi/scsi_dh.c @@ -289,20 +289,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) } EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); -static struct scsi_device *get_sdev_from_queue(struct request_queue *q) -{ - struct scsi_device *sdev; - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - return sdev; -} - /* * scsi_dh_activate - activate the path associated with the scsi_device * corresponding to the given request queue. @@ -321,7 +307,7 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) struct scsi_device *sdev; int err = SCSI_DH_NOSYS; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) { if (fn) fn(data, err); @@ -368,7 +354,7 @@ int scsi_dh_set_params(struct request_queue *q, const char *params) struct scsi_device *sdev; int err = -SCSI_DH_NOSYS; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return err; @@ -391,7 +377,7 @@ int scsi_dh_attach(struct request_queue *q, const char *name) struct scsi_device_handler *scsi_dh; int err = 0; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return -ENODEV; @@ -429,7 +415,7 @@ const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) struct scsi_device *sdev; const char *handler_name = NULL; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return NULL; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index cf5b99e1f12b..887045ae5d10 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1120,7 +1120,8 @@ int scsi_init_io(struct scsi_cmnd *cmd) bool is_mq = (rq->mq_ctx != NULL); int error; - BUG_ON(!rq->nr_phys_segments); + if (WARN_ON_ONCE(!rq->nr_phys_segments)) + return -EINVAL; error = scsi_init_sgtable(rq, &cmd->sdb); if (error) @@ -2214,6 +2215,29 @@ void scsi_mq_destroy_tags(struct Scsi_Host *shost) blk_mq_free_tag_set(&shost->tag_set); } +/** + * scsi_device_from_queue - return sdev associated with a request_queue + * @q: The request queue to return the sdev from + * + * Return the sdev associated with a request queue or NULL if the + * request_queue does not reference a SCSI device. + */ +struct scsi_device *scsi_device_from_queue(struct request_queue *q) +{ + struct scsi_device *sdev = NULL; + + if (q->mq_ops) { + if (q->mq_ops == &scsi_mq_ops) + sdev = q->queuedata; + } else if (q->request_fn == scsi_request_fn) + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + + return sdev; +} +EXPORT_SYMBOL_GPL(scsi_device_from_queue); + /* * Function: scsi_block_requests() * diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 98cabf409bf0..9f2c9a2fdd74 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1031,10 +1031,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) struct request_queue *rq = sdev->request_queue; struct scsi_target *starget = sdev->sdev_target; - error = scsi_device_set_state(sdev, SDEV_RUNNING); - if (error) - return error; - error = scsi_target_add(starget); if (error) return error; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 77b2da269d6e..93a523b42d3d 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -592,6 +592,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) sg_io_hdr_t *hp; unsigned char cmnd[SG_MAX_CDB_SIZE]; + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + return -EINVAL; + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, @@ -1757,6 +1760,10 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) return res; iov_iter_truncate(&i, hp->dxfer_len); + if (!iov_iter_count(&i)) { + kfree(iov); + return -EINVAL; + } res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC); kfree(iov); diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 0f636cc4c809..cd5c1c060481 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -135,6 +135,8 @@ struct hv_fc_wwn_packet { #define SRB_FLAGS_PORT_DRIVER_RESERVED 0x0F000000 #define SRB_FLAGS_CLASS_DRIVER_RESERVED 0xF0000000 +#define SP_UNTAGGED ((unsigned char) ~0) +#define SRB_SIMPLE_TAG_REQUEST 0x20 /* * Platform neutral description of a scsi request - @@ -354,6 +356,7 @@ enum storvsc_request_type { #define SRB_STATUS_SUCCESS 0x01 #define SRB_STATUS_ABORTED 0x02 #define SRB_STATUS_ERROR 0x04 +#define SRB_STATUS_DATA_OVERRUN 0x12 #define SRB_STATUS(status) \ (status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN)) @@ -864,6 +867,13 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, switch (SRB_STATUS(vm_srb->srb_status)) { case SRB_STATUS_ERROR: /* + * Let upper layer deal with error when + * sense message is present. + */ + + if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID) + break; + /* * If there is an error; offline the device since all * error recovery strategies would have already been * deployed on the host side. However, if the command @@ -927,6 +937,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) struct hv_host_device *host_dev = shost_priv(scmnd->device->host); struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; + u32 data_transfer_length; struct Scsi_Host *host; struct storvsc_device *stor_dev; struct hv_device *dev = host_dev->dev; @@ -937,6 +948,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) host = stor_dev->host; vm_srb = &cmd_request->vstor_packet.vm_srb; + data_transfer_length = vm_srb->data_transfer_length; scmnd->result = vm_srb->scsi_status; @@ -947,13 +959,20 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) &sense_hdr); } - if (vm_srb->srb_status != SRB_STATUS_SUCCESS) + if (vm_srb->srb_status != SRB_STATUS_SUCCESS) { storvsc_handle_error(vm_srb, scmnd, host, sense_hdr.asc, sense_hdr.ascq); + /* + * The Windows driver set data_transfer_length on + * SRB_STATUS_DATA_OVERRUN. On other errors, this value + * is untouched. In these cases we set it to 0. + */ + if (vm_srb->srb_status != SRB_STATUS_DATA_OVERRUN) + data_transfer_length = 0; + } scsi_set_resid(scmnd, - cmd_request->payload->range.len - - vm_srb->data_transfer_length); + cmd_request->payload->range.len - data_transfer_length); scmnd->scsi_done(scmnd); @@ -1409,6 +1428,13 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + if (scmnd->device->tagged_supported) { + vm_srb->win8_extension.srb_flags |= + (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE); + vm_srb->win8_extension.queue_tag = SP_UNTAGGED; + vm_srb->win8_extension.queue_action = SRB_SIMPLE_TAG_REQUEST; + } + /* Build the SRB */ switch (scmnd->sc_data_direction) { case DMA_TO_DEVICE: diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h index cdd6988418f7..24053c853a83 100644 --- a/drivers/soc/qcom/glink_private.h +++ b/drivers/soc/qcom/glink_private.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -693,6 +693,7 @@ enum ssr_command { * edge: The G-Link edge name for the channel associated with * this callback data * do_cleanup_data: Structure containing the G-Link SSR do_cleanup message. + * cb_kref: Kref object to maintain cb_data reference. */ struct ssr_notify_data { bool tx_done; @@ -700,6 +701,7 @@ struct ssr_notify_data { bool responded; const char *edge; struct do_cleanup_msg *do_cleanup_data; + struct kref cb_kref; }; /** @@ -734,6 +736,7 @@ struct subsys_info { int notify_list_len; bool link_up; spinlock_t link_up_lock; + spinlock_t cb_lock; }; /** diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 14cf10b92122..2dc4208cbc51 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -213,6 +213,7 @@ struct edge_info { bool tx_blocked_signal_sent; struct kthread_work kwork; struct kthread_worker kworker; + struct work_struct wakeup_work; struct task_struct *task; struct tasklet_struct tasklet; struct srcu_struct use_ref; @@ -826,7 +827,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) int i; bool granted; unsigned long flags; - bool trigger_wakeup = false; int rcu_id; uint16_t rcid; uint32_t name_len; @@ -851,22 +851,10 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) srcu_read_unlock(&einfo->use_ref, rcu_id); return; } - if (!atomic_ctx) { - if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { - einfo->tx_resume_needed = false; - einfo->xprt_if.glink_core_if_ptr->tx_resume( - &einfo->xprt_if); - } - spin_lock_irqsave(&einfo->write_lock, flags); - if (waitqueue_active(&einfo->tx_blocked_queue)) { - einfo->tx_blocked_signal_sent = false; - trigger_wakeup = true; - } - spin_unlock_irqrestore(&einfo->write_lock, flags); - if (trigger_wakeup) - wake_up_all(&einfo->tx_blocked_queue); - } + if ((atomic_ctx) && ((einfo->tx_resume_needed) || + (waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/ + schedule_work(&einfo->wakeup_work); /* * Access to the fifo needs to be synchronized, however only the calls @@ -1174,6 +1162,39 @@ static void rx_worker_atomic(unsigned long param) } /** + * tx_wakeup_worker() - worker function to wakeup tx blocked thread + * @work: kwork associated with the edge to process commands on. + */ +static void tx_wakeup_worker(struct work_struct *work) +{ + struct edge_info *einfo; + bool trigger_wakeup = false; + unsigned long flags; + int rcu_id; + + einfo = container_of(work, struct edge_info, wakeup_work); + rcu_id = srcu_read_lock(&einfo->use_ref); + if (einfo->in_ssr) { + srcu_read_unlock(&einfo->use_ref, rcu_id); + return; + } + if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { + einfo->tx_resume_needed = false; + einfo->xprt_if.glink_core_if_ptr->tx_resume( + &einfo->xprt_if); + } + spin_lock_irqsave(&einfo->write_lock, flags); + if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/ + einfo->tx_blocked_signal_sent = false; + trigger_wakeup = true; + } + spin_unlock_irqrestore(&einfo->write_lock, flags); + if (trigger_wakeup) + wake_up_all(&einfo->tx_blocked_queue); + srcu_read_unlock(&einfo->use_ref, rcu_id); +} + +/** * rx_worker() - worker function to process received commands * @work: kwork associated with the edge to process commands on. */ @@ -2275,6 +2296,7 @@ static int glink_smem_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2374,6 +2396,7 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2462,6 +2485,7 @@ static int glink_rpm_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->intentless = true; einfo->read_from_fifo = memcpy32_fromio; @@ -2622,6 +2646,7 @@ request_irq_fail: reg_xprt_fail: toc_init_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2753,6 +2778,7 @@ static int glink_mailbox_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2873,6 +2899,7 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c index 5e2dbc8b1d20..7e23b0bc3852 100644 --- a/drivers/soc/qcom/glink_ssr.c +++ b/drivers/soc/qcom/glink_ssr.c @@ -115,6 +115,44 @@ static LIST_HEAD(subsystem_list); static atomic_t responses_remaining = ATOMIC_INIT(0); static wait_queue_head_t waitqueue; +/** + * cb_data_release() - Free cb_data and set to NULL + * @kref_ptr: pointer to kref. + * + * This function releses cb_data. + */ +static inline void cb_data_release(struct kref *kref_ptr) +{ + struct ssr_notify_data *cb_data; + + cb_data = container_of(kref_ptr, struct ssr_notify_data, cb_kref); + kfree(cb_data); +} + +/** + * check_and_get_cb_data() - Try to get reference to kref of cb_data + * @ss_info: pointer to subsystem info structure. + * + * Return: NULL is cb_data is NULL, pointer to cb_data otherwise + */ +static struct ssr_notify_data *check_and_get_cb_data( + struct subsys_info *ss_info) +{ + struct ssr_notify_data *cb_data; + unsigned long flags; + + spin_lock_irqsave(&ss_info->cb_lock, flags); + if (ss_info->cb_data == NULL) { + GLINK_SSR_LOG("<SSR> %s: cb_data is NULL\n", __func__); + spin_unlock_irqrestore(&ss_info->cb_lock, flags); + return 0; + } + kref_get(&ss_info->cb_data->cb_kref); + cb_data = ss_info->cb_data; + spin_unlock_irqrestore(&ss_info->cb_lock, flags); + return cb_data; +} + static void rx_done_cb_worker(struct work_struct *work) { struct rx_done_ch_work *rx_done_work = @@ -338,8 +376,10 @@ void close_ch_worker(struct work_struct *work) ss_info->link_state_handle = link_state_handle; BUG_ON(!ss_info->cb_data); - kfree(ss_info->cb_data); + spin_lock_irqsave(&ss_info->cb_lock, flags); + kref_put(&ss_info->cb_data->cb_kref, cb_data_release); ss_info->cb_data = NULL; + spin_unlock_irqrestore(&ss_info->cb_lock, flags); kfree(close_work); } @@ -507,13 +547,18 @@ int notify_for_subsystem(struct subsys_info *ss_info) return -ENODEV; } handle = ss_info_channel->handle; - ss_leaf_entry->cb_data = ss_info_channel->cb_data; + ss_leaf_entry->cb_data = check_and_get_cb_data( + ss_info_channel); + if (!ss_leaf_entry->cb_data) { + GLINK_SSR_LOG("<SSR> %s: CB data is NULL\n", __func__); + atomic_dec(&responses_remaining); + continue; + } spin_lock_irqsave(&ss_info->link_up_lock, flags); if (IS_ERR_OR_NULL(ss_info_channel->handle) || - !ss_info_channel->cb_data || !ss_info_channel->link_up || - ss_info_channel->cb_data->event + ss_leaf_entry->cb_data->event != GLINK_CONNECTED) { GLINK_SSR_LOG( @@ -526,6 +571,8 @@ int notify_for_subsystem(struct subsys_info *ss_info) spin_unlock_irqrestore(&ss_info->link_up_lock, flags); atomic_dec(&responses_remaining); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); continue; } spin_unlock_irqrestore(&ss_info->link_up_lock, flags); @@ -536,6 +583,8 @@ int notify_for_subsystem(struct subsys_info *ss_info) GLINK_SSR_ERR( "%s %s: Could not allocate do_cleanup_msg\n", "<SSR>", __func__); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); return -ENOMEM; } @@ -567,6 +616,8 @@ int notify_for_subsystem(struct subsys_info *ss_info) __func__); } atomic_dec(&responses_remaining); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); continue; } @@ -596,10 +647,12 @@ int notify_for_subsystem(struct subsys_info *ss_info) __func__); } atomic_dec(&responses_remaining); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); continue; } - sequence_number++; + kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); } wait_ret = wait_event_timeout(waitqueue, @@ -608,6 +661,21 @@ int notify_for_subsystem(struct subsys_info *ss_info) list_for_each_entry(ss_leaf_entry, &ss_info->notify_list, notify_list_node) { + ss_info_channel = + get_info_for_subsystem(ss_leaf_entry->ssr_name); + if (ss_info_channel == NULL) { + GLINK_SSR_ERR( + "<SSR> %s: unable to find subsystem name\n", + __func__); + continue; + } + + ss_leaf_entry->cb_data = check_and_get_cb_data( + ss_info_channel); + if (!ss_leaf_entry->cb_data) { + GLINK_SSR_LOG("<SSR> %s: CB data is NULL\n", __func__); + continue; + } if (!wait_ret && !IS_ERR_OR_NULL(ss_leaf_entry->cb_data) && !ss_leaf_entry->cb_data->responded) { GLINK_SSR_ERR("%s %s: Subsystem %s %s\n", @@ -626,6 +694,7 @@ int notify_for_subsystem(struct subsys_info *ss_info) if (!IS_ERR_OR_NULL(ss_leaf_entry->cb_data)) ss_leaf_entry->cb_data->responded = false; + kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); } complete(¬ifications_successful_complete); return 0; @@ -644,6 +713,7 @@ static int configure_and_open_channel(struct subsys_info *ss_info) struct glink_open_config open_cfg; struct ssr_notify_data *cb_data = NULL; void *handle = NULL; + unsigned long flags; if (!ss_info) { GLINK_SSR_ERR("<SSR> %s: ss_info structure invalid\n", @@ -660,7 +730,10 @@ static int configure_and_open_channel(struct subsys_info *ss_info) cb_data->responded = false; cb_data->event = GLINK_SSR_EVENT_INIT; cb_data->edge = ss_info->edge; + spin_lock_irqsave(&ss_info->cb_lock, flags); ss_info->cb_data = cb_data; + kref_init(&cb_data->cb_kref); + spin_unlock_irqrestore(&ss_info->cb_lock, flags); memset(&open_cfg, 0, sizeof(struct glink_open_config)); @@ -876,6 +949,7 @@ static int glink_ssr_probe(struct platform_device *pdev) ss_info->link_state_handle = NULL; ss_info->cb_data = NULL; spin_lock_init(&ss_info->link_up_lock); + spin_lock_init(&ss_info->cb_lock); nb = kmalloc(sizeof(struct restart_notifier_block), GFP_KERNEL); if (!nb) { diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 3ec2c7ac01ba..70874da9d176 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -62,7 +62,7 @@ module_param(qmi_timeout, ulong, 0600); #define WLFW_CLIENT_ID 0x4b4e454c #define MAX_PROP_SIZE 32 #define NUM_LOG_PAGES 10 -#define NUM_REG_LOG_PAGES 4 +#define NUM_LOG_LONG_PAGES 4 #define ICNSS_MAGIC 0x5abc5abc #define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN" @@ -77,6 +77,11 @@ module_param(qmi_timeout, ulong, 0600); ipc_log_string(icnss_ipc_log_context, _x); \ } while (0) +#define icnss_ipc_log_long_string(_x...) do { \ + if (icnss_ipc_log_long_context) \ + ipc_log_string(icnss_ipc_log_long_context, _x); \ + } while (0) + #define icnss_pr_err(_fmt, ...) do { \ pr_err(_fmt, ##__VA_ARGS__); \ icnss_ipc_log_string("ERR: " pr_fmt(_fmt), \ @@ -101,6 +106,12 @@ module_param(qmi_timeout, ulong, 0600); ##__VA_ARGS__); \ } while (0) +#define icnss_pr_vdbg(_fmt, ...) do { \ + pr_debug(_fmt, ##__VA_ARGS__); \ + icnss_ipc_log_long_string("DBG: " pr_fmt(_fmt), \ + ##__VA_ARGS__); \ + } while (0) + #ifdef CONFIG_ICNSS_DEBUG #define ICNSS_ASSERT(_condition) do { \ if (!(_condition)) { \ @@ -138,6 +149,7 @@ uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01; module_param(dynamic_feature_mask, ullong, 0600); void *icnss_ipc_log_context; +void *icnss_ipc_log_long_context; #define ICNSS_EVENT_PENDING 2989 @@ -325,8 +337,9 @@ static struct icnss_priv { u32 pwr_pin_result; u32 phy_io_pin_result; u32 rf_pin_result; + uint32_t nr_mem_region; struct icnss_mem_region_info - icnss_mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; + mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; struct dentry *root_dentry; spinlock_t on_off_lock; struct icnss_stats stats; @@ -366,7 +379,7 @@ static void icnss_pm_stay_awake(struct icnss_priv *priv) if (atomic_inc_return(&priv->pm_count) != 1) return; - icnss_pr_dbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state, + icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state, atomic_read(&priv->pm_count)); pm_stay_awake(&priv->pdev->dev); @@ -383,7 +396,7 @@ static void icnss_pm_relax(struct icnss_priv *priv) if (r != 0) return; - icnss_pr_dbg("PM relax, state: 0x%lx, count: %d\n", priv->state, + icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state, atomic_read(&priv->pm_count)); pm_relax(&priv->pdev->dev); @@ -717,7 +730,7 @@ static int icnss_vreg_on(struct icnss_priv *priv) if (!vreg_info->reg) continue; - icnss_pr_dbg("Regulator %s being enabled\n", vreg_info->name); + icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name); ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v, vreg_info->max_v); @@ -779,7 +792,7 @@ static int icnss_vreg_off(struct icnss_priv *priv) if (!vreg_info->reg) continue; - icnss_pr_dbg("Regulator %s being disabled\n", vreg_info->name); + icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name); ret = regulator_disable(vreg_info->reg); if (ret) @@ -813,7 +826,7 @@ static int icnss_clk_init(struct icnss_priv *priv) if (!clk_info->handle) continue; - icnss_pr_dbg("Clock %s being enabled\n", clk_info->name); + icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name); if (clk_info->freq) { ret = clk_set_rate(clk_info->handle, clk_info->freq); @@ -860,7 +873,7 @@ static int icnss_clk_deinit(struct icnss_priv *priv) if (!clk_info->handle) continue; - icnss_pr_dbg("Clock %s being disabled\n", clk_info->name); + icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name); clk_disable_unprepare(clk_info->handle); } @@ -964,7 +977,7 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); -static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region) { int ret = 0; phys_addr_t addr; @@ -977,10 +990,10 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) int source_nelems = sizeof(source_vmlist)/sizeof(u32); int dest_nelems = 0; - addr = priv->icnss_mem_region[index].reg_addr; - size = priv->icnss_mem_region[index].size; + addr = mem_region->reg_addr; + size = mem_region->size; - if (!priv->icnss_mem_region[index].secure_flag) { + if (!mem_region->secure_flag) { dest_vmids[2] = VMID_WLAN_CE; dest_nelems = 3; } else { @@ -990,19 +1003,20 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, dest_vmids, dest_perms, dest_nelems); if (ret) { - icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n", - index, &addr, size, ret); + icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); goto out; } - icnss_pr_dbg("Hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", - index, source_vmlist[0], dest_nelems, - dest_vmids[0], dest_vmids[1], dest_vmids[2]); + + icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", + source_vmlist[0], dest_nelems, dest_vmids[0], + dest_vmids[1], dest_vmids[2]); out: return ret; } -static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region) { int ret = 0; phys_addr_t addr; @@ -1013,9 +1027,10 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) int source_nelems = 0; int dest_nelems = sizeof(dest_vmids)/sizeof(u32); - addr = priv->icnss_mem_region[index].reg_addr; - size = priv->icnss_mem_region[index].size; - if (!priv->icnss_mem_region[index].secure_flag) { + addr = mem_region->reg_addr; + size = mem_region->size; + + if (!mem_region->secure_flag) { source_vmlist[2] = VMID_WLAN_CE; source_nelems = 3; } else { @@ -1026,14 +1041,13 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, dest_vmids, dest_perms, dest_nelems); if (ret) { - icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n", - index, &addr, size, ret); + icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); goto out; } - icnss_pr_dbg("hypervisor unmap for region %u, source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", - index, source_nelems, - source_vmlist[0], source_vmlist[1], source_vmlist[2], - dest_vmids[0]); + icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", + source_nelems, source_vmlist[0], source_vmlist[1], + source_vmlist[2], dest_vmids[0]); out: return ret; } @@ -1041,34 +1055,37 @@ out: static int icnss_setup_msa_permissions(struct icnss_priv *priv) { int ret; + int i; if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) return 0; - ret = icnss_map_msa_permissions(priv, 0); - if (ret) - return ret; + for (i = 0; i < priv->nr_mem_region; i++) { - ret = icnss_map_msa_permissions(priv, 1); - if (ret) - goto err_map_msa; + ret = icnss_map_msa_permissions(&priv->mem_region[i]); + if (ret) + goto err_unmap; + } set_bit(ICNSS_MSA0_ASSIGNED, &priv->state); - return ret; + return 0; -err_map_msa: - icnss_unmap_msa_permissions(priv, 0); +err_unmap: + for (i--; i >= 0; i--) + icnss_unmap_msa_permissions(&priv->mem_region[i]); return ret; } static void icnss_remove_msa_permissions(struct icnss_priv *priv) { + int i; + if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) return; - icnss_unmap_msa_permissions(priv, 0); - icnss_unmap_msa_permissions(priv, 1); + for (i = 0; i < priv->nr_mem_region; i++) + icnss_unmap_msa_permissions(&priv->mem_region[i]); clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state); } @@ -1119,7 +1136,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void) icnss_pr_dbg("Receive mem_region_info_len: %d\n", resp.mem_region_info_len); - if (resp.mem_region_info_len > 2) { + if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) { icnss_pr_err("Invalid memory region length received: %d\n", resp.mem_region_info_len); ret = -EINVAL; @@ -1127,17 +1144,18 @@ static int wlfw_msa_mem_info_send_sync_msg(void) } penv->stats.msa_info_resp++; + penv->nr_mem_region = resp.mem_region_info_len; for (i = 0; i < resp.mem_region_info_len; i++) { - penv->icnss_mem_region[i].reg_addr = + penv->mem_region[i].reg_addr = resp.mem_region_info[i].region_addr; - penv->icnss_mem_region[i].size = + penv->mem_region[i].size = resp.mem_region_info[i].size; - penv->icnss_mem_region[i].secure_flag = + penv->mem_region[i].secure_flag = resp.mem_region_info[i].secure_flag; icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n", - i, penv->icnss_mem_region[i].reg_addr, - penv->icnss_mem_region[i].size, - penv->icnss_mem_region[i].secure_flag); + i, penv->mem_region[i].reg_addr, + penv->mem_region[i].size, + penv->mem_region[i].secure_flag); } return 0; @@ -1728,7 +1746,7 @@ static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work) if (!penv || !penv->wlfw_clnt) return; - icnss_pr_dbg("Receiving Event in work queue context\n"); + icnss_pr_vdbg("Receiving Event in work queue context\n"); do { } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0); @@ -1736,13 +1754,13 @@ static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work) if (ret != -ENOMSG) icnss_pr_err("Error receiving message: %d\n", ret); - icnss_pr_dbg("Receiving Event completed\n"); + icnss_pr_vdbg("Receiving Event completed\n"); } static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle, enum qmi_event_type event, void *notify_priv) { - icnss_pr_dbg("QMI client notify: %d\n", event); + icnss_pr_vdbg("QMI client notify: %d\n", event); if (!penv || !penv->wlfw_clnt) return; @@ -1930,6 +1948,9 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (!priv->ops || !priv->ops->probe) return 0; + if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + return -EINVAL; + icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state); icnss_hw_power_on(priv); @@ -2300,7 +2321,7 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, struct icnss_priv *priv = container_of(nb, struct icnss_priv, modem_ssr_nb); - icnss_pr_dbg("Modem-Notify: event %lu\n", code); + icnss_pr_vdbg("Modem-Notify: event %lu\n", code); if (code == SUBSYS_AFTER_SHUTDOWN && notif->crashed == CRASH_STATUS_ERR_FATAL) { @@ -2537,7 +2558,7 @@ static int icnss_pd_restart_enable(struct icnss_priv *priv) return 0; out: - icnss_pr_err("PD restart not enabled: %d\n", ret); + icnss_pr_err("Failed to enable PD restart: %d\n", ret); return ret; } @@ -2647,7 +2668,7 @@ int icnss_ce_request_irq(unsigned int ce_id, goto out; } - icnss_pr_dbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state); + icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id); @@ -2673,7 +2694,7 @@ int icnss_ce_request_irq(unsigned int ce_id, irq_entry->irq = irq; irq_entry->handler = handler; - icnss_pr_dbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id); + icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id); penv->stats.ce_irqs[ce_id].request++; out: @@ -2692,7 +2713,7 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx) goto out; } - icnss_pr_dbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state); + icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id); @@ -2726,7 +2747,7 @@ void icnss_enable_irq(unsigned int ce_id) return; } - icnss_pr_dbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, + icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { @@ -2750,7 +2771,7 @@ void icnss_disable_irq(unsigned int ce_id) return; } - icnss_pr_dbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, + icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { @@ -3150,22 +3171,23 @@ int icnss_trigger_recovery(struct device *dev) goto out; } - if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) { - icnss_pr_err("PD restart not enabled: state: 0x%lx\n", + if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) { + icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n", priv->state); ret = -EOPNOTSUPP; goto out; } - if (!priv->service_notifier[0].handle) { + if (!priv->service_notifier || !priv->service_notifier[0].handle) { icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n", priv->state); ret = -EINVAL; goto out; } - icnss_pr_dbg("Initiate PD restart at WLAN FW, state: 0x%lx\n", - priv->state); + WARN_ON(1); + icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n", + priv->state); priv->stats.trigger_recovery++; /* @@ -3363,6 +3385,7 @@ static int icnss_fw_debug_show(struct seq_file *s, void *data) seq_puts(s, " VAL: 0 (Test mode disable)\n"); seq_puts(s, " VAL: 1 (WLAN FW test)\n"); seq_puts(s, " VAL: 2 (CCPM test)\n"); + seq_puts(s, " VAL: 3 (Trigger Recovery)\n"); seq_puts(s, "\nCMD: dynamic_feature_mask\n"); seq_puts(s, " VAL: (64 bit feature mask)\n"); @@ -4248,7 +4271,7 @@ static int icnss_pm_suspend(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM Suspend, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->pm_suspend || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -4277,7 +4300,7 @@ static int icnss_pm_resume(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM resume, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->pm_resume || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -4306,7 +4329,7 @@ static int icnss_pm_suspend_noirq(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM suspend_noirq, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->suspend_noirq || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -4335,7 +4358,7 @@ static int icnss_pm_resume_noirq(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM resume_noirq, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->resume_noirq || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -4386,6 +4409,11 @@ static int __init icnss_initialize(void) if (!icnss_ipc_log_context) icnss_pr_err("Unable to create log context\n"); + icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES, + "icnss_long", 0); + if (!icnss_ipc_log_long_context) + icnss_pr_err("Unable to create log long context\n"); + return platform_driver_register(&icnss_driver); } @@ -4394,6 +4422,8 @@ static void __exit icnss_exit(void) platform_driver_unregister(&icnss_driver); ipc_log_context_destroy(icnss_ipc_log_context); icnss_ipc_log_context = NULL; + ipc_log_context_destroy(icnss_ipc_log_long_context); + icnss_ipc_log_long_context = NULL; } diff --git a/drivers/soc/qcom/mpm-of.c b/drivers/soc/qcom/mpm-of.c index 93f2de8a59dd..77c50528be4b 100644 --- a/drivers/soc/qcom/mpm-of.c +++ b/drivers/soc/qcom/mpm-of.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -129,18 +129,6 @@ static uint32_t *msm_mpm_falling_edge; static uint32_t *msm_mpm_rising_edge; static uint32_t *msm_mpm_polarity; -enum { - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0), - MSM_MPM_DEBUG_PENDING_IRQ = BIT(1), - MSM_MPM_DEBUG_WRITE = BIT(2), - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3), -}; - -static int msm_mpm_debug_mask = 1; -module_param_named( - debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP -); - enum mpm_state { MSM_MPM_GIC_IRQ_MAPPING_DONE = BIT(0), MSM_MPM_GPIO_IRQ_MAPPING_DONE = BIT(1), @@ -174,9 +162,6 @@ static inline void msm_mpm_write( unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index + 2; __raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4); - if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask) - pr_info("%s: reg %u.%u: 0x%08x\n", - __func__, reg, subreg_index, value); } static inline void msm_mpm_send_interrupt(void) @@ -513,37 +498,19 @@ int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type) static bool msm_mpm_interrupts_detectable(int d, bool from_idle) { unsigned long *irq_bitmap; - bool debug_mask, ret = false; + bool ret = false; struct mpm_irqs *unlisted = &unlisted_irqs[d]; if (!msm_mpm_is_initialized()) return false; - if (from_idle) { + if (from_idle) irq_bitmap = unlisted->enabled_irqs; - debug_mask = msm_mpm_debug_mask & - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE; - } else { + else irq_bitmap = unlisted->wakeup_irqs; - debug_mask = msm_mpm_debug_mask & - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ; - } ret = (bool) bitmap_empty(irq_bitmap, unlisted->size); - if (debug_mask && !ret) { - int i = 0; - i = find_first_bit(irq_bitmap, unlisted->size); - pr_info("%s(): %s preventing system sleep modes during %s\n", - __func__, unlisted->domain_name, - from_idle ? "idle" : "suspend"); - - while (i < unlisted->size) { - pr_info("\thwirq: %d\n", i); - i = find_next_bit(irq_bitmap, unlisted->size, i + 1); - } - } - return ret; } @@ -601,10 +568,6 @@ void msm_mpm_exit_sleep(bool from_idle) pending = msm_mpm_read(MSM_MPM_REG_STATUS, i); pending &= enabled_intr[i]; - if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask) - pr_info("%s: enabled_intr.%d pending.%d: 0x%08x 0x%08lx\n", - __func__, i, i, enabled_intr[i], pending); - k = find_first_bit(&pending, 32); while (k < 32) { unsigned int mpm_irq = 32 * i + k; diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c index 10fb4cc8ebff..aafe82f59b9a 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is Mree software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1349,6 +1349,7 @@ static void unregister_adhoc(struct msm_bus_client_handle *cl) cl->first_hop, cl->active_only); commit_data(); msm_bus_dbg_remove_client(cl); + kfree(cl->name); kfree(cl); exit_unregister_client: rt_mutex_unlock(&msm_bus_adhoc_lock); 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 06657a666f2e..305f31f31bf8 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c @@ -531,7 +531,7 @@ static int msm_bus_enable_node_qos_clk(struct msm_bus_node_device_type *node) { struct msm_bus_node_device_type *bus_node = NULL; int i; - int ret; + int ret = 0; long rounded_rate; if (!node || (!to_msm_bus_node(node->node_info->bus_device))) { diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 21e9f17e6a7e..6e5ddc4a3a7d 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -465,6 +465,8 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr, if (region == NULL) { pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n", size); + priv->region_start = 0; + priv->region_end = 0; return -ENOMEM; } @@ -920,7 +922,8 @@ out: &desc->attrs); priv->region = NULL; } - pil_clear_segment(desc); + if (desc->clear_fw_region && priv->region_start) + pil_clear_segment(desc); pil_release_mmap(desc); } return ret; diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h index 802abe26a960..9521cf726069 100644 --- a/drivers/soc/qcom/peripheral-loader.h +++ b/drivers/soc/qcom/peripheral-loader.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,6 +37,7 @@ struct pil_priv; * This defaults to iounmap if not specified. * @shutdown_fail: Set if PIL op for shutting down subsystem fails. * @modem_ssr: true if modem is restarting, false if booting for first time. + * @clear_fw_region: Clear fw region on failure in loading. * @subsys_vmid: memprot id for the subsystem. */ struct pil_desc { @@ -56,6 +57,7 @@ struct pil_desc { void *map_data; bool shutdown_fail; bool modem_ssr; + bool clear_fw_region; u32 subsys_vmid; }; diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c index 0e023a019280..793edc5b67ed 100644 --- a/drivers/soc/qcom/pil-q6v5-mss.c +++ b/drivers/soc/qcom/pil-q6v5-mss.c @@ -276,8 +276,12 @@ static int pil_mss_loadable_init(struct modem_data *drv, if (q6->cx_ipeak_vote) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cxip_lm_vote_clear"); - q6->cxip_lm_vote_clear = devm_ioremap_resource(&pdev->dev, - res); + if (!res) { + dev_err(&pdev->dev, "Failed to get resource for ipeak reg\n"); + return -EINVAL; + } + q6->cxip_lm_vote_clear = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); if (!q6->cxip_lm_vote_clear) return -ENOMEM; } diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c index a39c2b6aa672..34228f072b28 100644 --- a/drivers/soc/qcom/pil-q6v5.c +++ b/drivers/soc/qcom/pil-q6v5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -588,6 +588,7 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev) if (ret) return ERR_PTR(ret); + desc->clear_fw_region = false; desc->dev = &pdev->dev; drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node, diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c index b120883afbb0..a59b436234c7 100644 --- a/drivers/soc/qcom/qdsp6v2/audio_notifier.c +++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c @@ -626,9 +626,11 @@ static int __init audio_notifier_late_init(void) * If pdr registration failed, register clients on next service * Do in late init to ensure that SSR subsystem is initialized */ + mutex_lock(¬ifier_mutex); if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE)) audio_notifer_reg_all_clients(); + mutex_unlock(¬ifier_mutex); return 0; } late_initcall(audio_notifier_late_init); diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index c7059d60d786..c86eebcd390f 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "haptic: %s: " fmt, __func__ + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -25,23 +27,21 @@ #include <linux/qpnp/pwm.h> #include <linux/err.h> #include <linux/delay.h> -#include <linux/qpnp/qpnp-revid.h> +#include <linux/log2.h> +#include <linux/qpnp-misc.h> #include <linux/qpnp/qpnp-haptic.h> +#include <linux/qpnp/qpnp-revid.h> #include "../../staging/android/timed_output.h" -#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \ - IRQF_TRIGGER_FALLING | \ - IRQF_ONESHOT) - #define QPNP_HAP_STATUS(b) (b + 0x0A) #define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B) #define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C) #define QPNP_HAP_EN_CTL_REG(b) (b + 0x46) #define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48) #define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B) -#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C) -#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D) -#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E) +#define QPNP_HAP_CFG1_REG(b) (b + 0x4C) +#define QPNP_HAP_CFG2_REG(b) (b + 0x4D) +#define QPNP_HAP_SEL_REG(b) (b + 0x4E) #define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F) #define QPNP_HAP_VMAX_REG(b) (b + 0x51) #define QPNP_HAP_ILIM_REG(b) (b + 0x52) @@ -61,9 +61,10 @@ #define QPNP_HAP_TEST2_REG(b) (b + 0xE3) #define QPNP_HAP_STATUS_BUSY 0x02 -#define QPNP_HAP_ACT_TYPE_MASK 0xFE +#define QPNP_HAP_ACT_TYPE_MASK BIT(0) #define QPNP_HAP_LRA 0x0 #define QPNP_HAP_ERM 0x1 +#define QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT BIT(3) #define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4) #define QPNP_HAP_AUTO_RES_MODE_SHIFT 4 #define QPNP_HAP_PM660_AUTO_RES_MODE_BIT BIT(7) @@ -75,35 +76,33 @@ #define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3) #define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3 #define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0) -#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2) -#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 -#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0) +#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2) +#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 +#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0) #define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0) -#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 -#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 -#define QPNP_HAP_PM660_RES_CAL_PERIOD_MIN 4 +#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 +#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 #define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256 -#define QPNP_HAP_PLAY_MODE_MASK 0xCF -#define QPNP_HAP_PLAY_MODE_SHFT 4 -#define QPNP_HAP_VMAX_MASK 0xC1 +#define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4) +#define QPNP_HAP_WF_SOURCE_SHIFT 4 +#define QPNP_HAP_VMAX_MASK GENMASK(5, 1) #define QPNP_HAP_VMAX_SHIFT 1 #define QPNP_HAP_VMAX_MIN_MV 116 #define QPNP_HAP_VMAX_MAX_MV 3596 -#define QPNP_HAP_ILIM_MASK 0xFE +#define QPNP_HAP_ILIM_MASK BIT(0) #define QPNP_HAP_ILIM_MIN_MV 400 #define QPNP_HAP_ILIM_MAX_MV 800 -#define QPNP_HAP_SC_DEB_MASK 0xF8 -#define QPNP_HAP_SC_DEB_SUB 2 +#define QPNP_HAP_SC_DEB_MASK GENMASK(2, 0) #define QPNP_HAP_SC_DEB_CYCLES_MIN 0 #define QPNP_HAP_DEF_SC_DEB_CYCLES 8 #define QPNP_HAP_SC_DEB_CYCLES_MAX 32 #define QPNP_HAP_SC_CLR 1 -#define QPNP_HAP_INT_PWM_MASK 0xFC +#define QPNP_HAP_INT_PWM_MASK GENMASK(1, 0) #define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253 #define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505 #define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739 #define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076 -#define QPNP_HAP_WAV_SHAPE_MASK 0xFE +#define QPNP_HAP_WAV_SHAPE_MASK BIT(0) #define QPNP_HAP_RATE_CFG1_MASK 0xFF #define QPNP_HAP_RATE_CFG2_MASK 0xF0 #define QPNP_HAP_RATE_CFG2_SHFT 8 @@ -111,9 +110,9 @@ #define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0 #define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715 #define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475 -#define QPNP_HAP_WAV_REP_MASK 0x8F -#define QPNP_HAP_WAV_S_REP_MASK 0xFC -#define QPNP_HAP_WAV_REP_SHFT 4 +#define QPNP_HAP_WAV_REP_MASK GENMASK(6, 4) +#define QPNP_HAP_WAV_S_REP_MASK GENMASK(1, 0) +#define QPNP_HAP_WAV_REP_SHIFT 4 #define QPNP_HAP_WAV_REP_MIN 1 #define QPNP_HAP_WAV_REP_MAX 128 #define QPNP_HAP_WAV_S_REP_MIN 1 @@ -121,13 +120,13 @@ #define QPNP_HAP_BRAKE_PAT_MASK 0x3 #define QPNP_HAP_ILIM_MIN_MA 400 #define QPNP_HAP_ILIM_MAX_MA 800 -#define QPNP_HAP_EXT_PWM_MASK 0xFC +#define QPNP_HAP_EXT_PWM_MASK GENMASK(1, 0) #define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25 #define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50 #define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75 #define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100 #define PWM_MAX_DTEST_LINES 4 -#define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F +#define QPNP_HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4) #define QPNP_HAP_EXT_PWM_DTEST_SHFT 4 #define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F #define QPNP_HAP_EXT_PWM_HALF_DUTY 50 @@ -140,10 +139,9 @@ #define QPNP_HAP_BRAKE_PAT_LEN 4 #define QPNP_HAP_PLAY_EN 0x80 #define QPNP_HAP_EN 0x80 -#define QPNP_HAP_BRAKE_MASK 0xFE -#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F -#define QPNP_HAP_SEC_UNLOCK 0xA5 -#define AUTO_RES_ENABLE 0x80 +#define QPNP_HAP_BRAKE_MASK BIT(0) +#define QPNP_HAP_AUTO_RES_MASK BIT(7) +#define AUTO_RES_ENABLE BIT(7) #define AUTO_RES_ERR_BIT 0x10 #define SC_FOUND_BIT 0x08 #define SC_MAX_DURATION 5 @@ -155,12 +153,6 @@ #define QPNP_TEST_TIMER_MS 5 #define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 - -#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 -#define MISC_SEC_ACCESS 0x09D0 -#define MISC_SEC_UNLOCK 0xA5 -#define PMI8950_MISC_SID 2 - #define POLL_TIME_AUTO_RES_ERR_NS (20 * NSEC_PER_MSEC) #define MAX_POSITIVE_VARIATION_LRA_FREQ 30 @@ -172,11 +164,11 @@ ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \ / FREQ_VARIATION_STEP) #define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \ - (hap->init_drive_period_code = (hap->init_drive_period_code * \ - (1000 + rc_clk_err_percent_x10)) / 1000) + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 + rc_clk_err_percent_x10)) / 1000) #define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \ - (hap->init_drive_period_code = (hap->init_drive_period_code * \ - (1000 - rc_clk_err_percent_x10)) / 1000) + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 - rc_clk_err_percent_x10)) / 1000) u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE]; @@ -317,16 +309,16 @@ struct qpnp_pwm_info { * @ reg_play - play register * @ lra_res_cal_period - period for resonance calibration * @ sc_duration - counter to determine the duration of short circuit condition + * @ lra_hw_auto_resonance - enable hardware auto resonance * @ state - current state of haptics - * @ use_play_irq - play irq usage state - * @ use_sc_irq - short circuit irq usage state * @ wf_update - waveform update flag * @ pwm_cfg_state - pwm mode configuration state * @ buffer_cfg_state - buffer mode configuration state * @ en_brake - brake state * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency - * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present + * @ misc_clk_trim_error_reg - MISC clock trim error register if present + * @ clk_trim_error_code - MISC clock trim error code * @ perform_lra_auto_resonance_search - whether lra auto resonance search * algorithm should be performed or not. */ @@ -344,11 +336,13 @@ struct qpnp_hap { struct qpnp_pwm_info pwm_info; struct mutex lock; struct mutex wf_lock; + spinlock_t bus_lock; struct completion completion; enum qpnp_hap_mode play_mode; enum qpnp_hap_high_z lra_high_z; int lra_qwd_drive_duration; int calibrate_at_eop; + u32 misc_clk_trim_error_reg; u32 init_drive_period_code; u32 timeout_ms; u32 time_required_to_generate_back_emf_us; @@ -380,10 +374,10 @@ struct qpnp_hap { u8 ext_pwm_dtest_line; u8 pmic_subtype; u8 auto_res_mode; + u8 clk_trim_error_code; + bool lra_hw_auto_resonance; bool vcc_pon_enabled; bool state; - bool use_play_irq; - bool use_sc_irq; bool manage_pon_supply; bool wf_update; bool pwm_cfg_state; @@ -391,82 +385,98 @@ struct qpnp_hap { bool en_brake; bool sup_brake_pat; bool correct_lra_drive_freq; - bool misc_trim_error_rc19p2_clk_reg_present; bool perform_lra_auto_resonance_search; }; static struct qpnp_hap *ghap; /* helper to read a pmic register */ -static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr) +static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val) { int rc; - uint val; + uint tmp; - rc = regmap_read(hap->regmap, addr, &val); + rc = regmap_read(hap->regmap, addr, &tmp); if (rc < 0) - dev_err(&hap->pdev->dev, - "Error reading address: %X - ret %X\n", addr, rc); - *data = (u8)val; + pr_err("Error reading address: %X - ret %X\n", addr, rc); + *val = (u8)tmp; return rc; } /* helper to write a pmic register */ -static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) +static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val) { + unsigned long flags; int rc; - rc = regmap_write(hap->regmap, addr, *data); + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_write(hap->regmap, addr, val); if (rc < 0) - dev_err(&hap->pdev->dev, - "Error writing address: %X - ret %X\n", addr, rc); + pr_err("Error writing address: %X - ret %X\n", addr, rc); - dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data); + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); return rc; } -static int -qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u8 val, u16 addr, u8 mask) +/* helper to access secure registers */ +#define QPNP_HAP_SEC_UNLOCK 0xA5 +static int qpnp_hap_sec_masked_write_reg(struct qpnp_hap *hap, u16 addr, + u8 mask, u8 val) { + unsigned long flags; int rc; + u8 tmp = QPNP_HAP_SEC_UNLOCK; + + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_write(hap->regmap, QPNP_HAP_SEC_ACCESS_REG(hap->base), tmp); + if (rc < 0) { + pr_err("Error writing sec_code - ret %X\n", rc); + goto out; + } rc = regmap_update_bits(hap->regmap, addr, mask, val); if (rc < 0) - pr_err("Unable to update bits from 0x%04X, rc = %d\n", addr, - rc); - else - pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, addr); + pr_err("Error writing address: %X - ret %X\n", addr, rc); +out: + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); return rc; } -/* helper to access secure registers */ -static int qpnp_hap_sec_access(struct qpnp_hap *hap) +static int qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u16 addr, u8 mask, + u8 val) { + unsigned long flags; int rc; - u8 reg = QPNP_HAP_SEC_UNLOCK; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_SEC_ACCESS_REG(hap->base)); - if (rc) - return rc; + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_update_bits(hap->regmap, addr, mask, val); + if (rc < 0) + pr_err("Error writing address: %X - ret %X\n", addr, rc); - return 0; + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); + return rc; } static void qpnp_handle_sc_irq(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, sc_work.work); - u8 val, reg; + u8 val; - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); /* clear short circuit register */ if (val & SC_FOUND_BIT) { hap->sc_duration++; - reg = QPNP_HAP_SC_CLR; - qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_CLR_REG(hap->base)); + val = QPNP_HAP_SC_CLR; + qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val); } } @@ -484,10 +494,10 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) unsigned long sleep_time = QPNP_HAP_CYCLS * hap->wave_play_rate_us; - rc = qpnp_hap_read_reg(hap, &val, - QPNP_HAP_STATUS(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), + &val); - dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", val); + pr_debug("HAP_STATUS=0x%x\n", val); /* wait for QPNP_HAP_CYCLS cycles of play rate */ if (val & QPNP_HAP_STATUS_BUSY) { @@ -500,14 +510,12 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) } if (i >= QPNP_HAP_MAX_RETRIES) - dev_dbg(&hap->pdev->dev, - "Haptics Busy. Force disable\n"); + pr_debug("Haptics Busy. Force disable\n"); val &= ~QPNP_HAP_EN; } - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val); if (rc < 0) return rc; @@ -527,8 +535,7 @@ static int qpnp_hap_play(struct qpnp_hap *hap, int on) else val &= ~QPNP_HAP_PLAY_EN; - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_PLAY_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val); if (rc < 0) return rc; @@ -548,7 +555,7 @@ static ssize_t qpnp_hap_dump_regs_show(struct device *dev, u8 val; for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) { - qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]); + qpnp_hap_read_reg(hap, hap->base + qpnp_hap_dbg_regs[i], &val); count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: REG_0x%x = 0x%x\n", hap->base + qpnp_hap_dbg_regs[i], @@ -566,15 +573,15 @@ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int i, rc; - u8 reg; + u8 val; mutex_lock(&hap->wf_lock); /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) { - reg = hap->wave_samp[i] = hap->shadow_wave_samp[i]; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + val = hap->wave_samp[i] = hap->shadow_wave_samp[i]; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); if (rc) goto unlock; } @@ -591,13 +598,12 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int rc; - u8 disable_haptics = 0x00; u8 val; - dev_dbg(&hap->pdev->dev, "Short circuit detected\n"); + pr_debug("Short circuit detected\n"); if (hap->sc_duration < SC_MAX_DURATION) { - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (val & SC_FOUND_BIT) schedule_delayed_work(&hap->sc_work, QPNP_HAP_SC_IRQ_STATUS_DELAY); @@ -607,10 +613,10 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ - rc = qpnp_hap_write_reg(hap, &disable_haptics, - QPNP_HAP_EN_CTL_REG(hap->base)); - dev_err(&hap->pdev->dev, - "Haptics disabled permanently due to short circuit\n"); + val = 0; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), + val); + pr_err("Haptics disabled permanently due to short circuit\n"); } return IRQ_HANDLED; @@ -619,8 +625,8 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) /* configuration api for buffer mode */ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, i, temp; + u8 val = 0; + int rc, i; /* Configure the WAVE_REPEAT register */ if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN) @@ -633,44 +639,22 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_REP_MASK; - temp = fls(hap->wave_rep_cnt) - 1; - reg |= (temp << QPNP_HAP_WAV_REP_SHFT); - reg &= QPNP_HAP_WAV_S_REP_MASK; - temp = fls(hap->wave_s_rep_cnt) - 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); + val = ilog2(hap->wave_rep_cnt) << QPNP_HAP_WAV_REP_SHIFT | + ilog2(hap->wave_s_rep_cnt); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_REP_MASK | QPNP_HAP_WAV_S_REP_MASK, val); if (rc) return rc; /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ - for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { - reg = hap->wave_samp[i]; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { + val = hap->wave_samp[i]; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); if (rc) return rc; } - /* setup play irq */ - if (hap->use_play_irq) { - rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, - NULL, qpnp_hap_play_irq, - QPNP_IRQ_FLAGS, - "qpnp_play_irq", hap); - if (rc < 0) { - dev_err(&hap->pdev->dev, - "Unable to request play(%d) IRQ(err:%d)\n", - hap->play_irq, rc); - return rc; - } - } - hap->buffer_cfg_state = true; return 0; } @@ -678,59 +662,41 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) /* configuration api for pwm */ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; /* Configure the EXTERNAL_PWM register */ if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; - temp = 0; + val = 0; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_50_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ; - temp = 1; + val = 1; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_75_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ; - temp = 2; + val = 2; } else { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ; - temp = 3; + val = 3; } - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_EXT_PWM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_EXT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_EXT_PWM_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EXT_PWM_REG(hap->base), + QPNP_HAP_EXT_PWM_MASK, val); if (rc) return rc; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_TEST2_REG(hap->base)); - if (rc) - return rc; if (!hap->ext_pwm_dtest_line || hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) { - dev_err(&hap->pdev->dev, "invalid dtest line\n"); + pr_err("invalid dtest line\n"); return -EINVAL; } /* disable auto res for PWM mode */ - reg &= QPNP_HAP_EXT_PWM_DTEST_MASK; - temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; - reg |= temp; - - /* TEST2 is a secure access register */ - rc = qpnp_hap_sec_access(hap); - if (rc) - return rc; - - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_TEST2_REG(hap->base)); + val = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; + rc = qpnp_hap_sec_masked_write_reg(hap, QPNP_HAP_TEST2_REG(hap->base), + QPNP_HAP_EXT_PWM_DTEST_MASK | QPNP_HAP_AUTO_RES_MASK, val); if (rc) return rc; @@ -738,7 +704,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) hap->pwm_info.duty_us * NSEC_PER_USEC, hap->pwm_info.period_us * NSEC_PER_USEC); if (rc < 0) { - dev_err(&hap->pdev->dev, "hap pwm config failed\n"); + pr_err("hap pwm config failed\n"); pwm_free(hap->pwm_info.pwm_dev); return -ENODEV; } @@ -748,72 +714,180 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) return 0; } +static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap) +{ + int rc; + u8 val, mask; + + /* disable auto resonance for ERM */ + if (hap->act_type == QPNP_HAP_ERM) { + val = 0x00; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base), val); + return rc; + } + + if (hap->lra_hw_auto_resonance) { + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT, + QPNP_HAP_AUTO_RES_CTRL(hap->base), + QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT); + if (rc) + return rc; + } + + if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; + + if (hap->pmic_subtype == PM660_SUBTYPE) { + if (hap->lra_res_cal_period > + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; + + if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) + hap->lra_res_cal_period = 0; + } else { + if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; + } + + val = mask = 0; + if (hap->lra_res_cal_period) + val = ilog2(hap->lra_res_cal_period / + QPNP_HAP_RES_CAL_PERIOD_MIN); + + if (hap->pmic_subtype == PM660_SUBTYPE) { + val |= hap->auto_res_mode << + QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; + mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; + val |= hap->lra_high_z << + QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; + mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; + if (hap->lra_qwd_drive_duration != -EINVAL) { + val |= hap->lra_qwd_drive_duration << + QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; + mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; + } + if (hap->calibrate_at_eop != -EINVAL) { + val |= hap->calibrate_at_eop << + QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; + mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; + } + mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; + } else { + val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); + val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); + mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK | + QPNP_HAP_LRA_RES_CAL_PER_MASK; + } + + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val); + return rc; +} + /* configuration api for play mode */ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_PLAY_MODE_MASK; - temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); - if (rc) - return rc; - return 0; + val = hap->play_mode << QPNP_HAP_WF_SOURCE_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SEL_REG(hap->base), + QPNP_HAP_WF_SOURCE_MASK, val); + return rc; } -/* configuration api for max volatge */ +/* configuration api for max voltage */ static int qpnp_hap_vmax_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_VMAX_MASK; - temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; - reg |= (temp << QPNP_HAP_VMAX_SHIFT); - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); - if (rc) - return rc; + val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base), + QPNP_HAP_VMAX_MASK, val); + return rc; +} - return 0; +/* configuration api for ilim */ +static int qpnp_hap_ilim_config(struct qpnp_hap *hap) +{ + u8 val = 0; + int rc; + + if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; + else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; + + val = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) - 1; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_ILIM_REG(hap->base), + QPNP_HAP_ILIM_MASK, val); + return rc; } /* configuration api for short circuit debounce */ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN; else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_SC_DEB_MASK; - if (hap->sc_deb_cycles) { - temp = fls(hap->sc_deb_cycles) - 1; - reg |= temp - QPNP_HAP_SC_DEB_SUB; + if (hap->sc_deb_cycles != QPNP_HAP_SC_DEB_CYCLES_MIN) + val = ilog2(hap->sc_deb_cycles / + QPNP_HAP_DEF_SC_DEB_CYCLES) + 1; + else + val = QPNP_HAP_SC_DEB_CYCLES_MIN; + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SC_DEB_REG(hap->base), + QPNP_HAP_SC_DEB_MASK, val); + + return rc; +} + +static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap) +{ + int rc; + u8 val; + + if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { + if (hap->pmic_subtype == PM660_SUBTYPE) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + val = 1; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; + val = 0; + } + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + val = 1; + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; + val = 2; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; + val = 3; } - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_INT_PWM_REG(hap->base), + QPNP_HAP_INT_PWM_MASK, val); if (rc) return rc; - return 0; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_PWM_CAP_REG(hap->base), + QPNP_HAP_INT_PWM_MASK, val); + return rc; } /* DT parsing api for buffer mode */ @@ -830,7 +904,7 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_rep_cnt = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read rep cnt\n"); + pr_err("Unable to read rep cnt\n"); return rc; } @@ -840,30 +914,20 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_s_rep_cnt = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read samp rep cnt\n"); + pr_err("Unable to read samp rep cnt\n"); return rc; } prop = of_find_property(pdev->dev.of_node, "qcom,wave-samples", &temp); if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) { - dev_err(&pdev->dev, "Invalid wave samples, use default"); + pr_err("Invalid wave samples, use default"); for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX; } else { memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN); } - hap->use_play_irq = of_property_read_bool(pdev->dev.of_node, - "qcom,use-play-irq"); - if (hap->use_play_irq) { - hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); - if (hap->play_irq < 0) { - dev_err(&pdev->dev, "Unable to get play irq\n"); - return hap->play_irq; - } - } - return 0; } @@ -880,7 +944,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (!rc) { hap->ext_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read ext pwm freq\n"); + pr_err("Unable to read ext pwm freq\n"); return rc; } @@ -895,7 +959,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (IS_ERR(hap->pwm_info.pwm_dev)) { rc = PTR_ERR(hap->pwm_info.pwm_dev); - dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc); + pr_err("Cannot get PWM device rc:(%d)\n", rc); hap->pwm_info.pwm_dev = NULL; return rc; } @@ -930,7 +994,7 @@ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index) timed_dev); if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - dev_err(dev, "Invalid sample index(%d)\n", index); + pr_err("Invalid sample index(%d)\n", index); return -EINVAL; } @@ -996,7 +1060,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, int data, rc; if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - dev_err(dev, "Invalid sample index(%d)\n", index); + pr_err("Invalid sample index(%d)\n", index); return -EINVAL; } @@ -1005,7 +1069,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, return rc; if (data < 0 || data > 0xff) { - dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data); + pr_err("Invalid sample wf_%d (%d)\n", index, data); return -EINVAL; } @@ -1105,8 +1169,8 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc, temp; - u8 reg; + int data, rc; + u8 val; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1117,19 +1181,11 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_REP_MAX) data = QPNP_HAP_WAV_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_REP_MASK; - temp = fls(data) - 1; - reg |= (temp << QPNP_HAP_WAV_REP_SHFT); - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc) - return rc; - - hap->wave_rep_cnt = data; + val = ilog2(data) << QPNP_HAP_WAV_REP_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_REP_MASK, val); + if (!rc) + hap->wave_rep_cnt = data; return count; } @@ -1152,8 +1208,8 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc, temp; - u8 reg; + int data, rc; + u8 val; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1164,19 +1220,11 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_S_REP_MAX) data = QPNP_HAP_WAV_S_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_S_REP_MASK; - temp = fls(data) - 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc) - return rc; - - hap->wave_s_rep_cnt = data; + val = ilog2(hap->wave_s_rep_cnt); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_S_REP_MASK, val); + if (!rc) + hap->wave_s_rep_cnt = data; return count; } @@ -1397,25 +1445,22 @@ static int calculate_lra_code(struct qpnp_hap *hap) u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1; int rc = 0; - rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + &lra_drive_period_code_lo); if (rc) { - dev_err(&hap->pdev->dev, - "Error while reading RATE_CFG1 register\n"); + pr_err("Error while reading RATE_CFG1 register\n"); return rc; } - rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), + &lra_drive_period_code_hi); if (rc) { - dev_err(&hap->pdev->dev, - "Error while reading RATE_CFG2 register\n"); + pr_err("Error while reading RATE_CFG2 register\n"); return rc; } if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) { - dev_err(&hap->pdev->dev, - "Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); + pr_err("Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); return -EINVAL; } @@ -1433,16 +1478,14 @@ static int calculate_lra_code(struct qpnp_hap *hap) start_variation -= AUTO_RES_ERROR_CAPTURE_RES; } - dev_dbg(&hap->pdev->dev, - "lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" + pr_debug("lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" "lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n" "Calculated play rate code values are :\n", lra_drive_period_code_lo, lra_drive_period_code_hi, lra_drive_period_code, lra_drive_frequency_hz); for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i) - dev_dbg(&hap->pdev->dev, - " 0x%x", adjusted_lra_play_rate_code[i]); + pr_debug(" 0x%x", adjusted_lra_play_rate_code[i]); return 0; } @@ -1450,25 +1493,18 @@ static int calculate_lra_code(struct qpnp_hap *hap) static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) { int rc = 0; - u8 val = 0; - u16 addr; - - - if (hap->pmic_subtype == PM660_SUBTYPE) { - addr = QPNP_HAP_AUTO_RES_CTRL(hap->base); - } else { - addr = QPNP_HAP_TEST2_REG(hap->base); - /* TEST2 is a secure access register */ - rc = qpnp_hap_sec_access(hap); - if (rc) - return rc; - } - - if (enable) - val |= AUTO_RES_ENABLE; + u8 val; - rc = qpnp_hap_masked_write_reg(hap, val, addr, AUTO_RES_ENABLE); - if (rc) + val = enable ? AUTO_RES_ENABLE : 0; + if (hap->pmic_subtype == PM660_SUBTYPE) + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_AUTO_RES_CTRL(hap->base), + QPNP_HAP_AUTO_RES_MASK, val); + else + rc = qpnp_hap_sec_masked_write_reg(hap, + QPNP_HAP_TEST2_REG(hap->base), + QPNP_HAP_AUTO_RES_MASK, val); + if (rc < 0) return rc; if (enable) @@ -1476,7 +1512,7 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) else hap->status_flags &= ~AUTO_RESONANCE_ENABLED; - return 0; + return rc; } static void update_lra_frequency(struct qpnp_hap *hap) @@ -1485,19 +1521,18 @@ static void update_lra_frequency(struct qpnp_hap *hap) u32 play_rate_code; int rc; - qpnp_hap_read_reg(hap, &lra_auto_res_lo, - QPNP_HAP_LRA_AUTO_RES_LO(hap->base)); - qpnp_hap_read_reg(hap, &lra_auto_res_hi, - QPNP_HAP_LRA_AUTO_RES_HI(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base), + &lra_auto_res_lo); + qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base), + &lra_auto_res_hi); play_rate_code = (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF); - dev_dbg(&hap->pdev->dev, - "lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", + pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", lra_auto_res_lo, lra_auto_res_hi, play_rate_code); - rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (rc < 0) return; @@ -1511,22 +1546,25 @@ static void update_lra_frequency(struct qpnp_hap *hap) if ((val & AUTO_RES_ERR_BIT) || ((play_rate_code <= hap->drive_period_code_min_limit) || (play_rate_code >= hap->drive_period_code_max_limit))) { - dev_dbg(&hap->pdev->dev, - "Auto-resonance error, out of 25%%, [min: 0x%x, max: 0x%x]\n", - hap->drive_period_code_min_limit, - hap->drive_period_code_max_limit); + if (val & AUTO_RES_ERR_BIT) + pr_debug("Auto-resonance error %x\n", val); + else + pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n", + play_rate_code, + hap->drive_period_code_min_limit, + hap->drive_period_code_max_limit); rc = qpnp_hap_auto_res_enable(hap, 0); if (rc < 0) - dev_dbg(&hap->pdev->dev, "Auto-resonance write failed\n"); + pr_debug("Auto-resonance write failed\n"); return; } - qpnp_hap_write_reg(hap, &lra_auto_res_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + lra_auto_res_lo); lra_auto_res_hi = lra_auto_res_hi >> 4; - qpnp_hap_write_reg(hap, &lra_auto_res_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), + lra_auto_res_hi); } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) @@ -1535,7 +1573,6 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) auto_res_err_poll_timer); ktime_t currtime; - if (!(hap->status_flags & AUTO_RESONANCE_ENABLED)) return HRTIMER_NORESTART; @@ -1603,7 +1640,8 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) return rc; } if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) { + hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) { /* * Start timer to poll Auto Resonance error bit */ @@ -1621,13 +1659,15 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && - (hap->status_flags & AUTO_RESONANCE_ENABLED)) { + (hap->status_flags & AUTO_RESONANCE_ENABLED) && + !hap->lra_hw_auto_resonance) { update_lra_frequency(hap); } rc = qpnp_hap_mod_enable(hap, on); if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) { + hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) { hrtimer_cancel(&hap->auto_res_err_poll_timer); } } @@ -1645,7 +1685,8 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value) mutex_lock(&hap->lock); if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) + hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); @@ -1680,7 +1721,7 @@ int qpnp_hap_play_byte(u8 data, bool on) } if (hap->play_mode != QPNP_HAP_PWM) { - dev_err(&hap->pdev->dev, "only PWM mode is supported\n"); + pr_err("only PWM mode is supported\n"); return -EINVAL; } @@ -1721,8 +1762,7 @@ int qpnp_hap_play_byte(u8 data, bool on) if (rc) return rc; - dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data, - duty_percent); + pr_debug("data=0x%x duty_per=%d\n", data, duty_percent); rc = qpnp_hap_set(hap, true); @@ -1741,8 +1781,8 @@ static void qpnp_hap_worker(struct work_struct *work) if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) { rc = regulator_enable(hap->vcc_pon); if (rc < 0) - pr_err("%s: could not enable vcc_pon regulator rc=%d\n", - __func__, rc); + pr_err("could not enable vcc_pon regulator rc=%d\n", + rc); else hap->vcc_pon_enabled = true; } @@ -1751,8 +1791,8 @@ static void qpnp_hap_worker(struct work_struct *work) * exceeds the maximum limit (5 secs). */ if (hap->sc_duration == SC_MAX_DURATION) { - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), + val); } else { if (hap->play_mode == QPNP_HAP_PWM) qpnp_hap_mod_enable(hap, hap->state); @@ -1762,8 +1802,8 @@ static void qpnp_hap_worker(struct work_struct *work) if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) { rc = regulator_disable(hap->vcc_pon); if (rc) - pr_err("%s: could not disable vcc_pon regulator rc=%d\n", - __func__, rc); + pr_err("could not disable vcc_pon regulator rc=%d\n", + rc); else hap->vcc_pon_enabled = false; } @@ -1827,99 +1867,25 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); /* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { - u8 reg = 0, unlock_val, mask; + u8 val = 0; u32 temp; int rc, i; - uint error_code = 0; /* * This denotes the percentage error in rc clock multiplied by 10 */ u8 rc_clk_err_percent_x10; - /* Configure the ACTUATOR TYPE register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_ACT_TYPE_MASK; - reg |= hap->act_type; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); + /* Configure the CFG1 register for actuator type */ + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG1_REG(hap->base), + QPNP_HAP_ACT_TYPE_MASK, hap->act_type); if (rc) return rc; /* Configure auto resonance parameters */ - if (hap->act_type == QPNP_HAP_LRA) { - if (hap->pmic_subtype == PM660_SUBTYPE) { - if (hap->lra_res_cal_period < - QPNP_HAP_PM660_RES_CAL_PERIOD_MIN) - hap->lra_res_cal_period = - QPNP_HAP_PM660_RES_CAL_PERIOD_MIN; - else if (hap->lra_res_cal_period > - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; - } else if (hap->pmic_subtype != PM660_SUBTYPE) { - if (hap->lra_res_cal_period < - QPNP_HAP_RES_CAL_PERIOD_MIN) - hap->lra_res_cal_period = - QPNP_HAP_RES_CAL_PERIOD_MIN; - else if (hap->lra_res_cal_period > - QPNP_HAP_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = - QPNP_HAP_RES_CAL_PERIOD_MAX; - } - if (hap->pmic_subtype == PM660_SUBTYPE && - hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) { - hap->lra_res_cal_period = 0; - } - - reg = mask = 0; - if (hap->pmic_subtype == PM660_SUBTYPE) { - reg |= hap->auto_res_mode << - QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; - mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; - reg |= hap->lra_high_z << - QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; - mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; - if (hap->lra_qwd_drive_duration != -EINVAL) { - reg |= hap->lra_qwd_drive_duration << - QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; - mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; - } - if (hap->calibrate_at_eop != -EINVAL) { - reg |= hap->calibrate_at_eop << - QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; - mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; - } - if (hap->lra_res_cal_period) { - temp = fls(hap->lra_res_cal_period) - 1; - reg |= (temp - 1); - } - mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; - } else { - reg |= (hap->auto_res_mode << - QPNP_HAP_AUTO_RES_MODE_SHIFT); - mask = QPNP_HAP_AUTO_RES_MODE_MASK; - reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); - mask |= QPNP_HAP_LRA_HIGH_Z_MASK; - temp = fls(hap->lra_res_cal_period) - 1; - reg |= (temp - 2); - mask |= QPNP_HAP_LRA_RES_CAL_PER_MASK; - } - rc = qpnp_hap_masked_write_reg(hap, reg, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base), - mask); - if (rc) - return rc; - } else { - /* disable auto resonance for ERM */ - reg = 0x00; - - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); - if (rc) - return rc; - } + rc = qpnp_hap_lra_auto_res_config(hap); + if (rc) + return rc; /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); @@ -1932,18 +1898,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the ILIM register */ - if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; - else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_ILIM_MASK; - temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); + rc = qpnp_hap_ilim_config(hap); if (rc) return rc; @@ -1953,52 +1908,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the INTERNAL_PWM register */ - if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { - if (hap->pmic_subtype == PM660_SUBTYPE) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - temp = 1; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; - temp = 0; - } - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - temp = 1; - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; - temp = 2; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; - temp = 3; - } - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_INT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); - if (rc) - return rc; - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_INT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); + rc = qpnp_hap_int_pwm_config(hap); if (rc) return rc; /* Configure the WAVE SHAPE register */ - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_SHAPE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_SHAPE_MASK; - reg |= hap->wave_shape; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_SHAPE_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base), + QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape); if (rc) return rc; @@ -2019,35 +1935,24 @@ static int qpnp_hap_config(struct qpnp_hap *hap) * The frequency of 19.2Mzhz RC clock is subject to variation. Currently * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register * present in their MISC block. This register holds the frequency error - * in 19.2Mhz RC clock. + * in 19.2 MHz RC clock. */ if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq - && hap->misc_trim_error_rc19p2_clk_reg_present) { - unlock_val = MISC_SEC_UNLOCK; - /* - * This SID value may change depending on the PMI chip where - * the MISC block is present. - */ - rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val); - if (rc) - dev_err(&hap->pdev->dev, - "Unable to do SEC_ACCESS rc:%d\n", rc); - - regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK, - &error_code); - dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code); + && hap->misc_clk_trim_error_reg) { + pr_debug("TRIM register = 0x%x\n", hap->clk_trim_error_code); /* * Extract the 4 LSBs and multiply by 7 to get * the %error in RC clock multiplied by 10 */ - rc_clk_err_percent_x10 = (error_code & 0x0F) * 7; + rc_clk_err_percent_x10 = (hap->clk_trim_error_code & 0x0F) * 7; /* * If the TRIM register holds value less than 0x80, * then there is a positive error in the RC clock. * If the TRIM register holds value greater than or equal to - * 0x80, then there is a negative error in the RC clock. + * 0x80, then there is a negative error in the RC clock. Bit 7 + * is the sign bit for error code. * * The adjusted play rate code is calculated as follows: * LRA drive period code (RATE_CFG) = @@ -2060,24 +1965,21 @@ static int qpnp_hap_config(struct qpnp_hap *hap) * Since 200KHz * 1/LRA drive frequency is already calculated * above we only do rest of the scaling here. */ - if (error_code >= 128) + if (hap->clk_trim_error_code & BIT(7)) LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10); else LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10); } - dev_dbg(&hap->pdev->dev, - "Play rate code 0x%x\n", hap->init_drive_period_code); + pr_debug("Play rate code 0x%x\n", hap->init_drive_period_code); - reg = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + val = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), val); if (rc) return rc; - reg = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + val = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), val); if (rc) return rc; @@ -2094,45 +1996,40 @@ static int qpnp_hap_config(struct qpnp_hap *hap) (hap->init_drive_period_code * (100 - hap->drive_period_code_min_limit_percent_variation)) / 100; - dev_dbg(&hap->pdev->dev, "Drive period code max limit %x\n" - "Drive period code min limit %x\n", - hap->drive_period_code_max_limit, - hap->drive_period_code_min_limit); + pr_debug("Drive period code max limit %x min limit %x\n", + hap->drive_period_code_max_limit, + hap->drive_period_code_min_limit); } /* Configure BRAKE register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_BRAKE_MASK; - reg |= hap->en_brake; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base), + QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake); if (rc) return rc; if (hap->en_brake && hap->sup_brake_pat) { - for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) { + for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; temp = i << 1; - reg |= hap->brake_pat[i] << temp; + val |= hap->brake_pat[i] << temp; } - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_BRAKE_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base), + val); if (rc) return rc; } /* Cache enable control register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val); if (rc < 0) return rc; - hap->reg_en_ctl = reg; + hap->reg_en_ctl = val; /* Cache play register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val); if (rc < 0) return rc; - hap->reg_play = reg; + hap->reg_play = val; if (hap->play_mode == QPNP_HAP_BUFFER) rc = qpnp_hap_buffer_config(hap); @@ -2144,15 +2041,29 @@ static int qpnp_hap_config(struct qpnp_hap *hap) if (rc) return rc; + /* setup play irq */ + if (hap->play_irq >= 0) { + rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, + NULL, qpnp_hap_play_irq, IRQF_ONESHOT, "qpnp_hap_play", + hap); + if (rc < 0) { + pr_err("Unable to request play(%d) IRQ(err:%d)\n", + hap->play_irq, rc); + return rc; + } + + /* use play_irq only for buffer mode */ + if (hap->play_mode != QPNP_HAP_BUFFER) + disable_irq(hap->play_irq); + } + /* setup short circuit irq */ - if (hap->use_sc_irq) { + if (hap->sc_irq >= 0) { rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq, - NULL, qpnp_hap_sc_irq, - QPNP_IRQ_FLAGS, - "qpnp_sc_irq", hap); + NULL, qpnp_hap_sc_irq, IRQF_ONESHOT, "qpnp_hap_sc", + hap); if (rc < 0) { - dev_err(&hap->pdev->dev, - "Unable to request sc(%d) IRQ(err:%d)\n", + pr_err("Unable to request sc(%d) IRQ(err:%d)\n", hap->sc_irq, rc); return rc; } @@ -2167,18 +2078,46 @@ static int qpnp_hap_config(struct qpnp_hap *hap) static int qpnp_hap_parse_dt(struct qpnp_hap *hap) { struct platform_device *pdev = hap->pdev; + struct device_node *misc_node; struct property *prop; const char *temp_str; u32 temp; int rc; + if (of_find_property(pdev->dev.of_node, "qcom,pmic-misc", NULL)) { + misc_node = of_parse_phandle(pdev->dev.of_node, + "qcom,pmic-misc", 0); + if (!misc_node) + return -EINVAL; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,misc-clk-trim-error-reg", &temp); + if (rc < 0) { + pr_err("Missing misc-clk-trim-error-reg\n"); + return rc; + } + + if (!temp || temp > 0xFF) { + pr_err("Invalid misc-clk-trim-error-reg\n"); + return -EINVAL; + } + + hap->misc_clk_trim_error_reg = temp; + rc = qpnp_misc_read_reg(misc_node, hap->misc_clk_trim_error_reg, + &hap->clk_trim_error_code); + if (rc < 0) { + pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc); + return -EPROBE_DEFER; + } + } + hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,timeout-ms", &temp); if (!rc) { hap->timeout_ms = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read timeout\n"); + pr_err("Unable to read timeout\n"); return rc; } @@ -2191,11 +2130,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "lra") == 0) hap->act_type = QPNP_HAP_LRA; else { - dev_err(&pdev->dev, "Invalid actuator type\n"); + pr_err("Invalid actuator type\n"); return -EINVAL; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read actuator type\n"); + pr_err("Unable to read actuator type\n"); return rc; } @@ -2231,7 +2170,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) QPNP_HAP_AUTO_RES_ZXD_EOP; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read auto res mode\n"); + pr_err("Unable to read auto res mode\n"); return rc; } @@ -2253,7 +2192,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read LRA high-z\n"); + pr_err("Unable to read LRA high-z\n"); return rc; } @@ -2272,10 +2211,14 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->lra_res_cal_period = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read cal period\n"); + pr_err("Unable to read cal period\n"); return rc; } + hap->lra_hw_auto_resonance = + of_property_read_bool(pdev->dev.of_node, + "qcom,lra-hw-auto-resonance"); + hap->perform_lra_auto_resonance_search = of_property_read_bool(pdev->dev.of_node, "qcom,perform-lra-auto-resonance-search"); @@ -2298,10 +2241,6 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) hap->drive_period_code_min_limit_percent_variation = (u8) temp; - hap->misc_trim_error_rc19p2_clk_reg_present = - of_property_read_bool(pdev->dev.of_node, - "qcom,misc-trim-error-rc19p2-clk-reg-present"); - if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { hap->time_required_to_generate_back_emf_us = QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN; @@ -2328,11 +2267,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "audio") == 0) hap->play_mode = QPNP_HAP_AUDIO; else { - dev_err(&pdev->dev, "Invalid play mode\n"); + pr_err("Invalid play mode\n"); return -EINVAL; } } else { - dev_err(&pdev->dev, "Unable to read play mode\n"); + pr_err("Unable to read play mode\n"); return rc; } @@ -2341,7 +2280,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->vmax_mv = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read vmax\n"); + pr_err("Unable to read vmax\n"); return rc; } @@ -2350,7 +2289,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->ilim_ma = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read ILim\n"); + pr_err("Unable to read ILim\n"); return rc; } @@ -2360,7 +2299,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->sc_deb_cycles = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read sc debounce\n"); + pr_err("Unable to read sc debounce\n"); return rc; } @@ -2370,7 +2309,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->int_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read int pwm freq\n"); + pr_err("Unable to read int pwm freq\n"); return rc; } @@ -2383,11 +2322,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "square") == 0) hap->wave_shape = QPNP_HAP_WAV_SQUARE; else { - dev_err(&pdev->dev, "Unsupported wav shape\n"); + pr_err("Unsupported wav shape\n"); return -EINVAL; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read wav shape\n"); + pr_err("Unable to read wav shape\n"); return rc; } @@ -2397,7 +2336,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_play_rate_us = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read play rate\n"); + pr_err("Unable to read play rate\n"); return rc; } @@ -2416,9 +2355,9 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) prop = of_find_property(pdev->dev.of_node, "qcom,brake-pattern", &temp); if (!prop) { - dev_info(&pdev->dev, "brake pattern not found"); + pr_info("brake pattern not found"); } else if (temp != QPNP_HAP_BRAKE_PAT_LEN) { - dev_err(&pdev->dev, "Invalid len of brake pattern\n"); + pr_err("Invalid len of brake pattern\n"); return -EINVAL; } else { hap->sup_brake_pat = true; @@ -2427,14 +2366,14 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) } } - hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node, - "qcom,use-sc-irq"); - if (hap->use_sc_irq) { - hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); - if (hap->sc_irq < 0) { - dev_err(&pdev->dev, "Unable to get sc irq\n"); - return hap->sc_irq; - } + hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); + if (hap->play_irq < 0) + pr_warn("Unable to get play irq\n"); + + hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); + if (hap->sc_irq < 0) { + pr_err("Unable to get sc irq\n"); + return hap->sc_irq; } if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL)) @@ -2483,7 +2422,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) return -ENOMEM; hap->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hap->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + pr_err("Couldn't get parent's regmap\n"); return -EINVAL; } @@ -2491,8 +2430,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", + pr_err("Couldn't find reg in node = %s rc = %d\n", pdev->dev.of_node->full_name, rc); return rc; } @@ -2508,13 +2446,14 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = qpnp_hap_parse_dt(hap); if (rc) { - dev_err(&pdev->dev, "DT parsing failed\n"); + pr_err("DT parsing failed\n"); return rc; } + spin_lock_init(&hap->bus_lock); rc = qpnp_hap_config(hap); if (rc) { - dev_err(&pdev->dev, "hap config failed\n"); + pr_err("hap config failed\n"); return rc; } @@ -2534,7 +2473,8 @@ static int qpnp_haptic_probe(struct platform_device *pdev) hap->timed_dev.get_time = qpnp_hap_get_time; hap->timed_dev.enable = qpnp_hap_td_enable; - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) { hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hap->auto_res_err_poll_timer.function = detect_auto_res_error; @@ -2542,7 +2482,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = timed_output_dev_register(&hap->timed_dev); if (rc < 0) { - dev_err(&pdev->dev, "timed_output registration failed\n"); + pr_err("timed_output registration failed\n"); goto timed_output_fail; } @@ -2550,7 +2490,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = sysfs_create_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); if (rc < 0) { - dev_err(&pdev->dev, "sysfs creation failed\n"); + pr_err("sysfs creation failed\n"); goto sysfs_fail; } } @@ -2559,8 +2499,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) vcc_pon = regulator_get(&pdev->dev, "vcc_pon"); if (IS_ERR(vcc_pon)) { rc = PTR_ERR(vcc_pon); - dev_err(&pdev->dev, - "regulator get failed vcc_pon rc=%d\n", rc); + pr_err("regulator get failed vcc_pon rc=%d\n", rc); goto sysfs_fail; } hap->vcc_pon = vcc_pon; @@ -2577,7 +2516,8 @@ sysfs_fail: timed_output_dev_unregister(&hap->timed_dev); timed_output_fail: cancel_work_sync(&hap->work); - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); mutex_destroy(&hap->lock); @@ -2596,7 +2536,8 @@ static int qpnp_haptic_remove(struct platform_device *pdev) &qpnp_hap_attrs[i].attr); cancel_work_sync(&hap->work); - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); timed_output_dev_unregister(&hap->timed_dev); diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c index 20f406b9a2f7..f2784dedbc7a 100644 --- a/drivers/soc/qcom/rpm-smd.c +++ b/drivers/soc/qcom/rpm-smd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -716,51 +716,6 @@ int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata, return 0; } -static void msm_rpm_print_sleep_buffer(struct slp_buf *s) -{ - char buf[DEBUG_PRINT_BUFFER_SIZE] = {0}; - int pos; - int buflen = DEBUG_PRINT_BUFFER_SIZE; - char ch[5] = {0}; - struct kvp *e; - uint32_t type; - unsigned int id; - - if (!s) - return; - - if (!s->valid) - return; - - type = get_rsc_type(s->buf); - id = get_rsc_id(s->buf); - - memcpy(ch, &type, sizeof(u32)); - - pos = scnprintf(buf, buflen, - "Sleep request type = 0x%08x(%s)", - type, ch); - pos += scnprintf(buf + pos, buflen - pos, " id = 0%x", - id); - for_each_kvp(s->buf, e) { - uint32_t i; - char *data = get_data(e); - - memcpy(ch, &e->k, sizeof(u32)); - - pos += scnprintf(buf + pos, buflen - pos, - "\n\t\tkey = 0x%08x(%s)", - e->k, ch); - pos += scnprintf(buf + pos, buflen - pos, - " sz= %d data =", e->s); - - for (i = 0; i < e->s; i++) - pos += scnprintf(buf + pos, buflen - pos, - " 0x%02X", data[i]); - } - pos += scnprintf(buf + pos, buflen - pos, "\n"); - printk(buf); -} static struct msm_rpm_driver_data msm_rpm_data = { .smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open), @@ -821,9 +776,6 @@ static int msm_rpm_flush_requests(bool print) if (!s->valid) continue; - if (print) - msm_rpm_print_sleep_buffer(s); - set_msg_id(s->buf, msm_rpm_get_next_msg_id()); if (!glink_enabled) diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c index 90c7585d480c..4307937d9f6d 100644 --- a/drivers/soc/qcom/secure_buffer.c +++ b/drivers/soc/qcom/secure_buffer.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Google, Inc - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -379,6 +379,7 @@ int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list, sg_free_table(&table); return ret; } +EXPORT_SYMBOL(hyp_assign_phys); const char *msm_secure_vmid_to_string(int secure_vmid) { diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 0d6c1d62c732..5ac2a58899f4 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -375,6 +375,7 @@ int get_service_location(char *client_name, char *service_name, if (!pqw) { rc = -ENOMEM; pr_err("Allocation failed\n"); + kfree(pqcd); goto err; } pqw->notifier = locator_nb; diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index ebea5b7726e4..fa916ac5ade4 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -99,11 +99,12 @@ struct ind_req_resp { */ struct qmi_client_info { int instance_id; - int subsys_state; + enum pd_subsys_state subsys_state; struct work_struct svc_arrive; struct work_struct svc_exit; struct work_struct svc_rcv_msg; struct work_struct ind_ack; + struct work_struct qmi_handle_free; struct workqueue_struct *svc_event_wq; struct qmi_handle *clnt_handle; struct notifier_block notifier; @@ -123,6 +124,18 @@ static void root_service_clnt_recv_msg(struct work_struct *work); static void root_service_service_arrive(struct work_struct *work); static void root_service_exit_work(struct work_struct *work); +static void free_qmi_handle(struct work_struct *work) +{ + struct qmi_client_info *data = container_of(work, + struct qmi_client_info, qmi_handle_free); + + mutex_lock(&qmi_client_release_lock); + data->service_connected = false; + qmi_handle_destroy(data->clnt_handle); + data->clnt_handle = NULL; + mutex_unlock(&qmi_client_release_lock); +} + static struct service_notif_info *_find_service_info(const char *service_path) { struct service_notif_info *service_notif; @@ -426,11 +439,7 @@ static void root_service_service_exit(struct qmi_client_info *data, * Destroy client handle and try connecting when * service comes up again. */ - mutex_lock(&qmi_client_release_lock); - data->service_connected = false; - qmi_handle_destroy(data->clnt_handle); - data->clnt_handle = NULL; - mutex_unlock(&qmi_client_release_lock); + queue_work(data->svc_event_wq, &data->qmi_handle_free); } static void root_service_exit_work(struct work_struct *work) @@ -486,7 +495,7 @@ static int ssr_event_notify(struct notifier_block *this, info->subsys_state = ROOT_PD_SHUTDOWN; break; } - queue_work(info->svc_event_wq, &info->svc_exit); + root_service_service_exit(info, info->subsys_state); break; default: break; @@ -561,6 +570,7 @@ static void *add_service_notif(const char *service_path, int instance_id, INIT_WORK(&qmi_data->svc_exit, root_service_exit_work); INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg); INIT_WORK(&qmi_data->ind_ack, send_ind_ack); + INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle); *curr_state = service_notif->curr_state = SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01; diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 07610877f140..0c44d76bc7c7 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -1744,7 +1744,9 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, } } - pr_err("fd [%d] ion buf not found.\n", fd); + pr_err("no free entry to store ion handle of fd [%d].\n", fd); + /* decrement back the ref count */ + ion_free(spcom_dev->ion_client, ion_handle); return -EFAULT; } @@ -2241,7 +2243,7 @@ static ssize_t spcom_device_write(struct file *filp, } /** - * spcom_device_read() - handle channel file write() from user space. + * spcom_device_read() - handle channel file read() from user space. * * @filp: file pointer * @@ -2267,6 +2269,16 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, ch = filp->private_data; + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } + + if (!spcom_is_channel_open(ch)) { + pr_err("ch is not open, file [%s].\n", name); + return -EINVAL; + } + buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; @@ -2354,6 +2366,10 @@ static unsigned int spcom_device_poll(struct file *filp, done = (spcom_dev->link_state == GLINK_LINK_STATE_UP); break; case SPCOM_POLL_CH_CONNECT: + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name); if (wait) { reinit_completion(&ch->connect); diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index c6531de48f65..991bce363740 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1034,6 +1034,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev) d->desc.ops = &pil_ops_trusted; d->desc.proxy_timeout = PROXY_TIMEOUT_MS; + d->desc.clear_fw_region = true; rc = of_property_read_u32(pdev->dev.of_node, "qcom,proxy-timeout-ms", &proxy_timeout); diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 1ceded4db79f..f601e6646852 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -531,6 +531,13 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, u8 *payload; u32 ch_size, ch_cfg_size; + mutex_lock(&wpriv->glink_mutex); + if (wpriv->ch) { + dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n", + __func__); + ret = -EINVAL; + goto done; + } payload = (u8 *)pkt->payload; no_of_channels = pkt->no_of_channels; @@ -611,6 +618,7 @@ err_ch_mem: wpriv->no_of_channels = 0; done: + mutex_unlock(&wpriv->glink_mutex); return ret; } diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index 63bc3961de3b..e72663bd2138 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -224,6 +224,12 @@ static struct dentry *debugfs_poke; static struct dentry *debugfs_reg_dump; static unsigned int read_data; + +static bool swrm_is_msm_variant(int val) +{ + return (val == SWRM_VERSION_1_3); +} + static int swrm_debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -514,8 +520,17 @@ static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data, __func__, val, ret); goto err; } - if (cmd_id == 0xF) - wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10)); + if (cmd_id == 0xF) { + /* + * sleep for 10ms for MSM soundwire variant to allow broadcast + * command to complete. + */ + if (swrm_is_msm_variant(swrm->version)) + usleep_range(10000, 10100); + else + wait_for_completion_timeout(&swrm->broadcast, + (2 * HZ/10)); + } err: return ret; } @@ -1472,6 +1487,7 @@ static int swrm_probe(struct platform_device *pdev) mutex_unlock(&swrm->mlock); goto err_mstr_fail; } + swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION); /* Enumerate slave devices */ list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices, diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h index 8992318cdbd3..b7a3edac3e00 100755 --- a/drivers/soundwire/swr-wcd-ctrl.h +++ b/drivers/soundwire/swr-wcd-ctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,10 @@ #define SWR_MSTR_PORT_LEN 8 /* Number of master ports */ +#define SWRM_VERSION_1_0 0x01010000 +#define SWRM_VERSION_1_2 0x01030000 +#define SWRM_VERSION_1_3 0x01040000 + enum { SWR_MSTR_PAUSE, SWR_MSTR_RESUME, @@ -88,6 +92,7 @@ struct swr_mstr_ctrl { int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, void *data), void *swr_handle, int type); int irq; + int version; int num_enum_slaves; int slave_status; struct swr_mstr_port *mstr_port; diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h index c6923f301f9f..50c3ecfdd47d 100755 --- a/drivers/soundwire/swrm_registers.h +++ b/drivers/soundwire/swrm_registers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,7 @@ #define SWRM_BASE_ADDRESS 0x00 +#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS #define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004) #define SWRM_COMP_CFG_RMSK 0x3 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2 diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index a87cfd4ba17b..61a86d391599 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -127,37 +127,62 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) tclk_hz = clk_get_rate(orion_spi->clk); if (devdata->typ == ARMADA_SPI) { - unsigned int clk, spr, sppr, sppr2, err; - unsigned int best_spr, best_sppr, best_err; - - best_err = speed; - best_spr = 0; - best_sppr = 0; - - /* Iterate over the valid range looking for best fit */ - for (sppr = 0; sppr < 8; sppr++) { - sppr2 = 0x1 << sppr; - - spr = tclk_hz / sppr2; - spr = DIV_ROUND_UP(spr, speed); - if ((spr == 0) || (spr > 15)) - continue; - - clk = tclk_hz / (spr * sppr2); - err = speed - clk; - - if (err < best_err) { - best_spr = spr; - best_sppr = sppr; - best_err = err; - } - } + /* + * Given the core_clk (tclk_hz) and the target rate (speed) we + * determine the best values for SPR (in [0 .. 15]) and SPPR (in + * [0..7]) such that + * + * core_clk / (SPR * 2 ** SPPR) + * + * is as big as possible but not bigger than speed. + */ - if ((best_sppr == 0) && (best_spr == 0)) - return -EINVAL; + /* best integer divider: */ + unsigned divider = DIV_ROUND_UP(tclk_hz, speed); + unsigned spr, sppr; + + if (divider < 16) { + /* This is the easy case, divider is less than 16 */ + spr = divider; + sppr = 0; + + } else { + unsigned two_pow_sppr; + /* + * Find the highest bit set in divider. This and the + * three next bits define SPR (apart from rounding). + * SPPR is then the number of zero bits that must be + * appended: + */ + sppr = fls(divider) - 4; + + /* + * As SPR only has 4 bits, we have to round divider up + * to the next multiple of 2 ** sppr. + */ + two_pow_sppr = 1 << sppr; + divider = (divider + two_pow_sppr - 1) & -two_pow_sppr; + + /* + * recalculate sppr as rounding up divider might have + * increased it enough to change the position of the + * highest set bit. In this case the bit that now + * doesn't make it into SPR is 0, so there is no need to + * round again. + */ + sppr = fls(divider) - 4; + spr = divider >> sppr; + + /* + * Now do range checking. SPR is constructed to have a + * width of 4 bits, so this is fine for sure. So we + * still need to check for sppr to fit into 3 bits: + */ + if (sppr > 7) + return -EINVAL; + } - prescale = ((best_sppr & 0x6) << 5) | - ((best_sppr & 0x1) << 4) | best_spr; + prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr; } else { /* * the supported rates are: 4,6,8...30 diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 0f28c08fcb3c..77b551da5728 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -909,6 +909,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, if (err) { ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n", err); + goto out_free; } else { ssb_dbg("Using SPROM revision %d provided by platform\n", sprom->revision); diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index b132cff14f01..15ff4e149d75 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/smp.h> +#include <linux/sysrq.h> #include <linux/timer.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -401,7 +402,7 @@ static void fiq_debugger_work(struct work_struct *work) cmd += 6; while (*cmd == ' ') cmd++; - if (cmd != '\0') + if ((cmd != '\0') && sysrq_on()) kernel_restart(cmd); else kernel_restart(NULL); @@ -431,29 +432,39 @@ static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd) static void fiq_debugger_help(struct fiq_debugger_state *state) { fiq_debugger_printf(&state->output, - "FIQ Debugger commands:\n" - " pc PC status\n" - " regs Register dump\n" - " allregs Extended Register dump\n" - " bt Stack trace\n" - " reboot [<c>] Reboot with command <c>\n" - " reset [<c>] Hard reset with command <c>\n" - " irqs Interupt status\n" - " kmsg Kernel log\n" - " version Kernel version\n"); - fiq_debugger_printf(&state->output, - " sleep Allow sleep while in FIQ\n" - " nosleep Disable sleep while in FIQ\n" - " console Switch terminal to console\n" - " cpu Current CPU\n" - " cpu <number> Switch to CPU<number>\n"); + "FIQ Debugger commands:\n"); + if (sysrq_on()) { + fiq_debugger_printf(&state->output, + " pc PC status\n" + " regs Register dump\n" + " allregs Extended Register dump\n" + " bt Stack trace\n"); + fiq_debugger_printf(&state->output, + " reboot [<c>] Reboot with command <c>\n" + " reset [<c>] Hard reset with command <c>\n" + " irqs Interrupt status\n" + " kmsg Kernel log\n" + " version Kernel version\n"); + fiq_debugger_printf(&state->output, + " cpu Current CPU\n" + " cpu <number> Switch to CPU<number>\n" + " sysrq sysrq options\n" + " sysrq <param> Execute sysrq with <param>\n"); + } else { + fiq_debugger_printf(&state->output, + " reboot Reboot\n" + " reset Hard reset\n" + " irqs Interrupt status\n"); + } fiq_debugger_printf(&state->output, - " ps Process list\n" - " sysrq sysrq options\n" - " sysrq <param> Execute sysrq with <param>\n"); + " sleep Allow sleep while in FIQ\n" + " nosleep Disable sleep while in FIQ\n" + " console Switch terminal to console\n" + " ps Process list\n"); #ifdef CONFIG_KGDB - fiq_debugger_printf(&state->output, - " kgdb Enter kernel debugger\n"); + if (fiq_kgdb_enable) { + fiq_debugger_printf(&state->output, + " kgdb Enter kernel debugger\n"); #endif } @@ -485,18 +496,23 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { fiq_debugger_help(state); } else if (!strcmp(cmd, "pc")) { - fiq_debugger_dump_pc(&state->output, regs); + if (sysrq_on()) + fiq_debugger_dump_pc(&state->output, regs); } else if (!strcmp(cmd, "regs")) { - fiq_debugger_dump_regs(&state->output, regs); + if (sysrq_on()) + fiq_debugger_dump_regs(&state->output, regs); } else if (!strcmp(cmd, "allregs")) { - fiq_debugger_dump_allregs(&state->output, regs); + if (sysrq_on()) + fiq_debugger_dump_allregs(&state->output, regs); } else if (!strcmp(cmd, "bt")) { - fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp); + if (sysrq_on()) + fiq_debugger_dump_stacktrace(&state->output, regs, + 100, svc_sp); } else if (!strncmp(cmd, "reset", 5)) { cmd += 5; while (*cmd == ' ') cmd++; - if (*cmd) { + if (*cmd && sysrq_on()) { char tmp_cmd[32]; strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); machine_restart(tmp_cmd); @@ -506,9 +522,12 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, } else if (!strcmp(cmd, "irqs")) { fiq_debugger_dump_irqs(state); } else if (!strcmp(cmd, "kmsg")) { - fiq_debugger_dump_kernel_log(state); + if (sysrq_on()) + fiq_debugger_dump_kernel_log(state); } else if (!strcmp(cmd, "version")) { - fiq_debugger_printf(&state->output, "%s\n", linux_banner); + if (sysrq_on()) + fiq_debugger_printf(&state->output, "%s\n", + linux_banner); } else if (!strcmp(cmd, "sleep")) { state->no_sleep = false; fiq_debugger_printf(&state->output, "enabling sleep\n"); @@ -520,14 +539,17 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, fiq_debugger_uart_flush(state); state->console_enable = true; } else if (!strcmp(cmd, "cpu")) { - fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); - } else if (!strncmp(cmd, "cpu ", 4)) { + if (sysrq_on()) + fiq_debugger_printf(&state->output, "cpu %d\n", + state->current_cpu); + } else if (!strncmp(cmd, "cpu ", 4) && sysrq_on()) { unsigned long cpu = 0; if (kstrtoul(cmd + 4, 10, &cpu) == 0) fiq_debugger_switch_cpu(state, cpu); else fiq_debugger_printf(&state->output, "invalid cpu\n"); - fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); + fiq_debugger_printf(&state->output, "cpu %d\n", + state->current_cpu); } else { if (state->debug_busy) { fiq_debugger_printf(&state->output, diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 81bda878a7ec..b7fe42582e89 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -807,7 +807,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) struct ion_handle *handle = rb_entry(n, struct ion_handle, node); - seq_printf(s, "%16.16s: %16zx : %16d : %12p", + seq_printf(s, "%16.16s: %16zx : %16d : %12pK", handle->buffer->heap->name, handle->buffer->size, atomic_read(&handle->ref.refcount), diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index 03b2b8a38991..2ad4cc7a4785 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-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, 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 @@ -123,9 +123,11 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, gfp_t gfp_mask = low_order_gfp_flags; if (order) gfp_mask = high_order_gfp_flags; + page = alloc_pages(gfp_mask, order); - ion_pages_sync_for_device(dev, page, PAGE_SIZE << order, - DMA_BIDIRECTIONAL); + if (page) + ion_pages_sync_for_device(dev, page, PAGE_SIZE << order, + DMA_BIDIRECTIONAL); } if (!page) return 0; diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c index 5a536a00066f..b63472de761a 100644 --- a/drivers/staging/comedi/drivers/dt282x.c +++ b/drivers/staging/comedi/drivers/dt282x.c @@ -69,48 +69,49 @@ * Register map */ #define DT2821_ADCSR_REG 0x00 -#define DT2821_ADCSR_ADERR (1 << 15) -#define DT2821_ADCSR_ADCLK (1 << 9) -#define DT2821_ADCSR_MUXBUSY (1 << 8) -#define DT2821_ADCSR_ADDONE (1 << 7) -#define DT2821_ADCSR_IADDONE (1 << 6) +#define DT2821_ADCSR_ADERR BIT(15) +#define DT2821_ADCSR_ADCLK BIT(9) +#define DT2821_ADCSR_MUXBUSY BIT(8) +#define DT2821_ADCSR_ADDONE BIT(7) +#define DT2821_ADCSR_IADDONE BIT(6) #define DT2821_ADCSR_GS(x) (((x) & 0x3) << 4) #define DT2821_ADCSR_CHAN(x) (((x) & 0xf) << 0) #define DT2821_CHANCSR_REG 0x02 -#define DT2821_CHANCSR_LLE (1 << 15) -#define DT2821_CHANCSR_PRESLA(x) (((x) & 0xf) >> 8) +#define DT2821_CHANCSR_LLE BIT(15) +#define DT2821_CHANCSR_TO_PRESLA(x) (((x) >> 8) & 0xf) #define DT2821_CHANCSR_NUMB(x) ((((x) - 1) & 0xf) << 0) #define DT2821_ADDAT_REG 0x04 #define DT2821_DACSR_REG 0x06 -#define DT2821_DACSR_DAERR (1 << 15) +#define DT2821_DACSR_DAERR BIT(15) #define DT2821_DACSR_YSEL(x) ((x) << 9) -#define DT2821_DACSR_SSEL (1 << 8) -#define DT2821_DACSR_DACRDY (1 << 7) -#define DT2821_DACSR_IDARDY (1 << 6) -#define DT2821_DACSR_DACLK (1 << 5) -#define DT2821_DACSR_HBOE (1 << 1) -#define DT2821_DACSR_LBOE (1 << 0) +#define DT2821_DACSR_SSEL BIT(8) +#define DT2821_DACSR_DACRDY BIT(7) +#define DT2821_DACSR_IDARDY BIT(6) +#define DT2821_DACSR_DACLK BIT(5) +#define DT2821_DACSR_HBOE BIT(1) +#define DT2821_DACSR_LBOE BIT(0) #define DT2821_DADAT_REG 0x08 #define DT2821_DIODAT_REG 0x0a #define DT2821_SUPCSR_REG 0x0c -#define DT2821_SUPCSR_DMAD (1 << 15) -#define DT2821_SUPCSR_ERRINTEN (1 << 14) -#define DT2821_SUPCSR_CLRDMADNE (1 << 13) -#define DT2821_SUPCSR_DDMA (1 << 12) -#define DT2821_SUPCSR_DS_PIO (0 << 10) -#define DT2821_SUPCSR_DS_AD_CLK (1 << 10) -#define DT2821_SUPCSR_DS_DA_CLK (2 << 10) -#define DT2821_SUPCSR_DS_AD_TRIG (3 << 10) -#define DT2821_SUPCSR_BUFFB (1 << 9) -#define DT2821_SUPCSR_SCDN (1 << 8) -#define DT2821_SUPCSR_DACON (1 << 7) -#define DT2821_SUPCSR_ADCINIT (1 << 6) -#define DT2821_SUPCSR_DACINIT (1 << 5) -#define DT2821_SUPCSR_PRLD (1 << 4) -#define DT2821_SUPCSR_STRIG (1 << 3) -#define DT2821_SUPCSR_XTRIG (1 << 2) -#define DT2821_SUPCSR_XCLK (1 << 1) -#define DT2821_SUPCSR_BDINIT (1 << 0) +#define DT2821_SUPCSR_DMAD BIT(15) +#define DT2821_SUPCSR_ERRINTEN BIT(14) +#define DT2821_SUPCSR_CLRDMADNE BIT(13) +#define DT2821_SUPCSR_DDMA BIT(12) +#define DT2821_SUPCSR_DS(x) (((x) & 0x3) << 10) +#define DT2821_SUPCSR_DS_PIO DT2821_SUPCSR_DS(0) +#define DT2821_SUPCSR_DS_AD_CLK DT2821_SUPCSR_DS(1) +#define DT2821_SUPCSR_DS_DA_CLK DT2821_SUPCSR_DS(2) +#define DT2821_SUPCSR_DS_AD_TRIG DT2821_SUPCSR_DS(3) +#define DT2821_SUPCSR_BUFFB BIT(9) +#define DT2821_SUPCSR_SCDN BIT(8) +#define DT2821_SUPCSR_DACON BIT(7) +#define DT2821_SUPCSR_ADCINIT BIT(6) +#define DT2821_SUPCSR_DACINIT BIT(5) +#define DT2821_SUPCSR_PRLD BIT(4) +#define DT2821_SUPCSR_STRIG BIT(3) +#define DT2821_SUPCSR_XTRIG BIT(2) +#define DT2821_SUPCSR_XCLK BIT(1) +#define DT2821_SUPCSR_BDINIT BIT(0) #define DT2821_TMRCTR_REG 0x0e static const struct comedi_lrange range_dt282x_ai_lo_bipolar = { diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index 35ab4a9ef95d..c975f6e8be49 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -1929,7 +1929,7 @@ static int ni_ai_insn_read(struct comedi_device *dev, unsigned int *data) { struct ni_private *devpriv = dev->private; - unsigned int mask = (s->maxdata + 1) >> 1; + unsigned int mask = s->maxdata; int i, n; unsigned signbits; unsigned int d; @@ -1972,7 +1972,7 @@ static int ni_ai_insn_read(struct comedi_device *dev, return -ETIME; } d += signbits; - data[n] = d; + data[n] = d & 0xffff; } } else if (devpriv->is_6143) { for (n = 0; n < insn->n; n++) { @@ -2017,8 +2017,8 @@ static int ni_ai_insn_read(struct comedi_device *dev, data[n] = dl; } else { d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG); - d += signbits; /* subtle: needs to be short addition */ - data[n] = d; + d += signbits; + data[n] = d & 0xffff; } } } diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c index 5796ed2409d0..39bbbaaff07c 100644 --- a/drivers/staging/iio/adc/ad7606_core.c +++ b/drivers/staging/iio/adc/ad7606_core.c @@ -189,7 +189,7 @@ static ssize_t ad7606_store_oversampling_ratio(struct device *dev, mutex_lock(&indio_dev->mlock); gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1); gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1); - gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1); + gpio_set_value(st->pdata->gpio_os2, (ret >> 2) & 1); st->oversampling = lval; mutex_unlock(&indio_dev->mlock); diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c index 110b8c0b6cd7..0f2fe34e14c2 100644 --- a/drivers/staging/rtl8188eu/core/rtw_recv.c +++ b/drivers/staging/rtl8188eu/core/rtw_recv.c @@ -1405,6 +1405,9 @@ static int wlanhdr_to_ethhdr(struct recv_frame *precvframe) ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0))); } + if (!ptr) + return _FAIL; + memcpy(ptr, pattrib->dst, ETH_ALEN); memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN); diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c index 4ff530155187..04ac23cc47a8 100644 --- a/drivers/staging/rtl8712/rtl871x_recv.c +++ b/drivers/staging/rtl8712/rtl871x_recv.c @@ -641,11 +641,16 @@ sint r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe) /* append rx status for mp test packets */ ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + 2) - 24); + if (!ptr) + return _FAIL; memcpy(ptr, get_rxmem(precvframe), 24); ptr += 24; - } else + } else { ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0))); + if (!ptr) + return _FAIL; + } memcpy(ptr, pattrib->dst, ETH_ALEN); memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index bd810c109277..6ed80b05d674 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3436,7 +3436,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, if ((tpg->tpg_attrib.generate_node_acls == 0) && (tpg->tpg_attrib.demo_mode_discovery == 0) && - (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg, + (!target_tpg_has_node_acl(&tpg->tpg_se_tpg, cmd->conn->sess->sess_ops->InitiatorName))) { continue; } diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 23c95cd14167..68261b7dcefe 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -260,7 +260,6 @@ err_out: iscsi_release_param_list(tpg->param_list); tpg->param_list = NULL; } - kfree(tpg); return -ENOMEM; } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index dcd5ed26eb18..bb6a6c35324a 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -77,12 +77,16 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) &deve->read_bytes); se_lun = rcu_dereference(deve->se_lun); + + if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { + se_lun = NULL; + goto out_unlock; + } + se_cmd->se_lun = rcu_dereference(deve->se_lun); se_cmd->pr_res_key = deve->pr_res_key; se_cmd->orig_fe_lun = unpacked_lun; se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; - - percpu_ref_get(&se_lun->lun_ref); se_cmd->lun_ref_active = true; if ((se_cmd->data_direction == DMA_TO_DEVICE) && @@ -96,6 +100,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) goto ref_dev; } } +out_unlock: rcu_read_unlock(); if (!se_lun) { @@ -362,7 +367,15 @@ int core_enable_device_list_for_node( kfree(new); return -EINVAL; } - BUG_ON(orig->se_lun_acl != NULL); + if (orig->se_lun_acl != NULL) { + pr_warn_ratelimited("Detected existing explicit" + " se_lun_acl->se_lun_group reference for %s" + " mapped_lun: %llu, failing\n", + nacl->initiatorname, mapped_lun); + mutex_unlock(&nacl->lun_entry_mutex); + kfree(new); + return -EINVAL; + } rcu_assign_pointer(new->se_lun, lun); rcu_assign_pointer(new->se_lun_acl, lun_acl); @@ -818,6 +831,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) xcopy_lun = &dev->xcopy_lun; rcu_assign_pointer(xcopy_lun->lun_se_dev, dev); init_completion(&xcopy_lun->lun_ref_comp); + init_completion(&xcopy_lun->lun_shutdown_comp); INIT_LIST_HEAD(&xcopy_lun->lun_deve_list); INIT_LIST_HEAD(&xcopy_lun->lun_dev_link); mutex_init(&xcopy_lun->lun_tg_pt_md_mutex); diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index c220bb8dfa9d..2e27b1034ede 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -442,6 +442,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, int *post_ret) { struct se_device *dev = cmd->se_dev; + sense_reason_t ret = TCM_NO_SENSE; /* * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through @@ -449,9 +450,12 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, * sent to the backend driver. */ spin_lock_irq(&cmd->t_state_lock); - if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) { + if (cmd->transport_state & CMD_T_SENT) { cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST; *post_ret = 1; + + if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION) + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } spin_unlock_irq(&cmd->t_state_lock); @@ -461,7 +465,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, */ up(&dev->caw_sem); - return TCM_NO_SENSE; + return ret; } static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success, diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 5fb9dd7f08bb..2794c6ec5c3c 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -75,9 +75,21 @@ struct se_node_acl *core_tpg_get_initiator_node_acl( unsigned char *initiatorname) { struct se_node_acl *acl; - + /* + * Obtain se_node_acl->acl_kref using fabric driver provided + * initiatorname[] during node acl endpoint lookup driven by + * new se_session login. + * + * The reference is held until se_session shutdown -> release + * occurs via fabric driver invoked transport_deregister_session() + * or transport_free_session() code. + */ mutex_lock(&tpg->acl_node_mutex); acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); + if (acl) { + if (!kref_get_unless_zero(&acl->acl_kref)) + acl = NULL; + } mutex_unlock(&tpg->acl_node_mutex); return acl; @@ -232,6 +244,25 @@ static void target_add_node_acl(struct se_node_acl *acl) acl->initiatorname); } +bool target_tpg_has_node_acl(struct se_portal_group *tpg, + const char *initiatorname) +{ + struct se_node_acl *acl; + bool found = false; + + mutex_lock(&tpg->acl_node_mutex); + list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { + if (!strcmp(acl->initiatorname, initiatorname)) { + found = true; + break; + } + } + mutex_unlock(&tpg->acl_node_mutex); + + return found; +} +EXPORT_SYMBOL(target_tpg_has_node_acl); + struct se_node_acl *core_tpg_check_initiator_node_acl( struct se_portal_group *tpg, unsigned char *initiatorname) @@ -248,6 +279,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( acl = target_alloc_node_acl(tpg, initiatorname); if (!acl) return NULL; + /* + * When allocating a dynamically generated node_acl, go ahead + * and take the extra kref now before returning to the fabric + * driver caller. + * + * Note this reference will be released at session shutdown + * time within transport_free_session() code. + */ + kref_get(&acl->acl_kref); acl->dynamic_node_acl = 1; /* @@ -499,7 +539,7 @@ static void core_tpg_lun_ref_release(struct percpu_ref *ref) { struct se_lun *lun = container_of(ref, struct se_lun, lun_ref); - complete(&lun->lun_ref_comp); + complete(&lun->lun_shutdown_comp); } int core_tpg_register( @@ -626,6 +666,7 @@ struct se_lun *core_tpg_alloc_lun( lun->lun_link_magic = SE_LUN_LINK_MAGIC; atomic_set(&lun->lun_acl_count, 0); init_completion(&lun->lun_ref_comp); + init_completion(&lun->lun_shutdown_comp); INIT_LIST_HEAD(&lun->lun_deve_list); INIT_LIST_HEAD(&lun->lun_dev_link); atomic_set(&lun->lun_tg_pt_secondary_offline, 0); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2a67af4e2e13..df2059984e14 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -341,7 +341,6 @@ void __transport_register_session( &buf[0], PR_REG_ISID_LEN); se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]); } - kref_get(&se_nacl->acl_kref); spin_lock_irq(&se_nacl->nacl_sess_lock); /* @@ -424,14 +423,27 @@ static void target_complete_nacl(struct kref *kref) { struct se_node_acl *nacl = container_of(kref, struct se_node_acl, acl_kref); + struct se_portal_group *se_tpg = nacl->se_tpg; - complete(&nacl->acl_free_comp); + if (!nacl->dynamic_stop) { + complete(&nacl->acl_free_comp); + return; + } + + mutex_lock(&se_tpg->acl_node_mutex); + list_del(&nacl->acl_list); + mutex_unlock(&se_tpg->acl_node_mutex); + + core_tpg_wait_for_nacl_pr_ref(nacl); + core_free_device_list_for_node(nacl, se_tpg); + kfree(nacl); } void target_put_nacl(struct se_node_acl *nacl) { kref_put(&nacl->acl_kref, target_complete_nacl); } +EXPORT_SYMBOL(target_put_nacl); void transport_deregister_session_configfs(struct se_session *se_sess) { @@ -464,6 +476,42 @@ EXPORT_SYMBOL(transport_deregister_session_configfs); void transport_free_session(struct se_session *se_sess) { + struct se_node_acl *se_nacl = se_sess->se_node_acl; + + /* + * Drop the se_node_acl->nacl_kref obtained from within + * core_tpg_get_initiator_node_acl(). + */ + if (se_nacl) { + struct se_portal_group *se_tpg = se_nacl->se_tpg; + const struct target_core_fabric_ops *se_tfo = se_tpg->se_tpg_tfo; + unsigned long flags; + + se_sess->se_node_acl = NULL; + + /* + * Also determine if we need to drop the extra ->cmd_kref if + * it had been previously dynamically generated, and + * the endpoint is not caching dynamic ACLs. + */ + mutex_lock(&se_tpg->acl_node_mutex); + if (se_nacl->dynamic_node_acl && + !se_tfo->tpg_check_demo_mode_cache(se_tpg)) { + spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags); + if (list_empty(&se_nacl->acl_sess_list)) + se_nacl->dynamic_stop = true; + spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags); + + if (se_nacl->dynamic_stop) + list_del(&se_nacl->acl_list); + } + mutex_unlock(&se_tpg->acl_node_mutex); + + if (se_nacl->dynamic_stop) + target_put_nacl(se_nacl); + + target_put_nacl(se_nacl); + } if (se_sess->sess_cmd_map) { percpu_ida_destroy(&se_sess->sess_tag_pool); kvfree(se_sess->sess_cmd_map); @@ -475,16 +523,12 @@ EXPORT_SYMBOL(transport_free_session); void transport_deregister_session(struct se_session *se_sess) { struct se_portal_group *se_tpg = se_sess->se_tpg; - const struct target_core_fabric_ops *se_tfo; - struct se_node_acl *se_nacl; unsigned long flags; - bool comp_nacl = true, drop_nacl = false; if (!se_tpg) { transport_free_session(se_sess); return; } - se_tfo = se_tpg->se_tpg_tfo; spin_lock_irqsave(&se_tpg->session_lock, flags); list_del(&se_sess->sess_list); @@ -492,37 +536,16 @@ void transport_deregister_session(struct se_session *se_sess) se_sess->fabric_sess_ptr = NULL; spin_unlock_irqrestore(&se_tpg->session_lock, flags); - /* - * Determine if we need to do extra work for this initiator node's - * struct se_node_acl if it had been previously dynamically generated. - */ - se_nacl = se_sess->se_node_acl; - - mutex_lock(&se_tpg->acl_node_mutex); - if (se_nacl && se_nacl->dynamic_node_acl) { - if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) { - list_del(&se_nacl->acl_list); - se_tpg->num_node_acls--; - drop_nacl = true; - } - } - mutex_unlock(&se_tpg->acl_node_mutex); - - if (drop_nacl) { - core_tpg_wait_for_nacl_pr_ref(se_nacl); - core_free_device_list_for_node(se_nacl, se_tpg); - kfree(se_nacl); - comp_nacl = false; - } pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", se_tpg->se_tpg_tfo->get_fabric_name()); /* * If last kref is dropping now for an explicit NodeACL, awake sleeping * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group - * removal context. + * removal context from within transport_free_session() code. + * + * For dynamic ACL, target_put_nacl() uses target_complete_nacl() + * to release all remaining generate_node_acl=1 created ACL resources. */ - if (se_nacl && comp_nacl) - target_put_nacl(se_nacl); transport_free_session(se_sess); } @@ -2657,10 +2680,39 @@ void target_wait_for_sess_cmds(struct se_session *se_sess) } EXPORT_SYMBOL(target_wait_for_sess_cmds); +static void target_lun_confirm(struct percpu_ref *ref) +{ + struct se_lun *lun = container_of(ref, struct se_lun, lun_ref); + + complete(&lun->lun_ref_comp); +} + void transport_clear_lun_ref(struct se_lun *lun) { - percpu_ref_kill(&lun->lun_ref); + /* + * Mark the percpu-ref as DEAD, switch to atomic_t mode, drop + * the initial reference and schedule confirm kill to be + * executed after one full RCU grace period has completed. + */ + percpu_ref_kill_and_confirm(&lun->lun_ref, target_lun_confirm); + /* + * The first completion waits for percpu_ref_switch_to_atomic_rcu() + * to call target_lun_confirm after lun->lun_ref has been marked + * as __PERCPU_REF_DEAD on all CPUs, and switches to atomic_t + * mode so that percpu_ref_tryget_live() lookup of lun->lun_ref + * fails for all new incoming I/O. + */ wait_for_completion(&lun->lun_ref_comp); + /* + * The second completion waits for percpu_ref_put_many() to + * invoke ->release() after lun->lun_ref has switched to + * atomic_t mode, and lun->lun_ref.count has reached zero. + * + * At this point all target-core lun->lun_ref references have + * been dropped via transport_lun_remove_cmd(), and it's safe + * to proceed with the remaining LUN shutdown. + */ + wait_for_completion(&lun->lun_shutdown_comp); } static bool @@ -3058,7 +3110,6 @@ static void target_tmr_work(struct work_struct *work) spin_unlock_irqrestore(&cmd->t_state_lock, flags); goto check_stop; } - cmd->t_state = TRANSPORT_ISTATE_PROCESSING; spin_unlock_irqrestore(&cmd->t_state_lock, flags); cmd->se_tfo->queue_tm_rsp(cmd); @@ -3071,11 +3122,25 @@ int transport_generic_handle_tmr( struct se_cmd *cmd) { unsigned long flags; + bool aborted = false; spin_lock_irqsave(&cmd->t_state_lock, flags); - cmd->transport_state |= CMD_T_ACTIVE; + if (cmd->transport_state & CMD_T_ABORTED) { + aborted = true; + } else { + cmd->t_state = TRANSPORT_ISTATE_PROCESSING; + cmd->transport_state |= CMD_T_ACTIVE; + } spin_unlock_irqrestore(&cmd->t_state_lock, flags); + if (aborted) { + pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d" + "ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function, + cmd->se_tmr_req->ref_task_tag, cmd->tag); + transport_cmd_check_stop_to_fabric(cmd); + return 0; + } + INIT_WORK(&cmd->work, target_tmr_work); queue_work(cmd->se_dev->tmr_wq, &cmd->work); return 0; diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 5e6d6cb348fc..a7d30e894cab 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -645,8 +645,6 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data) target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION); cmd->se_cmd = NULL; - kmem_cache_free(tcmu_cmd_cache, cmd); - return 0; } diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 153a6f255b6d..6415e9b09a52 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -836,7 +836,7 @@ out: " CHECK_CONDITION -> sending response\n", rc); ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; } - target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION); + target_complete_cmd(ec_cmd, ec_cmd->scsi_status); } sense_reason_t target_do_xcopy(struct se_cmd *se_cmd) diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c index cff5b6e3fc63..efe80c24d270 100644 --- a/drivers/thermal/msm_lmh_dcvs.c +++ b/drivers/thermal/msm_lmh_dcvs.c @@ -114,9 +114,9 @@ static void msm_lmh_dcvs_get_max_freq(uint32_t cpu, uint32_t *max_freq) static uint32_t msm_lmh_mitigation_notify(struct msm_lmh_dcvs_hw *hw) { - uint32_t max_limit = 0, val = 0; + uint32_t val = 0; struct device *cpu_dev = NULL; - unsigned long freq_val; + unsigned long freq_val, max_limit = 0; struct dev_pm_opp *opp_entry; val = readl_relaxed(hw->osm_hw_reg); diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 06fd2ed9ef9d..705b0cafedbb 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -98,7 +98,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) int temperature; int ret; - ret = tz->ops->get_trip_temp(tz, 0, &temperature); + ret = tz->ops->get_crit_temp(tz, &temperature); if (ret) return ret; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 644ddb841d9f..6d1e2f746ab4 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -114,7 +114,7 @@ #define DEFAULT_TX_BUF_COUNT 3 struct n_hdlc_buf { - struct n_hdlc_buf *link; + struct list_head list_item; int count; char buf[1]; }; @@ -122,8 +122,7 @@ struct n_hdlc_buf { #define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe) struct n_hdlc_buf_list { - struct n_hdlc_buf *head; - struct n_hdlc_buf *tail; + struct list_head list; int count; spinlock_t spinlock; }; @@ -136,7 +135,6 @@ struct n_hdlc_buf_list { * @backup_tty - TTY to use if tty gets closed * @tbusy - reentrancy flag for tx wakeup code * @woke_up - FIXME: describe this field - * @tbuf - currently transmitting tx buffer * @tx_buf_list - list of pending transmit frame buffers * @rx_buf_list - list of received frame buffers * @tx_free_buf_list - list unused transmit frame buffers @@ -149,7 +147,6 @@ struct n_hdlc { struct tty_struct *backup_tty; int tbusy; int woke_up; - struct n_hdlc_buf *tbuf; struct n_hdlc_buf_list tx_buf_list; struct n_hdlc_buf_list rx_buf_list; struct n_hdlc_buf_list tx_free_buf_list; @@ -159,7 +156,8 @@ struct n_hdlc { /* * HDLC buffer list manipulation functions */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list); +static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, + struct n_hdlc_buf *buf); static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, struct n_hdlc_buf *buf); static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); @@ -209,16 +207,9 @@ static void flush_tx_queue(struct tty_struct *tty) { struct n_hdlc *n_hdlc = tty2n_hdlc(tty); struct n_hdlc_buf *buf; - unsigned long flags; while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); - spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); - if (n_hdlc->tbuf) { - n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf); - n_hdlc->tbuf = NULL; - } - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); } static struct tty_ldisc_ops n_hdlc_ldisc = { @@ -284,7 +275,6 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc) } else break; } - kfree(n_hdlc->tbuf); kfree(n_hdlc); } /* end of n_hdlc_release() */ @@ -403,13 +393,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) n_hdlc->woke_up = 0; spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); - /* get current transmit buffer or get new transmit */ - /* buffer from list of pending transmit buffers */ - - tbuf = n_hdlc->tbuf; - if (!tbuf) - tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); - + tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); while (tbuf) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)sending frame %p, count=%d\n", @@ -421,7 +405,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) /* rollback was possible and has been done */ if (actual == -ERESTARTSYS) { - n_hdlc->tbuf = tbuf; + n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); break; } /* if transmit error, throw frame away by */ @@ -436,10 +420,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) /* free current transmit buffer */ n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); - - /* this tx buffer is done */ - n_hdlc->tbuf = NULL; - + /* wait up sleeping writers */ wake_up_interruptible(&tty->write_wait); @@ -449,10 +430,12 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)frame %p pending\n", __FILE__,__LINE__,tbuf); - - /* buffer not accepted by driver */ - /* set this buffer as pending buffer */ - n_hdlc->tbuf = tbuf; + + /* + * the buffer was not accepted by driver, + * return it back into tx queue + */ + n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); break; } } @@ -750,7 +733,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, int error = 0; int count; unsigned long flags; - + struct n_hdlc_buf *buf = NULL; + if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", __FILE__,__LINE__,cmd); @@ -764,8 +748,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, /* report count of read data available */ /* in next available frame (if any) */ spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); - if (n_hdlc->rx_buf_list.head) - count = n_hdlc->rx_buf_list.head->count; + buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list, + struct n_hdlc_buf, list_item); + if (buf) + count = buf->count; else count = 0; spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); @@ -777,8 +763,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, count = tty_chars_in_buffer(tty); /* add size of next output frame in queue */ spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); - if (n_hdlc->tx_buf_list.head) - count += n_hdlc->tx_buf_list.head->count; + buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list, + struct n_hdlc_buf, list_item); + if (buf) + count += buf->count; spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); error = put_user(count, (int __user *)arg); break; @@ -826,14 +814,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_wait(filp, &tty->write_wait, wait); /* set bits for operations that won't block */ - if (n_hdlc->rx_buf_list.head) + if (!list_empty(&n_hdlc->rx_buf_list.list)) mask |= POLLIN | POLLRDNORM; /* readable */ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(filp)) mask |= POLLHUP; if (!tty_is_writelocked(tty) && - n_hdlc->tx_free_buf_list.head) + !list_empty(&n_hdlc->tx_free_buf_list.list)) mask |= POLLOUT | POLLWRNORM; /* writable */ } return mask; @@ -853,11 +841,16 @@ static struct n_hdlc *n_hdlc_alloc(void) if (!n_hdlc) return NULL; - n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); - + spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock); + spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); + spin_lock_init(&n_hdlc->rx_buf_list.spinlock); + spin_lock_init(&n_hdlc->tx_buf_list.spinlock); + + INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list); + INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list); + INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list); + INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list); + /* allocate free rx buffer list */ for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL); @@ -885,63 +878,65 @@ static struct n_hdlc *n_hdlc_alloc(void) } /* end of n_hdlc_alloc() */ /** - * n_hdlc_buf_list_init - initialize specified HDLC buffer list - * @list - pointer to buffer list + * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list + * @buf_list - pointer to the buffer list + * @buf - pointer to the buffer */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list) +static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, + struct n_hdlc_buf *buf) { - memset(list, 0, sizeof(*list)); - spin_lock_init(&list->spinlock); -} /* end of n_hdlc_buf_list_init() */ + unsigned long flags; + + spin_lock_irqsave(&buf_list->spinlock, flags); + + list_add(&buf->list_item, &buf_list->list); + buf_list->count++; + + spin_unlock_irqrestore(&buf_list->spinlock, flags); +} /** * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list - * @list - pointer to buffer list + * @buf_list - pointer to buffer list * @buf - pointer to buffer */ -static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, +static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list, struct n_hdlc_buf *buf) { unsigned long flags; - spin_lock_irqsave(&list->spinlock,flags); - - buf->link=NULL; - if (list->tail) - list->tail->link = buf; - else - list->head = buf; - list->tail = buf; - (list->count)++; - - spin_unlock_irqrestore(&list->spinlock,flags); - + + spin_lock_irqsave(&buf_list->spinlock, flags); + + list_add_tail(&buf->list_item, &buf_list->list); + buf_list->count++; + + spin_unlock_irqrestore(&buf_list->spinlock, flags); } /* end of n_hdlc_buf_put() */ /** * n_hdlc_buf_get - remove and return an HDLC buffer from list - * @list - pointer to HDLC buffer list + * @buf_list - pointer to HDLC buffer list * * Remove and return an HDLC buffer from the head of the specified HDLC buffer * list. * Returns a pointer to HDLC buffer if available, otherwise %NULL. */ -static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list) +static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) { unsigned long flags; struct n_hdlc_buf *buf; - spin_lock_irqsave(&list->spinlock,flags); - - buf = list->head; + + spin_lock_irqsave(&buf_list->spinlock, flags); + + buf = list_first_entry_or_null(&buf_list->list, + struct n_hdlc_buf, list_item); if (buf) { - list->head = buf->link; - (list->count)--; + list_del(&buf->list_item); + buf_list->count--; } - if (!list->head) - list->tail = NULL; - - spin_unlock_irqrestore(&list->spinlock,flags); + + spin_unlock_irqrestore(&buf_list->spinlock, flags); return buf; - } /* end of n_hdlc_buf_get() */ static char hdlc_banner[] __initdata = diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 029de3f99752..5b24ffd93649 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -2880,6 +2880,8 @@ enum pci_board_num_t { pbn_b0_4_1152000_200, pbn_b0_8_1152000_200, + pbn_b0_4_1250000, + pbn_b0_2_1843200, pbn_b0_4_1843200, @@ -3113,6 +3115,13 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 0x200, }, + [pbn_b0_4_1250000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1250000, + .uart_offset = 8, + }, + [pbn_b0_2_1843200] = { .flags = FL_BASE0, .num_ports = 2, @@ -5778,6 +5787,10 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 }, + /* MKS Tenta SCOM-080x serial cards */ + { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 }, + { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index e0b89b961e1b..a0f911641b04 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -470,6 +470,14 @@ static void atmel_stop_tx(struct uart_port *port) /* disable PDC transmit */ atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS); } + + /* + * Disable the transmitter. + * This is mandatory when DMA is used, otherwise the DMA buffer + * is fully transmitted. + */ + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS); + /* Disable interrupts */ atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); @@ -502,6 +510,9 @@ static void atmel_start_tx(struct uart_port *port) /* Enable interrupts */ atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask); + + /* re-enable the transmitter */ + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN); } /* diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 8f68acd1d95d..db329230c7ca 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1834,6 +1834,7 @@ static const struct of_device_id msm_match_table[] = { { .compatible = "qcom,msm-uartdm" }, {} }; +MODULE_DEVICE_TABLE(of, msm_match_table); #ifdef CONFIG_PM_SLEEP static int msm_serial_suspend(struct device *dev) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index f0d5c96ac2e0..830ef92ffe80 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -3,7 +3,7 @@ * MSM 7k High speed uart driver * * Copyright (c) 2008 Google Inc. - * Copyright (c) 2007-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved. * Modified: Nick Pelly <npelly@google.com> * * All source code in this file is licensed under the following license @@ -1436,13 +1436,13 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx", &tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count); sps_pipe_handle = tx->cons.pipe_handle; - /* Queue transfer request to SPS */ - ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count, - msm_uport, flags); /* Set 1 second timeout */ mod_timer(&tx->tx_timeout_timer, jiffies + msecs_to_jiffies(MSEC_PER_SEC)); + /* Queue transfer request to SPS */ + ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count, + msm_uport, flags); MSM_HS_DBG("%s:Enqueue Tx Cmd, ret %d\n", __func__, ret); } @@ -3155,6 +3155,11 @@ static void msm_hs_pm_suspend(struct device *dev) mutex_lock(&msm_uport->mtx); client_count = atomic_read(&msm_uport->client_count); + msm_uport->pm_state = MSM_HS_PM_SUSPENDED; + msm_hs_resource_off(msm_uport); + obs_manage_irq(msm_uport, false); + msm_hs_clk_bus_unvote(msm_uport); + /* For OBS, don't use wakeup interrupt, set gpio to suspended state */ if (msm_uport->obs) { ret = pinctrl_select_state(msm_uport->pinctrl, @@ -3164,10 +3169,6 @@ static void msm_hs_pm_suspend(struct device *dev) __func__); } - msm_uport->pm_state = MSM_HS_PM_SUSPENDED; - msm_hs_resource_off(msm_uport); - obs_manage_irq(msm_uport, false); - msm_hs_clk_bus_unvote(msm_uport); if (!atomic_read(&msm_uport->client_req_state)) enable_wakeup_interrupt(msm_uport); LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, @@ -3198,6 +3199,16 @@ static int msm_hs_pm_resume(struct device *dev) goto exit_pm_resume; if (!atomic_read(&msm_uport->client_req_state)) disable_wakeup_interrupt(msm_uport); + + /* For OBS, don't use wakeup interrupt, set gpio to active state */ + if (msm_uport->obs) { + ret = pinctrl_select_state(msm_uport->pinctrl, + msm_uport->gpio_state_active); + if (ret) + MSM_HS_ERR("%s():Error selecting active state", + __func__); + } + ret = msm_hs_clk_bus_vote(msm_uport); if (ret) { MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret); @@ -3208,15 +3219,6 @@ static int msm_hs_pm_resume(struct device *dev) msm_uport->pm_state = MSM_HS_PM_ACTIVE; msm_hs_resource_on(msm_uport); - /* For OBS, don't use wakeup interrupt, set gpio to active state */ - if (msm_uport->obs) { - ret = pinctrl_select_state(msm_uport->pinctrl, - msm_uport->gpio_state_active); - if (ret) - MSM_HS_ERR("%s():Error selecting active state", - __func__); - } - LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, "%s:PM State:Active client_count %d\n", __func__, client_count); exit_pm_resume: diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index ab8308ff7e69..cad76b1cf672 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1030,8 +1030,10 @@ static int s3c64xx_serial_startup(struct uart_port *port) if (ourport->dma) { ret = s3c24xx_serial_request_dma(ourport); if (ret < 0) { - dev_warn(port->dev, "DMA request failed\n"); - return ret; + dev_warn(port->dev, + "DMA request failed, DMA will not be used\n"); + devm_kfree(port->dev, ourport->dma); + ourport->dma = NULL; } } diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index edb5305b9d4d..7d5ee8a13ac6 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1230,7 +1230,7 @@ static int sc16is7xx_probe(struct device *dev, /* Setup interrupt */ ret = devm_request_irq(dev, irq, sc16is7xx_irq, - IRQF_ONESHOT | flags, dev_name(dev), s); + flags, dev_name(dev), s); if (!ret) return 0; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index e5139402e7f8..1ca9cea2eaf8 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -55,10 +55,11 @@ static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; -static bool sysrq_on(void) +bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; } +EXPORT_SYMBOL(sysrq_on); /* * A value of 1 means 'all', other nonzero values are an op mask: @@ -945,8 +946,8 @@ static const struct input_device_id sysrq_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { BIT_MASK(KEY_LEFTALT) }, + .evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) }, }, { }, }; diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 41987a55a538..988c564b61a8 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -982,7 +982,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev) KBD_LED_TRIGGER((_led_bit) + 8, _name) static struct kbd_led_trigger kbd_led_triggers[] = { - KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), + KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"), KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"), KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"), KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"), diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 5a048b7b92e8..2949289bb3c5 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -244,7 +244,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, - .flags = CI_HDRC_SET_NON_ZERO_TTHA, }; int ret; const struct of_device_id *of_id; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 4d77745f439f..96849e2e7435 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1708,6 +1708,7 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */ .driver_info = QUIRK_CONTROL_LINE_STATE, }, { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */ + { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */ { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ }, /* Motorola H24 HSPA module: */ diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index ff44cfa26af8..ac30a051ad71 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -205,6 +205,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (ifp->desc.bNumEndpoints >= num_ep) goto skip_to_next_endpoint_or_interface_descriptor; + /* Check for duplicate endpoint addresses */ + for (i = 0; i < ifp->desc.bNumEndpoints; ++i) { + if (ifp->endpoint[i].desc.bEndpointAddress == + d->bEndpointAddress) { + dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n", + cfgno, inum, asnum, d->bEndpointAddress); + goto skip_to_next_endpoint_or_interface_descriptor; + } + } + endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; ++ifp->desc.bNumEndpoints; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 29242ffe8dca..f84ef04284f5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -106,6 +106,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); static void hub_release(struct kref *kref); static int usb_reset_and_verify_device(struct usb_device *udev); +static int hub_port_disable(struct usb_hub *hub, int port1, int set_state); static inline char *portspeed(struct usb_hub *hub, int portstatus) { @@ -894,88 +895,6 @@ static int hub_set_port_link_state(struct usb_hub *hub, int port1, } /* - * If USB 3.0 ports are placed into the Disabled state, they will no longer - * detect any device connects or disconnects. This is generally not what the - * USB core wants, since it expects a disabled port to produce a port status - * change event when a new device connects. - * - * Instead, set the link state to Disabled, wait for the link to settle into - * that state, clear any change bits, and then put the port into the RxDetect - * state. - */ -static int hub_usb3_port_disable(struct usb_hub *hub, int port1) -{ - int ret; - int total_time; - u16 portchange, portstatus; - - if (!hub_is_superspeed(hub->hdev)) - return -EINVAL; - - ret = hub_port_status(hub, port1, &portstatus, &portchange); - if (ret < 0) - return ret; - - /* - * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI - * Controller [1022:7814] will have spurious result making the following - * usb 3.0 device hotplugging route to the 2.0 root hub and recognized - * as high-speed device if we set the usb 3.0 port link state to - * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we - * check the state here to avoid the bug. - */ - if ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_RX_DETECT) { - dev_dbg(&hub->ports[port1 - 1]->dev, - "Not disabling port; link state is RxDetect\n"); - return ret; - } - - ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED); - if (ret) - return ret; - - /* Wait for the link to enter the disabled state. */ - for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { - ret = hub_port_status(hub, port1, &portstatus, &portchange); - if (ret < 0) - return ret; - - if ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_DISABLED) - break; - if (total_time >= HUB_DEBOUNCE_TIMEOUT) - break; - msleep(HUB_DEBOUNCE_STEP); - } - if (total_time >= HUB_DEBOUNCE_TIMEOUT) - dev_warn(&hub->ports[port1 - 1]->dev, - "Could not disable after %d ms\n", total_time); - - return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT); -} - -static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) -{ - struct usb_port *port_dev = hub->ports[port1 - 1]; - struct usb_device *hdev = hub->hdev; - int ret = 0; - - if (port_dev->child && set_state) - usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED); - if (!hub->error) { - if (hub_is_superspeed(hub->hdev)) - ret = hub_usb3_port_disable(hub, port1); - else - ret = usb_clear_port_feature(hdev, port1, - USB_PORT_FEAT_ENABLE); - } - if (ret && ret != -ENODEV) - dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret); - return ret; -} - -/* * Disable a port and mark a logical connect-change event, so that some * time later hub_wq will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. @@ -4086,6 +4005,26 @@ void usb_unlocked_enable_lpm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); +/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */ +static void hub_usb3_port_prepare_disable(struct usb_hub *hub, + struct usb_port *port_dev) +{ + struct usb_device *udev = port_dev->child; + int ret; + + if (udev && udev->port_is_suspended && udev->do_remote_wakeup) { + ret = hub_set_port_link_state(hub, port_dev->portnum, + USB_SS_PORT_LS_U0); + if (!ret) { + msleep(USB_RESUME_TIMEOUT); + ret = usb_disable_remote_wakeup(udev); + } + if (ret) + dev_warn(&udev->dev, + "Port disable: can't disable remote wake\n"); + udev->do_remote_wakeup = 0; + } +} #else /* CONFIG_PM */ @@ -4093,6 +4032,9 @@ EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); #define hub_resume NULL #define hub_reset_resume NULL +static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub, + struct usb_port *port_dev) { } + int usb_disable_lpm(struct usb_device *udev) { return 0; @@ -4128,6 +4070,34 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, #endif /* CONFIG_PM */ +/* + * USB-3 does not have a similar link state as USB-2 that will avoid negotiating + * a connection with a plugged-in cable but will signal the host when the cable + * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices + */ +static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) +{ + struct usb_port *port_dev = hub->ports[port1 - 1]; + struct usb_device *hdev = hub->hdev; + int ret = 0; + + if (!hub->error) { + if (hub_is_superspeed(hub->hdev)) { + hub_usb3_port_prepare_disable(hub, port_dev); + ret = hub_set_port_link_state(hub, port_dev->portnum, + USB_SS_PORT_LS_U3); + } else { + ret = usb_clear_port_feature(hdev, port1, + USB_PORT_FEAT_ENABLE); + } + } + if (port_dev->child && set_state) + usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED); + if (ret && ret != -ENODEV) + dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret); + return ret; +} + /* USB 2.0 spec, 7.1.7.3 / fig 7-29: * diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index d2e50a27140c..24f9f98968a5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -37,6 +37,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* CBM - Flash disk */ { USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME }, + /* WORLDE easy key (easykey.25) MIDI controller */ + { USB_DEVICE(0x0218, 0x0401), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* HP 5300/5370C scanner */ { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 9de5e06430e1..f6e2bea7b9aa 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -44,7 +44,7 @@ #define DWC3_XHCI_RESOURCES_NUM 2 #define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ -#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE) +#define DWC3_EVENT_BUFFERS_SIZE 4096 #define DWC3_EVENT_TYPE_MASK 0xfe #define DWC3_EVENT_TYPE_DEV 0 diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 3d731d1b5c60..d2c0c1a8d979 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -37,6 +37,7 @@ #define PCI_DEVICE_ID_INTEL_BXT 0x0aaa #define PCI_DEVICE_ID_INTEL_APL 0x5aaa #define PCI_DEVICE_ID_INTEL_KBP 0xa2b0 +#define PCI_DEVICE_ID_INTEL_GLK 0x31aa static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; @@ -216,6 +217,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, { } /* Terminating Entry */ }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 9cd87513619c..c2a6fdbfcfee 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -62,20 +62,13 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) } } -static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, - u32 len, u32 type, bool chain) +static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum, + dma_addr_t buf_dma, u32 len, u32 type, bool chain) { - struct dwc3_gadget_ep_cmd_params params; struct dwc3_trb *trb; struct dwc3_ep *dep; - int ret; - dep = dwc->eps[epnum]; - if (dep->flags & DWC3_EP_BUSY) { - dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); - return 0; - } trb = &dwc->ep0_trb[dep->free_slot]; @@ -96,15 +89,25 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, trb->ctrl |= (DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_LST); - if (chain) + trace_dwc3_prepare_trb(dep, trb); +} + +static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_ep *dep; + int ret; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_BUSY) { + dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); return 0; + } memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); - trace_dwc3_prepare_trb(dep, trb); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { @@ -333,9 +336,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) { int ret; - ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, + dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8, DWC3_TRBCTL_CONTROL_SETUP, false); - + ret = dwc3_ep0_start_trans(dwc, 0); if (WARN_ON_ONCE(ret < 0)) dbg_event(dwc->eps[0]->number, "EOUTSTART", ret); } @@ -923,9 +926,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dwc->ep0_next_event = DWC3_EP0_COMPLETE; - ret = dwc3_ep0_start_trans(dwc, epnum, - dwc->ctrl_req_addr, 0, - DWC3_TRBCTL_CONTROL_DATA, false); + dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr, + 0, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, epnum); if (WARN_ON_ONCE(ret < 0)) dbg_event(epnum, "ECTRL_DATA", ret); } @@ -1010,9 +1013,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, req->direction = !!dep->number; if (req->request.length == 0) { - ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, dep->number); } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && (dep->number == 0)) { u32 transfer_size = 0; @@ -1030,7 +1034,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, if (req->request.length > DWC3_EP0_BOUNCE_SIZE) { transfer_size = ALIGN(req->request.length - maxpacket, maxpacket); - ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma, transfer_size, DWC3_TRBCTL_CONTROL_DATA, @@ -1042,9 +1046,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, dwc->ep0_bounced = true; - ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, dwc->ep0_bounce_addr, transfer_size, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, dep->number); } else { ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); @@ -1053,9 +1058,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, return; } - ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, + dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma, req->request.length, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, dep->number); } dbg_queue(dep->number, &req->request, ret); @@ -1069,8 +1075,9 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 : DWC3_TRBCTL_CONTROL_STATUS2; - return dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, dwc->ctrl_req_addr, 0, type, false); + return dwc3_ep0_start_trans(dwc, dep->number); } static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7a279db521ca..e1284b6cc2ef 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -285,11 +285,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, if (req->request.status == -EINPROGRESS) req->request.status = status; - if (dwc->ep0_bounced && dep->number == 0) + if (dwc->ep0_bounced && dep->number <= 1) dwc->ep0_bounced = false; - else - usb_gadget_unmap_request(&dwc->gadget, &req->request, - req->direction); + + usb_gadget_unmap_request(&dwc->gadget, &req->request, + req->direction); dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, @@ -388,8 +388,10 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, } return -ETIMEDOUT; } - - udelay(1); + if ((cmd & DWC3_DEPCMD_SETTRANSFRESOURCE)) + udelay(20); + else + udelay(1); } while (1); } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index a21962c8f513..99ff6df063a7 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -28,23 +28,23 @@ struct dwc3; #define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) /* DEPCFG parameter 1 */ -#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0) #define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) #define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) #define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) #define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) #define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) -#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) #define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) -#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) #define DWC3_DEPCFG_BULK_BASED (1 << 30) #define DWC3_DEPCFG_FIFO_BASED (1 << 31) /* DEPCFG parameter 0 */ -#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) -#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) -#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) -#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22) #define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) /* This applies for core versions earlier than 1.94a */ #define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9d795489c285..a844ea4d06db 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -160,11 +160,16 @@ int config_ep_by_speed(struct usb_gadget *g, ep_found: /* commit results */ - _ep->maxpacket = usb_endpoint_maxp(chosen_desc); + _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff; _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; - _ep->mult = 0; + _ep->mult = 1; + + if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) || + usb_endpoint_xfer_int(_ep->desc))) + _ep->mult = ((usb_endpoint_maxp(_ep->desc) & 0x1800) >> 11) + 1; + if (!want_comp_desc) return 0; @@ -181,7 +186,7 @@ ep_found: switch (usb_endpoint_type(_ep->desc)) { case USB_ENDPOINT_XFER_ISOC: /* mult: bits 1:0 of bmAttributes */ - _ep->mult = comp_desc->bmAttributes & 0x3; + _ep->mult = (comp_desc->bmAttributes & 0x3) + 1; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: _ep->maxburst = comp_desc->bMaxBurst + 1; @@ -1750,9 +1755,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) 1); break; - /* function drivers must handle get/set altsetting; if there's - * no get() method, we know only altsetting zero works. - */ + /* function drivers must handle get/set altsetting */ case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; @@ -1761,7 +1764,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[intf]; if (!f) break; - if (w_value && !f->set_alt) + + /* + * If there's no get_alt() method, we know only altsetting zero + * works. There is no need to check if set_alt() is not NULL + * as we check this in usb_add_function(). + */ + if (w_value && !f->get_alt) break; value = f->set_alt(f, w_index, w_value); if (value == USB_GADGET_DELAYED_STATUS) { diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 7a64b24b8bf6..a42550950953 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -60,7 +60,7 @@ usb_f_cdev-y := f_cdev.o obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o usb_f_qdss-y := f_qdss.o u_qdss.o obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o -usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o +usb_f_qcrndis-y := f_qc_rndis.o rndis.o u_data_ipa.o obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index db7903d19c43..a2a9185a0143 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -989,6 +989,7 @@ static ssize_t audio_source_pcm_show(struct device *dev, struct device *create_function_device(char *name); +#define AUDIO_SOURCE_DEV_NAME_LENGTH 20 static struct usb_function_instance *audio_source_alloc_inst(void) { struct audio_source_instance *fi_audio; @@ -997,6 +998,8 @@ static struct usb_function_instance *audio_source_alloc_inst(void) struct device *dev; void *err_ptr; int err = 0; + char device_name[AUDIO_SOURCE_DEV_NAME_LENGTH]; + static u8 count; fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); if (!fi_audio) @@ -1014,7 +1017,11 @@ static struct usb_function_instance *audio_source_alloc_inst(void) config_group_init_type_name(&fi_audio->func_inst.group, "", &audio_source_func_type); - dev = create_function_device("f_audio_source"); + + snprintf(device_name, AUDIO_SOURCE_DEV_NAME_LENGTH, + "f_audio_source%d", count++); + + dev = create_function_device(device_name); if (IS_ERR(dev)) { err_ptr = dev; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 31d3022b6ce7..a1c00525a598 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2437,6 +2437,8 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) return -EINVAL; length = le32_to_cpu(d->dwSize); + if (len < length) + return -EINVAL; type = le32_to_cpu(d->dwPropertyDataType); if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { @@ -2445,6 +2447,11 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, return -EINVAL; } pnl = le16_to_cpu(d->wPropertyNameLength); + if (length < 14 + pnl) { + pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n", + length, pnl, type); + return -EINVAL; + } pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); if (length != 14 + pnl + pdl) { pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", @@ -2534,6 +2541,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, } } if (flags & (1 << i)) { + if (len < 4) { + goto error; + } os_descs_count = get_unaligned_le32(data); data += 4; len -= 4; @@ -2611,7 +2621,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, ffs_log("enter: len %zu", len); - if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || + if (unlikely(len < 16 || + get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || get_unaligned_le32(data + 4) != len)) goto error; str_count = get_unaligned_le32(data + 8); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index e309dec68a75..1fd5a95b6e99 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2336,9 +2336,6 @@ reset: bh->outreq->complete = bulk_out_complete; } - /* prevents usb LPM until thread runs to completion */ - usb_gadget_autopm_get_noresume(common->gadget); - common->running = 1; for (i = 0; i < ARRAY_SIZE(common->luns); ++i) if (common->luns[i]) @@ -2354,6 +2351,10 @@ static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct fsg_dev *fsg = fsg_from_func(f); fsg->common->new_fsg = fsg; + + /* prevents usb LPM until thread runs to completion */ + usb_gadget_autopm_get_async(fsg->common->gadget); + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); return USB_GADGET_DELAYED_STATUS; } diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index bf7460f25e61..4a0b3a0aa65e 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1504,6 +1504,7 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; mutex_lock(&dev->read_mutex); while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 4e35ed9654b7..2a7d57cd14cb 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1426,17 +1426,39 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) */ if (!ncm_opts->bound) { mutex_lock(&ncm_opts->lock); + ncm_opts->net = gether_setup_default(); + if (IS_ERR(ncm_opts->net)) { + status = PTR_ERR(ncm_opts->net); + mutex_unlock(&ncm_opts->lock); + goto error; + } gether_set_gadget(ncm_opts->net, cdev->gadget); status = gether_register_netdev(ncm_opts->net); mutex_unlock(&ncm_opts->lock); - if (status) - return status; + if (status) { + free_netdev(ncm_opts->net); + goto error; + } ncm_opts->bound = true; } + + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(ncm_opts->net, ncm->ethaddr, + sizeof(ncm->ethaddr)); + if (status < 12) { /* strlen("01234567890a") */ + ERROR(cdev, "%s: failed to get host eth addr, err %d\n", + __func__, status); + status = -EINVAL; + goto netdev_cleanup; + } + ncm->port.ioport = netdev_priv(ncm_opts->net); + us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) - return PTR_ERR(us); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto netdev_cleanup; + } ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; @@ -1540,7 +1562,10 @@ fail: kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); } +netdev_cleanup: + gether_cleanup(netdev_priv(ncm_opts->net)); +error: ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; @@ -1588,8 +1613,6 @@ static void ncm_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_ncm_opts, func_inst); if (opts->bound) gether_cleanup(netdev_priv(opts->net)); - else - free_netdev(opts->net); kfree(opts); } @@ -1602,12 +1625,6 @@ static struct usb_function_instance *ncm_alloc_inst(void) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); opts->func_inst.free_func_inst = ncm_free_inst; - opts->net = gether_setup_default(); - if (IS_ERR(opts->net)) { - struct net_device *net = opts->net; - kfree(opts); - return ERR_CAST(net); - } config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); @@ -1630,6 +1647,8 @@ static void ncm_free(struct usb_function *f) static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ncm *ncm = func_to_ncm(f); + struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts, + func_inst); DBG(c->cdev, "ncm unbind\n"); @@ -1641,13 +1660,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); + + gether_cleanup(netdev_priv(opts->net)); + opts->bound = false; } static struct usb_function *ncm_alloc(struct usb_function_instance *fi) { struct f_ncm *ncm; struct f_ncm_opts *opts; - int status; /* allocate and initialize one new instance */ ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); @@ -1657,20 +1678,9 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) opts = container_of(fi, struct f_ncm_opts, func_inst); mutex_lock(&opts->lock); opts->refcnt++; - - /* export host's Ethernet address in CDC format */ - status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, - sizeof(ncm->ethaddr)); - if (status < 12) { /* strlen("01234567890a") */ - kfree(ncm); - mutex_unlock(&opts->lock); - return ERR_PTR(-EINVAL); - } ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; - spin_lock_init(&ncm->lock); ncm_reset_values(ncm); - ncm->port.ioport = netdev_priv(opts->net); mutex_unlock(&opts->lock); ncm->port.is_fixed = true; ncm->port.supports_multi_frame = true; diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 29263a84bbea..88db253aeef4 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -493,11 +493,7 @@ static void usb_qdss_disconnect_work(struct work_struct *work) NULL, NULL); - status = set_qdss_data_connection( - qdss->gadget, - qdss->port.data, - qdss->port.data->address, - 0); + status = set_qdss_data_connection(qdss, 0); if (status) pr_err("qdss_disconnect error"); } @@ -543,11 +539,7 @@ static void usb_qdss_connect_work(struct work_struct *work) } pr_debug("usb_qdss_connect_work\n"); - status = set_qdss_data_connection( - qdss->gadget, - qdss->port.data, - qdss->port.data->address, - 1); + status = set_qdss_data_connection(qdss, 1); if (status) { pr_err("set_qdss_data_connection error(%d)", status); return; @@ -868,14 +860,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch) if (status) pr_err("%s: uninit_data error\n", __func__); - status = set_qdss_data_connection( - gadget, - qdss->port.data, - qdss->port.data->address, - 0); + status = set_qdss_data_connection(qdss, 0); if (status) pr_err("%s:qdss_disconnect error\n", __func__); - usb_gadget_restart(gadget); } EXPORT_SYMBOL(usb_qdss_close); diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h index e3fe8ae03775..cad0f4cc06f9 100644 --- a/drivers/usb/gadget/function/f_qdss.h +++ b/drivers/usb/gadget/function/f_qdss.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -69,6 +69,5 @@ struct usb_qdss_opts { }; int uninit_data(struct usb_ep *ep); -int set_qdss_data_connection(struct usb_gadget *gadget, - struct usb_ep *data_ep, u8 data_addr, int enable); +int set_qdss_data_connection(struct f_qdss *qdss, int enable); #endif diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 12628dd36e55..12064d3bddf6 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1079,13 +1079,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); if (!agdev->out_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err; + return ret; } agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); if (!agdev->in_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err; + return ret; } uac2->p_prm.uac2 = uac2; @@ -1102,7 +1102,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL); if (ret) - goto err; + return ret; prm = &agdev->uac2.c_prm; prm->max_psize = hs_epout_desc.wMaxPacketSize; @@ -1117,19 +1117,19 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; - goto err_free_descs; + goto err; } ret = alsa_uac2_init(agdev); if (ret) - goto err_free_descs; + goto err; return 0; -err_free_descs: - usb_free_all_descriptors(fn); err: kfree(agdev->uac2.p_prm.rbuf); kfree(agdev->uac2.c_prm.rbuf); +err_free_descs: + usb_free_all_descriptors(fn); return -EINVAL; } diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 5718f71bcdea..d9a0b0e0b271 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -735,7 +735,7 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, { struct ipa_data_ch_info *port; unsigned long flags; - int ret; + int ret = 0; pr_debug("dev:%pK port#%d src_connection_idx:%d dst_connection_idx:%d\n", gp, func, src_connection_idx, dst_connection_idx); diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c index 42a9cda68659..0ef1e2ab34be 100644 --- a/drivers/usb/gadget/function/u_qdss.c +++ b/drivers/usb/gadget/function/u_qdss.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -40,19 +40,25 @@ static int alloc_sps_req(struct usb_ep *data_ep) } static int init_data(struct usb_ep *ep); -int set_qdss_data_connection(struct usb_gadget *gadget, - struct usb_ep *data_ep, u8 data_addr, int enable) +int set_qdss_data_connection(struct f_qdss *qdss, int enable) { enum usb_ctrl usb_bam_type; int res = 0; int idx; - struct f_qdss *qdss = data_ep->driver_data; - struct usb_qdss_bam_connect_info bam_info = qdss->bam_info; + struct usb_qdss_bam_connect_info bam_info; + struct usb_gadget *gadget; pr_debug("set_qdss_data_connection\n"); + if (!qdss) { + pr_err("%s: qdss ptr is NULL\n", __func__); + return -EINVAL; + } + + gadget = qdss->gadget; usb_bam_type = usb_bam_get_bam_type(gadget->name); + bam_info = qdss->bam_info; /* There is only one qdss pipe, so the pipe number can be set to 0 */ idx = usb_bam_get_connection_idx(usb_bam_type, QDSS_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0); @@ -67,14 +73,16 @@ int set_qdss_data_connection(struct usb_gadget *gadget, kzalloc(sizeof(struct sps_mem_buffer), GFP_KERNEL); if (!bam_info.data_fifo) { pr_err("qdss_data_connection: memory alloc failed\n"); + usb_bam_free_fifos(usb_bam_type, idx); return -ENOMEM; } get_bam2bam_connection_info(usb_bam_type, idx, &bam_info.usb_bam_pipe_idx, NULL, bam_info.data_fifo, NULL); - alloc_sps_req(data_ep); - msm_data_fifo_config(data_ep, bam_info.data_fifo->phys_base, + alloc_sps_req(qdss->port.data); + msm_data_fifo_config(qdss->port.data, + bam_info.data_fifo->phys_base, bam_info.data_fifo->size, bam_info.usb_bam_pipe_idx); init_data(qdss->port.data); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 3d0d5d94a62f..0f01c04d7cbd 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -243,7 +243,7 @@ uvc_video_alloc_requests(struct uvc_video *video) req_size = video->ep->maxpacket * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult + 1); + * (video->ep->mult); for (i = 0; i < UVC_NUM_REQUESTS; ++i) { video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index e57f48f9528f..de014436fb22 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1125,7 +1125,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* data and/or status stage for control request */ } else if (dev->state == STATE_DEV_SETUP) { - /* IN DATA+STATUS caller makes len <= wLength */ + len = min_t(size_t, len, dev->setup_wLength); if (dev->setup_in) { retval = setup_req (dev->gadget->ep0, dev->req, len); if (retval == 0) { @@ -1755,10 +1755,12 @@ static struct usb_gadget_driver probe_driver = { * such as configuration notifications. */ -static int is_valid_config (struct usb_config_descriptor *config) +static int is_valid_config(struct usb_config_descriptor *config, + unsigned int total) { return config->bDescriptorType == USB_DT_CONFIG && config->bLength == USB_DT_CONFIG_SIZE + && total >= USB_DT_CONFIG_SIZE && config->bConfigurationValue != 0 && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0 && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0; @@ -1783,7 +1785,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) } spin_unlock_irq(&dev->lock); - if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) + if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) || + (len > PAGE_SIZE * 4)) return -EINVAL; /* we might need to change message format someday */ @@ -1807,7 +1810,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* full or low speed config */ dev->config = (void *) kbuf; total = le16_to_cpu(dev->config->wTotalLength); - if (!is_valid_config (dev->config) || total >= length) + if (!is_valid_config(dev->config, total) || + total > length - USB_DT_DEVICE_SIZE) goto fail; kbuf += total; length -= total; @@ -1816,10 +1820,13 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (kbuf [1] == USB_DT_CONFIG) { dev->hs_config = (void *) kbuf; total = le16_to_cpu(dev->hs_config->wTotalLength); - if (!is_valid_config (dev->hs_config) || total >= length) + if (!is_valid_config(dev->hs_config, total) || + total > length - USB_DT_DEVICE_SIZE) goto fail; kbuf += total; length -= total; + } else { + dev->hs_config = NULL; } /* could support multiple configs, using another encoding! */ diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index dde44450dfa9..6610f7a023d3 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -330,7 +330,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep) /* caller must hold lock */ static void stop_activity(struct dummy *dum) { - struct dummy_ep *ep; + int i; /* prevent any more requests */ dum->address = 0; @@ -338,8 +338,8 @@ static void stop_activity(struct dummy *dum) /* The timer is left running so that outstanding URBs can fail */ /* nuke any pending requests first, so driver i/o is quiesced */ - list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list) - nuke(dum, ep); + for (i = 0; i < DUMMY_ENDPOINTS; ++i) + nuke(dum, &dum->ep[i]); /* driver now does any non-usb quiescing necessary */ } @@ -1033,6 +1033,8 @@ static int dummy_udc_probe(struct platform_device *pdev) int rc; dum = *((void **)dev_get_platdata(&pdev->dev)); + /* Clear usb_gadget region for new registration to udc-core */ + memzero_explicit(&dum->gadget, sizeof(struct usb_gadget)); dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.max_speed = USB_SPEED_SUPER; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index aab5221d6c2e..aac0ce8aeb0b 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1249,6 +1249,12 @@ static const struct usb_gadget_ops fsl_gadget_ops = { .udc_stop = fsl_udc_stop, }; +/* + * Empty complete function used by this driver to fill in the req->complete + * field when creating a request since the complete field is mandatory. + */ +static void fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { } + /* Set protocol stall on ep0, protocol stall will automatically be cleared on new transaction */ static void ep0stall(struct fsl_udc *udc) @@ -1283,7 +1289,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) req->req.length = 0; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + req->req.complete = fsl_noop_complete; req->dtd_count = 0; ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); @@ -1366,7 +1372,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req->req.length = 2; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + req->req.complete = fsl_noop_complete; req->dtd_count = 0; ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 940304c33224..02260cfdedb1 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -129,6 +129,10 @@ static int uhci_pci_init(struct usb_hcd *hcd) if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) uhci->wait_for_hp = 1; + /* Intel controllers use non-PME wakeup signalling */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL) + device_set_run_wake(uhci_dev(uhci), 1); + /* Set up pointers to PCI-specific functions */ uhci->reset_hc = uhci_pci_reset_hc; uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 34388950f96b..a190c97d11e4 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -111,7 +111,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); /* xhci 1.1 controllers have the HCCPARAMS2 register */ - if (hci_version > 100) { + if (hci_version > 0x100) { temp = readl(&xhci->cap_regs->hcc_params2); xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " HC %s Force save context capability", diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 163de4bde2d8..d885033d3322 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1529,6 +1529,35 @@ int xhci_bus_suspend(struct usb_hcd *hcd) return 0; } +/* + * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3. + * warm reset a USB3 device stuck in polling or compliance mode after resume. + * See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8 + */ +static bool xhci_port_missing_cas_quirk(int port_index, + __le32 __iomem **port_array) +{ + u32 portsc; + + portsc = readl(port_array[port_index]); + + /* if any of these are set we are not stuck */ + if (portsc & (PORT_CONNECT | PORT_CAS)) + return false; + + if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) && + ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE)) + return false; + + /* clear wakeup/change bits, and do a warm port reset */ + portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); + portsc |= PORT_WR; + writel(portsc, port_array[port_index]); + /* flush write */ + readl(port_array[port_index]); + return true; +} + int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -1566,6 +1595,14 @@ int xhci_bus_resume(struct usb_hcd *hcd) u32 temp; temp = readl(port_array[port_index]); + + /* warm reset CAS limited ports stuck in polling/compliance */ + if ((xhci->quirks & XHCI_MISSING_CAS) && + (hcd->speed >= HCD_USB3) && + xhci_port_missing_cas_quirk(port_index, port_array)) { + xhci_dbg(xhci, "reset stuck port %d\n", port_index); + continue; + } if (DEV_SUPERSPEED_ANY(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 8e5ab373dce9..82483599a882 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -964,6 +964,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) xhci->devs[slot_id] = NULL; } +/* + * Free a virt_device structure. + * If the virt_device added a tt_info (a hub) and has children pointing to + * that tt_info, then free the child first. Recursive. + * We can't rely on udev at this point to find child-parent relationships. + */ +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) +{ + struct xhci_virt_device *vdev; + struct list_head *tt_list_head; + struct xhci_tt_bw_info *tt_info, *next; + int i; + + vdev = xhci->devs[slot_id]; + if (!vdev) + return; + + tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts); + list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { + /* is this a hub device that added a tt_info to the tts list */ + if (tt_info->slot_id == slot_id) { + /* are any devices using this tt_info? */ + for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + vdev = xhci->devs[i]; + if (vdev && (vdev->tt_info == tt_info)) + xhci_free_virt_devices_depth_first( + xhci, i); + } + } + } + /* we are now at a leaf device */ + xhci_free_virt_device(xhci, slot_id); +} + int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags) { @@ -1933,7 +1967,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) struct device *dev = xhci_to_hcd(xhci)->self.controller; int i, j, num_ports; - del_timer_sync(&xhci->cmd_timer); + cancel_delayed_work_sync(&xhci->cmd_timer); xhci_event_ring_cleanup(xhci); @@ -1956,8 +1990,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } } - for (i = 1; i < MAX_HC_SLOTS; ++i) - xhci_free_virt_device(xhci, i); + for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) + xhci_free_virt_devices_depth_first(xhci, i); dma_pool_destroy(xhci->segment_pool); xhci->segment_pool = NULL; @@ -2636,9 +2670,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->cmd_list); - /* init command timeout timer */ - setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, - (unsigned long)xhci); + /* init command timeout work */ + INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); + init_completion(&xhci->cmd_ring_stop_completion); page_size = readl(&xhci->op_regs->page_size); xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2677,7 +2711,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * "physically contiguous and 64-byte (cache line) aligned". */ xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, - GFP_KERNEL); + flags); if (!xhci->dcbaa) goto fail; memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cf147ccac7d3..dd262f418140 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -51,6 +51,7 @@ #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f #define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 +#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 static const char hcd_name[] = "xhci_hcd"; @@ -165,9 +166,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) { + pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) { xhci->quirks |= XHCI_PME_STUCK_QUIRK; } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) + xhci->quirks |= XHCI_MISSING_CAS; + if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 3f106b428dba..7423645c204c 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -233,9 +233,6 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd_to_bus(xhci->shared_hcd)->skip_resume = true; - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; - hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); @@ -254,6 +251,9 @@ static int xhci_plat_probe(struct platform_device *pdev) device_wakeup_enable(&hcd->self.root_hub->dev); + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT); if (ret) goto dealloc_usb2_hcd; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 98d01acc8b11..12e38cf022ff 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -280,23 +280,76 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) readl(&xhci->dba->doorbell[0]); } -static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) +static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay) +{ + return mod_delayed_work(system_wq, &xhci->cmd_timer, delay); +} + +static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci) +{ + return list_first_entry_or_null(&xhci->cmd_list, struct xhci_command, + cmd_list); +} + +/* + * Turn all commands on command ring with status set to "aborted" to no-op trbs. + * If there are other commands waiting then restart the ring and kick the timer. + * This must be called with command ring stopped and xhci->lock held. + */ +static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, + struct xhci_command *cur_cmd) +{ + struct xhci_command *i_cmd; + u32 cycle_state; + + /* Turn all aborted commands in list to no-ops, then restart */ + list_for_each_entry(i_cmd, &xhci->cmd_list, cmd_list) { + + if (i_cmd->status != COMP_CMD_ABORT) + continue; + + i_cmd->status = COMP_CMD_STOP; + + xhci_dbg(xhci, "Turn aborted command %pK to no-op\n", + i_cmd->command_trb); + /* get cycle state from the original cmd trb */ + cycle_state = le32_to_cpu( + i_cmd->command_trb->generic.field[3]) & TRB_CYCLE; + /* modify the command trb to no-op command */ + i_cmd->command_trb->generic.field[0] = 0; + i_cmd->command_trb->generic.field[1] = 0; + i_cmd->command_trb->generic.field[2] = 0; + i_cmd->command_trb->generic.field[3] = cpu_to_le32( + TRB_TYPE(TRB_CMD_NOOP) | cycle_state); + + /* + * caller waiting for completion is called when command + * completion event is received for these no-op commands + */ + } + + xhci->cmd_ring_state = CMD_RING_STATE_RUNNING; + + /* ring command ring doorbell to restart the command ring */ + if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && + !(xhci->xhc_state & XHCI_STATE_DYING)) { + xhci->current_cmd = cur_cmd; + xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_ring_cmd_db(xhci); + } +} + +/* Must be called with xhci->lock held, releases and aquires lock back */ +static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) { u64 temp_64; int ret; xhci_dbg(xhci, "Abort command ring\n"); - temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); - xhci->cmd_ring_state = CMD_RING_STATE_ABORTED; + reinit_completion(&xhci->cmd_ring_stop_completion); - /* - * Writing the CMD_RING_ABORT bit should cause a cmd completion event, - * however on some host hw the CMD_RING_RUNNING bit is correctly cleared - * but the completion event in never sent. Use the cmd timeout timer to - * handle those cases. Use twice the time to cover the bit polling retry - */ - mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT)); + temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); xhci_write_64(xhci, temp_64 | CMD_RING_ABORT, &xhci->op_regs->cmd_ring); @@ -316,16 +369,30 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) udelay(1000); ret = xhci_handshake(&xhci->op_regs->cmd_ring, CMD_RING_RUNNING, 0, 3 * 1000 * 1000); - if (ret == 0) - return 0; - - xhci_err(xhci, "Stopped the command ring failed, " - "maybe the host is dead\n"); - del_timer(&xhci->cmd_timer); - xhci->xhc_state |= XHCI_STATE_DYING; - xhci_quiesce(xhci); - xhci_halt(xhci); - return -ESHUTDOWN; + if (ret < 0) { + xhci_err(xhci, "Stopped the command ring failed, " + "maybe the host is dead\n"); + xhci->xhc_state |= XHCI_STATE_DYING; + xhci_quiesce(xhci); + xhci_halt(xhci); + return -ESHUTDOWN; + } + } + /* + * Writing the CMD_RING_ABORT bit should cause a cmd completion event, + * however on some host hw the CMD_RING_RUNNING bit is correctly cleared + * but the completion event in never sent. Wait 2 secs (arbitrary + * number) to handle those cases after negation of CMD_RING_RUNNING. + */ + spin_unlock_irqrestore(&xhci->lock, flags); + ret = wait_for_completion_timeout(&xhci->cmd_ring_stop_completion, + msecs_to_jiffies(2000)); + spin_lock_irqsave(&xhci->lock, flags); + if (!ret) { + xhci_dbg(xhci, "No stop event for abort, ring start fail?\n"); + xhci_cleanup_command_queue(xhci); + } else { + xhci_handle_stopped_cmd_ring(xhci, xhci_next_queued_cmd(xhci)); } return 0; @@ -846,17 +913,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) spin_lock_irqsave(&xhci->lock, flags); ep->stop_cmds_pending--; - if (xhci->xhc_state & XHCI_STATE_REMOVING) { - spin_unlock_irqrestore(&xhci->lock, flags); - return; - } - if (xhci->xhc_state & XHCI_STATE_DYING) { - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Stop EP timer ran, but another timer marked " - "xHCI as DYING, exiting."); - spin_unlock_irqrestore(&xhci->lock, flags); - return; - } if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Stop EP timer ran, but no command pending, " @@ -1208,101 +1264,62 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci) xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT); } -/* - * Turn all commands on command ring with status set to "aborted" to no-op trbs. - * If there are other commands waiting then restart the ring and kick the timer. - * This must be called with command ring stopped and xhci->lock held. - */ -static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, - struct xhci_command *cur_cmd) -{ - struct xhci_command *i_cmd, *tmp_cmd; - u32 cycle_state; - - /* Turn all aborted commands in list to no-ops, then restart */ - list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list, - cmd_list) { - - if (i_cmd->status != COMP_CMD_ABORT) - continue; - - i_cmd->status = COMP_CMD_STOP; - - xhci_dbg(xhci, "Turn aborted command %pK to no-op\n", - i_cmd->command_trb); - /* get cycle state from the original cmd trb */ - cycle_state = le32_to_cpu( - i_cmd->command_trb->generic.field[3]) & TRB_CYCLE; - /* modify the command trb to no-op command */ - i_cmd->command_trb->generic.field[0] = 0; - i_cmd->command_trb->generic.field[1] = 0; - i_cmd->command_trb->generic.field[2] = 0; - i_cmd->command_trb->generic.field[3] = cpu_to_le32( - TRB_TYPE(TRB_CMD_NOOP) | cycle_state); - - /* - * caller waiting for completion is called when command - * completion event is received for these no-op commands - */ - } - - xhci->cmd_ring_state = CMD_RING_STATE_RUNNING; - - /* ring command ring doorbell to restart the command ring */ - if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && - !(xhci->xhc_state & XHCI_STATE_DYING)) { - xhci->current_cmd = cur_cmd; - mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT); - xhci_ring_cmd_db(xhci); - } - return; -} - - -void xhci_handle_command_timeout(unsigned long data) +void xhci_handle_command_timeout(struct work_struct *work) { struct xhci_hcd *xhci; int ret; unsigned long flags; u64 hw_ring_state; - bool second_timeout = false; - xhci = (struct xhci_hcd *) data; - /* mark this command to be cancelled */ + xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer); + spin_lock_irqsave(&xhci->lock, flags); - if (xhci->current_cmd) { - if (xhci->current_cmd->status == COMP_CMD_ABORT) - second_timeout = true; - xhci->current_cmd->status = COMP_CMD_ABORT; + + /* + * If timeout work is pending, or current_cmd is NULL, it means we + * raced with command completion. Command is handled so just return. + */ + if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) { + spin_unlock_irqrestore(&xhci->lock, flags); + return; } + /* mark this command to be cancelled */ + xhci->current_cmd->status = COMP_CMD_ABORT; /* Make sure command ring is running before aborting it */ hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) && (hw_ring_state & CMD_RING_RUNNING)) { - spin_unlock_irqrestore(&xhci->lock, flags); + /* Prevent new doorbell, and start command abort */ + xhci->cmd_ring_state = CMD_RING_STATE_ABORTED; xhci_dbg(xhci, "Command timeout\n"); - ret = xhci_abort_cmd_ring(xhci); + ret = xhci_abort_cmd_ring(xhci, flags); if (unlikely(ret == -ESHUTDOWN)) { xhci_err(xhci, "Abort command ring failed\n"); xhci_cleanup_command_queue(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); usb_hc_died(xhci_to_hcd(xhci)->primary_hcd); xhci_dbg(xhci, "xHCI host controller is dead.\n"); + + return; } - return; + + goto time_out_completed; } - /* command ring failed to restart, or host removed. Bail out */ - if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "command timed out twice, ring start fail?\n"); + /* host removed. Bail out */ + if (xhci->xhc_state & XHCI_STATE_REMOVING) { + xhci_dbg(xhci, "host removed, ring start fail?\n"); xhci_cleanup_command_queue(xhci); - return; + + goto time_out_completed; } /* command timeout on stopped ring, ring can't be aborted */ xhci_dbg(xhci, "Command timeout on stopped ring\n"); xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd); + +time_out_completed: spin_unlock_irqrestore(&xhci->lock, flags); return; } @@ -1335,7 +1352,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list); - del_timer(&xhci->cmd_timer); + cancel_delayed_work(&xhci->cmd_timer); trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event); @@ -1343,7 +1360,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, /* If CMD ring stopped we own the trbs between enqueue and dequeue */ if (cmd_comp_code == COMP_CMD_STOP) { - xhci_handle_stopped_cmd_ring(xhci, cmd); + complete_all(&xhci->cmd_ring_stop_completion); return; } @@ -1361,8 +1378,11 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, */ if (cmd_comp_code == COMP_CMD_ABORT) { xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; - if (cmd->status == COMP_CMD_ABORT) + if (cmd->status == COMP_CMD_ABORT) { + if (xhci->current_cmd == cmd) + xhci->current_cmd = NULL; goto event_handled; + } } cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3])); @@ -1423,7 +1443,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, if (cmd->cmd_list.next != &xhci->cmd_list) { xhci->current_cmd = list_entry(cmd->cmd_list.next, struct xhci_command, cmd_list); - mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + } else if (xhci->current_cmd == cmd) { + xhci->current_cmd = NULL; } event_handled: @@ -4206,9 +4228,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, /* if there are no other commands queued we start the timeout timer */ if (xhci->cmd_list.next == &cmd->cmd_list && - !timer_pending(&xhci->cmd_timer)) { + !delayed_work_pending(&xhci->cmd_timer)) { xhci->current_cmd = cmd; - mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); } queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c6bb15ef51f..aab1c7903288 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -121,10 +121,10 @@ int xhci_halt(struct xhci_hcd *xhci) xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; - if (timer_pending(&xhci->cmd_timer)) { + if (delayed_work_pending(&xhci->cmd_timer)) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Cleanup command queue"); - del_timer(&xhci->cmd_timer); + cancel_delayed_work(&xhci->cmd_timer); xhci_cleanup_command_queue(xhci); } @@ -138,7 +138,13 @@ static int xhci_start(struct xhci_hcd *xhci) { u32 temp; int ret; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + /* + * disable irq to avoid xhci_irq flooding due to unhandeled port + * change event in halt state, as soon as xhci_start clears halt bit + */ + disable_irq(hcd->irq); temp = readl(&xhci->op_regs->command); temp |= (CMD_RUN); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.", @@ -159,6 +165,8 @@ static int xhci_start(struct xhci_hcd *xhci) /* clear state flags. Including dying, halted or removing */ xhci->xhc_state = 0; + enable_irq(hcd->irq); + return ret; } @@ -1579,19 +1587,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) xhci_urb_free_priv(urb_priv); return ret; } - if ((xhci->xhc_state & XHCI_STATE_DYING) || - (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Ep 0x%x: URB %pK to be canceled on " - "non-responsive xHCI host.", - urb->ep->desc.bEndpointAddress, urb); - /* Let the stop endpoint command watchdog timer (which set this - * state) finish cleaning up the endpoint TD lists. We must - * have caught it in the middle of dropping a lock and giving - * back an URB. - */ - goto done; - } ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; @@ -3818,8 +3813,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, mutex_lock(&xhci->mutex); - if (xhci->xhc_state) /* dying, removing or halted */ + if (xhci->xhc_state) { /* dying, removing or halted */ + ret = -ESHUTDOWN; goto out; + } if (!udev->slot_id) { xhci_dbg_trace(xhci, trace_xhci_dbg_address, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 097cfa9e4692..8fcec1be6b1a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -312,6 +312,8 @@ struct xhci_op_regs { #define XDEV_U2 (0x2 << 5) #define XDEV_U3 (0x3 << 5) #define XDEV_INACTIVE (0x6 << 5) +#define XDEV_POLLING (0x7 << 5) +#define XDEV_COMP_MODE (0xa << 5) #define XDEV_RESUME (0xf << 5) /* true: port has power (see HCC_PPC) */ #define PORT_POWER (1 << 9) @@ -1553,7 +1555,8 @@ struct xhci_hcd { #define CMD_RING_STATE_STOPPED (1 << 2) struct list_head cmd_list; unsigned int cmd_ring_reserved_trbs; - struct timer_list cmd_timer; + struct delayed_work cmd_timer; + struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; struct xhci_ring *event_ring; struct xhci_erst erst; @@ -1639,6 +1642,7 @@ struct xhci_hcd { /* For controllers with a broken beyond repair streams implementation */ #define XHCI_BROKEN_STREAMS (1 << 19) #define XHCI_PME_STUCK_QUIRK (1 << 20) +#define XHCI_MISSING_CAS (1 << 24) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ @@ -1922,7 +1926,7 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_dequeue_state *deq_state); void xhci_stop_endpoint_command_watchdog(unsigned long arg); -void xhci_handle_command_timeout(unsigned long data); +void xhci_handle_command_timeout(struct work_struct *work); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 1950e87b4219..775690bed4c0 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -787,12 +787,6 @@ static int iowarrior_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; dev->product_id = le16_to_cpu(udev->descriptor.idProduct); - if (iface_desc->desc.bNumEndpoints < 1) { - dev_err(&interface->dev, "Invalid number of endpoints\n"); - retval = -EINVAL; - goto error; - } - /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; @@ -803,6 +797,21 @@ static int iowarrior_probe(struct usb_interface *interface, /* this one will match for the IOWarrior56 only */ dev->int_out_endpoint = endpoint; } + + if (!dev->int_in_endpoint) { + dev_err(&interface->dev, "no interrupt-in endpoint found\n"); + retval = -ENODEV; + goto error; + } + + if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) { + if (!dev->int_out_endpoint) { + dev_err(&interface->dev, "no interrupt-out endpoint found\n"); + retval = -ENODEV; + goto error; + } + } + /* we have to check the report_size often, so remember it in the endianness suitable for our machine */ dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 310238c6b5cd..896798071817 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -469,6 +469,7 @@ static const struct musb_platform_ops bfin_ops = { .init = bfin_musb_init, .exit = bfin_musb_exit, + .fifo_offset = bfin_fifo_offset, .readb = bfin_readb, .writeb = bfin_writeb, .readw = bfin_readw, diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index b03d3b867fca..9a9c82a4d35d 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -458,15 +458,11 @@ static int da8xx_musb_exit(struct musb *musb) } static const struct musb_platform_ops da8xx_ops = { - .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP, + .quirks = MUSB_INDEXED_EP, .init = da8xx_musb_init, .exit = da8xx_musb_exit, .fifo_mode = 2, -#ifdef CONFIG_USB_TI_CPPI_DMA - .dma_init = cppi_dma_controller_create, - .dma_exit = cppi_dma_controller_destroy, -#endif .enable = da8xx_musb_enable, .disable = da8xx_musb_disable, diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 2337d7a7d62d..90de7900e4b8 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -214,6 +214,7 @@ struct musb_platform_ops { dma_addr_t *dma_addr, u32 *len); void (*pre_root_reset_end)(struct musb *musb); void (*post_root_reset_end)(struct musb *musb); + void (*clear_ep_rxintr)(struct musb *musb, int epnum); }; /* @@ -612,4 +613,10 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb) musb->ops->post_root_reset_end(musb); } +static inline void musb_platform_clear_ep_rxintr(struct musb *musb, int epnum) +{ + if (musb->ops->clear_ep_rxintr) + musb->ops->clear_ep_rxintr(musb, epnum); +} + #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index eeb7d9ecf7df..5a021b26d7d2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -301,6 +301,17 @@ static void otg_timer(unsigned long _musb) spin_unlock_irqrestore(&musb->lock, flags); } +void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum) +{ + u32 epintr; + struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); + const struct dsps_musb_wrapper *wrp = glue->wrp; + + /* musb->lock might already been held */ + epintr = (1 << epnum) << wrp->rxep_shift; + musb_writel(musb->ctrl_base, wrp->epintr_status, epintr); +} + static irqreturn_t dsps_interrupt(int irq, void *hci) { struct musb *musb = hci; @@ -647,6 +658,7 @@ static struct musb_platform_ops dsps_ops = { .try_idle = dsps_musb_try_idle, .set_mode = dsps_musb_set_mode, .recover = dsps_musb_recover, + .clear_ep_rxintr = dsps_musb_clear_ep_rxintr, }; static u64 musb_dmamask = DMA_BIT_MASK(32); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e0a083f6ab68..13d5614f37f1 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2390,12 +2390,11 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) int is_in = usb_pipein(urb->pipe); int status = 0; u16 csr; + struct dma_channel *dma = NULL; musb_ep_select(regs, hw_end); if (is_dma_capable()) { - struct dma_channel *dma; - dma = is_in ? ep->rx_channel : ep->tx_channel; if (dma) { status = ep->musb->dma_controller->channel_abort(dma); @@ -2412,10 +2411,9 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) /* giveback saves bulk toggle */ csr = musb_h_flush_rxfifo(ep, 0); - /* REVISIT we still get an irq; should likely clear the - * endpoint's irq status here to avoid bogus irqs. - * clearing that status is platform-specific... - */ + /* clear the endpoint's irq status here to avoid bogus irqs */ + if (is_dma_capable() && dma) + musb_platform_clear_ep_rxintr(musb, ep->epnum); } else if (ep->epnum) { musb_h_tx_flush_fifo(ep); csr = musb_readw(epio, MUSB_TXCSR); diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h index f7b13fd25257..a3dcbd55e436 100644 --- a/drivers/usb/musb/musbhsdma.h +++ b/drivers/usb/musb/musbhsdma.h @@ -157,5 +157,5 @@ struct musb_dma_controller { void __iomem *base; u8 channel_count; u8 used_channels; - u8 irq; + int irq; }; diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 935bd0778bfb..c76ca5a94557 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -1417,6 +1417,7 @@ static void dr_swap(struct usbpd *pd) } pd_phy_update_roles(pd->current_dr, pd->current_pr); + dual_role_instance_changed(pd->dual_role); } @@ -2656,11 +2657,17 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role, enum dual_role_property prop) { + struct usbpd *pd = dual_role_get_drvdata(dual_role); + switch (prop) { case DUAL_ROLE_PROP_MODE: + return 1; case DUAL_ROLE_PROP_DR: case DUAL_ROLE_PROP_PR: - return 1; + if (pd) + return pd->current_state == PE_SNK_READY || + pd->current_state == PE_SRC_READY; + break; default: break; } diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c index 51fcb545a9d5..9ef889593ef5 100644 --- a/drivers/usb/phy/class-dual-role.c +++ b/drivers/usb/phy/class-dual-role.c @@ -70,15 +70,7 @@ static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper) return ret; } -static void dual_role_changed_work(struct work_struct *work) -{ - struct dual_role_phy_instance *dual_role = - container_of(work, struct dual_role_phy_instance, - changed_work); - - dev_dbg(&dual_role->dev, "%s\n", __func__); - kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); -} +static void dual_role_changed_work(struct work_struct *work); void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) { @@ -505,6 +497,17 @@ out: return ret; } +static void dual_role_changed_work(struct work_struct *work) +{ + struct dual_role_phy_instance *dual_role = + container_of(work, struct dual_role_phy_instance, + changed_work); + + dev_dbg(&dual_role->dev, "%s\n", __func__); + sysfs_update_group(&dual_role->dev.kobj, &dual_role_attr_group); + kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); +} + /******************* Module Init ***********************************/ static int __init dual_role_class_init(void) diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 7b3035ff9434..1b4d742a2397 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -126,10 +126,12 @@ struct phy_control *am335x_get_phy_control(struct device *dev) return NULL; dev = bus_find_device(&platform_bus_type, NULL, node, match); + of_node_put(node); if (!dev) return NULL; ctrl_usb = dev_get_drvdata(dev); + put_device(dev); if (!ctrl_usb) return NULL; return &ctrl_usb->phy_ctrl; diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 64916f5566b5..aa11cf2f7417 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,10 +27,10 @@ #include <linux/clk/msm-clk.h> #include <linux/reset.h> -enum core_ldo_levels { - CORE_LEVEL_NONE = 0, - CORE_LEVEL_MIN, - CORE_LEVEL_MAX, +enum ldo_levels { + VOLTAGE_LEVEL_NONE = 0, + VOLTAGE_LEVEL_MIN, + VOLTAGE_LEVEL_MAX, }; #define INIT_MAX_TIME_USEC 1000 @@ -40,6 +40,8 @@ enum core_ldo_levels { #define USB_SSPHY_1P2_VOL_MAX 1200000 /* uV */ #define USB_SSPHY_HPM_LOAD 23000 /* uA */ +#define USB_SSPHY_LOAD_DEFAULT -1 + /* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */ #define PHYSTATUS BIT(6) @@ -83,6 +85,9 @@ struct msm_ssphy_qmp { int vdd_levels[3]; /* none, low, high */ struct regulator *core_ldo; int core_voltage_levels[3]; + struct regulator *fpc_redrive_ldo; + int redrive_voltage_levels[3]; + int redrive_load; struct clk *ref_clk_src; struct clk *ref_clk; struct clk *aux_clk; @@ -162,6 +167,33 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy, } } +static int msm_ldo_enable(struct msm_ssphy_qmp *phy, + struct regulator *ldo, int *voltage_levels, int load) +{ + int ret = 0; + + dev_dbg(phy->phy.dev, + "ldo: min_vol:%duV max_vol:%duV\n", + voltage_levels[VOLTAGE_LEVEL_MIN], + voltage_levels[VOLTAGE_LEVEL_MAX]); + + if (load > 0) { + ret = regulator_set_load(ldo, load); + if (ret < 0) + return ret; + } + + ret = regulator_set_voltage(ldo, + voltage_levels[VOLTAGE_LEVEL_MIN], + voltage_levels[VOLTAGE_LEVEL_MAX]); + if (ret) + return ret; + + ret = regulator_enable(ldo); + + return ret; +} + static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) { int min, rc = 0; @@ -181,74 +213,65 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) if (!on) goto disable_regulators; - rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min], - phy->vdd_levels[2]); - if (rc) { - dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n"); - return rc; - } - - dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n", - phy->vdd_levels[min], phy->vdd_levels[2]); + if (phy->fpc_redrive_ldo) { + rc = msm_ldo_enable(phy, phy->fpc_redrive_ldo, + phy->redrive_voltage_levels, + phy->redrive_load); + if (rc < 0) { + dev_err(phy->phy.dev, + "enable phy->fpc_redrive_ldo failed\n"); + return rc; + } - rc = regulator_enable(phy->vdd); - if (rc) { - dev_err(phy->phy.dev, - "regulator_enable(phy->vdd) failed, ret=%d", - rc); - goto unconfig_vdd; + dev_dbg(phy->phy.dev, + "fpc redrive ldo: min_vol:%duV max_vol:%duV\n", + phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN], + phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]); } - rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD); + rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels, + USB_SSPHY_LOAD_DEFAULT); if (rc < 0) { - dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n"); - goto disable_vdd; + dev_err(phy->phy.dev, "enable phy->vdd failed\n"); + goto disable_fpc_redrive; } - rc = regulator_set_voltage(phy->core_ldo, - phy->core_voltage_levels[CORE_LEVEL_MIN], - phy->core_voltage_levels[CORE_LEVEL_MAX]); - if (rc) { - dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n"); - goto put_core_ldo_lpm; - } + dev_dbg(phy->phy.dev, + "vdd ldo: min_vol:%duV max_vol:%duV\n", + phy->vdd_levels[VOLTAGE_LEVEL_MIN], + phy->vdd_levels[VOLTAGE_LEVEL_MAX]); - rc = regulator_enable(phy->core_ldo); - if (rc) { - dev_err(phy->phy.dev, "Unable to enable core_ldo\n"); - goto unset_core_ldo; + rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels, + USB_SSPHY_HPM_LOAD); + if (rc < 0) { + dev_err(phy->phy.dev, "enable phy->core_ldo failed\n"); + goto disable_vdd; } + dev_dbg(phy->phy.dev, + "core ldo: min_vol:%duV max_vol:%duV\n", + phy->core_voltage_levels[VOLTAGE_LEVEL_MIN], + phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]); + return 0; disable_regulators: rc = regulator_disable(phy->core_ldo); if (rc) - dev_err(phy->phy.dev, "Unable to disable core_ldo\n"); - -unset_core_ldo: - rc = regulator_set_voltage(phy->core_ldo, - phy->core_voltage_levels[CORE_LEVEL_NONE], - phy->core_voltage_levels[CORE_LEVEL_MAX]); - if (rc) - dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n"); - -put_core_ldo_lpm: - rc = regulator_set_load(phy->core_ldo, 0); - if (rc < 0) - dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n"); + dev_err(phy->phy.dev, "disable phy->core_ldo failed\n"); disable_vdd: rc = regulator_disable(phy->vdd); if (rc) - dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d", - rc); + dev_err(phy->phy.dev, "disable phy->vdd failed\n"); -unconfig_vdd: - rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min], - phy->vdd_levels[2]); - if (rc) - dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n"); +disable_fpc_redrive: + if (phy->fpc_redrive_ldo) { + rc = regulator_disable(phy->fpc_redrive_ldo); + if (rc) + dev_err(phy->phy.dev, + "disable phy->fpc_redrive_ldo failed\n"); + } return rc < 0 ? rc : 0; } @@ -307,10 +330,6 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy) phy->clk_enabled = true; } - /* select usb3 phy mode */ - if (phy->tcsr_usb3_dp_phymode) - writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode); - writel_relaxed(0x01, phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]); @@ -386,6 +405,10 @@ static int msm_ssphy_qmp_reset(struct usb_phy *uphy) goto deassert_phy_phy_reset; } + /* select usb3 phy mode */ + if (phy->tcsr_usb3_dp_phymode) + writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode); + /* Deassert USB3 PHY CSR reset */ ret = reset_control_deassert(phy->phy_reset); if (ret) { @@ -683,9 +706,9 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) } /* Set default core voltage values */ - phy->core_voltage_levels[CORE_LEVEL_NONE] = 0; - phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN; - phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX; + phy->core_voltage_levels[VOLTAGE_LEVEL_NONE] = 0; + phy->core_voltage_levels[VOLTAGE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN; + phy->core_voltage_levels[VOLTAGE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX; if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) && len == sizeof(phy->core_voltage_levels)) { @@ -729,6 +752,39 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) goto err; } + phy->fpc_redrive_ldo = devm_regulator_get_optional(dev, "fpc-redrive"); + if (IS_ERR(phy->fpc_redrive_ldo)) { + phy->fpc_redrive_ldo = NULL; + dev_dbg(dev, "no FPC re-drive ldo regulator\n"); + } else { + if (of_get_property(dev->of_node, + "qcom,redrive-voltage-level", &len) && + len == sizeof(phy->redrive_voltage_levels)) { + ret = of_property_read_u32_array(dev->of_node, + "qcom,redrive-voltage-level", + (u32 *) phy->redrive_voltage_levels, + len / sizeof(u32)); + if (ret) { + dev_err(dev, + "err qcom,redrive-voltage-level\n"); + goto err; + } + } else { + ret = -EINVAL; + dev_err(dev, "err inputs for redrive-voltage-level\n"); + goto err; + } + + ret = of_property_read_u32(dev->of_node, "qcom,redrive-load", + &phy->redrive_load); + if (ret) { + dev_err(&pdev->dev, "unable to read redrive load\n"); + goto err; + } + + dev_dbg(dev, "Get FPC re-drive ldo regulator\n"); + } + phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src"); if (IS_ERR(phy->ref_clk_src)) phy->ref_clk_src = NULL; diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 1532cde8a437..7812052dc700 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -99,10 +99,17 @@ static int ark3116_read_reg(struct usb_serial *serial, usb_rcvctrlpipe(serial->dev, 0), 0xfe, 0xc0, 0, reg, buf, 1, ARK_TIMEOUT); - if (result < 0) + if (result < 1) { + dev_err(&serial->interface->dev, + "failed to read register %u: %d\n", + reg, result); + if (result >= 0) + result = -EIO; + return result; - else - return buf[0]; + } + + return buf[0]; } static inline int calc_divisor(int bps) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index c73808f095bb..71133d96f97d 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -99,6 +99,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request, r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, value, index, NULL, 0, DEFAULT_TIMEOUT); + if (r < 0) + dev_err(&dev->dev, "failed to send control message: %d\n", r); return r; } @@ -116,7 +118,20 @@ static int ch341_control_in(struct usb_device *dev, r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, value, index, buf, bufsize, DEFAULT_TIMEOUT); - return r; + if (r < bufsize) { + if (r >= 0) { + dev_err(&dev->dev, + "short control message received (%d < %u)\n", + r, bufsize); + r = -EIO; + } + + dev_err(&dev->dev, "failed to receive control message: %d\n", + r); + return r; + } + + return 0; } static int ch341_set_baudrate(struct usb_device *dev, @@ -158,9 +173,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control) static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) { + const unsigned int size = 2; char *buffer; int r; - const unsigned size = 8; unsigned long flags; buffer = kmalloc(size, GFP_KERNEL); @@ -171,14 +186,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - /* setup the private status if available */ - if (r == 2) { - r = 0; - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; - spin_unlock_irqrestore(&priv->lock, flags); - } else - r = -EPROTO; + spin_lock_irqsave(&priv->lock, flags); + priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; + spin_unlock_irqrestore(&priv->lock, flags); out: kfree(buffer); return r; @@ -188,9 +198,9 @@ out: kfree(buffer); static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) { + const unsigned int size = 2; char *buffer; int r; - const unsigned size = 8; buffer = kmalloc(size, GFP_KERNEL); if (!buffer) @@ -253,7 +263,6 @@ static int ch341_port_probe(struct usb_serial_port *port) spin_lock_init(&priv->lock); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; r = ch341_configure(port->serial->dev, priv); if (r < 0) @@ -315,7 +324,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) r = ch341_configure(serial->dev, priv); if (r) - goto out; + return r; if (tty) ch341_set_termios(tty, port, NULL); @@ -325,12 +334,19 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) if (r) { dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n", __func__, r); - goto out; + return r; } r = usb_serial_generic_open(tty, port); + if (r) + goto err_kill_interrupt_urb; -out: return r; + return 0; + +err_kill_interrupt_urb: + usb_kill_urb(port->interrupt_in_urb); + + return r; } /* Old_termios contains the original termios settings and @@ -345,26 +361,25 @@ static void ch341_set_termios(struct tty_struct *tty, baud_rate = tty_get_baud_rate(tty); - priv->baud_rate = baud_rate; - if (baud_rate) { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); + priv->baud_rate = baud_rate; ch341_set_baudrate(port->serial->dev, priv); - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); } - ch341_set_handshake(port->serial->dev, priv->line_control); - /* Unimplemented: * (cflag & CSIZE) : data bits [5, 8] * (cflag & PARENB) : parity {NONE, EVEN, ODD} * (cflag & CSTOPB) : stop bits [1, 2] */ + + spin_lock_irqsave(&priv->lock, flags); + if (C_BAUD(tty) == B0) + priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) + priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); + spin_unlock_irqrestore(&priv->lock, flags); + + ch341_set_handshake(port->serial->dev, priv->line_control); } static void ch341_break_ctl(struct tty_struct *tty, int break_state) @@ -539,14 +554,23 @@ static int ch341_tiocmget(struct tty_struct *tty) static int ch341_reset_resume(struct usb_serial *serial) { - struct ch341_private *priv; - - priv = usb_get_serial_port_data(serial->port[0]); + struct usb_serial_port *port = serial->port[0]; + struct ch341_private *priv = usb_get_serial_port_data(port); + int ret; /* reconfigure ch341 serial port after bus-reset */ ch341_configure(serial->dev, priv); - return 0; + if (test_bit(ASYNCB_INITIALIZED, &port->port.flags)) { + ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); + if (ret) { + dev_err(&port->dev, "failed to submit interrupt urb: %d\n", + ret); + return ret; + } + } + + return usb_serial_generic_resume(serial); } static struct usb_serial_driver ch341_device = { diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index fe7452f0f38a..33cec50978b8 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -171,6 +171,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */ + { USB_DEVICE(0x1901, 0x0195) }, /* GE B850/B650/B450 CP2104 DP UART interface */ + { USB_DEVICE(0x1901, 0x0196) }, /* GE B850 CP2105 DP UART interface */ { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 2916dea3ede8..8948f375e75d 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -50,6 +50,7 @@ #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ +static int cyberjack_attach(struct usb_serial *serial); static int cyberjack_port_probe(struct usb_serial_port *port); static int cyberjack_port_remove(struct usb_serial_port *port); static int cyberjack_open(struct tty_struct *tty, @@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = { .description = "Reiner SCT Cyberjack USB card reader", .id_table = id_table, .num_ports = 1, + .attach = cyberjack_attach, .port_probe = cyberjack_port_probe, .port_remove = cyberjack_port_remove, .open = cyberjack_open, @@ -100,6 +102,14 @@ struct cyberjack_private { short wrsent; /* Data already sent */ }; +static int cyberjack_attach(struct usb_serial *serial) +{ + if (serial->num_bulk_out < serial->num_ports) + return -ENODEV; + + return 0; +} + static int cyberjack_port_probe(struct usb_serial_port *port) { struct cyberjack_private *priv; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 3df7b7ec178e..e0b1fe2f60e1 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1483,16 +1483,20 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial *serial = port->serial; struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; int i; unsigned int rts; + if (urb->actual_length < 4) + return -1; + /* handle each oob command */ - for (i = 0; i < urb->actual_length - 3;) { - opcode = ((unsigned char *)urb->transfer_buffer)[i++]; - line = ((unsigned char *)urb->transfer_buffer)[i++]; - status = ((unsigned char *)urb->transfer_buffer)[i++]; - val = ((unsigned char *)urb->transfer_buffer)[i++]; + for (i = 0; i < urb->actual_length - 3; i += 4) { + opcode = buf[i]; + line = buf[i + 1]; + status = buf[i + 2]; + val = buf[i + 3]; dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", opcode, line, status, val); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d3d6ec455151..19a98116c2ab 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1807,8 +1807,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) mutex_init(&priv->cfg_lock); - priv->flags = ASYNC_LOW_LATENCY; - if (quirk && quirk->port_probe) quirk->port_probe(priv); @@ -2072,6 +2070,20 @@ static int ftdi_process_packet(struct usb_serial_port *port, priv->prev_status = status; } + /* save if the transmitter is empty or not */ + if (packet[1] & FTDI_RS_TEMT) + priv->transmit_empty = 1; + else + priv->transmit_empty = 0; + + len -= 2; + if (!len) + return 0; /* status only */ + + /* + * Break and error status must only be processed for packets with + * data payload to avoid over-reporting. + */ flag = TTY_NORMAL; if (packet[1] & FTDI_RS_ERR_MASK) { /* Break takes precedence over parity, which takes precedence @@ -2094,15 +2106,6 @@ static int ftdi_process_packet(struct usb_serial_port *port, } } - /* save if the transmitter is empty or not */ - if (packet[1] & FTDI_RS_TEMT) - priv->transmit_empty = 1; - else - priv->transmit_empty = 0; - - len -= 2; - if (!len) - return 0; /* status only */ port->icount.rx += len; ch = packet + 2; @@ -2433,8 +2436,12 @@ static int ftdi_get_modem_status(struct usb_serial_port *port, FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, 0, priv->interface, buf, len, WDR_TIMEOUT); - if (ret < 0) { + + /* NOTE: We allow short responses and handle that below. */ + if (ret < 1) { dev_err(&port->dev, "failed to get modem status: %d\n", ret); + if (ret >= 0) + ret = -EIO; ret = usb_translate_errors(ret); goto out; } diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index db591d19d416..37d0e8cc7af6 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1044,6 +1044,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); count = status; + kfree(buffer); } /* we are done with this urb, so let the host driver diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 1947ea0e0988..b63a6c3899c5 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2761,6 +2761,11 @@ static int edge_startup(struct usb_serial *serial) EDGE_COMPATIBILITY_MASK1, EDGE_COMPATIBILITY_MASK2 }; + if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + dev = serial->dev; /* create our private serial structure */ diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index fce82fd79f77..f1a8fdcd8674 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial, dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__); - /* return an error on purpose */ - return -ENODEV; + return 1; } stayinbootmode: @@ -1508,7 +1507,7 @@ stayinbootmode: dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__); serial->product_info.TiMode = TI_MODE_BOOT; - return 0; + return 1; } static int ti_do_config(struct edgeport_port *port, int feature, int on) @@ -1675,6 +1674,12 @@ static void edge_interrupt_callback(struct urb *urb) function = TIUMP_GET_FUNC_FROM_CODE(data[0]); dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__, port_number, function, data[1]); + + if (port_number >= edge_serial->serial->num_ports) { + dev_err(dev, "bad port number %d\n", port_number); + goto exit; + } + port = edge_serial->serial->port[port_number]; edge_port = usb_get_serial_port_data(port); if (!edge_port) { @@ -1756,7 +1761,7 @@ static void edge_bulk_in_callback(struct urb *urb) port_number = edge_port->port->port_number; - if (edge_port->lsr_event) { + if (urb->actual_length > 0 && edge_port->lsr_event) { edge_port->lsr_event = 0; dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n", __func__, port_number, edge_port->lsr_mask, *data); @@ -2549,6 +2554,13 @@ static int edge_startup(struct usb_serial *serial) int status; u16 product_id; + /* Make sure we have the required endpoints when in download mode. */ + if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) { + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) + return -ENODEV; + } + /* create our private serial structure */ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); if (!edge_serial) @@ -2556,14 +2568,18 @@ static int edge_startup(struct usb_serial *serial) mutex_init(&edge_serial->es_lock); edge_serial->serial = serial; + INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); usb_set_serial_data(serial, edge_serial); status = download_fw(edge_serial); - if (status) { + if (status < 0) { kfree(edge_serial); return status; } + if (status > 0) + return 1; /* bind but do not register any ports */ + product_id = le16_to_cpu( edge_serial->serial->dev->descriptor.idProduct); @@ -2575,7 +2591,6 @@ static int edge_startup(struct usb_serial *serial) } } - INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); edge_heartbeat_schedule(edge_serial); return 0; @@ -2583,6 +2598,9 @@ static int edge_startup(struct usb_serial *serial) static void edge_disconnect(struct usb_serial *serial) { + struct edgeport_serial *edge_serial = usb_get_serial_data(serial); + + cancel_delayed_work_sync(&edge_serial->heartbeat_work); } static void edge_release(struct usb_serial *serial) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 5ad4a0fb4b26..7ed7d33d6c10 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -68,6 +68,16 @@ struct iuu_private { u32 clk; }; +static int iuu_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports) + return -ENODEV; + + return 0; +} + static int iuu_port_probe(struct usb_serial_port *port) { struct iuu_private *priv; @@ -1196,6 +1206,7 @@ static struct usb_serial_driver iuu_device = { .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, .init_termios = iuu_init_termios, + .attach = iuu_attach, .port_probe = iuu_port_probe, .port_remove = iuu_port_remove, }; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 4f7e072e4e00..930be98d59b3 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -699,6 +699,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw"); MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); #endif +static int keyspan_pda_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int keyspan_pda_port_probe(struct usb_serial_port *port) { @@ -776,6 +789,7 @@ static struct usb_serial_driver keyspan_pda_device = { .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, .tiocmset = keyspan_pda_tiocmset, + .attach = keyspan_pda_attach, .port_probe = keyspan_pda_port_probe, .port_remove = keyspan_pda_port_remove, }; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index e020ad28a00c..83c823d32ff9 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -192,10 +192,11 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, status_buf, KLSI_STATUSBUF_LEN, 10000 ); - if (rc < 0) - dev_err(&port->dev, "Reading line status failed (error = %d)\n", - rc); - else { + if (rc != KLSI_STATUSBUF_LEN) { + dev_err(&port->dev, "reading line status failed: %d\n", rc); + if (rc >= 0) + rc = -EIO; + } else { status = get_unaligned_le16(status_buf); dev_info(&port->serial->dev->dev, "read status %x %x\n", @@ -296,7 +297,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) rc = usb_serial_generic_open(tty, port); if (rc) { retval = rc; - goto exit; + goto err_free_cfg; } rc = usb_control_msg(port->serial->dev, @@ -311,21 +312,38 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) if (rc < 0) { dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc); retval = rc; + goto err_generic_close; } else dev_dbg(&port->dev, "%s - enabled reading\n", __func__); rc = klsi_105_get_line_state(port, &line_state); - if (rc >= 0) { - spin_lock_irqsave(&priv->lock, flags); - priv->line_state = line_state; - spin_unlock_irqrestore(&priv->lock, flags); - dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state); - retval = 0; - } else + if (rc < 0) { retval = rc; + goto err_disable_read; + } + + spin_lock_irqsave(&priv->lock, flags); + priv->line_state = line_state; + spin_unlock_irqrestore(&priv->lock, flags); + dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, + line_state); + + return 0; -exit: +err_disable_read: + usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR | USB_DIR_OUT, + KL5KUSB105A_SIO_CONFIGURE_READ_OFF, + 0, /* index */ + NULL, 0, + KLSI_TIMEOUT); +err_generic_close: + usb_serial_generic_close(port); +err_free_cfg: kfree(cfg); + return retval; } diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2363654cafc9..813035f51fe7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -51,6 +51,7 @@ /* Function prototypes */ +static int kobil_attach(struct usb_serial *serial); static int kobil_port_probe(struct usb_serial_port *probe); static int kobil_port_remove(struct usb_serial_port *probe); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); @@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = { .description = "KOBIL USB smart card terminal", .id_table = id_table, .num_ports = 1, + .attach = kobil_attach, .port_probe = kobil_port_probe, .port_remove = kobil_port_remove, .ioctl = kobil_ioctl, @@ -113,6 +115,16 @@ struct kobil_private { }; +static int kobil_attach(struct usb_serial *serial) +{ + if (serial->num_interrupt_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n"); + return -ENODEV; + } + + return 0; +} + static int kobil_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 63db004af21f..e56cdb436de3 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -65,8 +65,6 @@ struct moschip_port { struct urb *write_urb_pool[NUM_URBS]; }; -static struct usb_serial_driver moschip7720_2port_driver; - #define USB_VENDOR_ID_MOSCHIP 0x9710 #define MOSCHIP_DEVICE_ID_7720 0x7720 #define MOSCHIP_DEVICE_ID_7715 0x7715 @@ -970,25 +968,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) tty_port_tty_wakeup(&mos7720_port->port->port); } -/* - * mos77xx_probe - * this function installs the appropriate read interrupt endpoint callback - * depending on whether the device is a 7720 or 7715, thus avoiding costly - * run-time checks in the high-frequency callback routine itself. - */ -static int mos77xx_probe(struct usb_serial *serial, - const struct usb_device_id *id) -{ - if (id->idProduct == MOSCHIP_DEVICE_ID_7715) - moschip7720_2port_driver.read_int_callback = - mos7715_interrupt_callback; - else - moschip7720_2port_driver.read_int_callback = - mos7720_interrupt_callback; - - return 0; -} - static int mos77xx_calc_num_ports(struct usb_serial *serial) { u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); @@ -1920,6 +1899,11 @@ static int mos7720_startup(struct usb_serial *serial) u16 product; int ret_val; + if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) { + dev_err(&serial->interface->dev, "missing bulk endpoints\n"); + return -ENODEV; + } + product = le16_to_cpu(serial->dev->descriptor.idProduct); dev = serial->dev; @@ -1944,19 +1928,18 @@ static int mos7720_startup(struct usb_serial *serial) tmp->interrupt_in_endpointAddress; serial->port[1]->interrupt_in_urb = NULL; serial->port[1]->interrupt_in_buffer = NULL; + + if (serial->port[0]->interrupt_in_urb) { + struct urb *urb = serial->port[0]->interrupt_in_urb; + + urb->complete = mos7715_interrupt_callback; + } } /* setting configuration feature to one */ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000); - /* start the interrupt urb */ - ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); - if (ret_val) - dev_err(&dev->dev, - "%s - Error %d submitting control urb\n", - __func__, ret_val); - #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT if (product == MOSCHIP_DEVICE_ID_7715) { ret_val = mos7715_parport_init(serial); @@ -1964,6 +1947,13 @@ static int mos7720_startup(struct usb_serial *serial) return ret_val; } #endif + /* start the interrupt urb */ + ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); + if (ret_val) { + dev_err(&dev->dev, "failed to submit interrupt urb: %d\n", + ret_val); + } + /* LSR For Port 1 */ read_mos_reg(serial, 0, MOS7720_LSR, &data); dev_dbg(&dev->dev, "LSR:%x\n", data); @@ -1973,6 +1963,8 @@ static int mos7720_startup(struct usb_serial *serial) static void mos7720_release(struct usb_serial *serial) { + usb_kill_urb(serial->port[0]->interrupt_in_urb); + #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT /* close the parallel port */ @@ -2056,7 +2048,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { .close = mos7720_close, .throttle = mos7720_throttle, .unthrottle = mos7720_unthrottle, - .probe = mos77xx_probe, .attach = mos7720_startup, .release = mos7720_release, .port_probe = mos7720_port_probe, @@ -2070,7 +2061,7 @@ static struct usb_serial_driver moschip7720_2port_driver = { .chars_in_buffer = mos7720_chars_in_buffer, .break_ctl = mos7720_break, .read_bulk_callback = mos7720_bulk_in_callback, - .read_int_callback = NULL /* dynamically assigned in probe() */ + .read_int_callback = mos7720_interrupt_callback, }; static struct usb_serial_driver * const serial_drivers[] = { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 7f3ddd7ba2ce..d17685cc00c9 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1024,6 +1024,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ if (port0->open_ports == 1) { + /* FIXME: Buffer never NULL, so URB is not submitted. */ if (serial->port[0]->interrupt_in_buffer == NULL) { /* set up interrupt urb */ usb_fill_int_urb(serial->port[0]->interrupt_in_urb, @@ -2116,6 +2117,18 @@ static int mos7840_calc_num_ports(struct usb_serial *serial) return mos7840_num_ports; } +static int mos7840_attach(struct usb_serial *serial) +{ + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports || + serial->num_interrupt_in < 1) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int mos7840_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -2391,6 +2404,7 @@ static struct usb_serial_driver moschip7840_4port_device = { .tiocmset = mos7840_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, + .attach = mos7840_attach, .port_probe = mos7840_port_probe, .port_remove = mos7840_port_remove, .read_bulk_callback = mos7840_bulk_in_callback, diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index f6c6900bccf0..76564b3bebb9 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -38,6 +38,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room(struct tty_struct *tty); static void omninet_disconnect(struct usb_serial *serial); +static int omninet_attach(struct usb_serial *serial); static int omninet_port_probe(struct usb_serial_port *port); static int omninet_port_remove(struct usb_serial_port *port); @@ -56,6 +57,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net lcd plus usb", .id_table = id_table, .num_ports = 1, + .attach = omninet_attach, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, .open = omninet_open, @@ -104,6 +106,17 @@ struct omninet_data { __u8 od_outseq; /* Sequence number for bulk_out URBs */ }; +static int omninet_attach(struct usb_serial *serial) +{ + /* The second bulk-out endpoint is used for writing. */ + if (serial->num_bulk_out < 2) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int omninet_port_probe(struct usb_serial_port *port) { struct omninet_data *od; @@ -129,12 +142,6 @@ static int omninet_port_remove(struct usb_serial_port *port) static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport; - - wport = serial->port[1]; - tty_port_tty_set(&wport->port, tty); - return usb_serial_generic_open(tty, port); } diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 4b7bfb394a32..64bf258e7e00 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -142,7 +142,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(port->serial->dev, port->read_urb->pipe); res = usb_serial_generic_open(tty, port); - if (!res) + if (res) return res; /* Request CTS line state, sometimes during opening the current diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 9894e341c6ac..42cc72e54c05 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -268,6 +268,8 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_CC864_SINGLE 0x1006 #define TELIT_PRODUCT_DE910_DUAL 0x1010 #define TELIT_PRODUCT_UE910_V2 0x1012 +#define TELIT_PRODUCT_LE922_USBCFG1 0x1040 +#define TELIT_PRODUCT_LE922_USBCFG2 0x1041 #define TELIT_PRODUCT_LE922_USBCFG0 0x1042 #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 @@ -1210,6 +1212,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1), + .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2), + .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), @@ -1989,6 +1995,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) }, + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ @@ -2000,6 +2007,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a4b88bc038b6..b8bf52bf7a94 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int oti6858_attach(struct usb_serial *serial); static int oti6858_port_probe(struct usb_serial_port *port); static int oti6858_port_remove(struct usb_serial_port *port); @@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = { .write_bulk_callback = oti6858_write_bulk_callback, .write_room = oti6858_write_room, .chars_in_buffer = oti6858_chars_in_buffer, + .attach = oti6858_attach, .port_probe = oti6858_port_probe, .port_remove = oti6858_port_remove, }; @@ -324,6 +326,20 @@ static void send_data(struct work_struct *work) usb_serial_port_softint(port); } +static int oti6858_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int oti6858_port_probe(struct usb_serial_port *port) { struct oti6858_private *priv; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ae682e4eeaef..1db4b61bdf7b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -49,6 +49,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, @@ -220,9 +221,17 @@ static int pl2303_probe(struct usb_serial *serial, static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; + unsigned char num_ports = serial->num_ports; enum pl2303_type type = TYPE_01; unsigned char *buf; + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index e3b7af8adfb7..09d9be88209e 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -27,6 +27,7 @@ #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 #define ATEN_PRODUCT_ID 0x2008 +#define ATEN_PRODUCT_ID2 0x2118 #define IODATA_VENDOR_ID 0x04bb #define IODATA_PRODUCT_ID 0x0a03 diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 1bc6089b9008..696458db7e3c 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -124,6 +124,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */ {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */ {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ + {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */ {USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */ {USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ {USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */ diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index b18974cbd995..a3ed07c58754 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -408,16 +408,12 @@ static void qt2_close(struct usb_serial_port *port) { struct usb_serial *serial; struct qt2_port_private *port_priv; - unsigned long flags; int i; serial = port->serial; port_priv = usb_get_serial_port_data(port); - spin_lock_irqsave(&port_priv->urb_lock, flags); usb_kill_urb(port_priv->write_urb); - port_priv->urb_in_use = false; - spin_unlock_irqrestore(&port_priv->urb_lock, flags); /* flush the port transmit buffer */ i = usb_control_msg(serial->dev, diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index b2dff0f14743..236ea43f7815 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -205,6 +205,11 @@ static void safe_process_read_urb(struct urb *urb) if (!safe) goto out; + if (length < 2) { + dev_err(&port->dev, "malformed packet\n"); + return; + } + fcs = fcs_compute10(data, length, CRC10_INITFCS); if (fcs) { dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index ef0dbf0703c5..ddfd787c461c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial, return 0; } +static int spcp8x5_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int spcp8x5_port_probe(struct usb_serial_port *port) { const struct usb_device_id *id = usb_get_serial_data(port->serial); @@ -219,11 +232,17 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, buf, 1, 100); - if (ret < 0) + if (ret < 1) { dev_err(&port->dev, "failed to get modem status: %d\n", ret); + if (ret >= 0) + ret = -EIO; + goto out; + } dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf); *status = *buf; + ret = 0; +out: kfree(buf); return ret; @@ -477,6 +496,7 @@ static struct usb_serial_driver spcp8x5_device = { .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, .probe = spcp8x5_probe, + .attach = spcp8x5_attach, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 2694df2f4559..535fcfafc097 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -339,6 +339,13 @@ static int ti_startup(struct usb_serial *serial) goto free_tdev; } + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + status = -ENODEV; + goto free_tdev; + } + return 0; free_tdev: diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 7ffe4209067b..640a2e2ec04d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2135,6 +2135,13 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), +/* Reported-by George Cherian <george.cherian@cavium.com> */ +UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999, + "JMicron", + "JMS56x", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + /* * Patch by Constantin Baranov <const@tltsu.ru> * Report by Andreas Koenecke. diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index 021755f7f32d..a04fa896361a 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c @@ -163,17 +163,18 @@ void fb_dealloc_cmap(struct fb_cmap *cmap) int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) { - int tooff = 0, fromoff = 0; - int size; + unsigned int tooff = 0, fromoff = 0; + size_t size; if (to->start > from->start) fromoff = to->start - from->start; else tooff = from->start - to->start; - size = to->len - tooff; - if (size > (int) (from->len - fromoff)) - size = from->len - fromoff; - if (size <= 0) + if (fromoff >= from->len || tooff >= to->len) + return -EINVAL; + + size = min_t(size_t, to->len - tooff, from->len - fromoff); + if (size == 0) return -EINVAL; size *= sizeof(u16); @@ -187,22 +188,22 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) { - u32 tooff = 0, fromoff = 0; - u32 size; + unsigned int tooff = 0, fromoff = 0; + size_t size; if (to->start > from->start) fromoff = to->start - from->start; else tooff = from->start - to->start; - if ((to->len <= tooff) || (from->len <= fromoff)) + if (fromoff >= from->len || tooff >= to->len) return -EINVAL; - size = to->len - tooff; - if (size > (from->len - fromoff)) - size = from->len - fromoff; - size *= sizeof(u16); + size = min_t(size_t, to->len - tooff, from->len - fromoff); if (size == 0) return -EINVAL; + size *= sizeof(u16); + + if (copy_to_user(to->red+tooff, from->red+fromoff, size)) return -EFAULT; diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index d9a4bd91f3eb..796246a856b4 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -210,6 +210,15 @@ enum mdss_mdp_pipe_type { MDSS_MDP_PIPE_TYPE_MAX, }; +enum mdss_mdp_intf_index { + MDSS_MDP_NO_INTF, + MDSS_MDP_INTF0, + MDSS_MDP_INTF1, + MDSS_MDP_INTF2, + MDSS_MDP_INTF3, + MDSS_MDP_MAX_INTF +}; + struct reg_bus_client { char name[MAX_CLIENT_NAME_LEN]; short usecase_ndx; diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index 3e1dbba7c9ae..e60869144339 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -44,6 +44,8 @@ #define INVALID_XIN_ID 0xFF +static DEFINE_MUTEX(mdss_debug_lock); + static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00}; static int panel_debug_base_open(struct inode *inode, struct file *file) @@ -93,8 +95,10 @@ static ssize_t panel_debug_base_offset_write(struct file *file, if (cnt > (dbg->max_offset - off)) cnt = dbg->max_offset - off; + mutex_lock(&mdss_debug_lock); dbg->off = off; dbg->cnt = cnt; + mutex_unlock(&mdss_debug_lock); pr_debug("offset=%x cnt=%d\n", off, cnt); @@ -114,15 +118,21 @@ static ssize_t panel_debug_base_offset_read(struct file *file, if (*ppos) return 0; /* the end */ + mutex_lock(&mdss_debug_lock); len = snprintf(buf, sizeof(buf), "0x%02zx %zx\n", dbg->off, dbg->cnt); - if (len < 0 || len >= sizeof(buf)) + if (len < 0 || len >= sizeof(buf)) { + mutex_unlock(&mdss_debug_lock); return 0; + } - if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { + mutex_unlock(&mdss_debug_lock); return -EFAULT; + } *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; } @@ -220,11 +230,16 @@ static ssize_t panel_debug_base_reg_read(struct file *file, if (!dbg) return -ENODEV; - if (!dbg->cnt) + mutex_lock(&mdss_debug_lock); + if (!dbg->cnt) { + mutex_unlock(&mdss_debug_lock); return 0; + } - if (*ppos) + if (*ppos) { + mutex_unlock(&mdss_debug_lock); return 0; /* the end */ + } /* '0x' + 2 digit + blank = 5 bytes for each number */ reg_buf_len = (dbg->cnt * PANEL_REG_FORMAT_LEN) @@ -265,11 +280,13 @@ static ssize_t panel_debug_base_reg_read(struct file *file, kfree(panel_reg_buf); *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; read_reg_fail: kfree(rx_buf); kfree(panel_reg_buf); + mutex_unlock(&mdss_debug_lock); return rc; } @@ -403,8 +420,10 @@ static ssize_t mdss_debug_base_offset_write(struct file *file, if (cnt > (dbg->max_offset - off)) cnt = dbg->max_offset - off; + mutex_lock(&mdss_debug_lock); dbg->off = off; dbg->cnt = cnt; + mutex_unlock(&mdss_debug_lock); pr_debug("offset=%x cnt=%x\n", off, cnt); @@ -424,15 +443,21 @@ static ssize_t mdss_debug_base_offset_read(struct file *file, if (*ppos) return 0; /* the end */ + mutex_lock(&mdss_debug_lock); len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); - if (len < 0 || len >= sizeof(buf)) + if (len < 0 || len >= sizeof(buf)) { + mutex_unlock(&mdss_debug_lock); return 0; + } - if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { + mutex_unlock(&mdss_debug_lock); return -EFAULT; + } *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; } @@ -489,6 +514,8 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, return -ENODEV; } + mutex_lock(&mdss_debug_lock); + if (!dbg->buf) { char dump_buf[64]; char *ptr; @@ -500,6 +527,7 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, if (!dbg->buf) { pr_err("not enough memory to hold reg dump\n"); + mutex_unlock(&mdss_debug_lock); return -ENOMEM; } @@ -530,17 +558,21 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, dbg->buf_len = tot; } - if (*ppos >= dbg->buf_len) + if (*ppos >= dbg->buf_len) { + mutex_unlock(&mdss_debug_lock); return 0; /* done reading */ + } len = min(count, dbg->buf_len - (size_t) *ppos); if (copy_to_user(user_buf, dbg->buf + *ppos, len)) { pr_err("failed to copy to user\n"); + mutex_unlock(&mdss_debug_lock); return -EFAULT; } *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; } diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index bd5e8862f6d0..619fab4cdb93 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1013,9 +1013,10 @@ static int dp_get_cable_status(struct platform_device *pdev, u32 vote) return hpd; } -static bool mdss_dp_is_dvi_mode(struct mdss_dp_drv_pdata *dp) +static bool mdss_dp_sink_audio_supp(struct mdss_dp_drv_pdata *dp) { - return hdmi_edid_is_dvi_mode(dp->panel_data.panel_info.edid_data); + return hdmi_edid_is_audio_supported( + dp->panel_data.panel_info.edid_data); } static int dp_audio_info_setup(struct platform_device *pdev, @@ -1290,6 +1291,23 @@ exit: return ret; } +static u32 mdss_dp_calc_max_pclk_rate(struct mdss_dp_drv_pdata *dp) +{ + u32 bpp = mdss_dp_get_bpp(dp); + u32 max_link_rate_khz = dp->dpcd.max_link_rate * + (DP_LINK_RATE_MULTIPLIER / 100); + u32 max_data_rate_khz = dp->dpcd.max_lane_count * + max_link_rate_khz * 8 / 10; + u32 max_pclk_rate_khz = max_data_rate_khz / bpp; + + pr_debug("bpp=%d, max_lane_cnt=%d, max_link_rate=%dKHz\n", bpp, + dp->dpcd.max_lane_count, max_link_rate_khz); + pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n", + max_data_rate_khz, max_pclk_rate_khz); + + return max_pclk_rate_khz; +} + static void mdss_dp_set_clock_rate(struct mdss_dp_drv_pdata *dp, char *name, u32 rate) { @@ -1689,14 +1707,17 @@ static int mdss_dp_send_audio_notification( goto end; } - if (!mdss_dp_is_dvi_mode(dp) || dp->audio_test_req) { + if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) { dp->audio_test_req = false; + pr_debug("sending audio notification\n"); flags |= MSM_EXT_DISP_HPD_AUDIO; if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, dp->ext_audio_data.type, val, flags); + } else { + pr_debug("sink does not support audio\n"); } end: @@ -1772,8 +1793,6 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata) dp_drv->edid_buf = edid_init_data.buf; dp_drv->edid_buf_size = edid_init_data.buf_size; - mdss_dp_set_default_resolution(dp_drv); - return 0; } @@ -1981,35 +2000,51 @@ end: static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) { int ret; + u32 max_pclk_khz; if (dp->sink_info_read) return 0; pr_debug("start\n"); - mdss_dp_dpcd_cap_read(dp); + ret = mdss_dp_dpcd_cap_read(dp); + if (ret || !mdss_dp_aux_is_link_rate_valid(dp->dpcd.max_link_rate) || + !mdss_dp_aux_is_lane_count_valid(dp->dpcd.max_lane_count)) { + /* + * If there is an error in parsing DPCD or if DPCD reports + * unsupported link parameters then set the default link + * parameters and continue to read EDID. + */ + pr_err("dpcd read failed, set failsafe parameters\n"); + mdss_dp_set_default_link_parameters(dp); + } ret = mdss_dp_edid_read(dp); if (ret) { - pr_debug("edid read error, setting default resolution\n"); - - mdss_dp_set_default_resolution(dp); - mdss_dp_set_default_link_parameters(dp); + pr_err("edid read error, setting default resolution\n"); goto notify; } + max_pclk_khz = mdss_dp_calc_max_pclk_rate(dp); + hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data, + min(dp->max_pclk_khz, max_pclk_khz)); + ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data); if (ret) { pr_err("edid parse failed, setting default resolution\n"); - - mdss_dp_set_default_resolution(dp); - mdss_dp_set_default_link_parameters(dp); goto notify; } dp->sink_info_read = true; notify: + if (ret) { + /* set failsafe parameters */ + pr_info("falling back to failsafe mode\n"); + mdss_dp_set_default_resolution(dp); + mdss_dp_set_default_link_parameters(dp); + } + /* Check if there is a PHY_TEST_PATTERN request when we get HPD high. * Update the DP driver with the test parameters including link rate, * lane count, voltage level, and pre-emphasis level. Do not notify diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 34b652d843aa..4decb26ea073 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -1038,7 +1038,7 @@ static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc) void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); -void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); +int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp); void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *dp); int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 479c367fdc92..8566b1d6985a 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -826,9 +826,9 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) return ret; } -static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, - int len) +int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) { + int const len = 16; /* read 16 bytes */ char *bp; char data; struct dpcd_cap *cap; @@ -838,8 +838,15 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, rlen = dp_aux_read_buf(ep, 0, len, 0); if (rlen <= 0) { pr_err("edp aux read failed\n"); - return; + return rlen; + } + + if (rlen != len) { + pr_debug("Read size expected(%d) bytes, actual(%d) bytes\n", + len, rlen); + return -EINVAL; } + rp = &ep->rxp; cap = &ep->dpcd; bp = rp->data; @@ -849,15 +856,11 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, data = *bp++; /* byte 0 */ cap->major = (data >> 4) & 0x0f; cap->minor = data & 0x0f; - if (--rlen <= 0) - return; pr_debug("version: %d.%d\n", cap->major, cap->minor); data = *bp++; /* byte 1 */ /* 162, 270 and 540 MB, symbol rate, NOT bit rate */ cap->max_link_rate = data; - if (--rlen <= 0) - return; pr_debug("link_rate=%d\n", cap->max_link_rate); data = *bp++; /* byte 2 */ @@ -873,8 +876,6 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, data &= 0x0f; cap->max_lane_count = data; - if (--rlen <= 0) - return; pr_debug("lane_count=%d\n", cap->max_lane_count); data = *bp++; /* byte 3 */ @@ -887,14 +888,10 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, cap->flags |= DPCD_NO_AUX_HANDSHAKE; pr_debug("NO Link Training\n"); } - if (--rlen <= 0) - return; data = *bp++; /* byte 4 */ cap->num_rx_port = (data & BIT(0)) + 1; pr_debug("rx_ports=%d", cap->num_rx_port); - if (--rlen <= 0) - return; data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */ cap->downstream_port.dfp_present = data & BIT(0); @@ -907,13 +904,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n", cap->downstream_port.format_conversion, cap->downstream_port.detailed_cap_info_available); - if (--rlen <= 0) - return; bp += 1; /* Skip Byte 6 */ - rlen -= 1; - if (rlen <= 0) - return; data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */ cap->downstream_port.dfp_count = data & 0x7; @@ -923,34 +915,23 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, cap->downstream_port.dfp_count, cap->downstream_port.msa_timing_par_ignored); pr_debug("oui_support = %d\n", cap->downstream_port.oui_support); - if (--rlen <= 0) - return; data = *bp++; /* byte 8 */ if (data & BIT(1)) { cap->flags |= DPCD_PORT_0_EDID_PRESENTED; pr_debug("edid presented\n"); } - if (--rlen <= 0) - return; data = *bp++; /* byte 9 */ cap->rx_port0_buf_size = (data + 1) * 32; pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size); - if (--rlen <= 0) - return; bp += 2; /* skip 10, 11 port1 capability */ - rlen -= 2; - if (rlen <= 0) - return; data = *bp++; /* byte 12 */ cap->i2c_speed_ctrl = data; if (cap->i2c_speed_ctrl > 0) pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl); - if (--rlen <= 0) - return; data = *bp++; /* byte 13 */ cap->scrambler_reset = data & BIT(0); @@ -962,8 +943,6 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, pr_debug("enhanced_framing=%d\n", cap->enhanced_frame); - if (--rlen <= 0) - return; data = *bp++; /* byte 14 */ if (data == 0) @@ -974,6 +953,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, cap->training_read_interval); dp_sink_parse_sink_count(ep); + + return 0; } int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len) @@ -2379,11 +2360,6 @@ clear: return ret; } -void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) -{ - dp_sink_capability_read(ep, 16); -} - void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep) { dp_sink_parse_sink_count(ep); diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 88eb794a5ff5..17722eac3006 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -34,7 +34,6 @@ #include "mdss_dsi_phy.h" #include "mdss_dba_utils.h" -#define XO_CLK_RATE 19200000 #define CMDLINE_DSI_CTL_NUM_STRING_LEN 2 /* Master structure to hold all the information about the DSI/panel */ @@ -1464,15 +1463,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) if (mipi->init_delay) usleep_range(mipi->init_delay, mipi->init_delay); - if (mipi->force_clk_lane_hs) { - u32 tmp; - - tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac); - tmp |= (1<<28); - MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp); - wmb(); - } - if (pdata->panel_info.type == MIPI_CMD_PANEL) mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle, MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF); @@ -1871,7 +1861,7 @@ static void __mdss_dsi_dyn_refresh_config( static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) { - u32 esc_clk_rate = XO_CLK_RATE; + u32 esc_clk_rate_hz; u32 pipe_delay, pipe_delay2 = 0, pll_delay; u32 hsync_period = 0; u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio; @@ -1898,9 +1888,11 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) pinfo = &pdata->panel_info; pd = &(pinfo->mipi.dsi_phy_db); - pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate); - byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate); - hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / esc_clk_rate); + esc_clk_rate_hz = ctrl_pdata->esc_clk_rate_hz; + pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate_hz); + byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate_hz); + hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / + esc_clk_rate_hz); hsync_period = mdss_panel_get_htotal(pinfo, true); pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio; @@ -1922,7 +1914,7 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) ((pd->timing[4] >> 1) + 1)) / hr_bit_to_esc_ratio); /* 130 us pll delay recommended by h/w doc */ - pll_delay = ((130 * esc_clk_rate) / 1000000) * 2; + pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2; MIPI_OUTP((ctrl_pdata->ctrl_base) + DSI_DYNAMIC_REFRESH_PIPE_DELAY, pipe_delay); @@ -2515,6 +2507,15 @@ static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl, return 0; } +static int mdss_dsi_register_clamp_handler(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_intf_ulp_clamp *clamp_handler) +{ + mutex_lock(&ctrl->mutex); + ctrl->clamp_handler = clamp_handler; + mutex_unlock(&ctrl->mutex); + return 0; +} + static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev) { struct device_node *fb_node; @@ -2699,6 +2700,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_register_mdp_callback(ctrl_pdata, (struct mdss_intf_recovery *)arg); break; + case MDSS_EVENT_REGISTER_CLAMP_HANDLER: + rc = mdss_dsi_register_clamp_handler(ctrl_pdata, + (struct mdss_intf_ulp_clamp *)arg); + break; case MDSS_EVENT_DSI_DYNAMIC_SWITCH: mode = (u32)(unsigned long) arg; mdss_dsi_switch_mode(pdata, mode); @@ -3057,7 +3062,7 @@ static int mdss_dsi_set_clk_rates(struct mdss_dsi_ctrl_pdata *ctrl_pdata) rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle, MDSS_DSI_LINK_ESC_CLK, - 19200000, + ctrl_pdata->esc_clk_rate_hz, MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON); if (rc) { pr_err("%s: dsi_esc_clk - clk_set_rate failed\n", @@ -4236,6 +4241,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev, pr_debug("%s: pclk=%d, bclk=%d\n", __func__, ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate); + ctrl_pdata->esc_clk_rate_hz = pinfo->esc_clk_rate_hz; + pr_debug("%s: esc clk=%d\n", __func__, + ctrl_pdata->esc_clk_rate_hz); rc = mdss_dsi_get_dt_vreg_data(&ctrl_pdev->dev, pan_node, &ctrl_pdata->panel_power_data, DSI_PANEL_PM); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index bd9fd6c7d6c5..2a76466abf3e 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -477,6 +477,7 @@ struct mdss_dsi_ctrl_pdata { u32 byte_clk_rate; u32 pclk_rate_bkp; u32 byte_clk_rate_bkp; + u32 esc_clk_rate_hz; bool refresh_clk_rate; /* flag to recalculate clk_rate */ struct dss_module_power panel_power_data; struct dss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */ @@ -484,6 +485,7 @@ struct mdss_dsi_ctrl_pdata { struct mdss_hw *dsi_hw; struct mdss_intf_recovery *recovery; struct mdss_intf_recovery *mdp_callback; + struct mdss_intf_ulp_clamp *clamp_handler; struct dsi_panel_cmds on_cmds; struct dsi_panel_cmds post_dms_on_cmds; @@ -697,6 +699,8 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, struct dsc_desc *dsc); void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl); void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl); +void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, + u32 bits, int set); void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off, u32 mask, u32 val); int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl); diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 98c5eda02f5b..9f4b7eb52492 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -41,6 +41,8 @@ #define LANE_SWAP_CTRL 0x0B0 #define LOGICAL_LANE_SWAP_CTRL 0x310 +#define MAX_BTA_WAIT_RETRY 5 + #define CEIL(x, y) (((x) + ((y)-1)) / (y)) struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; @@ -600,7 +602,7 @@ error: return rc; } -static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, +void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, u32 bits, int set) { u32 data; @@ -611,6 +613,7 @@ static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, else data &= ~bits; MIPI_OUTP(ctrl->ctrl_base + 0x0ac, data); + wmb(); /* make sure write happens */ } @@ -1451,8 +1454,7 @@ static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl) if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) { mdss_dsi_wait4video_done(ctrl); v_total = mdss_panel_get_vtotal(pinfo); - v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_front_porch + - pinfo->lcdc.v_pulse_width; + v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width; if (pinfo->dynamic_fps && pinfo->current_fps) fps = pinfo->current_fps; else @@ -1483,6 +1485,8 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) int ret = 0; unsigned long flag; int ignore_underflow = 0; + int retry_count = 0; + int in_blanking = 0; if (ctrl_pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -1508,7 +1512,25 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) reinit_completion(&ctrl_pdata->bta_comp); mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM); spin_unlock_irqrestore(&ctrl_pdata->mdp_lock, flag); +wait: mdss_dsi_wait4video_eng_busy(ctrl_pdata); + if (ctrl_pdata->panel_mode == DSI_VIDEO_MODE) { + in_blanking = ctrl_pdata->mdp_callback->fxn( + ctrl_pdata->mdp_callback->data, + MDP_INTF_CALLBACK_CHECK_LINE_COUNT); + /* Try for maximum of 5 attempts */ + if (in_blanking && (retry_count < MAX_BTA_WAIT_RETRY)) { + pr_debug("%s: not in active region\n", __func__); + retry_count++; + goto wait; + } + } + if (retry_count == MAX_BTA_WAIT_RETRY) + MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", + "dsi0_phy", "dsi1_ctrl", "dsi1_phy", + "vbif", "vbif_nrt", "dbg_bus", + "vbif_dbg_bus", "panic"); + /* mask out overflow errors */ if (ignore_underflow) mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000); diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 7cc9ce6e034d..bf701e2a4ac5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -2819,6 +2819,13 @@ static int mdss_panel_parse_dt(struct device_node *np, MSM_DBA_CHIP_NAME_MAX_LEN); } + rc = of_property_read_u32(np, + "qcom,mdss-dsi-host-esc-clk-freq-hz", + &pinfo->esc_clk_rate_hz); + if (rc) + pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; + pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); + return 0; error: diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index bf66c0cd430c..fe79b6fd52b4 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1275,6 +1275,7 @@ static int mdss_fb_probe(struct platform_device *pdev) mfd->fb_imgType = MDP_RGBA_8888; mfd->calib_mode_bl = 0; mfd->unset_bl_level = U32_MAX; + mfd->bl_extn_level = -1; mfd->pdev = pdev; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 3d8fe2ceebac..37c4be6135aa 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -137,6 +137,7 @@ struct hdmi_edid_ctrl { u16 video_latency; u32 present_3d; u32 page_id; + bool basic_audio_supp; u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE]; int adb_size; u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE]; @@ -1289,6 +1290,14 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl, return; } + /* Check if sink supports basic audio */ + if (in_buf[3] & BIT(6)) + edid_ctrl->basic_audio_supp = true; + else + edid_ctrl->basic_audio_supp = false; + pr_debug("%s: basic audio supported: %s\n", __func__, + edid_ctrl->basic_audio_supp ? "true" : "false"); + vsd = hdmi_edid_find_hfvsdb(in_buf); if (vsd) { @@ -1501,6 +1510,17 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl, */ active_h = ((((u32)data_buf[0x4] >> 0x4) & 0xF) << 8) | data_buf[0x2]; + /* + * It is possible that a sink might try to fit in the resolution + * which has an active_h of 4096 into a DTD. However, DTD has only + * 12 bit to represent active_h which would limit the maximum value + * to 4095. If such a case is detected, set the active_h explicitly + * to 4096. + */ + if (active_h == 0xFFF) { + pr_debug("overriding h_active to 4096\n"); + active_h++; + } /* * EDID_TIMING_DESC_H_BLANK[0x3]: Relative Offset to the EDID detailed @@ -2620,6 +2640,24 @@ void hdmi_edid_config_override(void *input, bool enable, } } +void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + edid_ctrl->init_data.max_pclk_khz = max_pclk_khz; +} + +bool hdmi_edid_is_audio_supported(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + /* + * return true if basic audio is supported or if an audio + * data block was successfully parsed. + */ + return (edid_ctrl->basic_audio_supp || edid_ctrl->adb_size); +} + void hdmi_edid_deinit(void *input) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index 16efb6ee4014..557e9326a81d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -79,5 +79,7 @@ void hdmi_edid_get_hdr_data(void *edid_ctrl, struct hdmi_edid_hdr_data **hdr_data); void hdmi_edid_config_override(void *input, bool enable, struct hdmi_edid_override_data *data); +void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz); +bool hdmi_edid_is_audio_supported(void *input); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index f05d4cb2922a..42845f9ff192 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -380,6 +380,13 @@ static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) return hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)); } /* hdmi_tx_is_dvi_mode */ +static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + return mdata->handoff_pending; +} + static inline bool hdmi_tx_is_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl) { return hdmi_ctrl->hpd_state && hdmi_ctrl->panel_power_on; @@ -416,15 +423,27 @@ static inline void hdmi_tx_cec_device_suspend(struct hdmi_tx_ctrl *hdmi_ctrl) } static inline void hdmi_tx_send_cable_notification( - struct hdmi_tx_ctrl *hdmi_ctrl, int val) + struct hdmi_tx_ctrl *hdmi_ctrl, int val, bool async) { if (hdmi_ctrl && hdmi_ctrl->ext_audio_data.intf_ops.hpd) { u32 flags = 0; - flags |= MSM_EXT_DISP_HPD_VIDEO; + if (async || hdmi_tx_is_in_splash(hdmi_ctrl)) { + flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO; - if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) - flags |= MSM_EXT_DISP_HPD_AUDIO; + if (async) { + if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) + flags |= MSM_EXT_DISP_HPD_ASYNC_AUDIO; + } else + if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) + flags |= MSM_EXT_DISP_HPD_AUDIO; + + } else { + flags |= MSM_EXT_DISP_HPD_VIDEO; + + if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) + flags |= MSM_EXT_DISP_HPD_AUDIO; + } hdmi_ctrl->ext_audio_data.intf_ops.hpd(hdmi_ctrl->ext_pdev, hdmi_ctrl->ext_audio_data.type, val, flags); @@ -859,7 +878,11 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, hdmi_tx_config_5v(hdmi_ctrl, false); } else { hdmi_tx_hpd_off(hdmi_ctrl); - hdmi_tx_send_cable_notification(hdmi_ctrl, 0); + /* + * No need to blocking wait for display/audio in this + * case since HAL is not up so no ACK can be expected. + */ + hdmi_tx_send_cable_notification(hdmi_ctrl, 0, true); } break; @@ -2339,7 +2362,7 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) mutex_unlock(&hdmi_ctrl->tx_lock); - hdmi_tx_send_cable_notification(hdmi_ctrl, hdmi_ctrl->hpd_state); + hdmi_tx_send_cable_notification(hdmi_ctrl, hdmi_ctrl->hpd_state, false); } /* hdmi_tx_hpd_int_work */ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) @@ -3956,7 +3979,7 @@ static int hdmi_tx_post_evt_handle_resume(struct hdmi_tx_ctrl *hdmi_ctrl) &hdmi_ctrl->hpd_int_done, HZ/10); if (!timeout) { pr_debug("cable removed during suspend\n"); - hdmi_tx_send_cable_notification(hdmi_ctrl, 0); + hdmi_tx_send_cable_notification(hdmi_ctrl, 0, false); } } @@ -3967,7 +3990,7 @@ static int hdmi_tx_post_evt_handle_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl) { if (hdmi_ctrl->panel_suspend) { pr_debug("panel suspend has triggered\n"); - hdmi_tx_send_cable_notification(hdmi_ctrl, 0); + hdmi_tx_send_cable_notification(hdmi_ctrl, 0, false); } return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 3cadfa4ecef7..171f44815430 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -2179,6 +2179,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED); break; case MDSS_MDP_HW_REV_320: + mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU); + mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT); case MDSS_MDP_HW_REV_330: mdata->max_target_zorder = 7; /* excluding base layer */ mdata->max_cursor_size = 512; @@ -2206,6 +2208,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map); set_bit(MDSS_QOS_IB_NOCR, mdata->mdss_qos_map); set_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, mdata->mdss_qos_map); + set_bit(MDSS_QOS_WB_QOS, mdata->mdss_qos_map); + set_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map); set_bit(MDSS_CAPS_YUV_CONFIG, mdata->mdss_caps_map); set_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED, mdata->mdss_caps_map); @@ -2216,8 +2220,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) set_bit(MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED, mdata->mdss_caps_map); mdss_mdp_init_default_prefill_factors(mdata); - mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU); - mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT); mdss_set_quirk(mdata, MDSS_QUIRK_MMSS_GDSC_COLLAPSE); mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE); mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR); diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 3d5d046c536a..2fd047edd3e8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1992,6 +1992,7 @@ void mdss_mdp_disable_hw_irq(struct mdss_data_type *mdata); void mdss_mdp_set_supported_formats(struct mdss_data_type *mdata); int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer); +void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num); #ifdef CONFIG_FB_MSM_MDP_NONE struct mdss_data_type *mdss_mdp_get_mdata(void) diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 294e05c2fbb0..7d495232c198 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -634,15 +634,6 @@ enum mdss_mdp_dspp_index { #define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_SAT_MASK BIT(30) #define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK BIT(31) -enum mdss_mpd_intf_index { - MDSS_MDP_NO_INTF, - MDSS_MDP_INTF0, - MDSS_MDP_INTF1, - MDSS_MDP_INTF2, - MDSS_MDP_INTF3, - MDSS_MDP_MAX_INTF -}; - #define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000 #define MDSS_MDP_REG_INTF_CONFIG 0x004 #define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008 @@ -703,6 +694,8 @@ enum mdss_mpd_intf_index { #define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F #define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A +#define MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE 0x064 + #define MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB BIT(7) enum mdss_mdp_pingpong_index { diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 583cfed598cd..c9ce56fb96a4 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -103,6 +103,7 @@ struct mdss_mdp_cmd_ctx { struct mdss_intf_recovery intf_recovery; struct mdss_intf_recovery intf_mdp_callback; + struct mdss_intf_ulp_clamp intf_clamp_handler; struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */ u32 pp_timeout_report_cnt; bool pingpong_split_slave; @@ -1281,6 +1282,32 @@ static void mdss_mdp_cmd_lineptr_done(void *arg) spin_unlock(&ctx->clk_lock); } +static int mdss_mdp_cmd_intf_clamp_ctrl(void *data, int intf_num, bool enable) +{ + struct mdss_mdp_ctl *ctl = data; + char __iomem *ctx_base = NULL; + + if (!data) { + pr_err("%s: invalid ctl\n", __func__); + return -EINVAL; + } + + ctx_base = + (char __iomem *)mdss_mdp_intf_get_ctx_base(ctl, intf_num); + if (!ctx_base) { + pr_err("%s: invalid ctx\n", __func__); + return -EINVAL; + } + + pr_debug("%s: intf num = %d, enable = %d\n", + __func__, intf_num, enable); + + writel_relaxed(enable, (ctx_base + MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE)); + wmb(); /* ensure clamp is enabled */ + + return 0; +} + static int mdss_mdp_cmd_intf_recovery(void *data, int event) { struct mdss_mdp_cmd_ctx *ctx = data; @@ -3209,6 +3236,12 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl, memset(ctx, 0, sizeof(*ctx)); /* intf stopped, no more kickoff */ ctx->intf_stopped = 1; + /* + * Restore clamp handler as it might get called later + * when DSI panel events are handled. + */ + ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl; + ctx->intf_clamp_handler.data = ctl; return 0; } @@ -3349,6 +3382,11 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) (void *)&ctx->intf_mdp_callback, CTL_INTF_EVENT_FLAG_DEFAULT); + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + (void *)&ctx->intf_clamp_handler, + CTL_INTF_EVENT_FLAG_DEFAULT); + mdss_mdp_tearcheck_enable(ctl, true); ctx->intf_stopped = 0; @@ -3407,6 +3445,10 @@ panel_events: goto end; } + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + pr_debug("%s: turn off panel\n", __func__); ctl->intf_ctx[MASTER_CTX] = NULL; ctl->intf_ctx[SLAVE_CTX] = NULL; @@ -3540,6 +3582,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl, ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback; ctx->intf_mdp_callback.data = ctx; + ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl; + ctx->intf_clamp_handler.data = ctl; + + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + (void *)&ctx->intf_clamp_handler, + CTL_INTF_EVENT_FLAG_DEFAULT); + ctx->intf_stopped = 0; pr_debug("%s: ctx=%pK num=%d aux=%d\n", __func__, ctx, diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 8470dc33dc3f..a3511a1a07ef 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -83,6 +83,8 @@ struct mdss_mdp_video_ctx { struct mutex vsync_mtx; struct list_head vsync_handlers; struct mdss_intf_recovery intf_recovery; + struct mdss_intf_recovery intf_mdp_callback; + struct mdss_intf_ulp_clamp intf_clamp_handler; struct work_struct early_wakeup_dfps_work; atomic_t lineptr_ref; @@ -149,6 +151,23 @@ static int mdss_mdp_intf_intr2index(u32 intr_type) return index; } +void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num) +{ + struct mdss_mdp_video_ctx *head; + int i = 0; + + if (!ctl) + return NULL; + + head = ctl->mdata->video_intf; + for (i = 0; i < ctl->mdata->nintf; i++) { + if (head[i].intf_num == intf_num) + return (void *)head[i].base; + } + + return NULL; +} + int mdss_mdp_set_intf_intr_callback(struct mdss_mdp_video_ctx *ctx, u32 intr_type, void (*fnc_ptr)(void *), void *arg) { @@ -315,6 +334,29 @@ int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata, return 0; } +static int mdss_mdp_video_intf_clamp_ctrl(void *data, int intf_num, bool enable) +{ + struct mdss_mdp_video_ctx *ctx = data; + + if (!data) { + pr_err("%s: invalid ctl\n", __func__); + return -EINVAL; + } + + if (intf_num != ctx->intf_num) { + pr_err("%s: invalid intf num\n", __func__); + return -EINVAL; + } + + pr_debug("%s: ctx intf num = %d, enable = %d\n", + __func__, ctx->intf_num, enable); + + mdp_video_write(ctx, MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE, enable); + wmb(); /* ensure clamp is enabled */ + + return 0; +} + static int mdss_mdp_video_intf_recovery(void *data, int event) { struct mdss_mdp_video_ctx *ctx; @@ -1668,12 +1710,42 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) return 0; } +static int mdss_mdp_video_splash_handoff(struct mdss_mdp_ctl *ctl) +{ + int i, ret = 0; + u32 data, flush; + + ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN, + NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + + if (ret) { + pr_err("%s:ctl%d failed to handle 'CONT_SPLASH_BEGIN' event\n" + , __func__, ctl->num); + return ret; + } + + /* clear up mixer0 and mixer1 */ + flush = 0; + for (i = 0; i < 2; i++) { + data = mdss_mdp_ctl_read(ctl, + MDSS_MDP_REG_CTL_LAYER(i)); + if (data) { + mdss_mdp_ctl_write(ctl, + MDSS_MDP_REG_CTL_LAYER(i), + MDSS_MDP_LM_BORDER_COLOR); + flush |= (0x40 << i); + } + } + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); + + return ret; +} + int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, bool handoff) { struct mdss_panel_data *pdata; - int i, ret = 0, off; - u32 data, flush; + int ret = 0, off; struct mdss_mdp_video_ctx *ctx, *sctx = NULL; struct mdss_mdp_ctl *sctl; @@ -1707,29 +1779,20 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, } if (!handoff) { - ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN, - NULL, CTL_INTF_EVENT_FLAG_DEFAULT); - if (ret) { - pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n" - , __func__); - return ret; - } + ret = mdss_mdp_video_splash_handoff(ctl); - /* clear up mixer0 and mixer1 */ - flush = 0; - for (i = 0; i < 2; i++) { - data = mdss_mdp_ctl_read(ctl, - MDSS_MDP_REG_CTL_LAYER(i)); - if (data) { - mdss_mdp_ctl_write(ctl, - MDSS_MDP_REG_CTL_LAYER(i), - MDSS_MDP_LM_BORDER_COLOR); - flush |= (0x40 << i); - } - } - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); + if (!ret && sctl) + ret = mdss_mdp_video_splash_handoff(sctl); + + if (ret) + return ret; mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0); + + if (sctx) + mdp_video_write(sctx, + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0); + mdss_mdp_video_timegen_flush(ctl, sctx); /* wait for 1 VSYNC for the pipe to be unstaged */ @@ -1738,6 +1801,12 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH, NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + + if (!ret && sctl) + ret = mdss_mdp_ctl_intf_event(sctl, + MDSS_EVENT_CONT_SPLASH_FINISH, NULL, + CTL_INTF_EVENT_FLAG_DEFAULT); + } return ret; @@ -1905,6 +1974,58 @@ static void mdss_mdp_handoff_programmable_fetch(struct mdss_mdp_ctl *ctl, } } +static int mdss_mdp_video_intf_callback(void *data, int event) +{ + struct mdss_mdp_video_ctx *ctx; + struct mdss_mdp_ctl *ctl = data; + struct mdss_panel_info *pinfo; + u32 line_cnt, min_ln_cnt, active_lns_cnt, line_buff = 50; + + if (!data) { + pr_err("%s: invalid ctl\n", __func__); + return -EINVAL; + } + + ctx = ctl->intf_ctx[MASTER_CTX]; + pr_debug("%s: ctl num = %d, event = %d\n", + __func__, ctl->num, event); + + if (!ctl->is_video_mode) + return 0; + + pinfo = &ctl->panel_data->panel_info; + min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width; + active_lns_cnt = pinfo->yres; + + switch (event) { + case MDP_INTF_CALLBACK_CHECK_LINE_COUNT: + if (!ctl || !ctx || !ctx->timegen_en) { + pr_debug("%s: no need to check for active line\n", + __func__); + goto end; + } + + line_cnt = mdss_mdp_video_line_count(ctl); + + if ((line_cnt >= min_ln_cnt) && (line_cnt < + (min_ln_cnt + active_lns_cnt - line_buff))) { + pr_debug("%s: line count is within active range=%d\n", + __func__, line_cnt); + goto end; + } else { + pr_debug("line count is less. line_cnt = %d\n", + line_cnt); + return -EPERM; + } + break; + default: + pr_debug("%s: unhandled event!\n", __func__); + break; + } +end: + return 0; +} + static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_video_ctx *ctx, struct mdss_panel_info *pinfo) { @@ -1938,6 +2059,20 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, pr_err("Failed to register intf recovery handler\n"); return -EINVAL; } + + ctx->intf_mdp_callback.fxn = mdss_mdp_video_intf_callback; + ctx->intf_mdp_callback.data = ctl; + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_MDP_CALLBACK, + (void *)&ctx->intf_mdp_callback, + CTL_INTF_EVENT_FLAG_DEFAULT); + + ctx->intf_clamp_handler.fxn = mdss_mdp_video_intf_clamp_ctrl; + ctx->intf_clamp_handler.data = ctx; + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + (void *)&ctx->intf_clamp_handler, + CTL_INTF_EVENT_FLAG_DEFAULT); } else { ctx->intf_recovery.fxn = NULL; ctx->intf_recovery.data = NULL; diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index fce667a2126d..7c6938d40e0b 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -1222,6 +1222,15 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, goto end; } + /* scaling is not allowed for solid_fill layers */ + if ((pipe->flags & MDP_SOLID_FILL) && + ((pipe->src.w != pipe->dst.w) || + (pipe->src.h != pipe->dst.h))) { + pr_err("solid fill pipe:%d cannot have scaling\n", pipe->num); + ret = -EINVAL; + goto end; + } + /* * unstage the pipe if it's current z_order does not match with new * z_order because client may only call the validate. diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index e75c4a3a7cc1..92413e078244 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -29,6 +29,9 @@ struct panel_id { #define DEFAULT_FRAME_RATE 60 #define DEFAULT_ROTATOR_FRAME_RATE 120 #define ROTATOR_LOW_FRAME_RATE 30 + +#define MDSS_DSI_MAX_ESC_CLK_RATE_HZ 19200000 + #define MDSS_DSI_RST_SEQ_LEN 10 /* worst case prefill lines for all chipsets including all vertical blank */ #define MDSS_MDP_MAX_PREFILL_FETCH 25 @@ -186,6 +189,7 @@ struct mdss_panel_cfg { enum { MDP_INTF_CALLBACK_DSI_WAIT, + MDP_INTF_CALLBACK_CHECK_LINE_COUNT, }; struct mdss_intf_recovery { @@ -193,6 +197,11 @@ struct mdss_intf_recovery { void *data; }; +struct mdss_intf_ulp_clamp { + int (*fxn)(void *ctx, int intf_num, bool enable); + void *data; +}; + /** * enum mdss_intf_events - Different events generated by MDP core * @@ -298,6 +307,7 @@ enum mdss_intf_events { MDSS_EVENT_UPDATE_PANEL_PPM, MDSS_EVENT_DSI_TIMING_DB_CTRL, MDSS_EVENT_AVR_MODE, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, MDSS_EVENT_MAX, }; @@ -354,6 +364,8 @@ static inline char *mdss_panel_intf_event_to_string(int event) return INTF_EVENT_STR(MDSS_EVENT_REGISTER_RECOVERY_HANDLER); case MDSS_EVENT_REGISTER_MDP_CALLBACK: return INTF_EVENT_STR(MDSS_EVENT_REGISTER_MDP_CALLBACK); + case MDSS_EVENT_REGISTER_CLAMP_HANDLER: + return INTF_EVENT_STR(MDSS_EVENT_REGISTER_CLAMP_HANDLER); case MDSS_EVENT_DSI_PANEL_STATUS: return INTF_EVENT_STR(MDSS_EVENT_DSI_PANEL_STATUS); case MDSS_EVENT_DSI_DYNAMIC_SWITCH: @@ -909,6 +921,9 @@ struct mdss_panel_info { /* HDR properties of display panel*/ struct mdss_panel_hdr_properties hdr_properties; + + /* esc clk recommended for the panel */ + u32 esc_clk_rate_hz; }; struct mdss_panel_timing { diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index 0307d570ff64..fdd1c0153ce0 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -2065,10 +2065,12 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr, return ret; } + mutex_lock(&mgr->lock); perf = mdss_rotator_find_session(private, config.session_id); if (!perf) { pr_err("No session with id=%u could be found\n", config.session_id); + mutex_unlock(&mgr->lock); return -EINVAL; } @@ -2091,6 +2093,7 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr, config.output.format); done: ATRACE_END(__func__); + mutex_unlock(&mgr->lock); return ret; } diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index e6151b4c75a1..03e78733d168 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1863,8 +1863,10 @@ static int mdss_dsi_ulps_config_default(struct mdss_dsi_ctrl_pdata *ctrl, * to be in stop state. */ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16); + wmb(); /* ensure lanes are put to stop state */ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0); + wmb(); /* ensure lanes are in proper state */ lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8); } @@ -1983,6 +1985,7 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl, struct mipi_panel_info *mipi = NULL; u32 clamp_reg, regval = 0; u32 clamp_reg_off; + u32 intf_num = 0; if (!ctrl) { pr_err("%s: invalid input\n", __func__); @@ -1994,6 +1997,21 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl, return -EINVAL; } + /* + * For DSI HW version 2.1.0 ULPS_CLAMP register + * is moved to interface level. + */ + if (ctrl->shared_data->hw_rev == MDSS_DSI_HW_REV_201) { + intf_num = ctrl->ndx ? MDSS_MDP_INTF2 : MDSS_MDP_INTF1; + if (ctrl->clamp_handler) { + ctrl->clamp_handler->fxn(ctrl->clamp_handler->data, + intf_num, enable); + pr_debug("%s: ndx: %d enable: %d\n", + __func__, ctrl->ndx, enable); + } + return 0; + } + clamp_reg_off = ctrl->shared_data->ulps_clamp_ctrl_off; mipi = &ctrl->panel_data.panel_info.mipi; @@ -2256,6 +2274,8 @@ int mdss_dsi_pre_clkoff_cb(void *priv, pdata = &ctrl->panel_data; if ((clk & MDSS_DSI_LINK_CLK) && (new_state == MDSS_DSI_CLK_OFF)) { + if (pdata->panel_info.mipi.force_clk_lane_hs) + mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 0); /* * If ULPS feature is enabled, enter ULPS first. * However, when blanking the panel, we should enter ULPS @@ -2371,6 +2391,8 @@ int mdss_dsi_post_clkon_cb(void *priv, goto error; } } + if (pdata->panel_info.mipi.force_clk_lane_hs) + mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1); } error: return rc; diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index b79a74a98a23..ad94d8a45728 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -467,7 +467,7 @@ static int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled, vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]); pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]); - *pci_base = (dma_addr_t)vme_base + pci_offset; + *pci_base = (dma_addr_t)*vme_base + pci_offset; *size = (unsigned long long)((vme_bound - *vme_base) + granularity); *enabled = 0; diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 049a884a756f..59d74d1b47a8 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -153,6 +153,9 @@ struct ds_device */ u16 spu_bit; + u8 st_buf[ST_SIZE]; + u8 byte_buf; + struct w1_bus_master master; }; @@ -174,7 +177,6 @@ struct ds_status u8 data_in_buffer_status; u8 reserved1; u8 reserved2; - }; static struct usb_device_id ds_id_table [] = { @@ -244,28 +246,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) return err; } -static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, - unsigned char *buf, int size) -{ - int count, err; - - memset(st, 0, sizeof(*st)); - - count = 0; - err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, - dev->ep[EP_STATUS]), buf, size, &count, 1000); - if (err < 0) { - pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", - dev->ep[EP_STATUS], err); - return err; - } - - if (count >= sizeof(*st)) - memcpy(st, buf, sizeof(*st)); - - return count; -} - static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) { pr_info("%45s: %8x\n", str, buf[off]); @@ -324,6 +304,35 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) } } +static int ds_recv_status(struct ds_device *dev, struct ds_status *st, + bool dump) +{ + int count, err; + + if (st) + memset(st, 0, sizeof(*st)); + + count = 0; + err = usb_interrupt_msg(dev->udev, + usb_rcvintpipe(dev->udev, + dev->ep[EP_STATUS]), + dev->st_buf, sizeof(dev->st_buf), + &count, 1000); + if (err < 0) { + pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", + dev->ep[EP_STATUS], err); + return err; + } + + if (dump) + ds_dump_status(dev, dev->st_buf, count); + + if (st && count >= sizeof(*st)) + memcpy(st, dev->st_buf, sizeof(*st)); + + return count; +} + static void ds_reset_device(struct ds_device *dev) { ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0); @@ -344,7 +353,6 @@ static void ds_reset_device(struct ds_device *dev) static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) { int count, err; - struct ds_status st; /* Careful on size. If size is less than what is available in * the input buffer, the device fails the bulk transfer and @@ -359,14 +367,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), buf, size, &count, 1000); if (err < 0) { - u8 buf[ST_SIZE]; - int count; - pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); - - count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); - ds_dump_status(dev, buf, count); + ds_recv_status(dev, NULL, true); return err; } @@ -404,7 +407,6 @@ int ds_stop_pulse(struct ds_device *dev, int limit) { struct ds_status st; int count = 0, err = 0; - u8 buf[ST_SIZE]; do { err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); @@ -413,7 +415,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit) err = ds_send_control(dev, CTL_RESUME_EXE, 0); if (err) break; - err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); + err = ds_recv_status(dev, &st, false); if (err) break; @@ -456,18 +458,17 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) static int ds_wait_status(struct ds_device *dev, struct ds_status *st) { - u8 buf[ST_SIZE]; int err, count = 0; do { st->status = 0; - err = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); + err = ds_recv_status(dev, st, false); #if 0 if (err >= 0) { int i; printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err); for (i=0; i<err; ++i) - printk("%02x ", buf[i]); + printk("%02x ", dev->st_buf[i]); printk("\n"); } #endif @@ -485,7 +486,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) * can do something with it). */ if (err > 16 || count >= 100 || err < 0) - ds_dump_status(dev, buf, err); + ds_dump_status(dev, dev->st_buf, err); /* Extended data isn't an error. Well, a short is, but the dump * would have already told the user that and we can't do anything @@ -608,7 +609,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) { int err; struct ds_status st; - u8 rbyte; err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte); if (err) @@ -621,11 +621,11 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) if (err) return err; - err = ds_recv_data(dev, &rbyte, sizeof(rbyte)); + err = ds_recv_data(dev, &dev->byte_buf, 1); if (err < 0) return err; - return !(byte == rbyte); + return !(byte == dev->byte_buf); } static int ds_read_byte(struct ds_device *dev, u8 *byte) @@ -712,7 +712,6 @@ static void ds9490r_search(void *data, struct w1_master *master, int err; u16 value, index; struct ds_status st; - u8 st_buf[ST_SIZE]; int search_limit; int found = 0; int i; @@ -724,7 +723,12 @@ static void ds9490r_search(void *data, struct w1_master *master, /* FIFO 128 bytes, bulk packet size 64, read a multiple of the * packet size. */ - u64 buf[2*64/8]; + const size_t bufsize = 2 * 64; + u64 *buf; + + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return; mutex_lock(&master->bus_mutex); @@ -745,10 +749,9 @@ static void ds9490r_search(void *data, struct w1_master *master, do { schedule_timeout(jtime); - if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < - sizeof(st)) { + err = ds_recv_status(dev, &st, false); + if (err < 0 || err < sizeof(st)) break; - } if (st.data_in_buffer_status) { /* Bulk in can receive partial ids, but when it does @@ -758,7 +761,7 @@ static void ds9490r_search(void *data, struct w1_master *master, * bulk without first checking if status says there * is data to read. */ - err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); + err = ds_recv_data(dev, (u8 *)buf, bufsize); if (err < 0) break; for (i = 0; i < err/8; ++i) { @@ -794,9 +797,14 @@ static void ds9490r_search(void *data, struct w1_master *master, } search_out: mutex_unlock(&master->bus_mutex); + kfree(buf); } #if 0 +/* + * FIXME: if this disabled code is ever used in the future all ds_send_data() + * calls must be changed to use a DMAable buffer. + */ static int ds_match_access(struct ds_device *dev, u64 init) { int err; @@ -845,13 +853,12 @@ static int ds_set_path(struct ds_device *dev, u64 init) static u8 ds9490r_touch_bit(void *data, u8 bit) { - u8 ret; struct ds_device *dev = data; - if (ds_touch_bit(dev, bit, &ret)) + if (ds_touch_bit(dev, bit, &dev->byte_buf)) return 0; - return ret; + return dev->byte_buf; } #if 0 @@ -866,13 +873,12 @@ static u8 ds9490r_read_bit(void *data) { struct ds_device *dev = data; int err; - u8 bit = 0; - err = ds_touch_bit(dev, 1, &bit); + err = ds_touch_bit(dev, 1, &dev->byte_buf); if (err) return 0; - return bit & 1; + return dev->byte_buf & 1; } #endif @@ -887,32 +893,52 @@ static u8 ds9490r_read_byte(void *data) { struct ds_device *dev = data; int err; - u8 byte = 0; - err = ds_read_byte(dev, &byte); + err = ds_read_byte(dev, &dev->byte_buf); if (err) return 0; - return byte; + return dev->byte_buf; } static void ds9490r_write_block(void *data, const u8 *buf, int len) { struct ds_device *dev = data; + u8 *tbuf; + + if (len <= 0) + return; + + tbuf = kmalloc(len, GFP_KERNEL); + if (!tbuf) + return; - ds_write_block(dev, (u8 *)buf, len); + memcpy(tbuf, buf, len); + ds_write_block(dev, tbuf, len); + + kfree(tbuf); } static u8 ds9490r_read_block(void *data, u8 *buf, int len) { struct ds_device *dev = data; int err; + u8 *tbuf; - err = ds_read_block(dev, buf, len); - if (err < 0) + if (len <= 0) + return 0; + + tbuf = kmalloc(len, GFP_KERNEL); + if (!tbuf) return 0; - return len; + err = ds_read_block(dev, tbuf, len); + if (err >= 0) + memcpy(buf, tbuf, len); + + kfree(tbuf); + + return err >= 0 ? len : 0; } static u8 ds9490r_reset(void *data) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index c9a7ff67d395..39886edfa222 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -763,6 +763,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__, sl->name); w1_family_put(sl->family); + atomic_dec(&sl->master->refcnt); kfree(sl); return err; } diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 1be5dd048622..308600adf6e0 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -804,7 +804,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) vma->vm_ops = &gntdev_vmops; - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_MIXEDMAP; if (use_ptemod) vma->vm_flags |= VM_DONTCOPY; diff --git a/fs/attr.c b/fs/attr.c index d62f674a605f..11be2265a2d5 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -187,7 +187,7 @@ EXPORT_SYMBOL(setattr_copy); * the file open for write, as there can be no conflicting delegation in * that case. */ -int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) { struct inode *inode = dentry->d_inode; umode_t mode = inode->i_mode; @@ -277,7 +277,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de if (error) return error; - if (inode->i_op->setattr) + if (mnt && inode->i_op->setattr2) + error = inode->i_op->setattr2(mnt, dentry, attr); + else if (inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); else error = simple_setattr(dentry, attr); @@ -290,4 +292,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de return error; } +EXPORT_SYMBOL(notify_change2); + +int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +{ + return notify_change2(NULL, dentry, attr, delegated_inode); +} EXPORT_SYMBOL(notify_change); diff --git a/fs/block_dev.c b/fs/block_dev.c index ac9b7553c02a..198aea66fe71 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -759,7 +759,7 @@ static bool bd_may_claim(struct block_device *bdev, struct block_device *whole, return true; /* already a holder */ else if (bdev->bd_holder != NULL) return false; /* held by someone else */ - else if (bdev->bd_contains == bdev) + else if (whole == bdev) return true; /* is a whole device which isn't held */ else if (whole->bd_holder == bd_may_claim) diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index 9aba42b78253..a09264d8b853 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c @@ -70,6 +70,20 @@ void btrfs_##name(struct work_struct *arg) \ normal_work_helper(work); \ } +bool btrfs_workqueue_normal_congested(struct btrfs_workqueue *wq) +{ + /* + * We could compare wq->normal->pending with num_online_cpus() + * to support "thresh == NO_THRESHOLD" case, but it requires + * moving up atomic_inc/dec in thresh_queue/exec_hook. Let's + * postpone it until someone needs the support of that case. + */ + if (wq->normal->thresh == NO_THRESHOLD) + return false; + + return atomic_read(&wq->normal->pending) > wq->normal->thresh * 2; +} + BTRFS_WORK_HELPER(worker_helper); BTRFS_WORK_HELPER(delalloc_helper); BTRFS_WORK_HELPER(flush_delalloc_helper); diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h index ad4d0647d1a6..8e1d6576d764 100644 --- a/fs/btrfs/async-thread.h +++ b/fs/btrfs/async-thread.h @@ -80,4 +80,5 @@ void btrfs_queue_work(struct btrfs_workqueue *wq, void btrfs_destroy_workqueue(struct btrfs_workqueue *wq); void btrfs_workqueue_set_max(struct btrfs_workqueue *wq, int max); void btrfs_set_work_high_priority(struct btrfs_work *work); +bool btrfs_workqueue_normal_congested(struct btrfs_workqueue *wq); #endif diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 1391f72c28c3..e847573c6db0 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3070,6 +3070,8 @@ btrfs_disk_balance_args_to_cpu(struct btrfs_balance_args *cpu, cpu->target = le64_to_cpu(disk->target); cpu->flags = le64_to_cpu(disk->flags); cpu->limit = le64_to_cpu(disk->limit); + cpu->stripes_min = le32_to_cpu(disk->stripes_min); + cpu->stripes_max = le32_to_cpu(disk->stripes_max); } static inline void @@ -3088,6 +3090,8 @@ btrfs_cpu_balance_args_to_disk(struct btrfs_disk_balance_args *disk, disk->target = cpu_to_le64(cpu->target); disk->flags = cpu_to_le64(cpu->flags); disk->limit = cpu_to_le64(cpu->limit); + disk->stripes_min = cpu_to_le32(cpu->stripes_min); + disk->stripes_max = cpu_to_le32(cpu->stripes_max); } /* struct btrfs_super_block */ diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 02b934d0ee65..09fa5af9782e 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1375,7 +1375,8 @@ release_path: total_done++; btrfs_release_prepared_delayed_node(delayed_node); - if (async_work->nr == 0 || total_done < async_work->nr) + if ((async_work->nr == 0 && total_done < BTRFS_DELAYED_WRITEBACK) || + total_done < async_work->nr) goto again; free_path: @@ -1391,7 +1392,8 @@ static int btrfs_wq_run_delayed_node(struct btrfs_delayed_root *delayed_root, { struct btrfs_async_delayed_work *async_work; - if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND) + if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND || + btrfs_workqueue_normal_congested(fs_info->delayed_workers)) return 0; async_work = kmalloc(sizeof(*async_work), GFP_NOFS); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 47cdc6f3390b..2a2e370399ba 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2520,11 +2520,11 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, if (ref && ref->seq && btrfs_check_delayed_seq(fs_info, delayed_refs, ref->seq)) { spin_unlock(&locked_ref->lock); - btrfs_delayed_ref_unlock(locked_ref); spin_lock(&delayed_refs->lock); locked_ref->processing = 0; delayed_refs->num_heads_ready++; spin_unlock(&delayed_refs->lock); + btrfs_delayed_ref_unlock(locked_ref); locked_ref = NULL; cond_resched(); count++; @@ -2570,7 +2570,10 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, */ if (must_insert_reserved) locked_ref->must_insert_reserved = 1; + spin_lock(&delayed_refs->lock); locked_ref->processing = 0; + delayed_refs->num_heads_ready++; + spin_unlock(&delayed_refs->lock); btrfs_debug(fs_info, "run_delayed_extent_op returned %d", ret); btrfs_delayed_ref_unlock(locked_ref); return ret; @@ -8486,14 +8489,13 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, ret = btrfs_lookup_extent_info(trans, root, bytenr, level - 1, 1, &wc->refs[level - 1], &wc->flags[level - 1]); - if (ret < 0) { - btrfs_tree_unlock(next); - return ret; - } + if (ret < 0) + goto out_unlock; if (unlikely(wc->refs[level - 1] == 0)) { btrfs_err(root->fs_info, "Missing references."); - BUG(); + ret = -EIO; + goto out_unlock; } *lookup_info = 0; @@ -8545,7 +8547,12 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, } level--; - BUG_ON(level != btrfs_header_level(next)); + ASSERT(level == btrfs_header_level(next)); + if (level != btrfs_header_level(next)) { + btrfs_err(root->fs_info, "mismatched level"); + ret = -EIO; + goto out_unlock; + } path->nodes[level] = next; path->slots[level] = 0; path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING; @@ -8560,8 +8567,15 @@ skip: if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) { parent = path->nodes[level]->start; } else { - BUG_ON(root->root_key.objectid != + ASSERT(root->root_key.objectid == btrfs_header_owner(path->nodes[level])); + if (root->root_key.objectid != + btrfs_header_owner(path->nodes[level])) { + btrfs_err(root->fs_info, + "mismatched block owner"); + ret = -EIO; + goto out_unlock; + } parent = 0; } @@ -8578,12 +8592,18 @@ skip: } ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, root->root_key.objectid, level - 1, 0); - BUG_ON(ret); /* -ENOMEM */ + if (ret) + goto out_unlock; } + + *lookup_info = 1; + ret = 1; + +out_unlock: btrfs_tree_unlock(next); free_extent_buffer(next); - *lookup_info = 1; - return 1; + + return ret; } /* @@ -9686,6 +9706,11 @@ int btrfs_read_block_groups(struct btrfs_root *root) struct extent_buffer *leaf; int need_clear = 0; u64 cache_gen; + u64 feature; + int mixed; + + feature = btrfs_super_incompat_flags(info->super_copy); + mixed = !!(feature & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS); root = info->extent_root; key.objectid = 0; @@ -9739,6 +9764,15 @@ int btrfs_read_block_groups(struct btrfs_root *root) btrfs_item_ptr_offset(leaf, path->slots[0]), sizeof(cache->item)); cache->flags = btrfs_block_group_flags(&cache->item); + if (!mixed && + ((cache->flags & BTRFS_BLOCK_GROUP_METADATA) && + (cache->flags & BTRFS_BLOCK_GROUP_DATA))) { + btrfs_err(info, +"bg %llu is a mixed block group but filesystem hasn't enabled mixed block groups", + cache->key.objectid); + ret = -EINVAL; + goto error; + } key.objectid = found_key.objectid + found_key.offset; btrfs_release_path(path); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 257bbdcb5df6..e767f347f2b1 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -5294,11 +5294,20 @@ int read_extent_buffer_pages(struct extent_io_tree *tree, lock_page(page); } locked_pages++; + } + /* + * We need to firstly lock all pages to make sure that + * the uptodate bit of our pages won't be affected by + * clear_extent_buffer_uptodate(). + */ + for (i = start_i; i < num_pages; i++) { + page = eb->pages[i]; if (!PageUptodate(page)) { num_reads++; all_uptodate = 0; } } + if (all_uptodate) { if (start_i == 0) set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a7e18dbadf74..317b99acdf4b 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3825,6 +3825,11 @@ process_slot: } btrfs_release_path(path); key.offset = next_key_min_offset; + + if (fatal_signal_pending(current)) { + ret = -EINTR; + goto out; + } } ret = 0; diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index bcc965ed5fa1..88d9b66e2207 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -2283,10 +2283,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) int err = -ENOMEM; int ret = 0; - mutex_lock(&fs_info->qgroup_rescan_lock); - fs_info->qgroup_rescan_running = true; - mutex_unlock(&fs_info->qgroup_rescan_lock); - path = btrfs_alloc_path(); if (!path) goto out; @@ -2397,6 +2393,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid, sizeof(fs_info->qgroup_rescan_progress)); fs_info->qgroup_rescan_progress.objectid = progress_objectid; init_completion(&fs_info->qgroup_rescan_completion); + fs_info->qgroup_rescan_running = true; spin_unlock(&fs_info->qgroup_lock); mutex_unlock(&fs_info->qgroup_rescan_lock); diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b4ca5454ef1a..8ca9aa92972d 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -921,9 +921,16 @@ again: path2->slots[level]--; eb = path2->nodes[level]; - WARN_ON(btrfs_node_blockptr(eb, path2->slots[level]) != - cur->bytenr); - + if (btrfs_node_blockptr(eb, path2->slots[level]) != + cur->bytenr) { + btrfs_err(root->fs_info, + "couldn't find block (%llu) (level %d) in tree (%llu) with key (%llu %u %llu)", + cur->bytenr, level - 1, root->objectid, + node_key->objectid, node_key->type, + node_key->offset); + err = -ENOENT; + goto out; + } lower = cur; need_check = true; for (; level < BTRFS_MAX_LEVEL; level++) { @@ -2343,6 +2350,10 @@ void free_reloc_roots(struct list_head *list) while (!list_empty(list)) { reloc_root = list_entry(list->next, struct btrfs_root, root_list); + free_extent_buffer(reloc_root->node); + free_extent_buffer(reloc_root->commit_root); + reloc_root->node = NULL; + reloc_root->commit_root = NULL; __del_reloc_root(reloc_root); } } @@ -2676,11 +2687,15 @@ static int do_relocation(struct btrfs_trans_handle *trans, if (!upper->eb) { ret = btrfs_search_slot(trans, root, key, path, 0, 1); - if (ret < 0) { - err = ret; + if (ret) { + if (ret < 0) + err = ret; + else + err = -ENOENT; + + btrfs_release_path(path); break; } - BUG_ON(ret > 0); if (!upper->eb) { upper->eb = path->nodes[upper->level]; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index f7441193bf35..ee7832e2d39d 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1923,12 +1923,11 @@ static noinline int find_dir_range(struct btrfs_root *root, next: /* check the next slot in the tree to see if it is a valid item */ nritems = btrfs_header_nritems(path->nodes[0]); + path->slots[0]++; if (path->slots[0] >= nritems) { ret = btrfs_next_leaf(root, path); if (ret) goto out; - } else { - path->slots[0]++; } btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index e7b130a637f9..f54f77037d22 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -274,12 +274,13 @@ static int parse_reply_info_extra(void **p, void *end, struct ceph_mds_reply_info_parsed *info, u64 features) { - if (info->head->op == CEPH_MDS_OP_GETFILELOCK) + u32 op = le32_to_cpu(info->head->op); + + if (op == CEPH_MDS_OP_GETFILELOCK) return parse_reply_info_filelock(p, end, info, features); - else if (info->head->op == CEPH_MDS_OP_READDIR || - info->head->op == CEPH_MDS_OP_LSSNAP) + else if (op == CEPH_MDS_OP_READDIR || op == CEPH_MDS_OP_LSSNAP) return parse_reply_info_dir(p, end, info, features); - else if (info->head->op == CEPH_MDS_OP_CREATE) + else if (op == CEPH_MDS_OP_CREATE) return parse_reply_info_create(p, end, info, features); else return -EIO; @@ -643,6 +644,9 @@ static void __unregister_request(struct ceph_mds_client *mdsc, { dout("__unregister_request %p tid %lld\n", req, req->r_tid); + /* Never leave an unregistered request on an unsafe list! */ + list_del_init(&req->r_unsafe_item); + if (req->r_tid == mdsc->oldest_tid) { struct rb_node *p = rb_next(&req->r_node); mdsc->oldest_tid = 0; @@ -1050,7 +1054,6 @@ static void cleanup_session_requests(struct ceph_mds_client *mdsc, while (!list_empty(&session->s_unsafe)) { req = list_first_entry(&session->s_unsafe, struct ceph_mds_request, r_unsafe_item); - list_del_init(&req->r_unsafe_item); pr_warn_ratelimited(" dropping unsafe request %llu\n", req->r_tid); __unregister_request(mdsc, req); @@ -2476,7 +2479,6 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) * useful we could do with a revised return value. */ dout("got safe reply %llu, mds%d\n", tid, mds); - list_del_init(&req->r_unsafe_item); /* last unsafe request during umount? */ if (mdsc->stopping && !__get_oldest_req(mdsc)) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c669a1471395..b76883606e4b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -627,6 +627,8 @@ struct TCP_Server_Info { #ifdef CONFIG_CIFS_SMB2 unsigned int max_read; unsigned int max_write; + struct delayed_work reconnect; /* reconnect workqueue job */ + struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ #endif /* CONFIG_CIFS_SMB2 */ }; @@ -826,6 +828,7 @@ cap_unix(struct cifs_ses *ses) struct cifs_tcon { struct list_head tcon_list; int tc_count; + struct list_head rlist; /* reconnect list */ struct list_head openFileList; spinlock_t open_file_lock; /* protects list above */ struct cifs_ses *ses; /* pointer to session associated with */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index c63fd1dde25b..54590fd33df1 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -205,6 +205,9 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, struct cifs_pending_open *open); extern void cifs_del_pending_open(struct cifs_pending_open *open); +extern void cifs_put_tcp_session(struct TCP_Server_Info *server, + int from_reconnect); +extern void cifs_put_tcon(struct cifs_tcon *tcon); #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) extern void cifs_dfs_release_automount_timer(void); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 812a8cb07c63..5d59f25521ce 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -52,6 +52,9 @@ #include "nterr.h" #include "rfc1002pdu.h" #include "fscache.h" +#ifdef CONFIG_CIFS_SMB2 +#include "smb2proto.h" +#endif #define CIFS_PORT 445 #define RFC1001_PORT 139 @@ -2113,8 +2116,8 @@ cifs_find_tcp_session(struct smb_vol *vol) return NULL; } -static void -cifs_put_tcp_session(struct TCP_Server_Info *server) +void +cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) { struct task_struct *task; @@ -2131,6 +2134,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) cancel_delayed_work_sync(&server->echo); +#ifdef CONFIG_CIFS_SMB2 + if (from_reconnect) + /* + * Avoid deadlock here: reconnect work calls + * cifs_put_tcp_session() at its end. Need to be sure + * that reconnect work does nothing with server pointer after + * that step. + */ + cancel_delayed_work(&server->reconnect); + else + cancel_delayed_work_sync(&server->reconnect); +#endif + spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); @@ -2195,6 +2211,10 @@ cifs_get_tcp_session(struct smb_vol *volume_info) INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); +#ifdef CONFIG_CIFS_SMB2 + INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); + mutex_init(&tcp_ses->reconnect_mutex); +#endif memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, sizeof(tcp_ses->srcaddr)); memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, @@ -2347,7 +2367,7 @@ cifs_put_smb_ses(struct cifs_ses *ses) spin_unlock(&cifs_tcp_ses_lock); sesInfoFree(ses); - cifs_put_tcp_session(server); + cifs_put_tcp_session(server, 0); } #ifdef CONFIG_KEYS @@ -2521,7 +2541,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) mutex_unlock(&ses->session_mutex); /* existing SMB ses has a server reference already */ - cifs_put_tcp_session(server); + cifs_put_tcp_session(server, 0); free_xid(xid); return ses; } @@ -2611,7 +2631,7 @@ cifs_find_tcon(struct cifs_ses *ses, const char *unc) return NULL; } -static void +void cifs_put_tcon(struct cifs_tcon *tcon) { unsigned int xid; @@ -3767,7 +3787,7 @@ mount_fail_check: else if (ses) cifs_put_smb_ses(ses); else - cifs_put_tcp_session(server); + cifs_put_tcp_session(server, 0); bdi_destroy(&cifs_sb->bdi); } @@ -4078,7 +4098,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); if (IS_ERR(ses)) { tcon = (struct cifs_tcon *)ses; - cifs_put_tcp_session(master_tcon->ses->server); + cifs_put_tcp_session(master_tcon->ses->server, 0); goto out; } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 833e5844a2db..97d1a15873c5 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -282,6 +282,7 @@ initiate_cifs_search(const unsigned int xid, struct file *file) rc = -ENOMEM; goto error_exit; } + spin_lock_init(&cifsFile->file_info_lock); file->private_data = cifsFile; cifsFile->tlink = cifs_get_tlink(tlink); tcon = tlink_tcon(tlink); diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index f9e766f464be..b2aff0c6f22c 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -260,7 +260,7 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile) * and check it for zero before using. */ max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; - if (!max_buf) { + if (max_buf < sizeof(struct smb2_lock_element)) { free_xid(xid); return -EINVAL; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 0dbbdf5e4aee..2fa754c5fd62 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -278,7 +278,7 @@ out: case SMB2_CHANGE_NOTIFY: case SMB2_QUERY_INFO: case SMB2_SET_INFO: - return -EAGAIN; + rc = -EAGAIN; } unload_nls(nls_codepage); return rc; @@ -1822,6 +1822,54 @@ smb2_echo_callback(struct mid_q_entry *mid) add_credits(server, credits_received, CIFS_ECHO_OP); } +void smb2_reconnect_server(struct work_struct *work) +{ + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, reconnect.work); + struct cifs_ses *ses; + struct cifs_tcon *tcon, *tcon2; + struct list_head tmp_list; + int tcon_exist = false; + + /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ + mutex_lock(&server->reconnect_mutex); + + INIT_LIST_HEAD(&tmp_list); + cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n"); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->need_reconnect) { + tcon->tc_count++; + list_add_tail(&tcon->rlist, &tmp_list); + tcon_exist = true; + } + } + } + /* + * Get the reference to server struct to be sure that the last call of + * cifs_put_tcon() in the loop below won't release the server pointer. + */ + if (tcon_exist) + server->srv_count++; + + spin_unlock(&cifs_tcp_ses_lock); + + list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { + smb2_reconnect(SMB2_ECHO, tcon); + list_del_init(&tcon->rlist); + cifs_put_tcon(tcon); + } + + cifs_dbg(FYI, "Reconnecting tcons finished\n"); + mutex_unlock(&server->reconnect_mutex); + + /* now we can safely release srv struct */ + if (tcon_exist) + cifs_put_tcp_session(server, 1); +} + int SMB2_echo(struct TCP_Server_Info *server) { @@ -1834,32 +1882,11 @@ SMB2_echo(struct TCP_Server_Info *server) cifs_dbg(FYI, "In echo request\n"); if (server->tcpStatus == CifsNeedNegotiate) { - struct list_head *tmp, *tmp2; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n"); - spin_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &server->smb_ses_list) { - ses = list_entry(tmp, struct cifs_ses, smb_ses_list); - list_for_each(tmp2, &ses->tcon_list) { - tcon = list_entry(tmp2, struct cifs_tcon, - tcon_list); - /* add check for persistent handle reconnect */ - if (tcon && tcon->need_reconnect) { - spin_unlock(&cifs_tcp_ses_lock); - rc = smb2_reconnect(SMB2_ECHO, tcon); - spin_lock(&cifs_tcp_ses_lock); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); + /* No need to send echo on newly established connections */ + queue_delayed_work(cifsiod_wq, &server->reconnect, 0); + return rc; } - /* if no session, renegotiate failed above */ - if (server->tcpStatus == CifsNeedNegotiate) - return -EIO; - rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req); if (rc) return rc; diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 9bc59f9c12fb..0a406ae78129 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -95,6 +95,7 @@ extern int smb2_open_file(const unsigned int xid, extern int smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, const unsigned int xid); extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); +extern void smb2_reconnect_server(struct work_struct *work); /* * SMB2 Worker functions - most of protocol specific implementation details diff --git a/fs/coredump.c b/fs/coredump.c index 5d15c4975ba1..fe0a28da18a6 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -720,7 +720,7 @@ void do_coredump(const siginfo_t *siginfo) goto close_fail; if (!(cprm.file->f_mode & FMODE_CAN_WRITE)) goto close_fail; - if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file)) + if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file)) goto close_fail; } diff --git a/fs/dcache.c b/fs/dcache.c index 9e5099997fcd..7b8feb6d60c8 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1322,8 +1322,11 @@ int d_set_mounted(struct dentry *dentry) } spin_lock(&dentry->d_lock); if (!d_unlinked(dentry)) { - dentry->d_flags |= DCACHE_MOUNTED; - ret = 0; + ret = -EBUSY; + if (!d_mountpoint(dentry)) { + dentry->d_flags |= DCACHE_MOUNTED; + ret = 0; + } } spin_unlock(&dentry->d_lock); out: diff --git a/fs/exec.c b/fs/exec.c index b06623a9347f..8c58183eccb7 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -19,7 +19,7 @@ * current->executable is only used by the procfs. This allows a dispatch * table to check for several different types of binary formats. We keep * trying until we recognize the file or we run out of supported binary - * formats. + * formats. */ #include <linux/slab.h> @@ -56,6 +56,7 @@ #include <linux/pipe_fs_i.h> #include <linux/oom.h> #include <linux/compat.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> @@ -1114,6 +1115,13 @@ int flush_old_exec(struct linux_binprm * bprm) flush_thread(); current->personality &= ~bprm->per_clear; + /* + * We have to apply CLOEXEC before we change whether the process is + * dumpable (in setup_new_exec) to avoid a race with a process in userspace + * trying to access the should-be-closed file descriptors of a process + * undergoing exec(2). + */ + do_close_on_exec(current->files); return 0; out: @@ -1123,8 +1131,22 @@ EXPORT_SYMBOL(flush_old_exec); void would_dump(struct linux_binprm *bprm, struct file *file) { - if (inode_permission(file_inode(file), MAY_READ) < 0) + struct inode *inode = file_inode(file); + if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) { + struct user_namespace *old, *user_ns; bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; + + /* Ensure mm->user_ns contains the executable */ + user_ns = old = bprm->mm->user_ns; + while ((user_ns != &init_user_ns) && + !privileged_wrt_inode_uidgid(user_ns, inode)) + user_ns = user_ns->parent; + + if (old != user_ns) { + bprm->mm->user_ns = get_user_ns(user_ns); + put_user_ns(old); + } + } } EXPORT_SYMBOL(would_dump); @@ -1154,7 +1176,6 @@ void setup_new_exec(struct linux_binprm * bprm) !gid_eq(bprm->cred->gid, current_egid())) { current->pdeath_signal = 0; } else { - would_dump(bprm, bprm->file); if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) set_dumpable(current->mm, suid_dumpable); } @@ -1163,7 +1184,6 @@ void setup_new_exec(struct linux_binprm * bprm) group */ current->self_exec_id++; flush_signal_handlers(current, 0); - do_close_on_exec(current->files); } EXPORT_SYMBOL(setup_new_exec); @@ -1254,7 +1274,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm) unsigned n_fs; if (p->ptrace) { - if (p->ptrace & PT_PTRACE_CAP) + if (ptracer_capable(p, current_user_ns())) bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP; else bprm->unsafe |= LSM_UNSAFE_PTRACE; @@ -1587,6 +1607,8 @@ static int do_execveat_common(int fd, struct filename *filename, if (retval < 0) goto out; + would_dump(bprm, bprm->file); + retval = exec_binprm(bprm); if (retval < 0) goto out; diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 5f5846211095..f817ed58f5ad 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -395,17 +395,19 @@ static inline int ext4_inode_journal_mode(struct inode *inode) return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */ /* We do not support data journalling with delayed allocation */ if (!S_ISREG(inode->i_mode) || - test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) - return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */ - if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) && - !test_opt(inode->i_sb, DELALLOC)) + test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA || + (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) && + !test_opt(inode->i_sb, DELALLOC))) { + /* We do not support data journalling for encrypted data */ + if (S_ISREG(inode->i_mode) && ext4_encrypted_inode(inode)) + return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */ return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */ + } if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA) return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */ if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA) return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */ - else - BUG(); + BUG(); } static inline int ext4_should_journal_data(struct inode *inode) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 9da42ace762a..8a456f9b8a44 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -5362,7 +5362,8 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, ext4_lblk_t stop, *iterator, ex_start, ex_end; /* Let path point to the last extent */ - path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0); + path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, + EXT4_EX_NOCACHE); if (IS_ERR(path)) return PTR_ERR(path); @@ -5371,15 +5372,15 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, if (!extent) goto out; - stop = le32_to_cpu(extent->ee_block) + - ext4_ext_get_actual_len(extent); + stop = le32_to_cpu(extent->ee_block); /* * In case of left shift, Don't start shifting extents until we make * sure the hole is big enough to accommodate the shift. */ if (SHIFT == SHIFT_LEFT) { - path = ext4_find_extent(inode, start - 1, &path, 0); + path = ext4_find_extent(inode, start - 1, &path, + EXT4_EX_NOCACHE); if (IS_ERR(path)) return PTR_ERR(path); depth = path->p_depth; @@ -5411,9 +5412,14 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, else iterator = &stop; - /* Its safe to start updating extents */ - while (start < stop) { - path = ext4_find_extent(inode, *iterator, &path, 0); + /* + * Its safe to start updating extents. Start and stop are unsigned, so + * in case of right shift if extent with 0 block is reached, iterator + * becomes NULL to indicate the end of the loop. + */ + while (iterator && start <= stop) { + path = ext4_find_extent(inode, *iterator, &path, + EXT4_EX_NOCACHE); if (IS_ERR(path)) return PTR_ERR(path); depth = path->p_depth; @@ -5440,8 +5446,11 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, ext4_ext_get_actual_len(extent); } else { extent = EXT_FIRST_EXTENT(path[depth].p_hdr); - *iterator = le32_to_cpu(extent->ee_block) > 0 ? - le32_to_cpu(extent->ee_block) - 1 : 0; + if (le32_to_cpu(extent->ee_block) > 0) + *iterator = le32_to_cpu(extent->ee_block) - 1; + else + /* Beginning is reached, end of the loop */ + iterator = NULL; /* Update path extent in case we need to stop */ while (le32_to_cpu(extent->ee_block) < start) extent++; diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index dfe3b9bafc0d..cb3d6f6419cd 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -336,8 +336,10 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode, len -= EXT4_MIN_INLINE_DATA_SIZE; value = kzalloc(len, GFP_NOFS); - if (!value) + if (!value) { + error = -ENOMEM; goto out; + } error = ext4_xattr_ibody_get(inode, i.name_index, i.name, value, len); @@ -931,8 +933,15 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, struct page *page) { int i_size_changed = 0; + int ret; - copied = ext4_write_inline_data_end(inode, pos, len, copied, page); + ret = ext4_write_inline_data_end(inode, pos, len, copied, page); + if (ret < 0) { + unlock_page(page); + put_page(page); + return ret; + } + copied = ret; /* * No need to use i_size_read() here, the i_size diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index eaab159a9c89..99fa2fca52b1 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1167,8 +1167,11 @@ static int ext4_write_end(struct file *file, if (ext4_has_inline_data(inode)) { ret = ext4_write_inline_data_end(inode, pos, len, copied, page); - if (ret < 0) + if (ret < 0) { + unlock_page(page); + put_page(page); goto errout; + } copied = ret; } else copied = block_write_end(file, mapping, pos, @@ -1222,7 +1225,9 @@ errout: * set the buffer to be dirty, since in data=journalled mode we need * to call ext4_handle_dirty_metadata() instead. */ -static void zero_new_buffers(struct page *page, unsigned from, unsigned to) +static void ext4_journalled_zero_new_buffers(handle_t *handle, + struct page *page, + unsigned from, unsigned to) { unsigned int block_start = 0, block_end; struct buffer_head *head, *bh; @@ -1239,7 +1244,7 @@ static void zero_new_buffers(struct page *page, unsigned from, unsigned to) size = min(to, block_end) - start; zero_user(page, start, size); - set_buffer_uptodate(bh); + write_end_fn(handle, bh); } clear_buffer_new(bh); } @@ -1268,18 +1273,25 @@ static int ext4_journalled_write_end(struct file *file, BUG_ON(!ext4_handle_valid(handle)); - if (ext4_has_inline_data(inode)) - copied = ext4_write_inline_data_end(inode, pos, len, - copied, page); - else { - if (copied < len) { - if (!PageUptodate(page)) - copied = 0; - zero_new_buffers(page, from+copied, to); + if (ext4_has_inline_data(inode)) { + ret = ext4_write_inline_data_end(inode, pos, len, + copied, page); + if (ret < 0) { + unlock_page(page); + put_page(page); + goto errout; } - + copied = ret; + } else if (unlikely(copied < len) && !PageUptodate(page)) { + copied = 0; + ext4_journalled_zero_new_buffers(handle, page, from, to); + } else { + if (unlikely(copied < len)) + ext4_journalled_zero_new_buffers(handle, page, + from + copied, to); ret = ext4_walk_page_buffers(handle, page_buffers(page), from, - to, &partial, write_end_fn); + from + copied, &partial, + write_end_fn); if (!partial) SetPageUptodate(page); } @@ -1305,6 +1317,7 @@ static int ext4_journalled_write_end(struct file *file, */ ext4_orphan_add(handle, inode); +errout: ret2 = ext4_journal_stop(handle); if (!ret) ret = ret2; @@ -3567,6 +3580,10 @@ static int ext4_block_truncate_page(handle_t *handle, unsigned blocksize; struct inode *inode = mapping->host; + /* If we are processing an encrypted inode during orphan list handling */ + if (ext4_encrypted_inode(inode) && !ext4_has_encryption_key(inode)) + return 0; + blocksize = inode->i_sb->s_blocksize; length = blocksize - (offset & (blocksize - 1)); @@ -4189,6 +4206,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) struct inode *inode; journal_t *journal = EXT4_SB(sb)->s_journal; long ret; + loff_t size; int block; uid_t i_uid; gid_t i_gid; @@ -4280,6 +4298,11 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ei->i_file_acl |= ((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32; inode->i_size = ext4_isize(raw_inode); + if ((size = i_size_read(inode)) < 0) { + EXT4_ERROR_INODE(inode, "bad i_size value: %lld", size); + ret = -EFSCORRUPTED; + goto bad_inode; + } ei->i_disksize = inode->i_size; #ifdef CONFIG_QUOTA ei->i_reserved_quota = 0; diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 0b1c97875686..c2810503eb50 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -669,7 +669,7 @@ static void ext4_mb_mark_free_simple(struct super_block *sb, ext4_grpblk_t min; ext4_grpblk_t max; ext4_grpblk_t chunk; - unsigned short border; + unsigned int border; BUG_ON(len > EXT4_CLUSTERS_PER_GROUP(sb)); @@ -2287,7 +2287,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) struct ext4_group_info *grinfo; struct sg { struct ext4_group_info info; - ext4_grpblk_t counters[16]; + ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2]; } sg; group--; @@ -3121,6 +3121,13 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, if (ar->pright && start + size - 1 >= ar->lright) size -= start + size - ar->lright; + /* + * Trim allocation request for filesystems with artificially small + * groups. + */ + if (size > EXT4_BLOCKS_PER_GROUP(ac->ac_sb)) + size = EXT4_BLOCKS_PER_GROUP(ac->ac_sb); + end = start + size; /* check we don't cross already preallocated blocks */ diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 127155b82e6e..6fe8e30eeb99 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -793,6 +793,7 @@ static void ext4_put_super(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_super_block *es = sbi->s_es; + int aborted = 0; int i, err; ext4_unregister_li_request(sb); @@ -802,9 +803,10 @@ static void ext4_put_super(struct super_block *sb) destroy_workqueue(sbi->rsv_conversion_wq); if (sbi->s_journal) { + aborted = is_journal_aborted(sbi->s_journal); err = jbd2_journal_destroy(sbi->s_journal); sbi->s_journal = NULL; - if (err < 0) + if ((err < 0) && !aborted) ext4_abort(sb, "Couldn't clean up the journal"); } @@ -816,7 +818,7 @@ static void ext4_put_super(struct super_block *sb) ext4_ext_release(sb); ext4_xattr_put_super(sb); - if (!(sb->s_flags & MS_RDONLY)) { + if (!(sb->s_flags & MS_RDONLY) && !aborted) { ext4_clear_feature_journal_needs_recovery(sb); es->s_state = cpu_to_le16(sbi->s_mount_state); } @@ -3037,10 +3039,15 @@ static int count_overhead(struct super_block *sb, ext4_group_t grp, ext4_set_bit(s++, buf); count++; } - for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) { - ext4_set_bit(EXT4_B2C(sbi, s++), buf); - count++; + j = ext4_bg_num_gdb(sb, grp); + if (s + j > EXT4_BLOCKS_PER_GROUP(sb)) { + ext4_error(sb, "Invalid number of block group " + "descriptor blocks: %d", j); + j = EXT4_BLOCKS_PER_GROUP(sb) - s; } + count += j; + for (; j > 0; j--) + ext4_set_bit(EXT4_B2C(sbi, s++), buf); } if (!count) return 0; @@ -3130,7 +3137,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) char *orig_data = kstrdup(data, GFP_KERNEL); struct buffer_head *bh; struct ext4_super_block *es = NULL; - struct ext4_sb_info *sbi; + struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); ext4_fsblk_t block; ext4_fsblk_t sb_block = get_sb_block(&data); ext4_fsblk_t logical_sb_block; @@ -3149,16 +3156,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; ext4_group_t first_not_zeroed; - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) - goto out_free_orig; + if ((data && !orig_data) || !sbi) + goto out_free_base; sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); - if (!sbi->s_blockgroup_lock) { - kfree(sbi); - goto out_free_orig; - } + if (!sbi->s_blockgroup_lock) + goto out_free_base; + sb->s_fs_info = sbi; sbi->s_sb = sb; sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS; @@ -3304,11 +3309,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) */ sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT; - if (!parse_options((char *) sbi->s_es->s_mount_opts, sb, - &journal_devnum, &journal_ioprio, 0)) { - ext4_msg(sb, KERN_WARNING, - "failed to parse options in superblock: %s", - sbi->s_es->s_mount_opts); + if (sbi->s_es->s_mount_opts[0]) { + char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts, + sizeof(sbi->s_es->s_mount_opts), + GFP_KERNEL); + if (!s_mount_opts) + goto failed_mount; + if (!parse_options(s_mount_opts, sb, &journal_devnum, + &journal_ioprio, 0)) { + ext4_msg(sb, KERN_WARNING, + "failed to parse options in superblock: %s", + s_mount_opts); + } + kfree(s_mount_opts); } sbi->s_def_mount_opt = sbi->s_mount_opt; if (!parse_options((char *) data, sb, &journal_devnum, @@ -3334,6 +3347,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "both data=journal and dax"); goto failed_mount; } + if (ext4_has_feature_encrypt(sb)) { + ext4_msg(sb, KERN_WARNING, + "encrypted files will use data=ordered " + "instead of data journaling mode"); + } if (test_opt(sb, DELALLOC)) clear_opt(sb, DELALLOC); } else { @@ -3496,12 +3514,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); - if (EXT4_INODE_SIZE(sb) == 0 || EXT4_INODES_PER_GROUP(sb) == 0) - goto cantfind_ext4; sbi->s_inodes_per_block = blocksize / EXT4_INODE_SIZE(sb); if (sbi->s_inodes_per_block == 0) goto cantfind_ext4; + if (sbi->s_inodes_per_group < sbi->s_inodes_per_block || + sbi->s_inodes_per_group > blocksize * 8) { + ext4_msg(sb, KERN_ERR, "invalid inodes per group: %lu\n", + sbi->s_blocks_per_group); + goto failed_mount; + } sbi->s_itb_per_group = sbi->s_inodes_per_group / sbi->s_inodes_per_block; sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb); @@ -3584,13 +3606,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) } sbi->s_cluster_ratio = clustersize / blocksize; - if (sbi->s_inodes_per_group > blocksize * 8) { - ext4_msg(sb, KERN_ERR, - "#inodes per group too big: %lu", - sbi->s_inodes_per_group); - goto failed_mount; - } - /* Do we have standard group size of clustersize * 8 blocks ? */ if (sbi->s_blocks_per_group == clustersize << 3) set_opt2(sb, STD_GROUP_SIZE); @@ -3650,6 +3665,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb); + if (ext4_has_feature_meta_bg(sb)) { + if (le32_to_cpu(es->s_first_meta_bg) >= db_count) { + ext4_msg(sb, KERN_WARNING, + "first meta block group too large: %u " + "(group descriptor block count %u)", + le32_to_cpu(es->s_first_meta_bg), db_count); + goto failed_mount; + } + } sbi->s_group_desc = ext4_kvmalloc(db_count * sizeof(struct buffer_head *), GFP_KERNEL); @@ -3724,7 +3748,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) * root first: it may be modified in the journal! */ if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) { - if (ext4_load_journal(sb, es, journal_devnum)) + err = ext4_load_journal(sb, es, journal_devnum); + if (err) goto failed_mount3a; } else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) && ext4_has_feature_journal_needs_recovery(sb)) { @@ -3994,7 +4019,9 @@ no_journal: if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount")) ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. " - "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts, + "Opts: %.*s%s%s", descr, + (int) sizeof(sbi->s_es->s_mount_opts), + sbi->s_es->s_mount_opts, *sbi->s_es->s_mount_opts ? "; " : "", orig_data); if (es->s_error_count) @@ -4064,8 +4091,8 @@ failed_mount: out_fail: sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); +out_free_base: kfree(sbi); -out_free_orig: kfree(orig_data); return err ? err : ret; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 972eab7ac071..f53826ec30f3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -742,6 +742,10 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock, static int get_data_block_bmap(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { + /* Block number less than F2FS MAX BLOCKS */ + if (unlikely(iblock >= max_file_size(0))) + return -EFBIG; + return __get_data_block(inode, iblock, bh_result, create, F2FS_GET_BLOCK_BMAP); } diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 478e5d54154f..24d6a51b48d1 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -352,6 +352,7 @@ static int stat_open(struct inode *inode, struct file *file) } static const struct file_operations stat_fops = { + .owner = THIS_MODULE, .open = stat_open, .read = seq_read, .llseek = seq_lseek, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9db5500d63d9..3c7594b9d109 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1715,6 +1715,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) * super.c */ int f2fs_commit_super(struct f2fs_sb_info *, bool); +loff_t max_file_size(unsigned bits); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) void f2fs_msg(struct super_block *, const char *, const char *, ...); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3a65e0132352..106dda1e743d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -898,7 +898,7 @@ static const struct export_operations f2fs_export_ops = { .get_parent = f2fs_get_parent, }; -static loff_t max_file_size(unsigned bits) +loff_t max_file_size(unsigned bits) { loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); loff_t leaf_count = ADDRS_PER_BLOCK; diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 8226557130a2..6abd78629140 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -92,7 +92,8 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent, err_brelse: brelse(bhs[0]); err: - fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)", (llu)blocknr); + fat_msg_ratelimit(sb, KERN_ERR, + "FAT read failed (blocknr %llu)", (llu)blocknr); return -EIO; } @@ -105,8 +106,8 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent, fatent->fat_inode = MSDOS_SB(sb)->fat_inode; fatent->bhs[0] = sb_bread(sb, blocknr); if (!fatent->bhs[0]) { - fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)", - (llu)blocknr); + fat_msg_ratelimit(sb, KERN_ERR, + "FAT read failed (blocknr %llu)", (llu)blocknr); return -EIO; } fatent->nr_bhs = 1; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 509411dd3698..a6c21fba6e9f 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -760,8 +760,9 @@ retry: fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset); bh = sb_bread(sb, blocknr); if (!bh) { - fat_msg(sb, KERN_ERR, "unable to read inode block " - "for updating (i_pos %lld)", i_pos); + fat_msg_ratelimit(sb, KERN_ERR, + "unable to read inode block for updating (i_pos %lld)", + i_pos); return -EIO; } spin_lock(&sbi->inode_hash_lock); @@ -1269,6 +1270,16 @@ out: return 0; } +static void fat_dummy_inode_init(struct inode *inode) +{ + /* Initialize this dummy inode to work as no-op. */ + MSDOS_I(inode)->mmu_private = 0; + MSDOS_I(inode)->i_start = 0; + MSDOS_I(inode)->i_logstart = 0; + MSDOS_I(inode)->i_attrs = 0; + MSDOS_I(inode)->i_pos = 0; +} + static int fat_read_root(struct inode *inode) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); @@ -1713,12 +1724,13 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, fat_inode = new_inode(sb); if (!fat_inode) goto out_fail; - MSDOS_I(fat_inode)->i_pos = 0; + fat_dummy_inode_init(fat_inode); sbi->fat_inode = fat_inode; fsinfo_inode = new_inode(sb); if (!fsinfo_inode) goto out_fail; + fat_dummy_inode_init(fsinfo_inode); fsinfo_inode->i_ino = MSDOS_FSINFO_INO; sbi->fsinfo_inode = fsinfo_inode; insert_inode_hash(fsinfo_inode); diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 7dca743b2ce1..940c683561dd 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -44,6 +44,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path) if (old_pwd.dentry) path_put(&old_pwd); } +EXPORT_SYMBOL(set_fs_pwd); static inline int replace_path(struct path *p, const struct path *old, const struct path *new) { @@ -89,6 +90,7 @@ void free_fs_struct(struct fs_struct *fs) path_put(&fs->pwd); kmem_cache_free(fs_cachep, fs); } +EXPORT_SYMBOL(free_fs_struct); void exit_fs(struct task_struct *tsk) { @@ -127,6 +129,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) } return fs; } +EXPORT_SYMBOL_GPL(copy_fs_struct); int unshare_fs_struct(void) { diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0443e06d1902..ca7d46de5ca3 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2102,7 +2102,6 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head) struct fuse_req *req; req = list_entry(head->next, struct fuse_req, list); req->out.h.error = -ECONNABORTED; - clear_bit(FR_PENDING, &req->flags); clear_bit(FR_SENT, &req->flags); list_del_init(&req->list); request_end(fc, req); @@ -2180,6 +2179,8 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_lock(&fiq->waitq.lock); fiq->connected = 0; list_splice_init(&fiq->pending, &to_end2); + list_for_each_entry(req, &to_end2, list) + clear_bit(FR_PENDING, &req->flags); while (forget_pending(fiq)) kfree(dequeue_forget(fiq, 1, NULL)); wake_up_all_locked(&fiq->waitq); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 64eb2c6ee450..66b34b14cb6b 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -113,6 +113,7 @@ static void fuse_file_put(struct fuse_file *ff, bool sync) iput(req->misc.release.inode); fuse_put_request(ff->fc, req); } else if (sync) { + __set_bit(FR_FORCE, &req->flags); __clear_bit(FR_BACKGROUND, &req->flags); fuse_request_send(ff->fc, req); iput(req->misc.release.inode); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 32e74710b1aa..9cd8c92b953d 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -651,9 +651,11 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, struct kmem_cache *cachep; int ret, tries = 0; + rcu_read_lock(); gl = rhashtable_lookup_fast(&gl_hash_table, &name, ht_parms); if (gl && !lockref_get_not_dead(&gl->gl_lockref)) gl = NULL; + rcu_read_unlock(); *glp = gl; if (gl) @@ -721,15 +723,18 @@ again: if (ret == -EEXIST) { ret = 0; + rcu_read_lock(); tmp = rhashtable_lookup_fast(&gl_hash_table, &name, ht_parms); if (tmp == NULL || !lockref_get_not_dead(&tmp->gl_lockref)) { if (++tries < 100) { + rcu_read_unlock(); cond_resched(); goto again; } tmp = NULL; ret = -ENOMEM; } + rcu_read_unlock(); } else { WARN_ON_ONCE(ret); } diff --git a/fs/inode.c b/fs/inode.c index 2c16b758831d..6a7234f0afea 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1721,7 +1721,7 @@ int dentry_needs_remove_privs(struct dentry *dentry) } EXPORT_SYMBOL(dentry_needs_remove_privs); -static int __remove_privs(struct dentry *dentry, int kill) +static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill) { struct iattr newattrs; @@ -1730,7 +1730,7 @@ static int __remove_privs(struct dentry *dentry, int kill) * Note we call this on write, so notify_change will not * encounter any conflicting delegations: */ - return notify_change(dentry, &newattrs, NULL); + return notify_change2(mnt, dentry, &newattrs, NULL); } /* @@ -1752,7 +1752,7 @@ int file_remove_privs(struct file *file) if (kill < 0) return kill; if (kill) - error = __remove_privs(dentry, kill); + error = __remove_privs(file->f_path.mnt, dentry, kill); if (!error) inode_has_no_xattr(inode); diff --git a/fs/internal.h b/fs/internal.h index 71859c4d0b41..6387b35a1c0d 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -84,9 +84,11 @@ extern struct file *get_empty_filp(void); * super.c */ extern int do_remount_sb(struct super_block *, int, void *, int); +extern int do_remount_sb2(struct vfsmount *, struct super_block *, int, + void *, int); extern bool trylock_super(struct super_block *sb); extern struct dentry *mount_fs(struct file_system_type *, - int, const char *, void *); + int, const char *, struct vfsmount *, void *); extern struct super_block *user_get_super(dev_t); /* diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index fa1b8e0dcacf..a2e724053919 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1876,7 +1876,9 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh) __blist_del_buffer(list, jh); jh->b_jlist = BJ_None; - if (test_clear_buffer_jbddirty(bh)) + if (transaction && is_journal_aborted(transaction->t_journal)) + clear_buffer_jbddirty(bh); + else if (test_clear_buffer_jbddirty(bh)) mark_buffer_dirty(bh); /* Expose it to the VM */ } diff --git a/fs/mount.h b/fs/mount.h index 14db05d424f7..3dc7dea5a357 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -86,7 +86,6 @@ static inline int is_mounted(struct vfsmount *mnt) } extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *); -extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *); extern int __legitimize_mnt(struct vfsmount *, unsigned); extern bool legitimize_mnt(struct vfsmount *, unsigned); diff --git a/fs/namei.c b/fs/namei.c index 1b4585e9a463..816b6e8e934e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -373,9 +373,11 @@ EXPORT_SYMBOL(generic_permission); * flag in inode->i_opflags, that says "this has not special * permission function, use the fast case". */ -static inline int do_inode_permission(struct inode *inode, int mask) +static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask) { if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) { + if (likely(mnt && inode->i_op->permission2)) + return inode->i_op->permission2(mnt, inode, mask); if (likely(inode->i_op->permission)) return inode->i_op->permission(inode, mask); @@ -399,7 +401,7 @@ static inline int do_inode_permission(struct inode *inode, int mask) * This does not check for a read-only file system. You probably want * inode_permission(). */ -int __inode_permission(struct inode *inode, int mask) +int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask) { int retval; @@ -411,7 +413,7 @@ int __inode_permission(struct inode *inode, int mask) return -EACCES; } - retval = do_inode_permission(inode, mask); + retval = do_inode_permission(mnt, inode, mask); if (retval) return retval; @@ -419,7 +421,14 @@ int __inode_permission(struct inode *inode, int mask) if (retval) return retval; - return security_inode_permission(inode, mask); + retval = security_inode_permission(inode, mask); + return retval; +} +EXPORT_SYMBOL(__inode_permission2); + +int __inode_permission(struct inode *inode, int mask) +{ + return __inode_permission2(NULL, inode, mask); } EXPORT_SYMBOL(__inode_permission); @@ -455,14 +464,20 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) * * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask. */ -int inode_permission(struct inode *inode, int mask) +int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask) { int retval; retval = sb_permission(inode->i_sb, inode, mask); if (retval) return retval; - return __inode_permission(inode, mask); + return __inode_permission2(mnt, inode, mask); +} +EXPORT_SYMBOL(inode_permission2); + +int inode_permission(struct inode *inode, int mask) +{ + return inode_permission2(NULL, inode, mask); } EXPORT_SYMBOL(inode_permission); @@ -1645,13 +1660,13 @@ static int lookup_slow(struct nameidata *nd, struct path *path) static inline int may_lookup(struct nameidata *nd) { if (nd->flags & LOOKUP_RCU) { - int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); + int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK); if (err != -ECHILD) return err; if (unlazy_walk(nd, NULL, 0)) return -ECHILD; } - return inode_permission(nd->inode, MAY_EXEC); + return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC); } static inline int handle_dots(struct nameidata *nd, int type) @@ -2005,11 +2020,12 @@ static const char *path_init(struct nameidata *nd, unsigned flags) nd->depth = 0; if (flags & LOOKUP_ROOT) { struct dentry *root = nd->root.dentry; + struct vfsmount *mnt = nd->root.mnt; struct inode *inode = root->d_inode; if (*s) { if (!d_can_lookup(root)) return ERR_PTR(-ENOTDIR); - retval = inode_permission(inode, MAY_EXEC); + retval = inode_permission2(mnt, inode, MAY_EXEC); if (retval) return ERR_PTR(retval); } @@ -2280,13 +2296,14 @@ EXPORT_SYMBOL(vfs_path_lookup); /** * lookup_one_len - filesystem helper to lookup single pathname component * @name: pathname component to lookup + * @mnt: mount we are looking up on * @base: base directory to lookup from * @len: maximum length @len should be interpreted to * * Note that this routine is purely a helper for filesystem usage and should * not be called by generic code. */ -struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len) { struct qstr this; unsigned int c; @@ -2320,12 +2337,18 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) return ERR_PTR(err); } - err = inode_permission(base->d_inode, MAY_EXEC); + err = inode_permission2(mnt, base->d_inode, MAY_EXEC); if (err) return ERR_PTR(err); return __lookup_hash(&this, base, 0); } +EXPORT_SYMBOL(lookup_one_len2); + +struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +{ + return lookup_one_len2(name, NULL, base, len); +} EXPORT_SYMBOL(lookup_one_len); int user_path_at_empty(int dfd, const char __user *name, unsigned flags, @@ -2552,7 +2575,7 @@ EXPORT_SYMBOL(__check_sticky); * 10. We don't allow removal of NFS sillyrenamed files; it's handled by * nfs_async_unlink(). */ -static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) +static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir) { struct inode *inode = d_backing_inode(victim); int error; @@ -2564,7 +2587,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) BUG_ON(victim->d_parent->d_inode != dir); audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) @@ -2595,14 +2618,14 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) * 3. We should have write and exec permissions on dir * 4. We can't do it if dir is immutable (done in permission()) */ -static inline int may_create(struct inode *dir, struct dentry *child) +static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child) { audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - return inode_permission(dir, MAY_WRITE | MAY_EXEC); + return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); } /* @@ -2649,10 +2672,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) } EXPORT_SYMBOL(unlock_rename); -int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool want_excl) +int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, + umode_t mode, bool want_excl) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -2674,11 +2697,19 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, return error; } +EXPORT_SYMBOL(vfs_create2); + +int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl) +{ + return vfs_create2(NULL, dir, dentry, mode, want_excl); +} EXPORT_SYMBOL(vfs_create); static int may_open(struct path *path, int acc_mode, int flag) { struct dentry *dentry = path->dentry; + struct vfsmount *mnt = path->mnt; struct inode *inode = dentry->d_inode; int error; @@ -2707,7 +2738,7 @@ static int may_open(struct path *path, int acc_mode, int flag) break; } - error = inode_permission(inode, acc_mode); + error = inode_permission2(mnt, inode, acc_mode); if (error) return error; @@ -2742,7 +2773,7 @@ static int handle_truncate(struct file *filp) if (!error) error = security_path_truncate(path); if (!error) { - error = do_truncate(path->dentry, 0, + error = do_truncate2(path->mnt, path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, filp); } @@ -2763,7 +2794,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode) if (error) return error; - error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); + error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); if (error) return error; @@ -2949,6 +2980,7 @@ static int lookup_open(struct nameidata *nd, struct path *path, bool got_write, int *opened) { struct dentry *dir = nd->path.dentry; + struct vfsmount *mnt = nd->path.mnt; struct inode *dir_inode = dir->d_inode; struct dentry *dentry; int error; @@ -2996,7 +3028,7 @@ static int lookup_open(struct nameidata *nd, struct path *path, error = security_path_mknod(&nd->path, dentry, mode, 0); if (error) goto out_dput; - error = vfs_create(dir->d_inode, dentry, mode, + error = vfs_create2(mnt, dir->d_inode, dentry, mode, nd->flags & LOOKUP_EXCL); if (error) goto out_dput; @@ -3258,7 +3290,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, goto out; dir = path.dentry->d_inode; /* we want directory to be writable */ - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + error = inode_permission2(path.mnt, dir, MAY_WRITE | MAY_EXEC); if (error) goto out2; if (!dir->i_op->tmpfile) { @@ -3492,9 +3524,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname, } EXPORT_SYMBOL(user_path_create); -int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -3526,6 +3558,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) return error; } +EXPORT_SYMBOL(vfs_mknod2); + +int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +{ + return vfs_mknod2(NULL, dir, dentry, mode, dev); +} EXPORT_SYMBOL(vfs_mknod); static int may_mknod(umode_t mode) @@ -3568,10 +3606,10 @@ retry: goto out; switch (mode & S_IFMT) { case 0: case S_IFREG: - error = vfs_create(path.dentry->d_inode,dentry,mode,true); + error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true); break; case S_IFCHR: case S_IFBLK: - error = vfs_mknod(path.dentry->d_inode,dentry,mode, + error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode, new_decode_dev(dev)); break; case S_IFIFO: case S_IFSOCK: @@ -3592,9 +3630,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d return sys_mknodat(AT_FDCWD, filename, mode, dev); } -int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); unsigned max_links = dir->i_sb->s_max_links; if (error) @@ -3616,6 +3654,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) fsnotify_mkdir(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mkdir2); + +int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + return vfs_mkdir2(NULL, dir, dentry, mode); +} EXPORT_SYMBOL(vfs_mkdir); SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) @@ -3634,7 +3678,7 @@ retry: mode &= ~current_umask(); error = security_path_mkdir(&path, dentry, mode); if (!error) - error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -3673,9 +3717,9 @@ void dentry_unhash(struct dentry *dentry) } EXPORT_SYMBOL(dentry_unhash); -int vfs_rmdir(struct inode *dir, struct dentry *dentry) +int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry) { - int error = may_delete(dir, dentry, 1); + int error = may_delete(mnt, dir, dentry, 1); if (error) return error; @@ -3710,6 +3754,12 @@ out: d_delete(dentry); return error; } +EXPORT_SYMBOL(vfs_rmdir2); + +int vfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + return vfs_rmdir2(NULL, dir, dentry); +} EXPORT_SYMBOL(vfs_rmdir); static long do_rmdir(int dfd, const char __user *pathname) @@ -3755,7 +3805,7 @@ retry: error = security_path_rmdir(&path, dentry); if (error) goto exit3; - error = vfs_rmdir(path.dentry->d_inode, dentry); + error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry); exit3: dput(dentry); exit2: @@ -3794,10 +3844,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) { struct inode *target = dentry->d_inode; - int error = may_delete(dir, dentry, 0); + int error = may_delete(mnt, dir, dentry, 0); if (error) return error; @@ -3832,6 +3882,12 @@ out: return error; } +EXPORT_SYMBOL(vfs_unlink2); + +int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +{ + return vfs_unlink2(NULL, dir, dentry, delegated_inode); +} EXPORT_SYMBOL(vfs_unlink); /* @@ -3879,7 +3935,7 @@ retry_deleg: error = security_path_unlink(&path, dentry); if (error) goto exit2; - error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); + error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode); exit2: dput(dentry); } @@ -3929,9 +3985,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) return do_unlinkat(AT_FDCWD, pathname); } -int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -3948,6 +4004,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_symlink2); + +int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +{ + return vfs_symlink2(NULL, dir, dentry, oldname); +} EXPORT_SYMBOL(vfs_symlink); SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, @@ -3970,7 +4032,7 @@ retry: error = security_path_symlink(&path, dentry, from->name); if (!error) - error = vfs_symlink(path.dentry->d_inode, dentry, from->name); + error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4005,7 +4067,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) { struct inode *inode = old_dentry->d_inode; unsigned max_links = dir->i_sb->s_max_links; @@ -4014,7 +4076,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de if (!inode) return -ENOENT; - error = may_create(dir, new_dentry); + error = may_create(mnt, dir, new_dentry); if (error) return error; @@ -4057,6 +4119,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de fsnotify_link(dir, inode, new_dentry); return error; } +EXPORT_SYMBOL(vfs_link2); + +int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +{ + return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode); +} EXPORT_SYMBOL(vfs_link); /* @@ -4112,7 +4180,7 @@ retry: error = security_path_link(old_path.dentry, &new_path, new_dentry); if (error) goto out_dput; - error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); + error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); out_dput: done_path_create(&new_path, new_dentry); if (delegated_inode) { @@ -4187,7 +4255,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname * ->i_mutex on parents, which works but leads to some truly excessive * locking]. */ -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, +int vfs_rename2(struct vfsmount *mnt, + struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct inode **delegated_inode, unsigned int flags) { @@ -4206,19 +4275,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0)) return 0; - error = may_delete(old_dir, old_dentry, is_dir); + error = may_delete(mnt, old_dir, old_dentry, is_dir); if (error) return error; if (!target) { - error = may_create(new_dir, new_dentry); + error = may_create(mnt, new_dir, new_dentry); } else { new_is_dir = d_is_dir(new_dentry); if (!(flags & RENAME_EXCHANGE)) - error = may_delete(new_dir, new_dentry, is_dir); + error = may_delete(mnt, new_dir, new_dentry, is_dir); else - error = may_delete(new_dir, new_dentry, new_is_dir); + error = may_delete(mnt, new_dir, new_dentry, new_is_dir); } if (error) return error; @@ -4235,12 +4304,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (new_dir != old_dir) { if (is_dir) { - error = inode_permission(source, MAY_WRITE); + error = inode_permission2(mnt, source, MAY_WRITE); if (error) return error; } if ((flags & RENAME_EXCHANGE) && new_is_dir) { - error = inode_permission(target, MAY_WRITE); + error = inode_permission2(mnt, target, MAY_WRITE); if (error) return error; } @@ -4323,6 +4392,14 @@ out: return error; } +EXPORT_SYMBOL(vfs_rename2); + +int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + struct inode **delegated_inode, unsigned int flags) +{ + return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags); +} EXPORT_SYMBOL(vfs_rename); SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, @@ -4436,7 +4513,7 @@ retry_deleg: &new_path, new_dentry, flags); if (error) goto exit5; - error = vfs_rename(old_path.dentry->d_inode, old_dentry, + error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode, flags); exit5: @@ -4481,7 +4558,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna int vfs_whiteout(struct inode *dir, struct dentry *dentry) { - int error = may_create(dir, dentry); + int error = may_create(NULL, dir, dentry); if (error) return error; diff --git a/fs/namespace.c b/fs/namespace.c index 5be02a0635be..c1477882a853 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -577,6 +577,7 @@ int sb_prepare_remount_readonly(struct super_block *sb) static void free_vfsmnt(struct mount *mnt) { + kfree(mnt->mnt.data); kfree_const(mnt->mnt_devname); #ifdef CONFIG_SMP free_percpu(mnt->mnt_pcp); @@ -638,28 +639,6 @@ struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) } /* - * find the last mount at @dentry on vfsmount @mnt. - * mount_lock must be held. - */ -struct mount *__lookup_mnt_last(struct vfsmount *mnt, struct dentry *dentry) -{ - struct mount *p, *res = NULL; - p = __lookup_mnt(mnt, dentry); - if (!p) - goto out; - if (!(p->mnt.mnt_flags & MNT_UMOUNT)) - res = p; - hlist_for_each_entry_continue(p, mnt_hash) { - if (&p->mnt_parent->mnt != mnt || p->mnt_mountpoint != dentry) - break; - if (!(p->mnt.mnt_flags & MNT_UMOUNT)) - res = p; - } -out: - return res; -} - -/* * lookup_mnt - Return the first child mount mounted at path * * "First" means first mounted chronologically. If you create the @@ -743,26 +722,50 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry) return NULL; } -static struct mountpoint *new_mountpoint(struct dentry *dentry) +static struct mountpoint *get_mountpoint(struct dentry *dentry) { - struct hlist_head *chain = mp_hash(dentry); - struct mountpoint *mp; + struct mountpoint *mp, *new = NULL; int ret; - mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL); - if (!mp) + if (d_mountpoint(dentry)) { +mountpoint: + read_seqlock_excl(&mount_lock); + mp = lookup_mountpoint(dentry); + read_sequnlock_excl(&mount_lock); + if (mp) + goto done; + } + + if (!new) + new = kmalloc(sizeof(struct mountpoint), GFP_KERNEL); + if (!new) return ERR_PTR(-ENOMEM); + + /* Exactly one processes may set d_mounted */ ret = d_set_mounted(dentry); - if (ret) { - kfree(mp); - return ERR_PTR(ret); - } - mp->m_dentry = dentry; - mp->m_count = 1; - hlist_add_head(&mp->m_hash, chain); - INIT_HLIST_HEAD(&mp->m_list); + /* Someone else set d_mounted? */ + if (ret == -EBUSY) + goto mountpoint; + + /* The dentry is not available as a mountpoint? */ + mp = ERR_PTR(ret); + if (ret) + goto done; + + /* Add the new mountpoint to the hash table */ + read_seqlock_excl(&mount_lock); + new->m_dentry = dentry; + new->m_count = 1; + hlist_add_head(&new->m_hash, mp_hash(dentry)); + INIT_HLIST_HEAD(&new->m_list); + read_sequnlock_excl(&mount_lock); + + mp = new; + new = NULL; +done: + kfree(new); return mp; } @@ -855,6 +858,13 @@ void mnt_set_mountpoint(struct mount *mnt, hlist_add_head(&child_mnt->mnt_mp_list, &mp->m_list); } +static void __attach_mnt(struct mount *mnt, struct mount *parent) +{ + hlist_add_head_rcu(&mnt->mnt_hash, + m_hash(&parent->mnt, mnt->mnt_mountpoint)); + list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); +} + /* * vfsmount lock must be held for write */ @@ -863,28 +873,45 @@ static void attach_mnt(struct mount *mnt, struct mountpoint *mp) { mnt_set_mountpoint(parent, mp, mnt); - hlist_add_head_rcu(&mnt->mnt_hash, m_hash(&parent->mnt, mp->m_dentry)); - list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); + __attach_mnt(mnt, parent); } -static void attach_shadowed(struct mount *mnt, - struct mount *parent, - struct mount *shadows) +void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct mount *mnt) { - if (shadows) { - hlist_add_behind_rcu(&mnt->mnt_hash, &shadows->mnt_hash); - list_add(&mnt->mnt_child, &shadows->mnt_child); - } else { - hlist_add_head_rcu(&mnt->mnt_hash, - m_hash(&parent->mnt, mnt->mnt_mountpoint)); - list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); - } + struct mountpoint *old_mp = mnt->mnt_mp; + struct dentry *old_mountpoint = mnt->mnt_mountpoint; + struct mount *old_parent = mnt->mnt_parent; + + list_del_init(&mnt->mnt_child); + hlist_del_init(&mnt->mnt_mp_list); + hlist_del_init_rcu(&mnt->mnt_hash); + + attach_mnt(mnt, parent, mp); + + put_mountpoint(old_mp); + + /* + * Safely avoid even the suggestion this code might sleep or + * lock the mount hash by taking advantage of the knowledge that + * mnt_change_mountpoint will not release the final reference + * to a mountpoint. + * + * During mounting, the mount passed in as the parent mount will + * continue to use the old mountpoint and during unmounting, the + * old mountpoint will continue to exist until namespace_unlock, + * which happens well after mnt_change_mountpoint. + */ + spin_lock(&old_mountpoint->d_lock); + old_mountpoint->d_lockref.count--; + spin_unlock(&old_mountpoint->d_lock); + + mnt_add_count(old_parent, -1); } /* * vfsmount lock must be held for write */ -static void commit_tree(struct mount *mnt, struct mount *shadows) +static void commit_tree(struct mount *mnt) { struct mount *parent = mnt->mnt_parent; struct mount *m; @@ -899,7 +926,7 @@ static void commit_tree(struct mount *mnt, struct mount *shadows) list_splice(&head, n->list.prev); - attach_shadowed(mnt, parent, shadows); + __attach_mnt(mnt, parent); touch_mnt_namespace(n); } @@ -942,11 +969,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (!mnt) return ERR_PTR(-ENOMEM); + mnt->mnt.data = NULL; + if (type->alloc_mnt_data) { + mnt->mnt.data = type->alloc_mnt_data(); + if (!mnt->mnt.data) { + mnt_free_id(mnt); + free_vfsmnt(mnt); + return ERR_PTR(-ENOMEM); + } + } if (flags & MS_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; - root = mount_fs(type, flags, name, data); + root = mount_fs(type, flags, name, &mnt->mnt, data); if (IS_ERR(root)) { + kfree(mnt->mnt.data); mnt_free_id(mnt); free_vfsmnt(mnt); return ERR_CAST(root); @@ -974,6 +1011,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, if (!mnt) return ERR_PTR(-ENOMEM); + if (sb->s_op->clone_mnt_data) { + mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data); + if (!mnt->mnt.data) { + err = -ENOMEM; + goto out_free; + } + } + if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE)) mnt->mnt_group_id = 0; /* not a peer of original */ else @@ -1042,6 +1087,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, return mnt; out_free: + kfree(mnt->mnt.data); mnt_free_id(mnt); free_vfsmnt(mnt); return ERR_PTR(err); @@ -1557,11 +1603,11 @@ void __detach_mounts(struct dentry *dentry) struct mount *mnt; namespace_lock(); + lock_mount_hash(); mp = lookup_mountpoint(dentry); if (IS_ERR_OR_NULL(mp)) goto out_unlock; - lock_mount_hash(); event++; while (!hlist_empty(&mp->m_list)) { mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list); @@ -1571,9 +1617,9 @@ void __detach_mounts(struct dentry *dentry) } else umount_tree(mnt, UMOUNT_CONNECTED); } - unlock_mount_hash(); put_mountpoint(mp); out_unlock: + unlock_mount_hash(); namespace_unlock(); } @@ -1694,7 +1740,6 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, continue; for (s = r; s; s = next_mnt(s, r)) { - struct mount *t = NULL; if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(s)) { s = skip_mnt_tree(s); @@ -1716,14 +1761,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, goto out; lock_mount_hash(); list_add_tail(&q->mnt_list, &res->mnt_list); - mnt_set_mountpoint(parent, p->mnt_mp, q); - if (!list_empty(&parent->mnt_mounts)) { - t = list_last_entry(&parent->mnt_mounts, - struct mount, mnt_child); - if (t->mnt_mp != p->mnt_mp) - t = NULL; - } - attach_shadowed(q, parent, t); + attach_mnt(q, parent, p->mnt_mp); unlock_mount_hash(); } } @@ -1901,10 +1939,18 @@ static int attach_recursive_mnt(struct mount *source_mnt, struct path *parent_path) { HLIST_HEAD(tree_list); + struct mountpoint *smp; struct mount *child, *p; struct hlist_node *n; int err; + /* Preallocate a mountpoint in case the new mounts need + * to be tucked under other mounts. + */ + smp = get_mountpoint(source_mnt->mnt.mnt_root); + if (IS_ERR(smp)) + return PTR_ERR(smp); + if (IS_MNT_SHARED(dest_mnt)) { err = invent_group_ids(source_mnt, true); if (err) @@ -1924,16 +1970,19 @@ static int attach_recursive_mnt(struct mount *source_mnt, touch_mnt_namespace(source_mnt->mnt_ns); } else { mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt); - commit_tree(source_mnt, NULL); + commit_tree(source_mnt); } hlist_for_each_entry_safe(child, n, &tree_list, mnt_hash) { struct mount *q; hlist_del_init(&child->mnt_hash); - q = __lookup_mnt_last(&child->mnt_parent->mnt, - child->mnt_mountpoint); - commit_tree(child, q); + q = __lookup_mnt(&child->mnt_parent->mnt, + child->mnt_mountpoint); + if (q) + mnt_change_mountpoint(child, smp, q); + commit_tree(child); } + put_mountpoint(smp); unlock_mount_hash(); return 0; @@ -1946,6 +1995,10 @@ static int attach_recursive_mnt(struct mount *source_mnt, unlock_mount_hash(); cleanup_group_ids(source_mnt, NULL); out: + read_seqlock_excl(&mount_lock); + put_mountpoint(smp); + read_sequnlock_excl(&mount_lock); + return err; } @@ -1962,9 +2015,7 @@ retry: namespace_lock(); mnt = lookup_mnt(path); if (likely(!mnt)) { - struct mountpoint *mp = lookup_mountpoint(dentry); - if (!mp) - mp = new_mountpoint(dentry); + struct mountpoint *mp = get_mountpoint(dentry); if (IS_ERR(mp)) { namespace_unlock(); mutex_unlock(&dentry->d_inode->i_mutex); @@ -1983,7 +2034,11 @@ retry: static void unlock_mount(struct mountpoint *where) { struct dentry *dentry = where->m_dentry; + + read_seqlock_excl(&mount_lock); put_mountpoint(where); + read_sequnlock_excl(&mount_lock); + namespace_unlock(); mutex_unlock(&dentry->d_inode->i_mutex); } @@ -2208,8 +2263,14 @@ static int do_remount(struct path *path, int flags, int mnt_flags, err = change_mount_flags(path->mnt, flags); else if (!capable(CAP_SYS_ADMIN)) err = -EPERM; - else - err = do_remount_sb(sb, flags, data, 0); + else { + err = do_remount_sb2(path->mnt, sb, flags, data, 0); + namespace_lock(); + lock_mount_hash(); + propagate_remount(mnt); + unlock_mount_hash(); + namespace_unlock(); + } if (!err) { lock_mount_hash(); mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK; @@ -3055,9 +3116,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, touch_mnt_namespace(current->nsproxy->mnt_ns); /* A moved mount should not expire automatically */ list_del_init(&new_mnt->mnt_expire); + put_mountpoint(root_mp); unlock_mount_hash(); chroot_fs_refs(&root, &new); - put_mountpoint(root_mp); error = 0; out4: unlock_mount(old_mp); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 46cfed63d229..52ee0b73ab4a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -462,7 +462,7 @@ void nfs_force_use_readdirplus(struct inode *dir) { if (!list_empty(&NFS_I(dir)->open_files)) { nfs_advise_use_readdirplus(dir); - nfs_zap_mapping(dir, dir->i_mapping); + invalidate_mapping_pages(dir->i_mapping, 0, -1); } } @@ -847,17 +847,6 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc) goto out; } -static bool nfs_dir_mapping_need_revalidate(struct inode *dir) -{ - struct nfs_inode *nfsi = NFS_I(dir); - - if (nfs_attribute_cache_expired(dir)) - return true; - if (nfsi->cache_validity & NFS_INO_INVALID_DATA) - return true; - return false; -} - /* The file offset position represents the dirent entry number. A last cookie cache takes care of the common case of reading the whole directory. @@ -890,7 +879,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0; nfs_block_sillyrename(dentry); - if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode)) + if (ctx->pos == 0 || nfs_attribute_cache_expired(inode)) res = nfs_revalidate_mapping(inode, file->f_mapping); if (res < 0) goto out; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 93e236429c5d..dc875cd0e11d 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -407,7 +407,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping, */ if (!PageUptodate(page)) { unsigned pglen = nfs_page_length(page); - unsigned end = offset + len; + unsigned end = offset + copied; if (pglen == 0) { zero_user_segments(page, 0, offset, diff --git a/fs/nfs/filelayout/filelayoutdev.c b/fs/nfs/filelayout/filelayoutdev.c index 4946ef40ba87..85ef38f9765f 100644 --- a/fs/nfs/filelayout/filelayoutdev.c +++ b/fs/nfs/filelayout/filelayoutdev.c @@ -283,7 +283,8 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) s->nfs_client->cl_rpcclient->cl_auth->au_flavor); out_test_devid: - if (filelayout_test_devid_unavailable(devid)) + if (ret->ds_clp == NULL || + filelayout_test_devid_unavailable(devid)) ret = NULL; out: return ret; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 3c69299c01ab..4e3679b25b9b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2422,7 +2422,8 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, sattr->ia_valid |= ATTR_MTIME; /* Except MODE, it seems harmless of setting twice. */ - if ((attrset[1] & FATTR4_WORD1_MODE)) + if (opendata->o_arg.createmode != NFS4_CREATE_EXCLUSIVE && + attrset[1] & FATTR4_WORD1_MODE) sattr->ia_valid &= ~ATTR_MODE; if (attrset[2] & FATTR4_WORD2_SECURITY_LABEL) @@ -2451,6 +2452,7 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, ret = PTR_ERR(state); if (IS_ERR(state)) goto out; + ctx->state = state; if (server->caps & NFS_CAP_POSIX_LOCK) set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); @@ -2473,7 +2475,6 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, if (ret != 0) goto out; - ctx->state = state; if (d_inode(dentry) == state->inode) { nfs_inode_attach_open_context(ctx); if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) @@ -4710,7 +4711,7 @@ out: */ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen) { - struct page *pages[NFS4ACL_MAXPAGES] = {NULL, }; + struct page *pages[NFS4ACL_MAXPAGES + 1] = {NULL, }; struct nfs_getaclargs args = { .fh = NFS_FH(inode), .acl_pages = pages, @@ -4724,13 +4725,9 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu .rpc_argp = &args, .rpc_resp = &res, }; - unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); + unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1; int ret = -ENOMEM, i; - /* As long as we're doing a round trip to the server anyway, - * let's be prepared for a page of acl data. */ - if (npages == 0) - npages = 1; if (npages > ARRAY_SIZE(pages)) return -ERANGE; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 4e4441216804..1cb50bb898b0 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -2487,7 +2487,7 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr, encode_compound_hdr(xdr, req, &hdr); encode_sequence(xdr, &args->seq_args, &hdr); encode_putfh(xdr, args->fh, &hdr); - replen = hdr.replen + op_decode_hdr_maxsz + 1; + replen = hdr.replen + op_decode_hdr_maxsz; encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr); xdr_inline_pages(&req->rq_rcv_buf, replen << 2, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 5cd3568eea06..3cae0726c1b1 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1185,13 +1185,11 @@ bool pnfs_wait_on_layoutreturn(struct inode *ino, struct rpc_task *task) * i_lock */ spin_lock(&ino->i_lock); lo = nfsi->layout; - if (lo && test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) + if (lo && test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) { + rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); sleep = true; + } spin_unlock(&ino->i_lock); - - if (sleep) - rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); - return sleep; } diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index c9d6c715c0fb..9eed219f57a5 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -189,10 +189,11 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, struct nfs4_layout_stateid *ls; struct nfs4_stid *stp; - stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache); + stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache, + nfsd4_free_layout_stateid); if (!stp) return NULL; - stp->sc_free = nfsd4_free_layout_stateid; + get_nfs4_file(fp); stp->sc_file = fp; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 55638110cb06..c7f1ce41442a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -553,8 +553,8 @@ out: return co; } -struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, - struct kmem_cache *slab) +struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab, + void (*sc_free)(struct nfs4_stid *)) { struct nfs4_stid *stid; int new_id; @@ -570,6 +570,8 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, idr_preload_end(); if (new_id < 0) goto out_free; + + stid->sc_free = sc_free; stid->sc_client = cl; stid->sc_stateid.si_opaque.so_id = new_id; stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid; @@ -595,15 +597,12 @@ out_free: static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp) { struct nfs4_stid *stid; - struct nfs4_ol_stateid *stp; - stid = nfs4_alloc_stid(clp, stateid_slab); + stid = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_ol_stateid); if (!stid) return NULL; - stp = openlockstateid(stid); - stp->st_stid.sc_free = nfs4_free_ol_stateid; - return stp; + return openlockstateid(stid); } static void nfs4_free_deleg(struct nfs4_stid *stid) @@ -701,11 +700,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct svc_fh *current_fh, goto out_dec; if (delegation_blocked(¤t_fh->fh_handle)) goto out_dec; - dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab)); + dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg)); if (dp == NULL) goto out_dec; - dp->dl_stid.sc_free = nfs4_free_deleg; /* * delegation seqid's are never incremented. The 4.1 special * meaning of seqid 0 isn't meaningful, really, but let's avoid @@ -5396,7 +5394,6 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo, stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner); get_nfs4_file(fp); stp->st_stid.sc_file = fp; - stp->st_stid.sc_free = nfs4_free_lock_stateid; stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; @@ -5439,7 +5436,7 @@ find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi, lst = find_lock_stateid(lo, fi); if (lst == NULL) { spin_unlock(&clp->cl_lock); - ns = nfs4_alloc_stid(clp, stateid_slab); + ns = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_lock_stateid); if (ns == NULL) return NULL; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 77860b75da9d..5134eedcb16c 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -583,8 +583,8 @@ extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp, __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, struct nfsd_net *nn); -struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, - struct kmem_cache *slab); +struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab, + void (*sc_free)(struct nfs4_stid *)); void nfs4_unhash_stid(struct nfs4_stid *s); void nfs4_put_stid(struct nfs4_stid *s); void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 994d66fbb446..91e0c5429b4d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -369,7 +369,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, __be32 err; int host_err; bool get_write_count; - int size_change = 0; + bool size_change = (iap->ia_valid & ATTR_SIZE); if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; @@ -382,11 +382,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, /* Get inode */ err = fh_verify(rqstp, fhp, ftype, accmode); if (err) - goto out; + return err; if (get_write_count) { host_err = fh_want_write(fhp); if (host_err) - return nfserrno(host_err); + goto out; } dentry = fhp->fh_dentry; @@ -397,20 +397,28 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, iap->ia_valid &= ~ATTR_MODE; if (!iap->ia_valid) - goto out; + return 0; nfsd_sanitize_attrs(inode, iap); + if (check_guard && guardtime != inode->i_ctime.tv_sec) + return nfserr_notsync; + /* * The size case is special, it changes the file in addition to the - * attributes. + * attributes, and file systems don't expect it to be mixed with + * "random" attribute changes. We thus split out the size change + * into a separate call to ->setattr, and do the rest as a separate + * setattr call. */ - if (iap->ia_valid & ATTR_SIZE) { + if (size_change) { err = nfsd_get_write_access(rqstp, fhp, iap); if (err) - goto out; - size_change = 1; + return err; + } + fh_lock(fhp); + if (size_change) { /* * RFC5661, Section 18.30.4: * Changing the size of a file with SETATTR indirectly @@ -418,29 +426,36 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, * * (and similar for the older RFCs) */ - if (iap->ia_size != i_size_read(inode)) - iap->ia_valid |= ATTR_MTIME; - } + struct iattr size_attr = { + .ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME, + .ia_size = iap->ia_size, + }; - iap->ia_valid |= ATTR_CTIME; + host_err = notify_change(dentry, &size_attr, NULL); + if (host_err) + goto out_unlock; + iap->ia_valid &= ~ATTR_SIZE; - if (check_guard && guardtime != inode->i_ctime.tv_sec) { - err = nfserr_notsync; - goto out_put_write_access; + /* + * Avoid the additional setattr call below if the only other + * attribute that the client sends is the mtime, as we update + * it as part of the size change above. + */ + if ((iap->ia_valid & ~ATTR_MTIME) == 0) + goto out_unlock; } - fh_lock(fhp); + iap->ia_valid |= ATTR_CTIME; host_err = notify_change(dentry, iap, NULL); - fh_unlock(fhp); - err = nfserrno(host_err); -out_put_write_access: +out_unlock: + fh_unlock(fhp); if (size_change) put_write_access(inode); - if (!err) - err = nfserrno(commit_metadata(fhp)); out: - return err; + if (!host_err) + host_err = commit_metadata(fhp); + return nfserrno(host_err); } #if defined(CONFIG_NFSD_V4) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a64313868d3a..2958e7a81f9c 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -488,7 +488,7 @@ static int fanotify_find_path(int dfd, const char __user *filename, } /* you can only watch an inode if you have read permissions on it */ - ret = inode_permission(path->dentry->d_inode, MAY_READ); + ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ); if (ret) path_put(path); out: diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index e2893f17dde2..4c5b43d15e6e 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -337,7 +337,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns if (error) return error; /* you can only watch an inode if you have read permissions on it */ - error = inode_permission(path->dentry->d_inode, MAY_READ); + error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ); if (error) path_put(path); return error; diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index b002acf50203..60a5f1548cd9 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3321,6 +3321,16 @@ static int ocfs2_downconvert_lock(struct ocfs2_super *osb, mlog(ML_BASTS, "lockres %s, level %d => %d\n", lockres->l_name, lockres->l_level, new_level); + /* + * On DLM_LKF_VALBLK, fsdlm behaves differently with o2cb. It always + * expects DLM_LKF_VALBLK being set if the LKB has LVB, so that + * we can recover correctly from node failure. Otherwise, we may get + * invalid LVB in LKB, but without DLM_SBF_VALNOTVALIDÂ being set. + */ + if (!ocfs2_is_o2cb_active() && + lockres->l_ops->flags & LOCK_TYPE_USES_LVB) + lvb = 1; + if (lvb) dlm_flags |= DLM_LKF_VALBLK; diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c index 5d965e83bd43..783bcdce5666 100644 --- a/fs/ocfs2/stackglue.c +++ b/fs/ocfs2/stackglue.c @@ -48,6 +48,12 @@ static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl"; */ static struct ocfs2_stack_plugin *active_stack; +inline int ocfs2_is_o2cb_active(void) +{ + return !strcmp(active_stack->sp_name, OCFS2_STACK_PLUGIN_O2CB); +} +EXPORT_SYMBOL_GPL(ocfs2_is_o2cb_active); + static struct ocfs2_stack_plugin *ocfs2_stack_lookup(const char *name) { struct ocfs2_stack_plugin *p; diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h index 66334a30cea8..e1b30931974d 100644 --- a/fs/ocfs2/stackglue.h +++ b/fs/ocfs2/stackglue.h @@ -298,4 +298,7 @@ void ocfs2_stack_glue_set_max_proto_version(struct ocfs2_protocol_version *max_p int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin); void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin); +/* In ocfs2_downconvert_lock(), we need to know which stack we are using */ +int ocfs2_is_o2cb_active(void); + #endif /* STACKGLUE_H */ diff --git a/fs/open.c b/fs/open.c index 157b9940dd73..e70cca15c976 100644 --- a/fs/open.c +++ b/fs/open.c @@ -34,8 +34,8 @@ #include "internal.h" -int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, - struct file *filp) +int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length, + unsigned int time_attrs, struct file *filp) { int ret; struct iattr newattrs; @@ -60,17 +60,24 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, mutex_lock(&dentry->d_inode->i_mutex); /* Note any delegations or leases have already been broken: */ - ret = notify_change(dentry, &newattrs, NULL); + ret = notify_change2(mnt, dentry, &newattrs, NULL); mutex_unlock(&dentry->d_inode->i_mutex); return ret; } +int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + struct file *filp) +{ + return do_truncate2(NULL, dentry, length, time_attrs, filp); +} long vfs_truncate(struct path *path, loff_t length) { struct inode *inode; + struct vfsmount *mnt; long error; inode = path->dentry->d_inode; + mnt = path->mnt; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ if (S_ISDIR(inode->i_mode)) @@ -82,7 +89,7 @@ long vfs_truncate(struct path *path, loff_t length) if (error) goto out; - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; @@ -106,7 +113,7 @@ long vfs_truncate(struct path *path, loff_t length) if (!error) error = security_path_truncate(path); if (!error) - error = do_truncate(path->dentry, length, 0, NULL); + error = do_truncate2(mnt, path->dentry, length, 0, NULL); put_write_and_out: put_write_access(inode); @@ -155,6 +162,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) { struct inode *inode; struct dentry *dentry; + struct vfsmount *mnt; struct fd f; int error; @@ -171,6 +179,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) small = 0; dentry = f.file->f_path.dentry; + mnt = f.file->f_path.mnt; inode = dentry->d_inode; error = -EINVAL; if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE)) @@ -190,7 +199,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) if (!error) error = security_path_truncate(&f.file->f_path); if (!error) - error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file); + error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file); sb_end_write(inode->i_sb); out_putf: fdput(f); @@ -340,6 +349,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) struct cred *override_cred; struct path path; struct inode *inode; + struct vfsmount *mnt; int res; unsigned int lookup_flags = LOOKUP_FOLLOW; @@ -370,6 +380,7 @@ retry: goto out; inode = d_backing_inode(path.dentry); + mnt = path.mnt; if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* @@ -381,7 +392,7 @@ retry: goto out_path_release; } - res = inode_permission(inode, mode | MAY_ACCESS); + res = inode_permission2(mnt, inode, mode | MAY_ACCESS); /* SuS v2 requires we report a read only fs too */ if (res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; @@ -425,7 +436,7 @@ retry: if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; @@ -445,6 +456,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) { struct fd f = fdget_raw(fd); struct inode *inode; + struct vfsmount *mnt; int error = -EBADF; error = -EBADF; @@ -452,12 +464,13 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) goto out; inode = file_inode(f.file); + mnt = f.file->f_path.mnt; error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = inode_permission(inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &f.file->f_path); out_putf: @@ -476,7 +489,7 @@ retry: if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; @@ -516,7 +529,7 @@ retry_deleg: goto out_unlock; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); out_unlock: mutex_unlock(&inode->i_mutex); if (delegated_inode) { @@ -596,7 +609,7 @@ retry_deleg: mutex_lock(&inode->i_mutex); error = security_path_chown(path, uid, gid); if (!error) - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); mutex_unlock(&inode->i_mutex); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); diff --git a/fs/pnode.c b/fs/pnode.c index 99899705b105..1d16bb3bdf5e 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -324,6 +324,21 @@ out: return ret; } +static struct mount *find_topper(struct mount *mnt) +{ + /* If there is exactly one mount covering mnt completely return it. */ + struct mount *child; + + if (!list_is_singular(&mnt->mnt_mounts)) + return NULL; + + child = list_first_entry(&mnt->mnt_mounts, struct mount, mnt_child); + if (child->mnt_mountpoint != mnt->mnt.mnt_root) + return NULL; + + return child; +} + /* * return true if the refcount is greater than count */ @@ -344,9 +359,8 @@ static inline int do_refcount_check(struct mount *mnt, int count) */ int propagate_mount_busy(struct mount *mnt, int refcnt) { - struct mount *m, *child; + struct mount *m, *child, *topper; struct mount *parent = mnt->mnt_parent; - int ret = 0; if (mnt == parent) return do_refcount_check(mnt, refcnt); @@ -361,12 +375,24 @@ int propagate_mount_busy(struct mount *mnt, int refcnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { - child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint); - if (child && list_empty(&child->mnt_mounts) && - (ret = do_refcount_check(child, 1))) - break; + int count = 1; + child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); + if (!child) + continue; + + /* Is there exactly one mount on the child that covers + * it completely whose reference should be ignored? + */ + topper = find_topper(child); + if (topper) + count += 1; + else if (!list_empty(&child->mnt_mounts)) + continue; + + if (do_refcount_check(child, count)) + return 1; } - return ret; + return 0; } /* @@ -383,7 +409,7 @@ void propagate_mount_unlock(struct mount *mnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { - child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint); + child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); if (child) child->mnt.mnt_flags &= ~MNT_LOCKED; } @@ -401,9 +427,11 @@ static void mark_umount_candidates(struct mount *mnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { - struct mount *child = __lookup_mnt_last(&m->mnt, + struct mount *child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); - if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) { + if (!child || (child->mnt.mnt_flags & MNT_UMOUNT)) + continue; + if (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m)) { SET_MNT_MARK(child); } } @@ -422,8 +450,8 @@ static void __propagate_umount(struct mount *mnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { - - struct mount *child = __lookup_mnt_last(&m->mnt, + struct mount *topper; + struct mount *child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); /* * umount the child only if the child has no children @@ -432,6 +460,15 @@ static void __propagate_umount(struct mount *mnt) if (!child || !IS_MNT_MARKED(child)) continue; CLEAR_MNT_MARK(child); + + /* If there is exactly one mount covering all of child + * replace child with that mount. + */ + topper = find_topper(child); + if (topper) + mnt_change_mountpoint(child->mnt_parent, child->mnt_mp, + topper); + if (list_empty(&child->mnt_mounts)) { list_del_init(&child->mnt_child); child->mnt.mnt_flags |= MNT_UMOUNT; @@ -458,3 +495,32 @@ int propagate_umount(struct list_head *list) __propagate_umount(mnt); return 0; } + +/* + * Iterates over all slaves, and slaves of slaves. + */ +static struct mount *next_descendent(struct mount *root, struct mount *cur) +{ + if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list)) + return first_slave(cur); + do { + if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list) + return next_slave(cur); + cur = cur->mnt_master; + } while (cur != root); + return NULL; +} + +void propagate_remount(struct mount *mnt) +{ + struct mount *m = mnt; + struct super_block *sb = mnt->mnt.mnt_sb; + + if (sb->s_op->copy_mnt_data) { + m = next_descendent(mnt, m); + while (m) { + sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data); + m = next_descendent(mnt, m); + } + } +} diff --git a/fs/pnode.h b/fs/pnode.h index 0fcdbe7ca648..f41fc9a6c48e 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -44,11 +44,14 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *, int propagate_umount(struct list_head *); int propagate_mount_busy(struct mount *, int); void propagate_mount_unlock(struct mount *); +void propagate_remount(struct mount *); void mnt_release_group_id(struct mount *); int get_dominating_id(struct mount *mnt, const struct path *root); unsigned int mnt_get_count(struct mount *mnt); void mnt_set_mountpoint(struct mount *, struct mountpoint *, struct mount *); +void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, + struct mount *mnt); struct mount *copy_tree(struct mount *, struct dentry *, int); bool is_path_reachable(struct mount *, struct dentry *, const struct path *root); diff --git a/fs/posix_acl.c b/fs/posix_acl.c index a60d3cc5b55d..993bb3b5f4d5 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -903,11 +903,10 @@ int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type) int error; if (type == ACL_TYPE_ACCESS) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) - return 0; - if (error == 0) - acl = NULL; + error = posix_acl_update_mode(inode, + &inode->i_mode, &acl); + if (error) + return error; } inode->i_ctime = CURRENT_TIME; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index fe5b6e6c4671..4dbe1e2daeca 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -703,7 +703,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx) ctl_dir = container_of(head, struct ctl_dir, header); if (!dir_emit_dots(file, ctx)) - return 0; + goto out; pos = 2; @@ -713,6 +713,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx) break; } } +out: sysctl_head_finish(head); return 0; } diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 87645955990d..961e597acfc6 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -118,7 +118,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt) if (err) goto out; show_mnt_opts(m, mnt); - if (sb->s_op->show_options) + if (sb->s_op->show_options2) + err = sb->s_op->show_options2(mnt, m, mnt_path.dentry); + else if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt_path.dentry); seq_puts(m, " 0 0\n"); out: @@ -178,7 +180,9 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) err = show_sb_opts(m, sb); if (err) goto out; - if (sb->s_op->show_options) + if (sb->s_op->show_options2) { + err = sb->s_op->show_options2(mnt, m, mnt->mnt_root); + } else if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt->mnt_root); seq_putc(m, '\n'); out: diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 41e0e11b3c35..0bb442338a85 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -30,11 +30,14 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->userid = pi->userid; ci->d_uid = pi->d_uid; ci->under_android = pi->under_android; + ci->under_cache = pi->under_cache; + ci->under_obb = pi->under_obb; + set_top(ci, pi->top); } /* helper function for derived state */ -void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android) +void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, + uid_t uid, bool under_android, struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -42,84 +45,293 @@ void setup_derived_state(struct inode *inode, perm_t perm, info->userid = userid; info->d_uid = uid; info->under_android = under_android; + info->under_cache = false; + info->under_obb = false; + set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); - struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); appid_t appid; + struct qstr q_Android = QSTR_LITERAL("Android"); + struct qstr q_data = QSTR_LITERAL("data"); + struct qstr q_obb = QSTR_LITERAL("obb"); + struct qstr q_media = QSTR_LITERAL("media"); + struct qstr q_cache = QSTR_LITERAL("cache"); /* By default, each inode inherits from its parent. * the properties are maintained on its private fields * because the inode attributes will be modified with that of * its lower inode. - * The derived state will be updated on the last - * stage of each system call by fix_derived_permission(inode). + * These values are used by our custom permission call instead + * of using the inode permissions. */ - inherit_derived_state(parent->d_inode, dentry->d_inode); + inherit_derived_state(d_inode(parent), d_inode(dentry)); + /* Files don't get special labels */ + if (!S_ISDIR(d_inode(dentry)->i_mode)) + return; /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { case PERM_INHERIT: + case PERM_ANDROID_PACKAGE_CACHE: /* Already inherited above */ break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); + info->userid = simple_strtoul(name->name, NULL, 10); + set_top(info, &info->vfs_inode); break; case PERM_ROOT: /* Assume masked off by default. */ - if (!strcasecmp(newdentry->d_name.name, "Android")) { + if (qstr_case_eq(name, &q_Android)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; info->under_android = true; + set_top(info, &info->vfs_inode); } break; case PERM_ANDROID: - if (!strcasecmp(newdentry->d_name.name, "data")) { + if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; - } else if (!strcasecmp(newdentry->d_name.name, "obb")) { + set_top(info, &info->vfs_inode); + } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; + info->under_obb = true; + set_top(info, &info->vfs_inode); /* Single OBB directory is always shared */ - } else if (!strcasecmp(newdentry->d_name.name, "media")) { + } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); } break; - case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: + case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: - appid = get_appid(sbi->pkgl_id, newdentry->d_name.name); - if (appid != 0) { + info->perm = PERM_ANDROID_PACKAGE; + appid = get_appid(name->name); + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } + set_top(info, &info->vfs_inode); + break; + case PERM_ANDROID_PACKAGE: + if (qstr_case_eq(name, &q_cache)) { + info->perm = PERM_ANDROID_PACKAGE_CACHE; + info->under_cache = true; + } break; } } void get_derived_permission(struct dentry *parent, struct dentry *dentry) { - get_derived_permission_new(parent, dentry, dentry); + get_derived_permission_new(parent, dentry, &dentry->d_name); +} + +static appid_t get_type(const char *name) { + const char *ext = strrchr(name, '.'); + appid_t id; + + if (ext && ext[0]) { + ext = &ext[1]; + id = get_ext_gid(ext); + return id?:AID_MEDIA_RW; + } + return AID_MEDIA_RW; +} + +void fixup_lower_ownership(struct dentry* dentry, const char *name) { + struct path path; + struct inode *inode; + struct inode *delegated_inode = NULL; + int error; + struct sdcardfs_inode_info *info; + struct sdcardfs_inode_info *info_top; + perm_t perm; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + uid_t uid = sbi->options.fs_low_uid; + gid_t gid = sbi->options.fs_low_gid; + struct iattr newattrs; + + info = SDCARDFS_I(d_inode(dentry)); + perm = info->perm; + if (info->under_obb) { + perm = PERM_ANDROID_OBB; + } else if (info->under_cache) { + perm = PERM_ANDROID_PACKAGE_CACHE; + } else if (perm == PERM_INHERIT) { + info_top = SDCARDFS_I(grab_top(info)); + perm = info_top->perm; + release_top(info); + } + + switch (perm) { + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + case PERM_ANDROID_PACKAGE: + case PERM_ANDROID_PACKAGE_CACHE: + uid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_OBB: + uid = AID_MEDIA_OBB; + break; + case PERM_PRE_ROOT: + default: + break; + } + switch (perm) { + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + if (S_ISDIR(d_inode(dentry)->i_mode)) + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + else + gid = multiuser_get_uid(info->userid, get_type(name)); + break; + case PERM_ANDROID_OBB: + gid = AID_MEDIA_OBB; + break; + case PERM_ANDROID_PACKAGE: + if (info->d_uid != 0) + gid = multiuser_get_ext_gid(info->userid, info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_PACKAGE_CACHE: + if (info->d_uid != 0) + gid = multiuser_get_cache_gid(info->userid, info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_PRE_ROOT: + default: + break; + } + + sdcardfs_get_lower_path(dentry, &path); + inode = d_inode(path.dentry); + if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) { +retry_deleg: + newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE; + newattrs.ia_uid = make_kuid(current_user_ns(), uid); + newattrs.ia_gid = make_kgid(current_user_ns(), gid); + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + mutex_lock(&inode->i_mutex); + error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid); + if (!error) + error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode); + mutex_unlock(&inode->i_mutex); + if (delegated_inode) { + error = break_deleg_wait(&delegated_inode); + if (!error) + goto retry_deleg; + } + if (error) + pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); + } +} + +static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) { + if (info->perm == PERM_ROOT) + return (limit->flags & BY_USERID)?info->userid == limit->userid:1; + if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) + return 1; + return 0; +} + +static int needs_fixup(perm_t perm) { + if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB + || perm == PERM_ANDROID_MEDIA) + return 1; + return 0; +} + +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) { + struct dentry *child; + struct sdcardfs_inode_info *info; + if (!dget(dentry)) + return; + if (!d_inode(dentry)) { + dput(dentry); + return; + } + info = SDCARDFS_I(d_inode(dentry)); + + if (needs_fixup(info->perm)) { + spin_lock(&dentry->d_lock); + list_for_each_entry(child, &dentry->d_subdirs, d_child) { + dget(child); + if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + if (d_inode(child)) { + get_derived_permission(dentry, child); + fixup_tmp_permissions(d_inode(child)); + dput(child); + break; + } + } + dput(child); + } + spin_unlock(&dentry->d_lock); + } else if (descendant_may_need_fixup(info, limit)) { + spin_lock(&dentry->d_lock); + list_for_each_entry(child, &dentry->d_subdirs, d_child) { + fixup_perms_recursive(child, limit); + } + spin_unlock(&dentry->d_lock); + } + dput(dentry); } -void get_derive_permissions_recursive(struct dentry *parent) { +void drop_recursive(struct dentry *parent) { struct dentry *dentry; + struct sdcardfs_inode_info *info; + if (!d_inode(parent)) + return; + info = SDCARDFS_I(d_inode(parent)); + spin_lock(&parent->d_lock); list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (dentry->d_inode) { - mutex_lock(&dentry->d_inode->i_mutex); - get_derived_permission(parent, dentry); - fix_derived_permission(dentry->d_inode); - get_derive_permissions_recursive(dentry); - mutex_unlock(&dentry->d_inode->i_mutex); + if (d_inode(dentry)) { + if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { + drop_recursive(dentry); + d_drop(dentry); + } + } + } + spin_unlock(&parent->d_lock); +} + +void fixup_top_recursive(struct dentry *parent) { + struct dentry *dentry; + struct sdcardfs_inode_info *info; + + if (!d_inode(parent)) + return; + info = SDCARDFS_I(d_inode(parent)); + spin_lock(&parent->d_lock); + list_for_each_entry(dentry, &parent->d_subdirs, d_child) { + if (d_inode(dentry)) { + if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { + get_derived_permission(parent, dentry); + fixup_tmp_permissions(d_inode(dentry)); + fixup_top_recursive(dentry); + } } } + spin_unlock(&parent->d_lock); } /* main function for updating derived permission */ @@ -127,7 +339,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !dentry->d_inode) { + if(!dentry || !d_inode(dentry)) { printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); return; } @@ -135,9 +347,8 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - mutex_lock(&dentry->d_inode->i_mutex); if(IS_ROOT(dentry)) { - //setup_default_pre_root_state(dentry->d_inode); + //setup_default_pre_root_state(d_inode(dentry)); } else { parent = dget_parent(dentry); if(parent) { @@ -145,19 +356,19 @@ inline void update_derived_permission_lock(struct dentry *dentry) dput(parent); } } - fix_derived_permission(dentry->d_inode); - mutex_unlock(&dentry->d_inode->i_mutex); + fixup_tmp_permissions(d_inode(dentry)); } int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct qstr obb = QSTR_LITERAL("obb"); if(parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ if(!(sbi->options.multiuser == false @@ -194,7 +405,7 @@ int is_obbpath_invalid(struct dentry *dent) } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || - strcasecmp(sbi->obbpath_s, obbpath_s)) { + !str_case_eq(sbi->obbpath_s, obbpath_s)) { ret = 1; } kfree(path_buf); @@ -212,17 +423,18 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { if(parent_info->perm == PERM_PRE_ROOT && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } } else if (parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } spin_unlock(&SDCARDFS_D(dentry)->lock); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index c249fa982d3c..23f8cd7f8877 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred); + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 2528da0d3ae1..68e615045616 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -19,18 +19,24 @@ */ #include "sdcardfs.h" +#include <linux/fs_struct.h> /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) +const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info) { struct cred * cred; const struct cred * old_cred; + uid_t uid; cred = prepare_creds(); if (!cred) return NULL; - cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid); + if (info->under_obb) + uid = AID_MEDIA_OBB; + else + uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); + cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); old_cred = override_creds(cred); @@ -53,11 +59,14 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, { int err; struct dentry *lower_dentry; + struct vfsmount *lower_dentry_mnt; struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; + struct fs_struct *saved_fs; + struct fs_struct *copied_fs; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if(!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -66,15 +75,26 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_dentry_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; - err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); + + /* temporarily change umask for lower fs write */ + saved_fs = current->fs; + copied_fs = copy_fs_struct(current->fs); + if (!copied_fs) { + err = -ENOMEM; + goto out_unlock; + } + current->fs = copied_fs; + current->fs->umask = 0; + err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -83,8 +103,12 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); + fixup_lower_ownership(dentry, dentry->d_name.name); out: + current->fs = saved_fs; + free_fs_struct(copied_fs); +out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -138,12 +162,13 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); struct dentry *lower_dir_dentry; struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if(!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -152,14 +177,15 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - err = vfs_unlink(lower_dir_inode, lower_dentry, NULL); + err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL); /* * Note: unlinking on top of NFS can cause silly-renamed files. @@ -240,18 +266,19 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int err; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); - char *page_buf; - char *nomedia_dir_name; - char *nomedia_fullpath; - int fullpath_namelen; int touch_err = 0; + struct fs_struct *saved_fs; + struct fs_struct *copied_fs; + struct qstr q_obb = QSTR_LITERAL("obb"); + struct qstr q_data = QSTR_LITERAL("data"); - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if(!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -260,7 +287,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { @@ -272,14 +299,28 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; - err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); - if (err) + /* temporarily change umask for lower fs write */ + saved_fs = current->fs; + copied_fs = copy_fs_struct(current->fs); + if (!copied_fs) { + err = -ENOMEM; + unlock_dir(lower_parent_dentry); + goto out_unlock; + } + current->fs = copied_fs; + current->fs->umask = 0; + err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); + + if (err) { + unlock_dir(lower_parent_dentry); goto out; + } /* if it is a local obb dentry, setup it with the base obbpath */ if(need_graft_path(dentry)) { @@ -301,58 +342,38 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); - if (err) + if (err) { + unlock_dir(lower_parent_dentry); goto out; + } fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); - - if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) + fixup_lower_ownership(dentry, dentry->d_name.name); + unlock_dir(lower_parent_dentry); + if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || - ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { - - page_buf = (char *)__get_free_page(GFP_KERNEL); - if (!page_buf) { - printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); - goto out; - } - - nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); - if (IS_ERR(nomedia_dir_name)) { - free_page((unsigned long)page_buf); - printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); - goto out; - } - - fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; - fullpath_namelen += strlen("/.nomedia"); - nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); - if (!nomedia_fullpath) { - free_page((unsigned long)page_buf); - printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); - goto out; - } - - strcpy(nomedia_fullpath, nomedia_dir_name); - free_page((unsigned long)page_buf); - strcat(nomedia_fullpath, "/.nomedia"); - touch_err = touch(nomedia_fullpath, 0664); + ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { + REVERT_CRED(saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); + set_fs_pwd(current->fs, &lower_path); + touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", - nomedia_fullpath, touch_err); - kfree(nomedia_fullpath); + printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + lower_path.dentry->d_name.name, touch_err); goto out; } - kfree(nomedia_fullpath); } out: - unlock_dir(lower_parent_dentry); + current->fs = saved_fs; + free_fs_struct(copied_fs); +out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); out_revert: REVERT_CRED(saved_cred); @@ -364,11 +385,12 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct dentry *lower_dir_dentry; + struct vfsmount *lower_mnt; int err; struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if(!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -377,16 +399,17 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. */ sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_dir_dentry = lock_parent(lower_dentry); - err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); + err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry); if (err) goto out; @@ -450,13 +473,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; + struct vfsmount *lower_mnt = NULL; struct dentry *trap = NULL; - struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || - !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { + if(!check_caller_access_to_name(old_dir, &old_dentry->d_name) || + !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " new_dentry: %s, task:%s\n", __func__, new_dentry->d_name.name, current->comm); @@ -465,12 +488,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; + lower_mnt = lower_old_path.mnt; lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); @@ -486,7 +510,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } - err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry, + err = vfs_rename2(lower_mnt, + d_inode(lower_old_dir_dentry), lower_old_dentry, d_inode(lower_new_dir_dentry), lower_new_dentry, NULL, 0); if (err) @@ -499,25 +524,11 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dir != old_dir) { sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); - - /* update the derived permission of the old_dentry - * with its new parent - */ - new_parent = dget_parent(new_dentry); - if(new_parent) { - if(d_inode(old_dentry)) { - update_derived_permission_lock(old_dentry); - } - dput(new_parent); - } } - /* At this point, not all dentry information has been moved, so - * we pass along new_dentry for the name.*/ - mutex_lock(&d_inode(old_dentry)->i_mutex); - get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); - fix_derived_permission(d_inode(old_dentry)); - get_derive_permissions_recursive(old_dentry); - mutex_unlock(&d_inode(old_dentry)->i_mutex); + get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); + fixup_tmp_permissions(d_inode(old_dentry)); + fixup_lower_ownership(old_dentry, new_dentry->d_name.name); + drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -586,16 +597,63 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) } #endif -static int sdcardfs_permission(struct inode *inode, int mask) +static int sdcardfs_permission_wrn(struct inode *inode, int mask) +{ + WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + return -EINVAL; +} + +void copy_attrs(struct inode *dest, const struct inode *src) +{ + dest->i_mode = src->i_mode; + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; +#ifdef CONFIG_FS_POSIX_ACL + dest->i_acl = src->i_acl; +#endif +#ifdef CONFIG_SECURITY + dest->i_security = src->i_security; +#endif +} + +static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask) { int err; + struct inode tmp; + struct inode *top = grab_top(SDCARDFS_I(inode)); + + if (!top) { + release_top(SDCARDFS_I(inode)); + WARN(1, "Top value was null!\n"); + return -EINVAL; + } /* * Permission check on sdcardfs inode. * Calling process should have AID_SDCARD_RW permission + * Since generic_permission only needs i_mode, i_uid, + * i_gid, and i_sb, we can create a fake inode to pass + * this information down in. + * + * The underlying code may attempt to take locks in some + * cases for features we're not using, but if that changes, + * locks must be dealt with to avoid undefined behavior. */ - err = generic_permission(inode, mask); - + copy_attrs(&tmp, inode); + tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + release_top(SDCARDFS_I(inode)); + tmp.i_sb = inode->i_sb; + if (IS_POSIXACL(inode)) + printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) * for checking inode permission. But doing such things here seems @@ -624,30 +682,70 @@ static int sdcardfs_permission(struct inode *inode, int mask) } -static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) +static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) +{ + WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + return -EINVAL; +} + +static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia) { int err; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct inode *inode; struct inode *lower_inode; struct path lower_path; struct iattr lower_ia; struct dentry *parent; + struct inode tmp; + struct inode *top; + const struct cred *saved_cred = NULL; inode = d_inode(dentry); + top = grab_top(SDCARDFS_I(inode)); + + if (!top) { + release_top(SDCARDFS_I(inode)); + return -EINVAL; + } + + /* + * Permission check on sdcardfs inode. + * Calling process should have AID_SDCARD_RW permission + * Since generic_permission only needs i_mode, i_uid, + * i_gid, and i_sb, we can create a fake inode to pass + * this information down in. + * + * The underlying code may attempt to take locks in some + * cases for features we're not using, but if that changes, + * locks must be dealt with to avoid undefined behavior. + * + */ + copy_attrs(&tmp, inode); + tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + tmp.i_size = i_size_read(inode); + release_top(SDCARDFS_I(inode)); + tmp.i_sb = inode->i_sb; /* * Check if user has permission to change inode. We don't check if * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ - err = inode_change_ok(inode, ia); + /* prepare our own lower struct iattr (with the lower file) */ + memcpy(&lower_ia, ia, sizeof(lower_ia)); + /* Allow touch updating timestamps. A previous permission check ensures + * we have write access. Changes to mode, owner, and group are ignored*/ + ia->ia_valid |= ATTR_FORCE; + err = inode_change_ok(&tmp, ia); - /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -659,12 +757,14 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (err) goto out_err; + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); + sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_inode = sdcardfs_lower_inode(inode); - /* prepare our own lower struct iattr (with the lower file) */ - memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); @@ -681,7 +781,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (current->mm) down_write(¤t->mm->mmap_sem); if (ia->ia_valid & ATTR_SIZE) { - err = inode_newsize_ok(inode, ia->ia_size); + err = inode_newsize_ok(&tmp, ia->ia_size); if (err) { if (current->mm) up_write(¤t->mm->mmap_sem); @@ -704,7 +804,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) * tries to open(), unlink(), then ftruncate() a file. */ mutex_lock(&d_inode(lower_dentry)->i_mutex); - err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */ + err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); mutex_unlock(&d_inode(lower_dentry)->i_mutex); if (current->mm) @@ -723,10 +823,35 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) out: sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); out_err: return err; } +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) +{ + struct sdcardfs_inode_info *info = SDCARDFS_I(inode); + struct inode *top = grab_top(info); + if (!top) + return -EINVAL; + + stat->dev = inode->i_sb->s_dev; + stat->ino = inode->i_ino; + stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + stat->nlink = inode->i_nlink; + stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + stat->rdev = inode->i_rdev; + stat->size = i_size_read(inode); + stat->atime = inode->i_atime; + stat->mtime = inode->i_mtime; + stat->ctime = inode->i_ctime; + stat->blksize = (1 << inode->i_blkbits); + stat->blocks = inode->i_blocks; + release_top(info); + return 0; +} + static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -735,9 +860,10 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct inode *lower_inode; struct path lower_path; struct dentry *parent; + int err; parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -752,19 +878,17 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_inode = sdcardfs_lower_inode(inode); - sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - - generic_fillattr(inode, stat); + err = sdcardfs_fillattr(mnt, inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); - return 0; + return err; } const struct inode_operations sdcardfs_symlink_iops = { - .permission = sdcardfs_permission, - .setattr = sdcardfs_setattr, + .permission2 = sdcardfs_permission, + .setattr2 = sdcardfs_setattr, /* XXX Following operations are implemented, * but FUSE(sdcard) or FAT does not support them * These methods are *NOT* perfectly tested. @@ -777,14 +901,14 @@ const struct inode_operations sdcardfs_symlink_iops = { const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, -#if 0 - .permission = sdcardfs_permission, -#endif + .permission = sdcardfs_permission_wrn, + .permission2 = sdcardfs_permission, .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, .rename = sdcardfs_rename, - .setattr = sdcardfs_setattr, + .setattr = sdcardfs_setattr_wrn, + .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, /* XXX Following operations are implemented, * but FUSE(sdcard) or FAT does not support them @@ -796,7 +920,9 @@ const struct inode_operations sdcardfs_dir_iops = { }; const struct inode_operations sdcardfs_main_iops = { - .permission = sdcardfs_permission, - .setattr = sdcardfs_setattr, + .permission = sdcardfs_permission_wrn, + .permission2 = sdcardfs_permission, + .setattr = sdcardfs_setattr_wrn, + .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, }; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a01b06a514fd..9135866b7766 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -179,7 +179,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, struct inode *lower_inode; struct super_block *lower_sb; - lower_inode = lower_path->dentry->d_inode; + lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ @@ -219,9 +219,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; - const char *name; + const struct qstr *name; struct path lower_path; - struct qstr this; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -231,15 +230,39 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (IS_ROOT(dentry)) goto out; - name = dentry->d_name.name; + name = &dentry->d_name; /* now start the actual lookup procedure */ lower_dir_dentry = lower_parent_path->dentry; lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0, &lower_path); + /* check for other cases */ + if (err == -ENOENT) { + struct dentry *child; + struct dentry *match = NULL; + mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); + spin_lock(&lower_dir_dentry->d_lock); + list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { + if (child && d_inode(child)) { + if (qstr_case_eq(&child->d_name, name)) { + match = dget(child); + break; + } + } + } + spin_unlock(&lower_dir_dentry->d_lock); + mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); + if (match) { + err = vfs_path_lookup(lower_dir_dentry, + lower_dir_mnt, + match->d_name.name, 0, + &lower_path); + dput(match); + } + } /* no error: handle positive dentries */ if (!err) { @@ -283,14 +306,11 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - this.name = name; - this.len = strlen(name); - this.hash = full_name_hash(this.name, this.len); - lower_dentry = d_lookup(lower_dir_dentry, &this); + lower_dentry = d_lookup(lower_dir_dentry, name); if (lower_dentry) goto setup_lower; - lower_dentry = d_alloc(lower_dir_dentry, &this); + lower_dentry = d_alloc(lower_dir_dentry, name); if (!lower_dentry) { err = -ENOMEM; goto out; @@ -335,7 +355,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -344,7 +364,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(parent, &lower_parent_path); @@ -362,18 +382,17 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } if (ret) dentry = ret; - if (dentry->d_inode) { - fsstack_copy_attr_times(dentry->d_inode, - sdcardfs_lower_inode(dentry->d_inode)); - /* get drived permission */ - mutex_lock(&dentry->d_inode->i_mutex); + if (d_inode(dentry)) { + fsstack_copy_attr_times(d_inode(dentry), + sdcardfs_lower_inode(d_inode(dentry))); + /* get derived permission */ get_derived_permission(parent, dentry); - fix_derived_permission(dentry->d_inode); - mutex_unlock(&dentry->d_inode->i_mutex); + fixup_tmp_permissions(d_inode(dentry)); + fixup_lower_ownership(dentry, dentry->d_name.name); } /* update parent directory's atime */ - fsstack_copy_attr_atime(parent->d_inode, - sdcardfs_lower_inode(parent->d_inode)); + fsstack_copy_attr_atime(d_inode(parent), + sdcardfs_lower_inode(d_inode(parent))); out: sdcardfs_put_lower_path(parent, &lower_parent_path); diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index a6522286d731..7a8eae29e44d 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -28,7 +28,6 @@ enum { Opt_fsgid, Opt_gid, Opt_debug, - Opt_lower_fs, Opt_mask, Opt_multiuser, // May need? Opt_userid, @@ -49,7 +48,8 @@ static const match_table_t sdcardfs_tokens = { }; static int parse_options(struct super_block *sb, char *options, int silent, - int *debug, struct sdcardfs_mount_options *opts) + int *debug, struct sdcardfs_vfsmount_options *vfsopts, + struct sdcardfs_mount_options *opts) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -58,10 +58,10 @@ static int parse_options(struct super_block *sb, char *options, int silent, /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; - opts->mask = 0; + vfsopts->mask = 0; opts->multiuser = false; opts->fs_user_id = 0; - opts->gid = 0; + vfsopts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -94,7 +94,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_gid: if (match_int(&args[0], &option)) return 0; - opts->gid = option; + vfsopts->gid = option; break; case Opt_userid: if (match_int(&args[0], &option)) @@ -104,7 +104,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_mask: if (match_int(&args[0], &option)) return 0; - opts->mask = option; + vfsopts->mask = option; break; case Opt_multiuser: opts->multiuser = true; @@ -135,6 +135,65 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; } +int parse_options_remount(struct super_block *sb, char *options, int silent, + struct sdcardfs_vfsmount_options *vfsopts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + int debug; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, sdcardfs_tokens, args); + + switch (token) { + case Opt_debug: + debug = 1; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + vfsopts->gid = option; + + break; + case Opt_mask: + if (match_int(&args[0], &option)) + return 0; + vfsopts->mask = option; + break; + case Opt_multiuser: + case Opt_userid: + case Opt_fsuid: + case Opt_fsgid: + case Opt_reserved_mb: + printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + break; + /* unknown option */ + default: + if (!silent) { + printk( KERN_ERR "Unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + + if (debug) { + printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); + printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); + printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + } + + return 0; +} + #if 0 /* * our custom d_alloc_root work-alike @@ -172,14 +231,15 @@ EXPORT_SYMBOL_GPL(sdcardfs_super_list); * There is no need to lock the sdcardfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. */ -static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, - void *raw_data, int silent) +static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, + const char *dev_name, void *raw_data, int silent) { int err = 0; int debug; struct super_block *lower_sb; struct path lower_path; struct sdcardfs_sb_info *sb_info; + struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -193,6 +253,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); + printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, @@ -212,7 +273,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info = sb->s_fs_info; /* parse options */ - err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); + err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { printk(KERN_ERR "sdcardfs: invalid options\n"); goto out_freesbi; @@ -236,7 +297,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb->s_op = &sdcardfs_sops; /* get a new inode and allocate our root dentry */ - inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0); + inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_sput; @@ -268,16 +329,16 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); if(sb_info->options.multiuser) { - setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false); + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); /*err = prepare_dir(sb_info->obbpath_s, sb_info->options.fs_low_uid, sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } - fix_derived_permission(sb->s_root->d_inode); + fixup_tmp_permissions(d_inode(sb->s_root)); sb_info->sb = sb; list_add(&sb_info->list, &sdcardfs_super_list); mutex_unlock(&sdcardfs_super_list_lock); @@ -306,9 +367,9 @@ out: } /* A feature which supports mount_nodev() with options */ -static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, - int (*fill_super)(struct super_block *, const char *, void *, int)) +static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, + struct file_system_type *fs_type, int flags, const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) { int error; @@ -319,7 +380,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, s->s_flags = flags; - error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0); + error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0); if (error) { deactivate_locked_super(s); return ERR_PTR(error); @@ -328,15 +389,27 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, return dget(s->s_root); } -struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, +static struct dentry *sdcardfs_mount(struct vfsmount *mnt, + struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) { /* * dev_name is a lower_path_name, * raw_data is a option string. */ - return mount_nodev_with_options(fs_type, flags, dev_name, - raw_data, sdcardfs_read_super); + return mount_nodev_with_options(mnt, fs_type, flags, dev_name, + raw_data, sdcardfs_read_super); +} + +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + WARN(1, "sdcardfs does not support mount. Use mount2.\n"); + return ERR_PTR(-EINVAL); +} + +void *sdcardfs_alloc_mnt_data(void) { + return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } void sdcardfs_kill_sb(struct super_block *sb) { @@ -353,7 +426,9 @@ void sdcardfs_kill_sb(struct super_block *sb) { static struct file_system_type sdcardfs_fs_type = { .owner = THIS_MODULE, .name = SDCARDFS_NAME, - .mount = sdcardfs_mount, + .mount = sdcardfs_mount_wrn, + .mount2 = sdcardfs_mount, + .alloc_mnt_data = sdcardfs_alloc_mnt_data, .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 923ba101dfa9..52bc20080904 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -18,20 +18,32 @@ * General Public License. */ -#define MULTIUSER_APP_PER_USER_RANGE 100000 +#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ +#define AID_APP_START 10000 /* first app user */ +#define AID_APP_END 19999 /* last app user */ +#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ +#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ typedef uid_t userid_t; typedef uid_t appid_t; -static inline userid_t multiuser_get_user_id(uid_t uid) { - return uid / MULTIUSER_APP_PER_USER_RANGE; +static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) { + return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline appid_t multiuser_get_app_id(uid_t uid) { - return uid % MULTIUSER_APP_PER_USER_RANGE; +static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) { + if (app_id >= AID_APP_START && app_id <= AID_APP_END) { + return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); + } else { + return -1; + } } -static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) { - return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) { + if (app_id >= AID_APP_START && app_id <= AID_APP_END) { + return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); + } else { + return -1; + } } - diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 9c3340528eee..d96fcde041cc 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -21,7 +21,8 @@ #include "sdcardfs.h" #include <linux/hashtable.h> #include <linux/delay.h> - +#include <linux/radix-tree.h> +#include <linux/dcache.h> #include <linux/init.h> #include <linux/module.h> @@ -29,70 +30,120 @@ #include <linux/configfs.h> -#define STRING_BUF_SIZE (512) - struct hashtable_entry { struct hlist_node hlist; - void *key; - unsigned int value; + struct hlist_node dlist; /* for deletion cleanup */ + struct qstr key; + atomic_t value; }; -struct sb_list { - struct super_block *sb; - struct list_head list; -}; +static DEFINE_HASHTABLE(package_to_appid, 8); +static DEFINE_HASHTABLE(package_to_userid, 8); +static DEFINE_HASHTABLE(ext_to_groupid, 8); -struct packagelist_data { - DECLARE_HASHTABLE(package_to_appid,8); - struct mutex hashtable_lock; -}; +static struct kmem_cache *hashtable_entry_cachep; -static struct packagelist_data *pkgl_data_all; +static void inline qstr_init(struct qstr *q, const char *name) { + q->name = name; + q->len = strlen(q->name); + q->hash = full_name_hash(q->name, q->len); +} -static struct kmem_cache *hashtable_entry_cachep; +static inline int qstr_copy(const struct qstr *src, struct qstr *dest) { + dest->name = kstrdup(src->name, GFP_KERNEL); + dest->hash_len = src->hash_len; + return !!dest->name; +} -static unsigned int str_hash(const char *key) { - int i; - unsigned int h = strlen(key); - char *data = (char *)key; - for (i = 0; i < strlen(key); i++) { - h = h * 31 + *data; - data++; +static appid_t __get_appid(const struct qstr *key) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = key->hash; + appid_t ret_id; + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key)) { + ret_id = atomic_read(&hash_cur->value); + rcu_read_unlock(); + return ret_id; + } } - return h; + rcu_read_unlock(); + return 0; } -appid_t get_appid(void *pkgl_id, const char *app_name) +appid_t get_appid(const char *key) +{ + struct qstr q; + qstr_init(&q, key); + return __get_appid(&q); +} + +static appid_t __get_ext_gid(const struct qstr *key) { - struct packagelist_data *pkgl_dat = pkgl_data_all; struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(app_name); + unsigned int hash = key->hash; appid_t ret_id; - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(app_name, hash_cur->key)) { - ret_id = (appid_t)hash_cur->value; - mutex_unlock(&pkgl_dat->hashtable_lock); + rcu_read_lock(); + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key)) { + ret_id = atomic_read(&hash_cur->value); + rcu_read_unlock(); return ret_id; } } - mutex_unlock(&pkgl_dat->hashtable_lock); + rcu_read_unlock(); + return 0; +} + +appid_t get_ext_gid(const char *key) +{ + struct qstr q; + qstr_init(&q, key); + return __get_ext_gid(&q); +} + +static appid_t __is_excluded(const struct qstr *app_name, userid_t user) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = app_name->hash; + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (atomic_read(&hash_cur->value) == user && + qstr_case_eq(app_name, &hash_cur->key)) { + rcu_read_unlock(); + return 1; + } + } + rcu_read_unlock(); return 0; } +appid_t is_excluded(const char *key, userid_t user) +{ + struct qstr q; + qstr_init(&q, key); + return __is_excluded(&q, user); +} + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char* name) { +int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { + struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); + struct qstr q__android_secure = QSTR_LITERAL(".android_secure"); + struct qstr q_android_secure = QSTR_LITERAL("android_secure"); /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { - if (!strcasecmp(name, "autorun.inf") - || !strcasecmp(name, ".android_secure") - || !strcasecmp(name, "android_secure")) { + if (qstr_case_eq(name, &q_autorun) + || qstr_case_eq(name, &q__android_secure) + || qstr_case_eq(name, &q_android_secure)) { return 0; } } @@ -120,290 +171,675 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key, - unsigned int value) +static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, + appid_t value) +{ + struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, + GFP_KERNEL); + if (!ret) + return NULL; + + if (!qstr_copy(key, &ret->key)) { + kmem_cache_free(hashtable_entry_cachep, ret); + return NULL; + } + + atomic_set(&ret->value, value); + return ret; +} + +static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { - hash_cur->value = value; + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key)) { + atomic_set(&hash_cur->value, value); return 0; } } - new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + new_entry = alloc_hashtable_entry(key, value); if (!new_entry) return -ENOMEM; - new_entry->key = kstrdup(key, GFP_KERNEL); - new_entry->value = value; - hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash); + hash_add_rcu(package_to_appid, &new_entry->hlist, hash); return 0; } -static void fixup_perms(struct super_block *sb) { - if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - mutex_lock(&sb->s_root->d_inode->i_mutex); - get_derive_permissions_recursive(sb->s_root); - mutex_unlock(&sb->s_root->d_inode->i_mutex); +static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + unsigned int hash = key->hash; + + /* An extension can only belong to one gid */ + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key)) + return -EINVAL; } + new_entry = alloc_hashtable_entry(key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash); + return 0; } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, - unsigned int value) { - int ret; +static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + unsigned int hash = key->hash; + + /* Only insert if not already present */ + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (atomic_read(&hash_cur->value) == value && + qstr_case_eq(key, &hash_cur->key)) + return 0; + } + new_entry = alloc_hashtable_entry(key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(package_to_userid, &new_entry->hlist, hash); + return 0; +} + +static void fixup_all_perms_name(const struct qstr *key) +{ struct sdcardfs_sb_info *sbinfo; - mutex_lock(&sdcardfs_super_list_lock); - mutex_lock(&pkgl_dat->hashtable_lock); - ret = insert_str_to_int_lock(pkgl_dat, key, value); - mutex_unlock(&pkgl_dat->hashtable_lock); + struct limit_search limit = { + .flags = BY_NAME, + .name = key->name, + .length = key->len, + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); + } +} +static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) +{ + struct sdcardfs_sb_info *sbinfo; + struct limit_search limit = { + .flags = BY_NAME | BY_USERID, + .name = key->name, + .length = key->len, + .userid = userid, + }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { - if (sbinfo) { - fixup_perms(sbinfo->sb); - } + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); } +} + +static void fixup_all_perms_userid(userid_t userid) +{ + struct sdcardfs_sb_info *sbinfo; + struct limit_search limit = { + .flags = BY_USERID, + .userid = userid, + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); + } +} + +static int insert_packagelist_entry(const struct qstr *key, appid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_packagelist_appid_entry_locked(key, value); + if (!err) + fixup_all_perms_name(key); mutex_unlock(&sdcardfs_super_list_lock); - return ret; + + return err; } -static void remove_str_to_int_lock(struct hashtable_entry *h_entry) { - kfree(h_entry->key); - hash_del(&h_entry->hlist); - kmem_cache_free(hashtable_entry_cachep, h_entry); +static int insert_ext_gid_entry(const struct qstr *key, appid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_ext_gid_entry_locked(key, value); + mutex_unlock(&sdcardfs_super_list_lock); + + return err; } -static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key) +static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) { - struct sdcardfs_sb_info *sbinfo; - struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + int err; + mutex_lock(&sdcardfs_super_list_lock); - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { - remove_str_to_int_lock(hash_cur); + err = insert_userid_exclude_entry_locked(key, value); + if (!err) + fixup_all_perms_name_userid(key, value); + mutex_unlock(&sdcardfs_super_list_lock); + + return err; +} + +static void free_hashtable_entry(struct hashtable_entry *entry) +{ + kfree(entry->key.name); + hash_del_rcu(&entry->dlist); + kmem_cache_free(hashtable_entry_cachep, entry); +} + +static void remove_packagelist_entry_locked(const struct qstr *key) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = key->hash; + struct hlist_node *h_t; + HLIST_HEAD(free_list); + + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key)) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key)) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); break; } } - mutex_unlock(&pkgl_dat->hashtable_lock); - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { - if (sbinfo) { - fixup_perms(sbinfo->sb); + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) + free_hashtable_entry(hash_cur); +} + +static void remove_packagelist_entry(const struct qstr *key) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_packagelist_entry_locked(key); + fixup_all_perms_name(key); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + +static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = key->hash; + + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) { + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_hashtable_entry(hash_cur); + break; } } +} + +static void remove_ext_gid_entry(const struct qstr *key, gid_t group) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_ext_gid_entry_locked(key, group); mutex_unlock(&sdcardfs_super_list_lock); return; } -static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) +static void remove_userid_all_entry_locked(userid_t userid) { struct hashtable_entry *hash_cur; struct hlist_node *h_t; + HLIST_HEAD(free_list); int i; - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) - remove_str_to_int_lock(hash_cur); - mutex_unlock(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); + + hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) { + if (atomic_read(&hash_cur->value) == userid) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) { + free_hashtable_entry(hash_cur); + } } -static struct packagelist_data * packagelist_create(void) +static void remove_userid_all_entry(userid_t userid) { - struct packagelist_data *pkgl_dat; + mutex_lock(&sdcardfs_super_list_lock); + remove_userid_all_entry_locked(userid); + fixup_all_perms_userid(userid); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} - pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); - if (!pkgl_dat) { - printk(KERN_ERR "sdcardfs: Failed to create hash\n"); - return ERR_PTR(-ENOMEM); +static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = key->hash; + + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (qstr_case_eq(key, &hash_cur->key) && + atomic_read(&hash_cur->value) == userid) { + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_hashtable_entry(hash_cur); + break; + } } +} - mutex_init(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); - - return pkgl_dat; +static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_userid_exclude_entry_locked(key, userid); + fixup_all_perms_name_userid(key, userid); + mutex_unlock(&sdcardfs_super_list_lock); + return; } -static void packagelist_destroy(struct packagelist_data *pkgl_dat) +static void packagelist_destroy(void) { - remove_all_hashentrys(pkgl_dat); + struct hashtable_entry *hash_cur; + struct hlist_node *h_t; + HLIST_HEAD(free_list); + int i; + mutex_lock(&sdcardfs_super_list_lock); + hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) + free_hashtable_entry(hash_cur); + mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); - kfree(pkgl_dat); } -struct package_appid { +#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO | S_IWUGO, \ + .ca_owner = THIS_MODULE, \ + .show = _pfx##_name##_show, \ + .store = _pfx##_name##_store, \ +} + +#define SDCARDFS_CONFIGFS_ATTR_RO(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO, \ + .ca_owner = THIS_MODULE, \ + .show = _pfx##_name##_show, \ +} + +#define SDCARDFS_CONFIGFS_ATTR_WO(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IWUGO, \ + .ca_owner = THIS_MODULE, \ + .store = _pfx##_name##_store, \ +} + +struct package_details { struct config_item item; - int add_pid; + struct qstr name; }; -static inline struct package_appid *to_package_appid(struct config_item *item) +static inline struct package_details *to_package_details(struct config_item *item) +{ + return item ? container_of(item, struct package_details, item) : NULL; +} + +static ssize_t package_details_appid_show(struct config_item *item, char *page) { - return item ? container_of(item, struct package_appid, item) : NULL; + return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&to_package_details(item)->name)); } -static ssize_t package_appid_attr_show(struct config_item *item, +static ssize_t package_details_appid_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + + ret = insert_packagelist_entry(&to_package_details(item)->name, tmp); + + if (ret) + return ret; + + return count; +} + +static ssize_t package_details_excluded_userids_show(struct config_item *item, char *page) { - ssize_t count; - count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name)); + struct package_details *package_details = to_package_details(item); + struct hashtable_entry *hash_cur; + unsigned int hash = package_details->name.hash; + int count = 0; + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (qstr_case_eq(&package_details->name, &hash_cur->key)) + count += scnprintf(page + count, PAGE_SIZE - count, + "%d ", atomic_read(&hash_cur->value)); + } + rcu_read_unlock(); + if (count) + count--; + count += scnprintf(page + count, PAGE_SIZE - count, "\n"); return count; } -static ssize_t package_appid_attr_store(struct config_item *item, +static ssize_t package_details_excluded_userids_store(struct config_item *item, const char *page, size_t count) { - struct package_appid *package_appid = to_package_appid(item); - unsigned long tmp; - char *p = (char *) page; + unsigned int tmp; int ret; - tmp = simple_strtoul(p, &p, 10); - if (!p || (*p && (*p != '\n'))) - return -EINVAL; + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + + ret = insert_userid_exclude_entry(&to_package_details(item)->name, tmp); - if (tmp > INT_MAX) - return -ERANGE; - ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp); - package_appid->add_pid = tmp; if (ret) return ret; return count; } -static struct configfs_attribute package_appid_attr_add_pid = { - .ca_owner = THIS_MODULE, - .ca_name = "appid", - .ca_mode = S_IRUGO | S_IWUGO, - .show = package_appid_attr_show, - .store = package_appid_attr_store, -}; +static ssize_t package_details_clear_userid_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; -static struct configfs_attribute *package_appid_attrs[] = { - &package_appid_attr_add_pid, - NULL, -}; + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + remove_userid_exclude_entry(&to_package_details(item)->name, tmp); + return count; +} -static void package_appid_release(struct config_item *item) +static void package_details_release(struct config_item *item) { - printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); - /* item->ci_name is freed already, so we rely on the dentry */ - remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name); - kfree(to_package_appid(item)); + struct package_details *package_details = to_package_details(item); + printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + remove_packagelist_entry(&package_details->name); + kfree(package_details->name.name); + kfree(package_details); } -static struct configfs_item_operations package_appid_item_ops = { - .release = package_appid_release, +SDCARDFS_CONFIGFS_ATTR(package_details_, appid); +SDCARDFS_CONFIGFS_ATTR(package_details_, excluded_userids); +SDCARDFS_CONFIGFS_ATTR_WO(package_details_, clear_userid); + +static struct configfs_attribute *package_details_attrs[] = { + &package_details_attr_appid, + &package_details_attr_excluded_userids, + &package_details_attr_clear_userid, + NULL, +}; + +static struct configfs_item_operations package_details_item_ops = { + .release = package_details_release, }; static struct config_item_type package_appid_type = { - .ct_item_ops = &package_appid_item_ops, - .ct_attrs = package_appid_attrs, + .ct_item_ops = &package_details_item_ops, + .ct_attrs = package_details_attrs, .ct_owner = THIS_MODULE, }; - -struct sdcardfs_packages { +struct extensions_value { struct config_group group; + unsigned int num; +}; + +struct extension_details { + struct config_item item; + struct qstr name; + unsigned int num; +}; + +static inline struct extensions_value *to_extensions_value(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL; +} + +static inline struct extension_details *to_extension_details(struct config_item *item) +{ + return item ? container_of(item, struct extension_details, item) : NULL; +} + +static void extension_details_release(struct config_item *item) +{ + struct extension_details *extension_details = to_extension_details(item); + + printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + extension_details->name.name, extension_details->num); + remove_ext_gid_entry(&extension_details->name, extension_details->num); + kfree(extension_details->name.name); + kfree(extension_details); +} + +static struct configfs_item_operations extension_details_item_ops = { + .release = extension_details_release, }; -static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item) +static struct config_item_type extension_details_type = { + .ct_item_ops = &extension_details_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *extension_details_make_item(struct config_group *group, const char *name) { - return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL; + struct extensions_value *extensions_value = to_extensions_value(&group->cg_item); + struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); + const char *tmp; + int ret; + if (!extension_details) + return ERR_PTR(-ENOMEM); + + tmp = kstrdup(name, GFP_KERNEL); + if (!tmp) { + kfree(extension_details); + return ERR_PTR(-ENOMEM); + } + qstr_init(&extension_details->name, tmp); + ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num); + + if (ret) { + kfree(extension_details->name.name); + kfree(extension_details); + return ERR_PTR(ret); + } + config_item_init_type_name(&extension_details->item, name, &extension_details_type); + + return &extension_details->item; } -static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name) +static struct configfs_group_operations extensions_value_group_ops = { + .make_item = extension_details_make_item, +}; + +static struct config_item_type extensions_name_type = { + .ct_group_ops = &extensions_value_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *extensions_make_group(struct config_group *group, const char *name) { - struct package_appid *package_appid; + struct extensions_value *extensions_value; + unsigned int tmp; + int ret; - package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL); - if (!package_appid) + extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL); + if (!extensions_value) return ERR_PTR(-ENOMEM); + ret = kstrtouint(name, 10, &tmp); + if (ret) { + kfree(extensions_value); + return ERR_PTR(ret); + } + + extensions_value->num = tmp; + config_group_init_type_name(&extensions_value->group, name, + &extensions_name_type); + return &extensions_value->group; +} + +static void extensions_drop_group(struct config_group *group, struct config_item *item) +{ + struct extensions_value *value = to_extensions_value(item); + printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + kfree(value); +} - config_item_init_type_name(&package_appid->item, name, - &package_appid_type); +static struct configfs_group_operations extensions_group_ops = { + .make_group = extensions_make_group, + .drop_item = extensions_drop_group, +}; - package_appid->add_pid = 0; +static struct config_item_type extensions_type = { + .ct_group_ops = &extensions_group_ops, + .ct_owner = THIS_MODULE, +}; - return &package_appid->item; +struct config_group extension_group = { + .cg_item = { + .ci_namebuf = "extensions", + .ci_type = &extensions_type, + }, +}; + +static struct config_item *packages_make_item(struct config_group *group, const char *name) +{ + struct package_details *package_details; + const char *tmp; + + package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL); + if (!package_details) + return ERR_PTR(-ENOMEM); + tmp = kstrdup(name, GFP_KERNEL); + if (!tmp) { + kfree(package_details); + return ERR_PTR(-ENOMEM); + } + qstr_init(&package_details->name, tmp); + config_item_init_type_name(&package_details->item, name, + &package_appid_type); + + return &package_details->item; } -static ssize_t packages_attr_show(struct config_item *item, - char *page) +static ssize_t packages_list_show(struct config_item *item, char *page) { - struct hashtable_entry *hash_cur; - struct hlist_node *h_t; + struct hashtable_entry *hash_cur_app; + struct hashtable_entry *hash_cur_user; int i; int count = 0, written = 0; - char errormsg[] = "<truncated>\n"; - - mutex_lock(&pkgl_data_all->hashtable_lock); - hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) { - written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); - if (count + written == PAGE_SIZE - sizeof(errormsg)) { + const char errormsg[] = "<truncated>\n"; + unsigned int hash; + + rcu_read_lock(); + hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) { + written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", + hash_cur_app->key.name, atomic_read(&hash_cur_app->value)); + hash = hash_cur_app->key.hash; + hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { + if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) { + written += scnprintf(page + count + written - 1, + PAGE_SIZE - sizeof(errormsg) - count - written + 1, + " %d\n", atomic_read(&hash_cur_user->value)) - 1; + } + } + if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) { count += scnprintf(page + count, PAGE_SIZE - count, errormsg); break; } count += written; } - mutex_unlock(&pkgl_data_all->hashtable_lock); + rcu_read_unlock(); return count; } -static struct configfs_attribute sdcardfs_packages_attr_description = { - .ca_owner = THIS_MODULE, - .ca_name = "packages_gid.list", - .ca_mode = S_IRUGO, - .show = packages_attr_show, -}; - -static struct configfs_attribute *sdcardfs_packages_attrs[] = { - &sdcardfs_packages_attr_description, - NULL, -}; - -static void sdcardfs_packages_release(struct config_item *item) +static ssize_t packages_remove_userid_store(struct config_item *item, + const char *page, size_t count) { + unsigned int tmp; + int ret; - printk(KERN_INFO "sdcardfs: destroyed something?\n"); - kfree(to_sdcardfs_packages(item)); + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + remove_userid_all_entry(tmp); + return count; } -static struct configfs_item_operations sdcardfs_packages_item_ops = { - .release = sdcardfs_packages_release, +static struct configfs_attribute packages_attr_packages_gid_list = { + .ca_name = "packages_gid.list", + .ca_mode = S_IRUGO, + .ca_owner = THIS_MODULE, + .show = packages_list_show, +}; + +SDCARDFS_CONFIGFS_ATTR_WO(packages_, remove_userid); + +static struct configfs_attribute *packages_attrs[] = { + &packages_attr_packages_gid_list, + &packages_attr_remove_userid, + NULL, }; /* * Note that, since no extra work is required on ->drop_item(), * no ->drop_item() is provided. */ -static struct configfs_group_operations sdcardfs_packages_group_ops = { - .make_item = sdcardfs_packages_make_item, +static struct configfs_group_operations packages_group_ops = { + .make_item = packages_make_item, }; -static struct config_item_type sdcardfs_packages_type = { - .ct_item_ops = &sdcardfs_packages_item_ops, - .ct_group_ops = &sdcardfs_packages_group_ops, - .ct_attrs = sdcardfs_packages_attrs, +static struct config_item_type packages_type = { + .ct_group_ops = &packages_group_ops, + .ct_attrs = packages_attrs, .ct_owner = THIS_MODULE, }; -static struct configfs_subsystem sdcardfs_packages_subsys = { +struct config_group *sd_default_groups[] = { + &extension_group, + NULL, +}; + +static struct configfs_subsystem sdcardfs_packages = { .su_group = { .cg_item = { .ci_namebuf = "sdcardfs", - .ci_type = &sdcardfs_packages_type, + .ci_type = &packages_type, }, + .default_groups = sd_default_groups, }, }; static int configfs_sdcardfs_init(void) { - int ret; - struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; - + int ret, i; + struct configfs_subsystem *subsys = &sdcardfs_packages; + for (i = 0; sd_default_groups[i]; i++) { + config_group_init(sd_default_groups[i]); + } config_group_init(&subsys->su_group); mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); @@ -417,7 +853,7 @@ static int configfs_sdcardfs_init(void) static void configfs_sdcardfs_exit(void) { - configfs_unregister_subsystem(&sdcardfs_packages_subsys); + configfs_unregister_subsystem(&sdcardfs_packages); } int packagelist_init(void) @@ -430,7 +866,6 @@ int packagelist_init(void) return -ENOMEM; } - pkgl_data_all = packagelist_create(); configfs_sdcardfs_init(); return 0; } @@ -438,7 +873,7 @@ int packagelist_init(void) void packagelist_exit(void) { configfs_sdcardfs_exit(); - packagelist_destroy(pkgl_data_all); + packagelist_destroy(); if (hashtable_entry_cachep) kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index f111f898b630..f3cced313108 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -65,17 +65,26 @@ #define AID_SDCARD_PICS 1033 /* external storage photos access */ #define AID_SDCARD_AV 1034 /* external storage audio/video access */ #define AID_SDCARD_ALL 1035 /* access all users external storage */ +#define AID_MEDIA_OBB 1059 /* obb files */ + +#define AID_SDCARD_IMAGE 1057 #define AID_PACKAGE_INFO 1027 -#define fix_derived_permission(x) \ + +/* + * Permissions are handled by our permission function. + * We don't want anyone who happens to look at our inode value to prematurely + * block access, so store more permissive values. These are probably never + * used. + */ +#define fixup_tmp_permissions(x) \ do { \ (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ - (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \ - (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\ + (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) - /* OVERRIDE_CRED() and REVERT_CRED() * OVERRID_CRED() * backup original task->cred @@ -85,12 +94,12 @@ * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ -#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \ - saved_cred = override_fsids(sdcardfs_sbi); \ +#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ + saved_cred = override_fsids(sdcardfs_sbi, info); \ if (!saved_cred) { return -ENOMEM; } -#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \ - saved_cred = override_fsids(sdcardfs_sbi); \ +#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ + saved_cred = override_fsids(sdcardfs_sbi, info); \ if (!saved_cred) { return ERR_PTR(-ENOMEM); } #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) @@ -121,13 +130,18 @@ typedef enum { PERM_ANDROID_OBB, /* This node is "/Android/media" */ PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; struct sdcardfs_mount_options; +struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi); +const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred * old_cred); @@ -169,6 +183,10 @@ struct sdcardfs_inode_info { userid_t userid; uid_t d_uid; bool under_android; + bool under_cache; + bool under_obb; + /* top folder for ownership */ + struct inode *top; struct inode vfs_inode; }; @@ -185,12 +203,18 @@ struct sdcardfs_mount_options { uid_t fs_low_uid; gid_t fs_low_gid; userid_t fs_user_id; - gid_t gid; - mode_t mask; bool multiuser; unsigned int reserved_mb; }; +struct sdcardfs_vfsmount_options { + gid_t gid; + mode_t mask; +}; + +extern int parse_options_remount(struct super_block *sb, char *options, int silent, + struct sdcardfs_vfsmount_options *vfsopts); + /* sdcardfs super-block data in memory */ struct sdcardfs_sb_info { struct super_block *sb; @@ -321,9 +345,44 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) -static inline int get_gid(struct sdcardfs_inode_info *info) { - struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); - if (sb_info->options.gid == AID_SDCARD_RW) { +static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) +{ + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; +} + +/* grab a refererence if we aren't linking to ourself */ +static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) +{ + struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); + if (info->top && info->top != &info->vfs_inode) { + old_top = info->top; + } + if (top != &info->vfs_inode) + igrab(top); + info->top = top; + iput(old_top); +} + +static inline struct inode *grab_top(struct sdcardfs_inode_info *info) +{ + struct inode *top = info->top; + if (top) { + return igrab(top); + } else { + return NULL; + } +} + +static inline void release_top(struct sdcardfs_inode_info *info) +{ + iput(info->top); +} + +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + struct sdcardfs_vfsmount_options *opts = mnt->data; + + if (opts->gid == AID_SDCARD_RW) { /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing @@ -331,14 +390,15 @@ static inline int get_gid(struct sdcardfs_inode_info *info) { * assigned to app directories are still multiuser aware. */ return AID_SDCARD_RW; } else { - return multiuser_get_uid(info->userid, sb_info->options.gid); + return multiuser_get_uid(info->userid, opts->gid); } } -static inline int get_mode(struct sdcardfs_inode_info *info) { +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { int owner_mode; int filtered_mode; - struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); - int visible_mode = 0775 & ~sb_info->options.mask; + struct sdcardfs_vfsmount_options *opts = mnt->data; + int visible_mode = 0775 & ~opts->mask; + if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure @@ -348,7 +408,7 @@ static inline int get_mode(struct sdcardfs_inode_info *info) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still * leave +x open for the default view. */ - if (sb_info->options.gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) { visible_mode = visible_mode & ~0006; } else { visible_mode = visible_mode & ~0007; @@ -396,20 +456,34 @@ extern struct mutex sdcardfs_super_list_lock; extern struct list_head sdcardfs_super_list; /* for packagelist.c */ -extern appid_t get_appid(void *pkgl_id, const char *app_name); -extern int check_caller_access_to_name(struct inode *parent_node, const char* name); +extern appid_t get_appid(const char *app_name); +extern appid_t get_ext_gid(const char *app_name); +extern appid_t is_excluded(const char *app_name, userid_t userid); +extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ -extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android); +#define BY_NAME (1 << 0) +#define BY_USERID (1 << 1) +struct limit_search { + unsigned int flags; + const char *name; + size_t length; + userid_t userid; +}; + +extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, + uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); -extern void get_derive_permissions_recursive(struct dentry *parent); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); +extern void drop_recursive(struct dentry *parent); +extern void fixup_top_recursive(struct dentry *parent); +extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); +void fixup_lower_ownership(struct dentry* dentry, const char *name); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); @@ -444,7 +518,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m goto out_unlock; } - err = vfs_mkdir(d_inode(parent.dentry), dent, mode); + err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode); if (err) { if (err == -EEXIST) err = 0; @@ -455,7 +529,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; mutex_lock(&d_inode(dent)->i_mutex); - notify_change(dent, &attrs, NULL); + notify_change2(parent.mnt, dent, &attrs, NULL); mutex_unlock(&d_inode(dent)->i_mutex); out_dput: @@ -513,12 +587,16 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d return 1; } -/* Copies attrs and maintains sdcardfs managed attrs */ +/* + * Copies attrs and maintains sdcardfs managed attrs + * Since our permission check handles all special permissions, set those to be open + */ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src) { - dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest)); + dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG | + S_IROTH | S_IXOTH; /* 0775 */ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); - dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest))); + dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); dest->i_rdev = src->i_rdev; dest->i_atime = src->i_atime; dest->i_mtime = src->i_mtime; @@ -527,4 +605,17 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct dest->i_flags = src->i_flags; set_nlink(dest, src->i_nlink); } + +static inline bool str_case_eq(const char *s1, const char *s2) +{ + return !strcasecmp(s1, s2); +} + +static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) +{ + return q1->len == q2->len && str_case_eq(q1->name, q2->name); +} + +#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) + #endif /* not _SDCARDFS_H_ */ diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 1d6490128c99..edda32b68dc0 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -109,6 +109,50 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options } /* + * @mnt: mount point we are remounting + * @sb: superblock we are remounting + * @flags: numeric mount options + * @options: mount options string + */ +static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, + int *flags, char *options) +{ + int err = 0; + + /* + * The VFS will take care of "ro" and "rw" flags among others. We + * can safely accept a few flags (RDONLY, MANDLOCK), and honor + * SILENT, but anything else left over is an error. + */ + if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { + printk(KERN_ERR + "sdcardfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + } + printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); + + + return err; +} + +static void* sdcardfs_clone_mnt_data(void *data) { + struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options* old = data; + if(!opt) return NULL; + opt->gid = old->gid; + opt->mask = old->mask; + return opt; +} + +static void sdcardfs_copy_mnt_data(void *data, void *newdata) { + struct sdcardfs_vfsmount_options* old = data; + struct sdcardfs_vfsmount_options* new = newdata; + old->gid = new->gid; + old->mask = new->mask; +} + +/* * Called by iput() when the inode reference count reached zero * and the inode is not hashed anywhere. Used to clear anything * that needs to be, before the inode is completely destroyed and put @@ -126,6 +170,7 @@ static void sdcardfs_evict_inode(struct inode *inode) */ lower_inode = sdcardfs_lower_inode(inode); sdcardfs_set_lower_inode(inode, NULL); + set_top(SDCARDFS_I(inode), inode); iput(lower_inode); } @@ -190,19 +235,24 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; + struct sdcardfs_vfsmount_options *vfsopts = mnt->data; if (opts->fs_low_uid != 0) - seq_printf(m, ",uid=%u", opts->fs_low_uid); + seq_printf(m, ",fsuid=%u", opts->fs_low_uid); if (opts->fs_low_gid != 0) - seq_printf(m, ",gid=%u", opts->fs_low_gid); - + seq_printf(m, ",fsgid=%u", opts->fs_low_gid); + if (vfsopts->gid != 0) + seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) seq_printf(m, ",multiuser"); - + if (vfsopts->mask) + seq_printf(m, ",mask=%u", vfsopts->mask); + if (opts->fs_user_id) + seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); @@ -213,9 +263,12 @@ const struct super_operations sdcardfs_sops = { .put_super = sdcardfs_put_super, .statfs = sdcardfs_statfs, .remount_fs = sdcardfs_remount_fs, + .remount_fs2 = sdcardfs_remount_fs2, + .clone_mnt_data = sdcardfs_clone_mnt_data, + .copy_mnt_data = sdcardfs_copy_mnt_data, .evict_inode = sdcardfs_evict_inode, .umount_begin = sdcardfs_umount_begin, - .show_options = sdcardfs_show_options, + .show_options2 = sdcardfs_show_options, .alloc_inode = sdcardfs_alloc_inode, .destroy_inode = sdcardfs_destroy_inode, .drop_inode = generic_delete_inode, diff --git a/fs/splice.c b/fs/splice.c index 0f77e9682857..8398974e1538 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -211,6 +211,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe, buf->len = spd->partial[page_nr].len; buf->private = spd->partial[page_nr].private; buf->ops = spd->ops; + buf->flags = 0; if (spd->flags & SPLICE_F_GIFT) buf->flags |= PIPE_BUF_FLAG_GIFT; diff --git a/fs/super.c b/fs/super.c index b938b14f6041..c96434ea71e2 100644 --- a/fs/super.c +++ b/fs/super.c @@ -703,7 +703,8 @@ rescan: } /** - * do_remount_sb - asks filesystem to change mount options. + * do_remount_sb2 - asks filesystem to change mount options. + * @mnt: mount we are looking at * @sb: superblock in question * @flags: numeric part of options * @data: the rest of options @@ -711,7 +712,7 @@ rescan: * * Alters the mount options of a mounted file system. */ -int do_remount_sb(struct super_block *sb, int flags, void *data, int force) +int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force) { int retval; int remount_ro; @@ -753,7 +754,16 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) } } - if (sb->s_op->remount_fs) { + if (mnt && sb->s_op->remount_fs2) { + retval = sb->s_op->remount_fs2(mnt, sb, &flags, data); + if (retval) { + if (!force) + goto cancel_readonly; + /* If forced remount, go ahead despite any errors */ + WARN(1, "forced remount of a %s fs returned %i\n", + sb->s_type->name, retval); + } + } else if (sb->s_op->remount_fs) { retval = sb->s_op->remount_fs(sb, &flags, data); if (retval) { if (!force) @@ -785,6 +795,11 @@ cancel_readonly: return retval; } +int do_remount_sb(struct super_block *sb, int flags, void *data, int force) +{ + return do_remount_sb2(NULL, sb, flags, data, force); +} + static void do_emergency_remount(struct work_struct *work) { struct super_block *sb, *p = NULL; @@ -1104,7 +1119,7 @@ struct dentry *mount_single(struct file_system_type *fs_type, EXPORT_SYMBOL(mount_single); struct dentry * -mount_fs(struct file_system_type *type, int flags, const char *name, void *data) +mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data) { struct dentry *root; struct super_block *sb; @@ -1121,7 +1136,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data) goto out_free_secdata; } - root = type->mount(type, flags, name, data); + if (type->mount2) + root = type->mount2(mnt, type, flags, name, data); + else + root = type->mount(type, flags, name, data); if (IS_ERR(root)) { error = PTR_ERR(root); goto out_free_secdata; diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index fa9a20cc60d6..fe5e8d4970ae 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -34,6 +34,11 @@ #include <linux/slab.h> #include "ubifs.h" +static int try_read_node(const struct ubifs_info *c, void *buf, int type, + int len, int lnum, int offs); +static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, + struct ubifs_zbranch *zbr, void *node); + /* * Returned codes of 'matches_name()' and 'fallible_matches_name()' functions. * @NAME_LESS: name corresponding to the first argument is less than second @@ -402,7 +407,19 @@ static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, return 0; } - err = ubifs_tnc_read_node(c, zbr, node); + if (c->replaying) { + err = fallible_read_node(c, &zbr->key, zbr, node); + /* + * When the node was not found, return -ENOENT, 0 otherwise. + * Negative return codes stay as-is. + */ + if (err == 0) + err = -ENOENT; + else if (err == 1) + err = 0; + } else { + err = ubifs_tnc_read_node(c, zbr, node); + } if (err) return err; @@ -2766,7 +2783,11 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, if (nm->name) { if (err) { /* Handle collisions */ - err = resolve_collision(c, key, &znode, &n, nm); + if (c->replaying) + err = fallible_resolve_collision(c, key, &znode, &n, + nm, 0); + else + err = resolve_collision(c, key, &znode, &n, nm); dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n); if (unlikely(err < 0)) diff --git a/fs/utimes.c b/fs/utimes.c index cb771c30d102..a35e909cf8e3 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -91,7 +91,7 @@ static int utimes_common(struct path *path, struct timespec *times) } retry_deleg: mutex_lock(&inode->i_mutex); - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); mutex_unlock(&inode->i_mutex); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 5991cdcb9040..8cab78eeb0c2 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -3980,6 +3980,7 @@ xlog_recover_clear_agi_bucket( agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO); offset = offsetof(xfs_agi_t, agi_unlinked) + (sizeof(xfs_agino_t) * bucket); + xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF); xfs_trans_log_buf(tp, agibp, offset, (offset + sizeof(xfs_agino_t) - 1)); diff --git a/include/dt-bindings/clock/msm-clocks-8996.h b/include/dt-bindings/clock/msm-clocks-8996.h index 22109a6766db..da794841d1eb 100644 --- a/include/dt-bindings/clock/msm-clocks-8996.h +++ b/include/dt-bindings/clock/msm-clocks-8996.h @@ -54,6 +54,8 @@ #define clk_ipa_clk 0xfa685cda #define clk_ln_bb_clk 0x3ab0b36d #define clk_ln_bb_a_clk 0xc7257ea8 +#define clk_ln_bb_clk_pin 0x1b1c476a +#define clk_ln_bb_a_clk_pin 0x9cbb5411 #define clk_mcd_ce1_clk 0xbb615d26 #define clk_pnoc_keepalive_a_clk 0xf8f91f0b #define clk_pnoc_msmbus_clk 0x38b95c77 diff --git a/include/dt-bindings/clock/msm-clocks-hwio-8996.h b/include/dt-bindings/clock/msm-clocks-hwio-8996.h index 10e25be91752..21dc1e6c55e3 100644 --- a/include/dt-bindings/clock/msm-clocks-hwio-8996.h +++ b/include/dt-bindings/clock/msm-clocks-hwio-8996.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,6 +42,7 @@ #define RF_CLK1_ID 0x4 #define RF_CLK2_ID 0x5 #define LN_BB_CLK_ID 0x8 +#define LN_BB_CLK_PIN_ID 0x8 #define DIV_CLK1_ID 0xb #define DIV_CLK2_ID 0xc #define DIV_CLK3_ID 0xd diff --git a/include/dt-bindings/msm/power-on.h b/include/dt-bindings/msm/power-on.h index 49f37674bd5a..f43841eea7b7 100644 --- a/include/dt-bindings/msm/power-on.h +++ b/include/dt-bindings/msm/power-on.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,7 +16,9 @@ #define PON_POWER_OFF_RESERVED 0x00 #define PON_POWER_OFF_WARM_RESET 0x01 #define PON_POWER_OFF_SHUTDOWN 0x04 +#define PON_POWER_OFF_DVDD_SHUTDOWN 0x05 #define PON_POWER_OFF_HARD_RESET 0x07 +#define PON_POWER_OFF_DVDD_HARD_RESET 0x08 #define PON_POWER_OFF_MAX_TYPE 0x10 #endif diff --git a/include/linux/can/core.h b/include/linux/can/core.h index a0875001b13c..df08a41d5be5 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -45,10 +45,9 @@ struct can_proto { extern int can_proto_register(const struct can_proto *cp); extern void can_proto_unregister(const struct can_proto *cp); -extern int can_rx_register(struct net_device *dev, canid_t can_id, - canid_t mask, - void (*func)(struct sk_buff *, void *), - void *data, char *ident); +int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, + void (*func)(struct sk_buff *, void *), + void *data, char *ident, struct sock *sk); extern void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask, diff --git a/include/linux/capability.h b/include/linux/capability.h index 5f8249d378a2..b20ffe23a09b 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -40,8 +40,6 @@ struct inode; struct dentry; struct user_namespace; -struct user_namespace *current_user_ns(void); - extern const kernel_cap_t __cap_empty_set; extern const kernel_cap_t __cap_init_eff_set; @@ -247,8 +245,10 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap) return true; } #endif /* CONFIG_MULTIUSER */ +extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); +extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns); /* audit system wants to get cap info from files as well */ extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); diff --git a/include/linux/ceph/osdmap.h b/include/linux/ceph/osdmap.h index e55c08bc3a96..0abc56140c83 100644 --- a/include/linux/ceph/osdmap.h +++ b/include/linux/ceph/osdmap.h @@ -49,7 +49,7 @@ static inline bool ceph_can_shift_osds(struct ceph_pg_pool_info *pool) case CEPH_POOL_TYPE_EC: return false; default: - BUG_ON(1); + BUG(); } } diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 8281cd72b349..a157a69097b5 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -144,22 +144,16 @@ enum { { .notifier_call = fn, .priority = pri }; \ __register_cpu_notifier(&fn##_nb); \ } -#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */ -#define cpu_notifier(fn, pri) do { (void)(fn); } while (0) -#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0) -#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */ -#ifdef CONFIG_HOTPLUG_CPU extern int register_cpu_notifier(struct notifier_block *nb); extern int __register_cpu_notifier(struct notifier_block *nb); extern void unregister_cpu_notifier(struct notifier_block *nb); extern void __unregister_cpu_notifier(struct notifier_block *nb); -#else -#ifndef MODULE -extern int register_cpu_notifier(struct notifier_block *nb); -extern int __register_cpu_notifier(struct notifier_block *nb); -#else +#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */ +#define cpu_notifier(fn, pri) do { (void)(fn); } while (0) +#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0) + static inline int register_cpu_notifier(struct notifier_block *nb) { return 0; @@ -169,7 +163,6 @@ static inline int __register_cpu_notifier(struct notifier_block *nb) { return 0; } -#endif static inline void unregister_cpu_notifier(struct notifier_block *nb) { diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index d81d6a2db342..7e956e33618f 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -562,7 +562,7 @@ static inline void cpumask_copy(struct cpumask *dstp, static inline int cpumask_parse_user(const char __user *buf, int len, struct cpumask *dstp) { - return bitmap_parse_user(buf, len, cpumask_bits(dstp), nr_cpu_ids); + return bitmap_parse_user(buf, len, cpumask_bits(dstp), nr_cpumask_bits); } /** @@ -577,7 +577,7 @@ static inline int cpumask_parselist_user(const char __user *buf, int len, struct cpumask *dstp) { return bitmap_parselist_user(buf, len, cpumask_bits(dstp), - nr_cpu_ids); + nr_cpumask_bits); } /** @@ -592,7 +592,7 @@ static inline int cpumask_parse(const char *buf, struct cpumask *dstp) char *nl = strchr(buf, '\n'); unsigned int len = nl ? (unsigned int)(nl - buf) : strlen(buf); - return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpu_ids); + return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpumask_bits); } /** @@ -604,7 +604,7 @@ static inline int cpumask_parse(const char *buf, struct cpumask *dstp) */ static inline int cpulist_parse(const char *buf, struct cpumask *dstp) { - return bitmap_parselist(buf, cpumask_bits(dstp), nr_cpu_ids); + return bitmap_parselist(buf, cpumask_bits(dstp), nr_cpumask_bits); } /** diff --git a/include/linux/cred.h b/include/linux/cred.h index 8d70e1361ecd..257db64562e5 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -377,7 +377,10 @@ extern struct user_namespace init_user_ns; #ifdef CONFIG_USER_NS #define current_user_ns() (current_cred_xxx(user_ns)) #else -#define current_user_ns() (&init_user_ns) +static inline struct user_namespace *current_user_ns(void) +{ + return &init_user_ns; +} #endif diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h index 0ae23ddbc528..21a0917119ce 100644 --- a/include/linux/diagchar.h +++ b/include/linux/diagchar.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -66,6 +66,7 @@ #define DIAG_IOCTL_PERIPHERAL_BUF_DRAIN 36 #define DIAG_IOCTL_REGISTER_CALLBACK 37 #define DIAG_IOCTL_HDLC_TOGGLE 38 +#define DIAG_IOCTL_QUERY_PD_LOGGING 39 /* PC Tools IDs */ #define APQ8060_TOOLS_ID 4062 @@ -144,10 +145,10 @@ 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 25 -#define APPS_EVENT_LAST_ID 0x0B2A +#define APPS_EVENT_LAST_ID 0x0B3F #define MSG_SSID_0 0 -#define MSG_SSID_0_LAST 120 +#define MSG_SSID_0_LAST 121 #define MSG_SSID_1 500 #define MSG_SSID_1_LAST 506 #define MSG_SSID_2 1000 @@ -159,7 +160,7 @@ the appropriate macros. */ #define MSG_SSID_5 4000 #define MSG_SSID_5_LAST 4010 #define MSG_SSID_6 4500 -#define MSG_SSID_6_LAST 4573 +#define MSG_SSID_6_LAST 4583 #define MSG_SSID_7 4600 #define MSG_SSID_7_LAST 4615 #define MSG_SSID_8 5000 @@ -183,7 +184,7 @@ the appropriate macros. */ #define MSG_SSID_17 9000 #define MSG_SSID_17_LAST 9008 #define MSG_SSID_18 9500 -#define MSG_SSID_18_LAST 9510 +#define MSG_SSID_18_LAST 9521 #define MSG_SSID_19 10200 #define MSG_SSID_19_LAST 10210 #define MSG_SSID_20 10251 @@ -338,7 +339,8 @@ static const uint32_t msg_bld_masks_0[] = { MSG_LVL_MED, MSG_LVL_HIGH, MSG_LVL_LOW, - MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL + MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL, + MSG_LVL_HIGH }; static const uint32_t msg_bld_masks_1[] = { @@ -858,7 +860,7 @@ static const uint32_t msg_bld_masks_23[] = { /* LOG CODES */ static const uint32_t log_code_last_tbl[] = { 0x0, /* EQUIP ID 0 */ - 0x1A02, /* EQUIP ID 1 */ + 0x1A11, /* EQUIP ID 1 */ 0x0, /* EQUIP ID 2 */ 0x0, /* EQUIP ID 3 */ 0x4910, /* EQUIP ID 4 */ diff --git a/include/linux/esoc_client.h b/include/linux/esoc_client.h index 43c03389ecac..8d13c88eda81 100644 --- a/include/linux/esoc_client.h +++ b/include/linux/esoc_client.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,7 @@ struct esoc_desc { const char *name; const char *link; + const char *link_info; void *priv; }; diff --git a/include/linux/fs.h b/include/linux/fs.h index e5761fb6341d..28c8f7038ae0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1548,13 +1548,21 @@ extern bool inode_owner_or_capable(const struct inode *inode); * VFS helper functions.. */ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); +extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool); extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); +extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t); extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); +extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t); extern int vfs_symlink(struct inode *, struct dentry *, const char *); +extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *); extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); +extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **); extern int vfs_rmdir(struct inode *, struct dentry *); +extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); +extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); +extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); extern int vfs_whiteout(struct inode *, struct dentry *); /* @@ -1680,6 +1688,7 @@ struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); const char * (*follow_link) (struct dentry *, void **); int (*permission) (struct inode *, int); + int (*permission2) (struct vfsmount *, struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); @@ -1697,6 +1706,7 @@ struct inode_operations { int (*rename2) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*setattr) (struct dentry *, struct iattr *); + int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); @@ -1742,9 +1752,13 @@ struct super_operations { int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); + int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *); + void *(*clone_mnt_data) (void *); + void (*copy_mnt_data) (void *, void *); void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct dentry *); + int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *); int (*show_devname)(struct seq_file *, struct dentry *); int (*show_path)(struct seq_file *, struct dentry *); int (*show_stats)(struct seq_file *, struct dentry *); @@ -1976,6 +1990,9 @@ struct file_system_type { #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); + struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int, + const char *, void *); + void *(*alloc_mnt_data) (void); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; @@ -2255,6 +2272,8 @@ struct filename { extern long vfs_truncate(struct path *, loff_t); extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, struct file *filp); +extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start, + unsigned int time_attrs, struct file *filp); extern int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len); extern long do_sys_open(int dfd, const char __user *filename, int flags, @@ -2479,8 +2498,11 @@ extern void emergency_remount(void); extern sector_t bmap(struct inode *, sector_t); #endif extern int notify_change(struct dentry *, struct iattr *, struct inode **); +extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **); extern int inode_permission(struct inode *, int); +extern int inode_permission2(struct vfsmount *, struct inode *, int); extern int __inode_permission(struct inode *, int); +extern int __inode_permission2(struct vfsmount *, struct inode *, int); extern int generic_permission(struct inode *, int); extern int __check_sticky(struct inode *dir, struct inode *inode); diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index 9364ba71873e..6eb18a6d4e72 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -54,7 +54,8 @@ struct gpio_keys_platform_data { unsigned int rep:1; int (*enable)(struct device *dev); void (*disable)(struct device *dev); - const char *name; + const char *name; /* input device name */ + bool use_syscore; }; #endif diff --git a/include/linux/i2c/i2c-msm-v2.h b/include/linux/i2c/i2c-msm-v2.h index 54974c02725d..468a1d6fa58d 100644 --- a/include/linux/i2c/i2c-msm-v2.h +++ b/include/linux/i2c/i2c-msm-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015,2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -552,6 +552,7 @@ struct i2c_msm_xfer { int msg_cnt; enum i2c_msm_xfer_mode_id mode_id; struct completion complete; + struct completion rx_complete; size_t rx_cnt; size_t tx_cnt; size_t rx_ovrhd_cnt; diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index d49e26c6cdc7..23e129ef6726 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -153,8 +153,8 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60) #define DMA_TLB_DSI_FLUSH (((u64)2) << 60) #define DMA_TLB_PSI_FLUSH (((u64)3) << 60) -#define DMA_TLB_IIRG(type) ((type >> 60) & 7) -#define DMA_TLB_IAIG(val) (((val) >> 57) & 7) +#define DMA_TLB_IIRG(type) ((type >> 60) & 3) +#define DMA_TLB_IAIG(val) (((val) >> 57) & 3) #define DMA_TLB_READ_DRAIN (((u64)1) << 49) #define DMA_TLB_WRITE_DRAIN (((u64)1) << 48) #define DMA_TLB_DID(id) (((u64)((id) & 0xffff)) << 32) @@ -164,9 +164,9 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) /* INVALID_DESC */ #define DMA_CCMD_INVL_GRANU_OFFSET 61 -#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3) -#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3) -#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3) +#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 4) +#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 4) +#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 4) #define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7) #define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6) #define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16))) @@ -316,8 +316,8 @@ enum { #define QI_DEV_EIOTLB_SIZE (((u64)1) << 11) #define QI_DEV_EIOTLB_GLOB(g) ((u64)g) #define QI_DEV_EIOTLB_PASID(p) (((u64)p) << 32) -#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 32) -#define QI_DEV_EIOTLB_QDEP(qd) (((qd) & 0x1f) << 16) +#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 16) +#define QI_DEV_EIOTLB_QDEP(qd) ((u64)((qd) & 0x1f) << 4) #define QI_DEV_EIOTLB_MAX_INVS 32 #define QI_PGRP_IDX(idx) (((u64)(idx)) << 55) diff --git a/include/linux/jump_label_ratelimit.h b/include/linux/jump_label_ratelimit.h index 089f70f83e97..23da3af459fe 100644 --- a/include/linux/jump_label_ratelimit.h +++ b/include/linux/jump_label_ratelimit.h @@ -14,6 +14,7 @@ struct static_key_deferred { #ifdef HAVE_JUMP_LABEL extern void static_key_slow_dec_deferred(struct static_key_deferred *key); +extern void static_key_deferred_flush(struct static_key_deferred *key); extern void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); @@ -26,6 +27,10 @@ static inline void static_key_slow_dec_deferred(struct static_key_deferred *key) STATIC_KEY_CHECK_USE(); static_key_slow_dec(&key->key); } +static inline void static_key_deferred_flush(struct static_key_deferred *key) +{ + STATIC_KEY_CHECK_USE(); +} static inline void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl) diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 3f021dc5da8c..30201b9be7bc 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -83,6 +83,8 @@ struct nd_cmd_desc { struct nd_interleave_set { u64 cookie; + /* compatibility with initial buggy Linux implementation */ + u64 altcookie; }; struct nd_region_desc { diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index c15373894a42..b37dee3acaba 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -355,7 +355,8 @@ static inline int nlm_privileged_requester(const struct svc_rqst *rqstp) static inline int nlm_compare_locks(const struct file_lock *fl1, const struct file_lock *fl2) { - return fl1->fl_pid == fl2->fl_pid + return file_inode(fl1->fl_file) == file_inode(fl2->fl_file) + && fl1->fl_pid == fl2->fl_pid && fl1->fl_owner == fl2->fl_owner && fl1->fl_start == fl2->fl_start && fl1->fl_end == fl2->fl_end diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 2ea574ff9714..538488bd1d3d 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -85,7 +85,8 @@ extern int zone_grow_waitqueues(struct zone *zone, unsigned long nr_pages); extern int add_one_highpage(struct page *page, int pfn, int bad_ppro); /* VM interface that may be used by firmware interface */ extern int online_pages(unsigned long, unsigned long, int); -extern int test_pages_in_a_zone(unsigned long, unsigned long); +extern int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn, + unsigned long *valid_start, unsigned long *valid_end); extern void __offline_isolated_pages(unsigned long, unsigned long); typedef void (*online_page_callback_t)(struct page *page); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index ea0009064bbc..734169ff797e 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -480,6 +480,7 @@ struct mm_struct { */ struct task_struct __rcu *owner; #endif + struct user_namespace *user_ns; /* store ref to file /proc/<pid>/exe symlink points to */ struct file __rcu *exe_file; diff --git a/include/linux/mount.h b/include/linux/mount.h index f822c3c11377..0e9b0977237a 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -67,6 +67,7 @@ struct vfsmount { struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ int mnt_flags; + void *data; }; struct file; /* forward dec */ diff --git a/include/linux/msm_ext_display.h b/include/linux/msm_ext_display.h index d9831d7cbb4e..fc53e861eba4 100644 --- a/include/linux/msm_ext_display.h +++ b/include/linux/msm_ext_display.h @@ -26,9 +26,13 @@ * interface: * MSM_EXT_DISP_HPD_AUDIO: audio will be routed to external display * MSM_EXT_DISP_HPD_VIDEO: video will be routed to external display + * MSM_EXT_DISP_HPD_ASYNC_AUDIO: don't wait audio notification once wake it up + * MSM_EXT_DISP_HPD_ASYNC_VIDEO: don't wait video notification once wake it up */ #define MSM_EXT_DISP_HPD_AUDIO BIT(0) #define MSM_EXT_DISP_HPD_VIDEO BIT(1) +#define MSM_EXT_DISP_HPD_ASYNC_AUDIO BIT(2) +#define MSM_EXT_DISP_HPD_ASYNC_VIDEO BIT(3) /** * struct ext_disp_cable_notify - cable notify handler structure diff --git a/include/linux/namei.h b/include/linux/namei.h index d53c25453aca..023359f18567 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -79,6 +79,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int); extern int follow_down_one(struct path *); extern int follow_down(struct path *); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c909ba0ba997..0a306b431ece 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1399,6 +1399,7 @@ enum netdev_priv_flags { * @mtu: Interface MTU value * @type: Interface hardware type * @hard_header_len: Maximum hardware header length. + * @min_header_len: Minimum hardware header length * * @needed_headroom: Extra headroom the hardware may need, but not in all * cases can this be guaranteed @@ -1619,6 +1620,7 @@ struct net_device { unsigned int mtu; unsigned short type; unsigned short hard_header_len; + unsigned short min_header_len; unsigned short needed_headroom; unsigned short needed_tailroom; @@ -2325,14 +2327,19 @@ static inline int skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen) return NAPI_GRO_CB(skb)->frag0_len < hlen; } +static inline void skb_gro_frag0_invalidate(struct sk_buff *skb) +{ + NAPI_GRO_CB(skb)->frag0 = NULL; + NAPI_GRO_CB(skb)->frag0_len = 0; +} + static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen, unsigned int offset) { if (!pskb_may_pull(skb, hlen)) return NULL; - NAPI_GRO_CB(skb)->frag0 = NULL; - NAPI_GRO_CB(skb)->frag0_len = 0; + skb_gro_frag0_invalidate(skb); return skb->data + offset; } @@ -2536,6 +2543,8 @@ static inline bool dev_validate_header(const struct net_device *dev, { if (likely(len >= dev->hard_header_len)) return true; + if (len < dev->min_header_len) + return false; if (capable(CAP_SYS_RAWIO)) { memset(ll_header + len, 0, dev->hard_header_len - len); diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index e7e78537aea2..63a817631f06 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -266,7 +266,7 @@ enum nfsstat4 { static inline bool seqid_mutating_err(u32 err) { - /* rfc 3530 section 8.1.5: */ + /* See RFC 7530, section 9.1.7 */ switch (err) { case NFS4ERR_STALE_CLIENTID: case NFS4ERR_STALE_STATEID: @@ -275,6 +275,7 @@ static inline bool seqid_mutating_err(u32 err) case NFS4ERR_BADXDR: case NFS4ERR_RESOURCE: case NFS4ERR_NOFILEHANDLE: + case NFS4ERR_MOVED: return false; }; return true; diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 12c9b485beb7..abd7c01c84db 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -206,7 +206,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref) static inline bool percpu_ref_tryget(struct percpu_ref *ref) { unsigned long __percpu *percpu_count; - int ret; + bool ret; rcu_read_lock_sched(); @@ -240,7 +240,7 @@ static inline bool percpu_ref_tryget(struct percpu_ref *ref) static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) { unsigned long __percpu *percpu_count; - int ret = false; + bool ret = false; rcu_read_lock_sched(); diff --git a/drivers/power/supply/qcom/pmic-voter.h b/include/linux/pmic-voter.h index f202bf704055..f202bf704055 100644 --- a/drivers/power/supply/qcom/pmic-voter.h +++ b/include/linux/pmic-voter.h diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 1effc355d7d0..864f7f6a0d01 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -217,6 +217,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_DP_DM, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, POWER_SUPPLY_PROP_CURRENT_QNOVO, POWER_SUPPLY_PROP_VOLTAGE_QNOVO, POWER_SUPPLY_PROP_RERUN_AICL, diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 504c98a278d4..e13bfdf7f314 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -19,7 +19,6 @@ #define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ #define PT_PTRACED 0x00000001 #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ -#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */ #define PT_OPT_FLAG_SHIFT 3 /* PT_TRACE_* event enable flags */ diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h index 4023e3a683d3..a0e2283ef4c9 100644 --- a/include/linux/qpnp/qpnp-revid.h +++ b/include/linux/qpnp/qpnp-revid.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -181,6 +181,7 @@ #define PM660L_SUBTYPE 0x1A #define PM660_SUBTYPE 0x1B +/* PMI8998 REV_ID */ #define PMI8998_V1P0_REV1 0x00 #define PMI8998_V1P0_REV2 0x00 #define PMI8998_V1P0_REV3 0x00 @@ -196,6 +197,26 @@ #define PMI8998_V2P0_REV3 0x00 #define PMI8998_V2P0_REV4 0x02 +/* PM660 REV_ID */ +#define PM660_V1P0_REV1 0x00 +#define PM660_V1P0_REV2 0x00 +#define PM660_V1P0_REV3 0x00 +#define PM660_V1P0_REV4 0x01 + +#define PM660_V1P1_REV1 0x00 +#define PM660_V1P1_REV2 0x00 +#define PM660_V1P1_REV3 0x01 +#define PM660_V1P1_REV4 0x01 + +/* PMI8998 FAB_ID */ +#define PMI8998_FAB_ID_SMIC 0x11 +#define PMI8998_FAB_ID_GF 0x30 + +/* PM660 FAB_ID */ +#define PM660_FAB_ID_GF 0x0 +#define PM660_FAB_ID_TSMC 0x2 +#define PM660_FAB_ID_MX 0x3 + /* PM8005 */ #define PM8005_SUBTYPE 0x18 diff --git a/include/linux/sched.h b/include/linux/sched.h index 3a7521064fe3..95d758d63784 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1712,6 +1712,7 @@ struct task_struct { struct list_head cpu_timers[3]; /* process credentials */ + const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */ const struct cred __rcu *real_cred; /* objective and real subjective task * credentials (COW) */ const struct cred __rcu *cred; /* effective (overridable) subjective task diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 9b6027c51736..316a5525b730 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -180,5 +180,6 @@ const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); int rpc_localaddr(struct rpc_clnt *, struct sockaddr *, size_t); const char *rpc_proc_name(const struct rpc_task *task); +void rpc_cleanup_clids(void); #endif /* __KERNEL__ */ #endif /* _LINUX_SUNRPC_CLNT_H */ diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h index 387fa7d05c98..d802692acb53 100644 --- a/include/linux/sysrq.h +++ b/include/linux/sysrq.h @@ -42,6 +42,7 @@ struct sysrq_key_op { * are available -- else NULL's). */ +bool sysrq_on(void); void handle_sysrq(int key); void __handle_sysrq(int key, bool check_mask); int register_sysrq_key(int key, struct sysrq_key_op *op); diff --git a/include/linux/tcp.h b/include/linux/tcp.h index b386361ba3e8..318c24612458 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -56,8 +56,13 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb) /* TCP Fast Open Cookie as stored in memory */ struct tcp_fastopen_cookie { + union { + u8 val[TCP_FASTOPEN_COOKIE_MAX]; +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr addr; +#endif + }; s8 len; - u8 val[TCP_FASTOPEN_COOKIE_MAX]; bool exp; /* In RFC6994 experimental option format */ }; diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h index 623fbfce6c67..1e677f95a375 100644 --- a/include/media/msmb_pproc.h +++ b/include/media/msmb_pproc.h @@ -5,6 +5,8 @@ #include <linux/compat.h> +#define MSM_OUTPUT_BUF_CNT 8 + #ifdef CONFIG_COMPAT struct msm_cpp_frame_info32_t { int32_t frame_id; @@ -26,7 +28,7 @@ struct msm_cpp_frame_info32_t { uint32_t feature_mask; uint8_t we_disable; struct msm_cpp_buffer_info_t input_buffer_info; - struct msm_cpp_buffer_info_t output_buffer_info[8]; + struct msm_cpp_buffer_info_t output_buffer_info[MSM_OUTPUT_BUF_CNT]; struct msm_cpp_buffer_info_t duplicate_buffer_info; struct msm_cpp_buffer_info_t tnr_scratch_buffer_info[2]; uint32_t reserved; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9e88da2764d7..85534884c1a0 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4465,6 +4465,17 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss); /** + * cfg80211_abandon_assoc - notify cfg80211 of abandoned association attempt + * @dev: network device + * @bss: The BSS entry with which association was abandoned. + * + * Call this whenever - for reasons reported through other API, like deauth RX, + * an association attempt was abandoned. + * This function may sleep. The caller must hold the corresponding wdev's mutex. + */ +void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss); + +/** * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame * @dev: network device * @buf: 802.11 frame (header + body) diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index 3ebb168b9afc..a34b141f125f 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -309,6 +309,10 @@ static inline int cipso_v4_validate(const struct sk_buff *skb, } for (opt_iter = 6; opt_iter < opt_len;) { + if (opt_iter + 1 == opt_len) { + err_offset = opt_iter; + goto out; + } tag_len = opt[opt_iter + 1]; if ((tag_len == 0) || (tag_len > (opt_len - opt_iter))) { err_offset = opt_iter + 1; diff --git a/include/net/cnss_nl.h b/include/net/cnss_nl.h new file mode 100644 index 000000000000..86c2fccc930e --- /dev/null +++ b/include/net/cnss_nl.h @@ -0,0 +1,100 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _NET_CNSS_GENETLINK_H_ +#define _NET_CNSS_GENETLINK_H_ + +#define CLD80211_MAX_COMMANDS 40 +#define CLD80211_MAX_NL_DATA 4096 + +/** + * enum cld80211_attr - Driver/Application embeds the data in nlmsg with the + * help of below attributes + * + * @CLD80211_ATTR_VENDOR_DATA: Embed all other attributes in this nested + * attribute. + * @CLD80211_ATTR_DATA: Embed complete data in this attribute + * + * Any new message in future can be added as another attribute + */ +enum cld80211_attr { + CLD80211_ATTR_VENDOR_DATA = 1, + CLD80211_ATTR_DATA, + /* add new attributes above here */ + + __CLD80211_ATTR_AFTER_LAST, + CLD80211_ATTR_MAX = __CLD80211_ATTR_AFTER_LAST - 1 +}; + +/** + * enum cld80211_multicast_groups - List of multicast groups supported + * + * @CLD80211_MCGRP_SVC_MSGS: WLAN service message will be sent to this group. + * Ex: Status ind messages + * @CLD80211_MCGRP_HOST_LOGS: All logging related messages from driver will be + * sent to this multicast group + * @CLD80211_MCGRP_FW_LOGS: Firmware logging messages will be sent to this group + * @CLD80211_MCGRP_PER_PKT_STATS: Messages related packet stats debugging infra + * will be sent to this group + * @CLD80211_MCGRP_DIAG_EVENTS: Driver/Firmware status logging diag events will + * be sent to this group + * @CLD80211_MCGRP_FATAL_EVENTS: Any fatal message generated in driver/firmware + * will be sent to this group + * @CLD80211_MCGRP_OEM_MSGS: All OEM message will be sent to this group + * Ex: LOWI messages + */ +enum cld80211_multicast_groups { + CLD80211_MCGRP_SVC_MSGS, + CLD80211_MCGRP_HOST_LOGS, + CLD80211_MCGRP_FW_LOGS, + CLD80211_MCGRP_PER_PKT_STATS, + CLD80211_MCGRP_DIAG_EVENTS, + CLD80211_MCGRP_FATAL_EVENTS, + CLD80211_MCGRP_OEM_MSGS, +}; + +/** + * typedef cld80211_cb - Callback to be called when an nlmsg is received with + * the registered cmd_id command from userspace + * @data: Payload of the message to be sent to driver + * @data_len: Length of the payload + * @cb_ctx: callback context to be returned to driver when the callback + * is called + * @pid: process id of the sender + */ +typedef void (*cld80211_cb)(const void *data, int data_len, + void *cb_ctx, int pid); + +/** + * register_cld_cmd_cb() - Allows cld driver to register for commands with + * callback + * @cmd_id: Command to be registered. Valid range [1, CLD80211_MAX_COMMANDS] + * @cb: Callback to be called when an nlmsg is received with cmd_id command + * from userspace + * @cb_ctx: context provided by driver; Send this as cb_ctx of func() + * to driver + */ +int register_cld_cmd_cb(u8 cmd_id, cld80211_cb cb, void *cb_ctx); + +/** + * deregister_cld_cmd_cb() - Allows cld driver to de-register the command it + * has already registered + * @cmd_id: Command to be deregistered. + */ +int deregister_cld_cmd_cb(u8 cmd_id); + +/** + * cld80211_get_genl_family() - Returns current netlink family context + */ +struct genl_family *cld80211_get_genl_family(void); + +#endif /* _NET_CNSS_GENETLINK_H_ */ diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index bdd985f41022..bd2b5c007561 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -8,6 +8,11 @@ #include <net/flow.h> #include <net/rtnetlink.h> +struct fib_kuid_range { + kuid_t start; + kuid_t end; +}; + struct fib_rule { struct list_head list; int iifindex; @@ -29,8 +34,7 @@ struct fib_rule { int suppress_prefixlen; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; - kuid_t uid_start; - kuid_t uid_end; + struct fib_kuid_range uid_range; struct rcu_head rcu; }; @@ -89,11 +93,10 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ - [FRA_UID_START] = { .type = NLA_U32 }, \ - [FRA_UID_END] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ - [FRA_GOTO] = { .type = NLA_U32 } + [FRA_GOTO] = { .type = NLA_U32 }, \ + [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index ba82feec2590..af0e8c081191 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -118,7 +118,8 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif, u32 mark, kuid_t uid); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); -void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); +void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, + kuid_t uid); void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, u32 mark); void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk); diff --git a/include/net/route.h b/include/net/route.h index d016a8cb45cf..3adb9c724818 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -154,8 +154,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport, - sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID); + daddr, saddr, dport, sport, sock_net_uid(net, sk)); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -269,7 +268,7 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, protocol, flow_flags, dst, src, dport, sport, - sock_i_uid(sk)); + sk->sk_uid); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/include/net/sock.h b/include/net/sock.h index 628c2b588468..653118a2d285 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -446,6 +446,7 @@ struct sock { void *sk_security; #endif __u32 sk_mark; + kuid_t sk_uid; #ifdef CONFIG_CGROUP_NET_CLASSID u32 sk_classid; #endif @@ -1694,6 +1695,7 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) sk->sk_wq = parent->wq; parent->sk = sk; sk_set_socket(sk, parent); + sk->sk_uid = SOCK_INODE(parent)->i_uid; security_sock_graft(sk, parent); write_unlock_bh(&sk->sk_callback_lock); } @@ -1701,6 +1703,11 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) kuid_t sock_i_uid(struct sock *sk); unsigned long sock_i_ino(struct sock *sk); +static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk) +{ + return sk ? sk->sk_uid : make_kuid(net->user_ns, 0); +} + static inline u32 net_tx_rndhash(void) { u32 v = prandom_u32(); diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h index 11528591d0d7..a78ff97eb249 100644 --- a/include/rdma/ib_addr.h +++ b/include/rdma/ib_addr.h @@ -197,10 +197,12 @@ static inline void iboe_addr_get_sgid(struct rdma_dev_addr *dev_addr, dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if); if (dev) { - ip4 = (struct in_device *)dev->ip_ptr; - if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address) + ip4 = in_dev_get(dev); + if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address) { ipv6_addr_set_v4mapped(ip4->ifa_list->ifa_address, (struct in6_addr *)gid); + in_dev_put(ip4); + } dev_put(dev); } } diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h index 301969552d0a..b43e64d69734 100644 --- a/include/rdma/ib_sa.h +++ b/include/rdma/ib_sa.h @@ -138,12 +138,12 @@ struct ib_sa_path_rec { union ib_gid sgid; __be16 dlid; __be16 slid; - int raw_traffic; + u8 raw_traffic; /* reserved */ __be32 flow_label; u8 hop_limit; u8 traffic_class; - int reversible; + u8 reversible; u8 numb_path; __be16 pkey; __be16 qos_class; @@ -204,7 +204,7 @@ struct ib_sa_mcmember_rec { u8 hop_limit; u8 scope; u8 join_state; - int proxy_join; + u8 proxy_join; }; /* Service Record Component Mask Sec 15.2.5.14 Ver 1.1 */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index c37b22101473..5dfc8d01b95c 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -311,6 +311,7 @@ extern void scsi_remove_device(struct scsi_device *); extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); void scsi_attach_vpd(struct scsi_device *sdev); +extern struct scsi_device *scsi_device_from_queue(struct request_queue *q); extern int scsi_device_get(struct scsi_device *); extern void scsi_device_put(struct scsi_device *); extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *, diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h index dc10c52e0e91..393362bdb860 100644 --- a/include/soc/at91/at91sam9_ddrsdr.h +++ b/include/soc/at91/at91sam9_ddrsdr.h @@ -81,6 +81,7 @@ #define AT91_DDRSDRC_LPCB_POWER_DOWN 2 #define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3 #define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */ +#define AT91_DDRSDRC_LPDDR2_PWOFF (1 << 3) /* LPDDR Power Off */ #define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */ #define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */ #define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */ @@ -96,7 +97,9 @@ #define AT91_DDRSDRC_MD_SDR 0 #define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 #define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 +#define AT91_DDRSDRC_MD_LPDDR3 5 #define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */ +#define AT91_DDRSDRC_MD_LPDDR2 7 #define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */ #define AT91_DDRSDRC_DBW_32BITS (0 << 4) #define AT91_DDRSDRC_DBW_16BITS (1 << 4) diff --git a/include/soc/qcom/clock-alpha-pll.h b/include/soc/qcom/clock-alpha-pll.h index 0b5329ba817c..acffe33dc4f3 100644 --- a/include/soc/qcom/clock-alpha-pll.h +++ b/include/soc/qcom/clock-alpha-pll.h @@ -79,6 +79,7 @@ struct alpha_pll_clk { * that the workaround is required. */ bool offline_bit_workaround; + bool no_irq_dis; bool is_fabia; unsigned long min_supported_freq; struct clk c; diff --git a/include/soc/qcom/profiler.h b/include/soc/qcom/profiler.h new file mode 100644 index 000000000000..c53b510fc149 --- /dev/null +++ b/include/soc/qcom/profiler.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PROFILER_H_ +#define __PROFILER_H_ + + +struct profiler_bw_cntrs_req { + uint32_t total; + uint32_t gpu; + uint32_t cpu; + uint32_t cmd; +}; + +struct compat_profiler_bw_cntrs_req { + compat_uint_t total; + compat_uint_t gpu; + compat_uint_t cpu; + compat_uint_t cmd; +}; + +/* Error types */ +enum tz_bw_svc_err { + E_BW_SUCCESS = 0, /* Operation successful */ + E_BW_FAILURE = 1, /* Operation failed due to unknown err */ + E_BW_NULL_PARAM = 2, /* Null Parameter */ + E_BW_INVALID_ARG = 3, /* Arg is not recognized */ + E_BW_BAD_ADDRESS = 4, /* Ptr arg is bad address */ + E_BW_INVALID_ARG_LEN = 5, /* Arg length is wrong */ + E_BW_NOT_SUPPORTED = 6, /* Operation not supported */ + E_BW_NOT_PERMITTED = 7, /* Operation not permitted on platform */ + E_BW_TIME_LOCKED = 8, /* Operation not permitted right now */ + E_BW_RESERVED = 0x7FFFFFFF +}; + +#define TZ_BW_SVC_VERSION (1) +#define PROFILER_IOC_MAGIC 0x98 + +#define PROFILER_IOCTL_GET_BW_INFO \ + _IOWR(PROFILER_IOC_MAGIC, 1, struct profiler_bw_cntrs_req) + +#define COMPAT_PROFILER_IOCTL_GET_BW_INFO \ + _IOWR(PROFILER_IOC_MAGIC, 1, struct compat_profiler_bw_cntrs_req) + +/* Command types */ +enum tz_bw_svc_cmd { + TZ_BW_SVC_START_ID = 0x00000001, + TZ_BW_SVC_GET_ID = 0x00000002, + TZ_BW_SVC_STOP_ID = 0x00000003, + TZ_BW_SVC_LAST_ID = 0x7FFFFFFF +}; +/* Start Request */ +struct tz_bw_svc_start_req { + enum tz_bw_svc_cmd cmd_id; + uint32_t version; +} __packed; + +/* Get Request */ +struct tz_bw_svc_get_req { + enum tz_bw_svc_cmd cmd_id; + uint64_t buf_ptr; + uint32_t buf_size; +} __packed; + +/* Stop Request */ +struct tz_bw_svc_stop_req { + enum tz_bw_svc_cmd cmd_id; +} __packed; + +struct tz_bw_svc_resp { + enum tz_bw_svc_cmd cmd_id; + enum tz_bw_svc_err status; +} __packed; + +__packed union tz_bw_svc_req { + struct tz_bw_svc_start_req *start_req; + struct tz_bw_svc_get_req *get_req; + struct tz_bw_svc_stop_req *stop_req; +}; + +struct tz_bw_svc_buf { + union tz_bw_svc_req bwreq; + struct tz_bw_svc_resp bwresp; + uint32_t req_size; +} __packed; + + +#define TZ_SVC_INFO 6 /* Misc. information services */ +#define TZ_SVC_BW_PROF_ID 0x07 + +#endif /* __PROFILER_H_ */ diff --git a/include/soc/qcom/scm.h b/include/soc/qcom/scm.h index ac8b2ebadbdd..ad57eda97f9d 100644 --- a/include/soc/qcom/scm.h +++ b/include/soc/qcom/scm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -30,6 +30,7 @@ #define SCM_SVC_SMMU_PROGRAM 0x15 #define SCM_SVC_QDSS 0x16 #define SCM_SVC_TZSCHEDULER 0xFC +#define SCM_SVC_BW 0xFD #define SCM_FUSE_READ 0x7 #define SCM_CMD_HDCP 0x01 diff --git a/include/soc/qcom/secure_buffer.h b/include/soc/qcom/secure_buffer.h index 59971c08ed74..b5e71387a6fc 100644 --- a/include/soc/qcom/secure_buffer.h +++ b/include/soc/qcom/secure_buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -53,7 +53,7 @@ int hyp_assign_table(struct sg_table *table, u32 *source_vm_list, int source_nelems, int *dest_vmids, int *dest_perms, int dest_nelems); -int hyp_assign_phys(phys_addr_t addr, u64 size, +extern int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vmlist, int source_nelems, int *dest_vmids, int *dest_perms, int dest_nelems); bool msm_secure_v2_is_supported(void); diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 06c273252484..a5ba1496eef7 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -443,6 +443,11 @@ struct adm_param_data_v5 { */ } __packed; +#define ASM_STREAM_CMD_REGISTER_PP_EVENTS 0x00013213 +#define ASM_STREAM_PP_EVENT 0x00013214 +#define DSP_STREAM_CMD "ADSP Stream Cmd" +#define DSP_STREAM_CALLBACK "ADSP Stream Callback Event" + /* set customized mixing on matrix mixer */ #define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5 0x00010344 struct adm_cmd_set_pspd_mtmx_strtr_params_v5 { diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 4947c30287a3..29707b26644a 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -211,6 +211,7 @@ struct audio_client { int session; app_cb cb; atomic_t cmd_state; + atomic_t cmd_state_pp; /* Relative or absolute TS */ atomic_t time_flag; atomic_t nowait_cmd_cnt; @@ -617,6 +618,9 @@ int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp); int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, uint32_t params_length); +int q6asm_send_stream_cmd(struct audio_client *ac, uint32_t opcode, + void *param, uint32_t params_length); + /* Client can set the IO mode to either AIO/SIO mode */ int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 6afc6f388edf..ed66414b91f0 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -544,6 +544,7 @@ struct se_node_acl { /* Used to signal demo mode created ACL, disabled by default */ bool dynamic_node_acl; bool acl_stop:1; + bool dynamic_stop; u32 queue_depth; u32 acl_index; enum target_prot_type saved_prot_type; @@ -739,6 +740,7 @@ struct se_lun { struct config_group lun_group; struct se_port_stat_grps port_stat_grps; struct completion lun_ref_comp; + struct completion lun_shutdown_comp; struct percpu_ref lun_ref; struct list_head lun_dev_link; struct hlist_node link; diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index ce9ea736f1d7..97069ecabe49 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -168,6 +168,8 @@ void core_allocate_nexus_loss_ua(struct se_node_acl *acl); struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg, unsigned char *); +bool target_tpg_has_node_acl(struct se_portal_group *tpg, + const char *); struct se_node_acl *core_tpg_check_initiator_node_acl(struct se_portal_group *, unsigned char *); int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *, diff --git a/include/trace/events/net.h b/include/trace/events/net.h index 49cc7c3de252..89d009e10938 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -57,7 +57,7 @@ TRACE_EVENT(net_dev_start_xmit, __entry->gso_type = skb_shinfo(skb)->gso_type; ), - TP_printk("dev=%s queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u network_offset=%d transport_offset_valid=%d transport_offset=%d tx_flags=%d gso_size=%d gso_segs=%d gso_type=%#x", + TP_printk("dev=%s queue_mapping=%u skbaddr=%pK vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u network_offset=%d transport_offset_valid=%d transport_offset=%d tx_flags=%d gso_size=%d gso_segs=%d gso_type=%#x", __get_str(name), __entry->queue_mapping, __entry->skbaddr, __entry->vlan_tagged, __entry->vlan_proto, __entry->vlan_tci, __entry->protocol, __entry->ip_summed, __entry->len, @@ -90,7 +90,7 @@ TRACE_EVENT(net_dev_xmit, __assign_str(name, dev->name); ), - TP_printk("dev=%s skbaddr=%p len=%u rc=%d", + TP_printk("dev=%s skbaddr=%pK len=%u rc=%d", __get_str(name), __entry->skbaddr, __entry->len, __entry->rc) ); @@ -112,7 +112,7 @@ DECLARE_EVENT_CLASS(net_dev_template, __assign_str(name, skb->dev->name); ), - TP_printk("dev=%s skbaddr=%p len=%u", + TP_printk("dev=%s skbaddr=%pK len=%u", __get_str(name), __entry->skbaddr, __entry->len) ) @@ -191,7 +191,7 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template, __entry->gso_type = skb_shinfo(skb)->gso_type; ), - TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x", + TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%pK vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x", __get_str(name), __entry->napi_id, __entry->queue_mapping, __entry->skbaddr, __entry->vlan_tagged, __entry->vlan_proto, __entry->vlan_tci, __entry->protocol, __entry->ip_summed, diff --git a/include/trace/events/syscalls.h b/include/trace/events/syscalls.h index 14e49c798135..b35533b94277 100644 --- a/include/trace/events/syscalls.h +++ b/include/trace/events/syscalls.h @@ -1,5 +1,6 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM raw_syscalls +#undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE syscalls #if !defined(_TRACE_EVENTS_SYSCALLS_H) || defined(TRACE_HEADER_MULTI_READ) diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 99fe34d25fc5..8baf2bf6df2e 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -253,6 +253,66 @@ struct drm_msm_event_resp { __u8 data[]; }; +#define MSM_COUNTER_GROUP_CP 0 +#define MSM_COUNTER_GROUP_RBBM 1 +#define MSM_COUNTER_GROUP_PC 2 +#define MSM_COUNTER_GROUP_VFD 3 +#define MSM_COUNTER_GROUP_HLSQ 4 +#define MSM_COUNTER_GROUP_VPC 5 +#define MSM_COUNTER_GROUP_TSE 6 +#define MSM_COUNTER_GROUP_RAS 7 +#define MSM_COUNTER_GROUP_UCHE 8 +#define MSM_COUNTER_GROUP_TP 9 +#define MSM_COUNTER_GROUP_SP 10 +#define MSM_COUNTER_GROUP_RB 11 +#define MSM_COUNTER_GROUP_VBIF 12 +#define MSM_COUNTER_GROUP_VBIF_PWR 13 +#define MSM_COUNTER_GROUP_VSC 23 +#define MSM_COUNTER_GROUP_CCU 24 +#define MSM_COUNTER_GROUP_LRZ 25 +#define MSM_COUNTER_GROUP_CMP 26 +#define MSM_COUNTER_GROUP_ALWAYSON 27 +#define MSM_COUNTER_GROUP_SP_PWR 28 +#define MSM_COUNTER_GROUP_TP_PWR 29 +#define MSM_COUNTER_GROUP_RB_PWR 30 +#define MSM_COUNTER_GROUP_CCU_PWR 31 +#define MSM_COUNTER_GROUP_UCHE_PWR 32 +#define MSM_COUNTER_GROUP_CP_PWR 33 +#define MSM_COUNTER_GROUP_GPMU_PWR 34 +#define MSM_COUNTER_GROUP_ALWAYSON_PWR 35 + +/** + * struct drm_msm_counter - allocate or release a GPU performance counter + * @groupid: The group ID of the counter to get/put + * @counterid: For GET returns the counterid that was assigned. For PUT + * release the counter identified by groupid/counterid + * @countable: For GET the countable for the counter + */ +struct drm_msm_counter { + __u32 groupid; + int counterid; + __u32 countable; + __u32 counter_lo; + __u32 counter_hi; +}; + +struct drm_msm_counter_read_op { + __u64 value; + __u32 groupid; + int counterid; +}; + +/** + * struct drm_msm_counter_read - Read a number of GPU performance counters + * ops: Pointer to the list of struct drm_msm_counter_read_op operations + * nr_ops: Number of operations in the list + */ +struct drm_msm_counter_read { + __u64 __user ops; + __u32 nr_ops; +}; + + #define DRM_MSM_GET_PARAM 0x00 /* placeholder: #define DRM_MSM_SET_PARAM 0x01 @@ -267,6 +327,9 @@ struct drm_msm_event_resp { #define DRM_SDE_WB_CONFIG 0x40 #define DRM_MSM_REGISTER_EVENT 0x41 #define DRM_MSM_DEREGISTER_EVENT 0x42 +#define DRM_MSM_COUNTER_GET 0x43 +#define DRM_MSM_COUNTER_PUT 0x44 +#define DRM_MSM_COUNTER_READ 0x45 /** * Currently DRM framework supports only VSYNC event. @@ -289,4 +352,12 @@ struct drm_msm_event_resp { DRM_MSM_REGISTER_EVENT), struct drm_msm_event_req) #define DRM_IOCTL_MSM_DEREGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \ DRM_MSM_DEREGISTER_EVENT), struct drm_msm_event_req) +#define DRM_IOCTL_MSM_COUNTER_GET \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_COUNTER_GET, struct drm_msm_counter) +#define DRM_IOCTL_MSM_COUNTER_PUT \ + DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_COUNTER_PUT, struct drm_msm_counter) +#define DRM_IOCTL_MSM_COUNTER_READ \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_COUNTER_READ, \ + struct drm_msm_counter_read) + #endif /* __MSM_DRM_H__ */ diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h index 9692cda5f8fc..c48d93a28d1a 100644 --- a/include/uapi/linux/can.h +++ b/include/uapi/linux/can.h @@ -196,5 +196,6 @@ struct can_filter { }; #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ +#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */ #endif /* !_UAPI_CAN_H */ diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h index 1e70483e7352..57266ed29fb3 100644 --- a/include/uapi/linux/esoc_ctrl.h +++ b/include/uapi/linux/esoc_ctrl.h @@ -3,11 +3,11 @@ #define ESOC_CODE 0xCC -#define ESOC_CMD_EXE _IOW(ESOC_CODE, 1, u32) -#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, u32) -#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, u32) -#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, u32) -#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, u32) +#define ESOC_CMD_EXE _IOW(ESOC_CODE, 1, unsigned int) +#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, unsigned int) +#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, unsigned int) +#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, unsigned int) +#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, unsigned int) #define ESOC_REG_REQ_ENG _IO(ESOC_CODE, 7) #define ESOC_REG_CMD_ENG _IO(ESOC_CODE, 8) diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index ce19c5bf51f7..bbf02a63a011 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -29,6 +29,11 @@ struct fib_rule_hdr { __u32 flags; }; +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + enum { FRA_UNSPEC, FRA_DST, /* destination address */ @@ -49,8 +54,9 @@ enum { FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, - FRA_UID_START, /* UID range */ - FRA_UID_END, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + FRA_UID_RANGE, /* UID range */ __FRA_MAX }; diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index c6cad0ff1bbf..f8e36262b3f4 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -52,7 +52,7 @@ #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" -#define SDCARDFS_SUPER_MAGIC 0xb550ca10 +#define SDCARDFS_SUPER_MAGIC 0x5dca2df5 #define SMB_SUPER_MAGIC 0x517B #define CGROUP_SUPER_MAGIC 0x27e0eb diff --git a/include/uapi/linux/rmnet_data.h b/include/uapi/linux/rmnet_data.h index 8cfe0270ef4f..7ddfa20cec32 100644 --- a/include/uapi/linux/rmnet_data.h +++ b/include/uapi/linux/rmnet_data.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -223,8 +223,19 @@ enum rmnet_netlink_message_types_e { * uint32_t MAP Flow Handle * Returns: status code */ - RMNET_NETLINK_DEL_VND_TC_FLOW + RMNET_NETLINK_DEL_VND_TC_FLOW, + + /* + * RMNET_NETLINK_NEW_VND_WITH_NAME - Creates a new virtual network + * device node with the specified + * device name + * Args: int32_t node number + * char[] vnd_name - Use as name + * Returns: status code + */ + RMNET_NETLINK_NEW_VND_WITH_NAME }; +#define RMNET_NETLINK_NEW_VND_WITH_NAME RMNET_NETLINK_NEW_VND_WITH_NAME enum rmnet_config_endpoint_modes_e { /* Pass the frame up the stack with no modifications to skb->dev */ diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index a2fad11894ff..d7d7599b112a 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -306,12 +306,14 @@ enum rtattr_type_t { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, - RTA_UID, RTA_VIA, RTA_NEWDST, RTA_PREF, RTA_ENCAP_TYPE, RTA_ENCAP, + RTA_EXPIRES, + RTA_PAD, + RTA_UID, __RTA_MAX }; diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h index f59f034a72b9..13bee7a56a0e 100644 --- a/include/uapi/media/msm_media_info.h +++ b/include/uapi/media/msm_media_info.h @@ -222,7 +222,7 @@ enum color_fmts { * Y_Stride = align(Width, 128) * UV_Stride = align(Width, 128) * Y_Scanlines = align(Height, 32) - * UV_Scanlines = align((Height + 96)/2, 16) + * UV_Scanlines = align(Height/2, 16) * Y_UBWC_Plane_size = align(Y_Stride * Y_Scanlines, 4096) * UV_UBWC_Plane_size = align(UV_Stride * UV_Scanlines, 4096) * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) @@ -235,7 +235,7 @@ enum color_fmts { * * Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size + * Y_Meta_Plane_size + UV_Meta_Plane_size - * + Extradata), 4096) + * + max(Extradata, Y_Stride * 64), 4096) */ COLOR_FMT_NV12_UBWC, /* Venus NV12 10-bit UBWC: @@ -311,7 +311,7 @@ enum color_fmts { * Y_Stride = align(Width * 4/3, 128) * UV_Stride = align(Width * 4/3, 128) * Y_Scanlines = align(Height, 32) - * UV_Scanlines = align((Height + 96)/2, 16) + * UV_Scanlines = align(Height/2, 16) * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096) * UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096) * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) @@ -324,7 +324,7 @@ enum color_fmts { * * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size + * Y_Meta_Plane_size + UV_Meta_Plane_size - * + Extradata), 4096) + * + max(Extradata, Y_Stride * 64), 4096) */ COLOR_FMT_NV12_BPP10_UBWC, /* Venus RGBA8888 format: @@ -970,7 +970,6 @@ static inline unsigned int VENUS_BUFFER_SIZE( break; case COLOR_FMT_NV12_UBWC: case COLOR_FMT_NV12_BPP10_UBWC: - uv_sclines = VENUS_UV_SCANLINES(color_fmt, height + 96); y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); uv_ubwc_plane = MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096); y_meta_stride = VENUS_Y_META_STRIDE(color_fmt, width); @@ -983,7 +982,8 @@ static inline unsigned int VENUS_BUFFER_SIZE( uv_meta_scanlines, 4096); size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + - uv_meta_plane + extra_size; + uv_meta_plane + MSM_MEDIA_MAX(extra_size, + 64 * y_stride); size = MSM_MEDIA_ALIGN(size, 4096); break; case COLOR_FMT_P010_UBWC: diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index 071331ef6882..df9807e72e47 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -51,7 +51,7 @@ #define MSM_CAMERA_SUBDEV_IR_LED 17 #define MSM_CAMERA_SUBDEV_IR_CUT 18 #define MSM_CAMERA_SUBDEV_EXT 19 - +#define MSM_CAMERA_SUBDEV_TOF 20 #define MSM_MAX_CAMERA_SENSORS 5 /* The below macro is defined to put an upper limit on maximum diff --git a/init/Kconfig b/init/Kconfig index 9461bbc0e5fc..7b8b4171ce00 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -395,6 +395,7 @@ endchoice config SCHED_WALT bool "Support window based load tracking" depends on SMP + depends on FAIR_GROUP_SCHED help This feature will allow the scheduler to maintain a tunable window based set of metrics for tasks and runqueues. These metrics can be diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 161a1807e6ef..25b7a678f9ef 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -747,7 +747,7 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, } mode &= ~current_umask(); - ret = vfs_create(dir, path->dentry, mode, true); + ret = vfs_create2(path->mnt, dir, path->dentry, mode, true); path->dentry->d_fsdata = NULL; if (ret) return ERR_PTR(ret); @@ -763,7 +763,7 @@ static struct file *do_open(struct path *path, int oflag) if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) return ERR_PTR(-EINVAL); acc = oflag2acc[oflag & O_ACCMODE]; - if (inode_permission(d_inode(path->dentry), acc)) + if (inode_permission2(path->mnt, d_inode(path->dentry), acc)) return ERR_PTR(-EACCES); return dentry_open(path, oflag, current_cred()); } @@ -796,7 +796,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, ro = mnt_want_write(mnt); /* we'll drop it in any case */ error = 0; mutex_lock(&d_inode(root)->i_mutex); - path.dentry = lookup_one_len(name->name, root, strlen(name->name)); + path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name)); if (IS_ERR(path.dentry)) { error = PTR_ERR(path.dentry); goto out_putfd; @@ -867,7 +867,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) if (err) goto out_name; mutex_lock_nested(&d_inode(mnt->mnt_root)->i_mutex, I_MUTEX_PARENT); - dentry = lookup_one_len(name->name, mnt->mnt_root, + dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root, strlen(name->name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); @@ -879,7 +879,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) err = -ENOENT; } else { ihold(inode); - err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL); + err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL); } dput(dentry); diff --git a/ipc/shm.c b/ipc/shm.c index 88a6f1e78066..09267be8d27b 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1083,8 +1083,8 @@ out_unlock1: * "raddr" thing points to kernel space, and there has to be a wrapper around * this. */ -long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, - unsigned long shmlba) +long do_shmat(int shmid, char __user *shmaddr, int shmflg, + ulong *raddr, unsigned long shmlba) { struct shmid_kernel *shp; unsigned long addr; @@ -1105,8 +1105,13 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, goto out; else if ((addr = (ulong)shmaddr)) { if (addr & (shmlba - 1)) { - if (shmflg & SHM_RND) - addr &= ~(shmlba - 1); /* round down */ + /* + * 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 #ifndef __ARCH_FORCE_SHMLBA if (addr & ~PAGE_MASK) diff --git a/kernel/capability.c b/kernel/capability.c index 00411c82dac5..4984e1f552eb 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -457,6 +457,19 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns, EXPORT_SYMBOL(file_ns_capable); /** + * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode? + * @ns: The user namespace in question + * @inode: The inode in question + * + * Return true if the inode uid and gid are within the namespace. + */ +bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode) +{ + return kuid_has_mapping(ns, inode->i_uid) && + kgid_has_mapping(ns, inode->i_gid); +} + +/** * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped * @inode: The inode in question * @cap: The capability in question @@ -469,7 +482,26 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap) { struct user_namespace *ns = current_user_ns(); - return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && - kgid_has_mapping(ns, inode->i_gid); + return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode); } EXPORT_SYMBOL(capable_wrt_inode_uidgid); + +/** + * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace + * @tsk: The task that may be ptraced + * @ns: The user namespace to search for CAP_SYS_PTRACE in + * + * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE + * in the specified user namespace. + */ +bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns) +{ + int ret = 0; /* An absent tracer adds no restrictions */ + const struct cred *cred; + rcu_read_lock(); + cred = rcu_dereference(tsk->ptracer_cred); + if (cred) + ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE); + rcu_read_unlock(); + return (ret == 0); +} diff --git a/kernel/cpu.c b/kernel/cpu.c index 2432cc630ffb..8b6940755e4a 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -232,12 +232,6 @@ static int cpu_notify(unsigned long val, void *v) return __cpu_notify(val, v, -1, NULL); } -#ifdef CONFIG_HOTPLUG_CPU - -static void cpu_notify_nofail(unsigned long val, void *v) -{ - BUG_ON(cpu_notify(val, v)); -} EXPORT_SYMBOL(register_cpu_notifier); EXPORT_SYMBOL(__register_cpu_notifier); @@ -255,6 +249,12 @@ void __unregister_cpu_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(__unregister_cpu_notifier); +#ifdef CONFIG_HOTPLUG_CPU +static void cpu_notify_nofail(unsigned long val, void *v) +{ + BUG_ON(cpu_notify(val, v)); +} + /** * clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU * @cpu: a CPU id diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0874e2edd275..79517e5549f1 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -598,11 +598,11 @@ return_normal: /* * Wait for the other CPUs to be notified and be waiting for us: */ - time_left = loops_per_jiffy * HZ; + time_left = MSEC_PER_SEC; while (kgdb_do_roundup && --time_left && (atomic_read(&masters_in_kgdb) + atomic_read(&slaves_in_kgdb)) != online_cpus) - cpu_relax(); + udelay(1000); if (!time_left) pr_crit("Timed out waiting for secondary CPUs.\n"); diff --git a/kernel/events/core.c b/kernel/events/core.c index 8d2482d77c04..f9c6f554460e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6224,6 +6224,27 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) char *buf = NULL; char *name; + if (vma->vm_flags & VM_READ) + prot |= PROT_READ; + if (vma->vm_flags & VM_WRITE) + prot |= PROT_WRITE; + if (vma->vm_flags & VM_EXEC) + prot |= PROT_EXEC; + + if (vma->vm_flags & VM_MAYSHARE) + flags = MAP_SHARED; + else + flags = MAP_PRIVATE; + + if (vma->vm_flags & VM_DENYWRITE) + flags |= MAP_DENYWRITE; + if (vma->vm_flags & VM_MAYEXEC) + flags |= MAP_EXECUTABLE; + if (vma->vm_flags & VM_LOCKED) + flags |= MAP_LOCKED; + if (vma->vm_flags & VM_HUGETLB) + flags |= MAP_HUGETLB; + if (file) { struct inode *inode; dev_t dev; @@ -6250,27 +6271,6 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) maj = MAJOR(dev); min = MINOR(dev); - if (vma->vm_flags & VM_READ) - prot |= PROT_READ; - if (vma->vm_flags & VM_WRITE) - prot |= PROT_WRITE; - if (vma->vm_flags & VM_EXEC) - prot |= PROT_EXEC; - - if (vma->vm_flags & VM_MAYSHARE) - flags = MAP_SHARED; - else - flags = MAP_PRIVATE; - - if (vma->vm_flags & VM_DENYWRITE) - flags |= MAP_DENYWRITE; - if (vma->vm_flags & VM_MAYEXEC) - flags |= MAP_EXECUTABLE; - if (vma->vm_flags & VM_LOCKED) - flags |= MAP_LOCKED; - if (vma->vm_flags & VM_HUGETLB) - flags |= MAP_HUGETLB; - goto got_name; } else { if (vma->vm_ops && vma->vm_ops->name) { diff --git a/kernel/fork.c b/kernel/fork.c index 7de03658692b..75573eeb49b2 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -587,7 +587,8 @@ static void mm_init_owner(struct mm_struct *mm, struct task_struct *p) #endif } -static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p) +static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, + struct user_namespace *user_ns) { mm->mmap = NULL; mm->mm_rb = RB_ROOT; @@ -627,6 +628,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p) if (init_new_context(p, mm)) goto fail_nocontext; + mm->user_ns = get_user_ns(user_ns); return mm; fail_nocontext: @@ -672,7 +674,7 @@ struct mm_struct *mm_alloc(void) return NULL; memset(mm, 0, sizeof(*mm)); - return mm_init(mm, current); + return mm_init(mm, current, current_user_ns()); } /* @@ -687,6 +689,7 @@ void __mmdrop(struct mm_struct *mm) destroy_context(mm); mmu_notifier_mm_destroy(mm); check_mm(mm); + put_user_ns(mm->user_ns); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop); @@ -948,7 +951,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk) memcpy(mm, oldmm, sizeof(*mm)); - if (!mm_init(mm, tsk)) + if (!mm_init(mm, tsk, mm->user_ns)) goto fail_nomem; err = dup_mmap(mm, oldmm); diff --git a/kernel/futex.c b/kernel/futex.c index e8af73cc51a7..beb042dcc332 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -3199,4 +3199,4 @@ static int __init futex_init(void) return 0; } -__initcall(futex_init); +core_initcall(futex_init); diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 4b353e0be121..453ec4232852 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -138,6 +138,13 @@ void static_key_slow_dec_deferred(struct static_key_deferred *key) } EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred); +void static_key_deferred_flush(struct static_key_deferred *key) +{ + STATIC_KEY_CHECK_USE(); + flush_delayed_work(&key->work); +} +EXPORT_SYMBOL_GPL(static_key_deferred_flush); + void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl) { diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 8251e75dd9c0..b066724d7a5b 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -65,8 +65,72 @@ static inline void clear_rt_mutex_waiters(struct rt_mutex *lock) static void fixup_rt_mutex_waiters(struct rt_mutex *lock) { - if (!rt_mutex_has_waiters(lock)) - clear_rt_mutex_waiters(lock); + unsigned long owner, *p = (unsigned long *) &lock->owner; + + if (rt_mutex_has_waiters(lock)) + return; + + /* + * The rbtree has no waiters enqueued, now make sure that the + * lock->owner still has the waiters bit set, otherwise the + * following can happen: + * + * CPU 0 CPU 1 CPU2 + * l->owner=T1 + * rt_mutex_lock(l) + * lock(l->lock) + * l->owner = T1 | HAS_WAITERS; + * enqueue(T2) + * boost() + * unlock(l->lock) + * block() + * + * rt_mutex_lock(l) + * lock(l->lock) + * l->owner = T1 | HAS_WAITERS; + * enqueue(T3) + * boost() + * unlock(l->lock) + * block() + * signal(->T2) signal(->T3) + * lock(l->lock) + * dequeue(T2) + * deboost() + * unlock(l->lock) + * lock(l->lock) + * dequeue(T3) + * ==> wait list is empty + * deboost() + * unlock(l->lock) + * lock(l->lock) + * fixup_rt_mutex_waiters() + * if (wait_list_empty(l) { + * l->owner = owner + * owner = l->owner & ~HAS_WAITERS; + * ==> l->owner = T1 + * } + * lock(l->lock) + * rt_mutex_unlock(l) fixup_rt_mutex_waiters() + * if (wait_list_empty(l) { + * owner = l->owner & ~HAS_WAITERS; + * cmpxchg(l->owner, T1, NULL) + * ===> Success (l->owner = NULL) + * + * l->owner = owner + * ==> l->owner = T1 + * } + * + * With the check for the waiter bit in place T3 on CPU2 will not + * overwrite. All tasks fiddling with the waiters bit are + * serialized by l->lock, so nothing else can modify the waiters + * bit. If the bit is set then nothing can change l->owner either + * so the simple RMW is safe. The cmpxchg() will simply fail if it + * happens in the middle of the RMW because the waiters bit is + * still set. + */ + owner = READ_ONCE(*p); + if (owner & RT_MUTEX_HAS_WAITERS) + WRITE_ONCE(*p, owner & ~RT_MUTEX_HAS_WAITERS); } /* diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h index 4f5f83c7d2d3..e317e1cbb3eb 100644 --- a/kernel/locking/rtmutex_common.h +++ b/kernel/locking/rtmutex_common.h @@ -75,8 +75,9 @@ task_top_pi_waiter(struct task_struct *p) static inline struct task_struct *rt_mutex_owner(struct rt_mutex *lock) { - return (struct task_struct *) - ((unsigned long)lock->owner & ~RT_MUTEX_OWNER_MASKALL); + unsigned long owner = (unsigned long) READ_ONCE(lock->owner); + + return (struct task_struct *) (owner & ~RT_MUTEX_OWNER_MASKALL); } /* diff --git a/kernel/membarrier.c b/kernel/membarrier.c index 536c727a56e9..9f9284f37f8d 100644 --- a/kernel/membarrier.c +++ b/kernel/membarrier.c @@ -16,6 +16,7 @@ #include <linux/syscalls.h> #include <linux/membarrier.h> +#include <linux/tick.h> /* * Bitmask made from a "or" of all commands within enum membarrier_cmd, @@ -51,6 +52,9 @@ */ SYSCALL_DEFINE2(membarrier, int, cmd, int, flags) { + /* MEMBARRIER_CMD_SHARED is not compatible with nohz_full. */ + if (tick_nohz_full_enabled()) + return -ENOSYS; if (unlikely(flags)) return -EINVAL; switch (cmd) { diff --git a/kernel/memremap.c b/kernel/memremap.c index 25ced161ebeb..f719c925cb54 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c @@ -159,7 +159,9 @@ static void devm_memremap_pages_release(struct device *dev, void *res) struct page_map *page_map = res; /* pages are dead and unused, undo the arch mapping */ + mem_hotplug_begin(); arch_remove_memory(page_map->res.start, resource_size(&page_map->res)); + mem_hotplug_done(); } void *devm_memremap_pages(struct device *dev, struct resource *res) @@ -189,7 +191,9 @@ void *devm_memremap_pages(struct device *dev, struct resource *res) if (nid < 0) nid = numa_mem_id(); + mem_hotplug_begin(); error = arch_add_memory(nid, res->start, resource_size(res), true); + mem_hotplug_done(); if (error) { devres_free(page_map); return ERR_PTR(error); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 7b884dc55bd0..9fcb521fab0e 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1440,7 +1440,7 @@ static void call_console_drivers(int level, { struct console *con; - trace_console(text, len); + trace_console_rcuidle(text, len); if (level >= console_loglevel && !ignore_loglevel) return; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 3189e51db7e8..a46c40bfb5f6 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -39,6 +39,9 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) BUG_ON(!list_empty(&child->ptrace_entry)); list_add(&child->ptrace_entry, &new_parent->ptraced); child->parent = new_parent; + rcu_read_lock(); + child->ptracer_cred = get_cred(__task_cred(new_parent)); + rcu_read_unlock(); } /** @@ -71,11 +74,15 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) */ void __ptrace_unlink(struct task_struct *child) { + const struct cred *old_cred; BUG_ON(!child->ptrace); child->ptrace = 0; child->parent = child->real_parent; list_del_init(&child->ptrace_entry); + old_cred = child->ptracer_cred; + child->ptracer_cred = NULL; + put_cred(old_cred); spin_lock(&child->sighand->siglock); @@ -219,7 +226,7 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) static int __ptrace_may_access(struct task_struct *task, unsigned int mode) { const struct cred *cred = current_cred(), *tcred; - int dumpable = 0; + struct mm_struct *mm; kuid_t caller_uid; kgid_t caller_gid; @@ -270,16 +277,11 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) return -EPERM; ok: rcu_read_unlock(); - smp_rmb(); - if (task->mm) - dumpable = get_dumpable(task->mm); - rcu_read_lock(); - if (dumpable != SUID_DUMP_USER && - !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { - rcu_read_unlock(); - return -EPERM; - } - rcu_read_unlock(); + mm = task->mm; + if (mm && + ((get_dumpable(mm) != SUID_DUMP_USER) && + !ptrace_has_cap(mm->user_ns, mode))) + return -EPERM; return security_ptrace_access_check(task, mode); } @@ -343,10 +345,6 @@ static int ptrace_attach(struct task_struct *task, long request, if (seize) flags |= PT_SEIZED; - rcu_read_lock(); - if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE)) - flags |= PT_PTRACE_CAP; - rcu_read_unlock(); task->ptrace = flags; __ptrace_link(task, current); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 312ffdad034a..1017a3f77391 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -88,6 +88,7 @@ #include "sched.h" #include "../workqueue_internal.h" #include "../smpboot.h" +#include "../time/tick-internal.h" #define CREATE_TRACE_POINTS #include <trace/events/sched.h> @@ -3260,7 +3261,8 @@ void scheduler_tick(void) if (curr->sched_class == &fair_sched_class) check_for_migration(rq, curr); - core_ctl_check(wallclock); + if (cpu == tick_do_timer_cpu) + core_ctl_check(wallclock); } #ifdef CONFIG_NO_HZ_FULL diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c index 1e3accddd103..1dde338d30f2 100644 --- a/kernel/sched/core_ctl.c +++ b/kernel/sched/core_ctl.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "core_ctl: " fmt + #include <linux/init.h> #include <linux/notifier.h> #include <linux/cpu.h> @@ -50,7 +52,6 @@ struct cluster_data { }; struct cpu_data { - bool online; bool is_busy; unsigned int busy; unsigned int cpu; @@ -242,22 +243,6 @@ static ssize_t show_is_big_cluster(const struct cluster_data *state, char *buf) return snprintf(buf, PAGE_SIZE, "%u\n", state->is_big_cluster); } -static ssize_t show_cpus(const struct cluster_data *state, char *buf) -{ - struct cpu_data *c; - ssize_t count = 0; - unsigned long flags; - - spin_lock_irqsave(&state_lock, flags); - list_for_each_entry(c, &state->lru, sib) { - count += snprintf(buf + count, PAGE_SIZE - count, - "CPU%u (%s)\n", c->cpu, - c->online ? "Online" : "Offline"); - } - spin_unlock_irqrestore(&state_lock, flags); - return count; -} - static ssize_t show_need_cpus(const struct cluster_data *state, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", state->need_cpus); @@ -286,10 +271,11 @@ static ssize_t show_global_state(const struct cluster_data *state, char *buf) count += snprintf(buf + count, PAGE_SIZE - count, "\tCPU: %u\n", c->cpu); count += snprintf(buf + count, PAGE_SIZE - count, - "\tOnline: %u\n", c->online); + "\tOnline: %u\n", + cpu_online(c->cpu)); count += snprintf(buf + count, PAGE_SIZE - count, - "\tActive: %u\n", - !cpu_isolated(c->cpu)); + "\tIsolated: %u\n", + cpu_isolated(c->cpu)); count += snprintf(buf + count, PAGE_SIZE - count, "\tFirst CPU: %u\n", cluster->first_cpu); @@ -376,7 +362,6 @@ core_ctl_attr_rw(busy_up_thres); core_ctl_attr_rw(busy_down_thres); core_ctl_attr_rw(task_thres); core_ctl_attr_rw(is_big_cluster); -core_ctl_attr_ro(cpus); core_ctl_attr_ro(need_cpus); core_ctl_attr_ro(active_cpus); core_ctl_attr_ro(global_state); @@ -390,7 +375,6 @@ static struct attribute *default_attrs[] = { &busy_down_thres.attr, &task_thres.attr, &is_big_cluster.attr, - &cpus.attr, &need_cpus.attr, &active_cpus.attr, &global_state.attr, @@ -534,7 +518,7 @@ static unsigned int get_active_cpu_count(const struct cluster_data *cluster) static bool is_active(const struct cpu_data *state) { - return state->online && !cpu_isolated(state->cpu); + return cpu_online(state->cpu) && !cpu_isolated(state->cpu); } static bool adjustment_possible(const struct cluster_data *cluster, @@ -669,6 +653,9 @@ int core_ctl_set_boost(bool boost) int ret = 0; bool boost_state_changed = false; + if (unlikely(!initialized)) + return 0; + spin_lock_irqsave(&state_lock, flags); for_each_cluster(cluster, index) { if (cluster->is_big_cluster) { @@ -815,7 +802,7 @@ static void __try_to_unisolate(struct cluster_data *cluster, if (!c->isolated_by_us) continue; - if ((c->online && !cpu_isolated(c->cpu)) || + if ((cpu_online(c->cpu) && !cpu_isolated(c->cpu)) || (!force && c->not_preferred)) continue; if (cluster->active_cpus == need) @@ -904,19 +891,7 @@ static int __ref cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; switch (action & ~CPU_TASKS_FROZEN) { - case CPU_UP_PREPARE: - - /* If online state of CPU somehow got out of sync, fix it. */ - if (state->online) { - state->online = false; - cluster->active_cpus = get_active_cpu_count(cluster); - pr_warn("CPU%d offline when state is online\n", cpu); - } - break; - case CPU_ONLINE: - - state->online = true; cluster->active_cpus = get_active_cpu_count(cluster); /* @@ -941,15 +916,6 @@ static int __ref cpu_callback(struct notifier_block *nfb, /* Move a CPU to the end of the LRU when it goes offline. */ move_cpu_lru(state); - /* Fall through */ - - case CPU_UP_CANCELED: - - /* If online state of CPU somehow got out of sync, fix it. */ - if (!state->online) - pr_warn("CPU%d online when state is offline\n", cpu); - - state->online = false; state->busy = 0; cluster->active_cpus = get_active_cpu_count(cluster); break; @@ -968,6 +934,42 @@ static struct notifier_block __refdata cpu_notifier = { /* ============================ init code ============================== */ +static cpumask_var_t core_ctl_disable_cpumask; +static bool core_ctl_disable_cpumask_present; + +static int __init core_ctl_disable_setup(char *str) +{ + if (!*str) + return -EINVAL; + + alloc_bootmem_cpumask_var(&core_ctl_disable_cpumask); + + if (cpulist_parse(str, core_ctl_disable_cpumask) < 0) { + free_bootmem_cpumask_var(core_ctl_disable_cpumask); + return -EINVAL; + } + + core_ctl_disable_cpumask_present = true; + pr_info("disable_cpumask=%*pbl\n", + cpumask_pr_args(core_ctl_disable_cpumask)); + + return 0; +} +early_param("core_ctl_disable_cpumask", core_ctl_disable_setup); + +static bool should_skip(const struct cpumask *mask) +{ + if (!core_ctl_disable_cpumask_present) + return false; + + /* + * We operate on a cluster basis. Disable the core_ctl for + * a cluster, if all of it's cpus are specified in + * core_ctl_disable_cpumask + */ + return cpumask_subset(mask, core_ctl_disable_cpumask); +} + static struct cluster_data *find_cluster_by_first_cpu(unsigned int first_cpu) { unsigned int i; @@ -989,6 +991,9 @@ static int cluster_init(const struct cpumask *mask) unsigned int cpu; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + if (should_skip(mask)) + return 0; + if (find_cluster_by_first_cpu(first_cpu)) return 0; @@ -1028,8 +1033,6 @@ static int cluster_init(const struct cpumask *mask) state = &per_cpu(cpu_state, cpu); state->cluster = cluster; state->cpu = cpu; - if (cpu_online(cpu)) - state->online = true; list_add_tail(&state->sib, &cluster->lru); } cluster->active_cpus = get_active_cpu_count(cluster); @@ -1091,6 +1094,9 @@ static int __init core_ctl_init(void) { unsigned int cpu; + if (should_skip(cpu_possible_mask)) + return 0; + core_ctl_check_interval = (rq_avg_period_ms - RQ_AVG_TOLERANCE) * NSEC_PER_MSEC; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index cf55fc2663fb..8d5353906c8d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3844,6 +3844,10 @@ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) cfs_rq->load_last_update_time_copy = sa->last_update_time; #endif + /* Trace CPU load, unless cfs_rq belongs to a non-root task_group */ + if (cfs_rq == &rq_of(cfs_rq)->cfs) + trace_sched_load_avg_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq); + return decayed || removed; } @@ -3867,7 +3871,6 @@ static inline void update_load_avg(struct sched_entity *se, int update_tg) if (entity_is_task(se)) trace_sched_load_avg_task(task_of(se), &se->avg); - trace_sched_load_avg_cpu(cpu, cfs_rq); } static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c index 2ffb1680b380..6e053bd9830c 100644 --- a/kernel/sched/walt.c +++ b/kernel/sched/walt.c @@ -62,8 +62,6 @@ static unsigned int max_possible_freq = 1; */ static unsigned int min_max_freq = 1; -static unsigned int max_capacity = 1024; -static unsigned int min_capacity = 1024; static unsigned int max_load_scale_factor = 1024; static unsigned int max_possible_capacity = 1024; @@ -869,39 +867,6 @@ void walt_fixup_busy_time(struct task_struct *p, int new_cpu) double_rq_unlock(src_rq, dest_rq); } -/* Keep track of max/min capacity possible across CPUs "currently" */ -static void __update_min_max_capacity(void) -{ - int i; - int max = 0, min = INT_MAX; - - for_each_online_cpu(i) { - if (cpu_rq(i)->capacity > max) - max = cpu_rq(i)->capacity; - if (cpu_rq(i)->capacity < min) - min = cpu_rq(i)->capacity; - } - - max_capacity = max; - min_capacity = min; -} - -static void update_min_max_capacity(void) -{ - unsigned long flags; - int i; - - local_irq_save(flags); - for_each_possible_cpu(i) - raw_spin_lock(&cpu_rq(i)->lock); - - __update_min_max_capacity(); - - for_each_possible_cpu(i) - raw_spin_unlock(&cpu_rq(i)->lock); - local_irq_restore(flags); -} - /* * Return 'capacity' of a cpu in reference to "least" efficient cpu, such that * least efficient cpu gets capacity of 1024 @@ -984,15 +949,9 @@ static int cpufreq_notifier_policy(struct notifier_block *nb, /* Initialized to policy->max in case policy->related_cpus is empty! */ unsigned int orig_max_freq = policy->max; - if (val != CPUFREQ_NOTIFY && val != CPUFREQ_REMOVE_POLICY && - val != CPUFREQ_CREATE_POLICY) + if (val != CPUFREQ_NOTIFY) return 0; - if (val == CPUFREQ_REMOVE_POLICY || val == CPUFREQ_CREATE_POLICY) { - update_min_max_capacity(); - return 0; - } - for_each_cpu(i, policy->related_cpus) { cpumask_copy(&cpu_rq(i)->freq_domain_cpumask, policy->related_cpus); @@ -1082,8 +1041,6 @@ static int cpufreq_notifier_policy(struct notifier_block *nb, max_load_scale_factor = highest_mplsf; } - __update_min_max_capacity(); - return 0; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 51eef8e7df39..8cc5167e4b04 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2734,6 +2734,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int break; if (neg) continue; + val = convmul * val / convdiv; if ((min && val < *min) || (max && val > *max)) continue; *i = val; diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index f6aae7977824..d2a20e83ebae 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -871,6 +871,9 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { int cpu = smp_processor_id(); + if (!bc) + return; + /* Set it up only once ! */ if (bc->event_handler != tick_handle_oneshot_broadcast) { int was_periodic = clockevent_state_periodic(bc); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index ede4bf13d3e9..5fa544f3f560 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -298,10 +298,10 @@ u32 (*arch_gettimeoffset)(void) = default_arch_gettimeoffset; static inline u32 arch_gettimeoffset(void) { return 0; } #endif -static inline s64 timekeeping_delta_to_ns(struct tk_read_base *tkr, +static inline u64 timekeeping_delta_to_ns(struct tk_read_base *tkr, cycle_t delta) { - s64 nsec; + u64 nsec; nsec = delta * tkr->mult + tkr->xtime_nsec; nsec >>= tkr->shift; diff --git a/kernel/trace/ipc_logging.c b/kernel/trace/ipc_logging.c index 2c3e0998d400..ed29c38cd7fb 100644 --- a/kernel/trace/ipc_logging.c +++ b/kernel/trace/ipc_logging.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -507,8 +507,8 @@ int ipc_log_string(void *ilctxt, const char *fmt, ...) tsv_qtimer_write(&ectxt); avail_size = (MAX_MSG_SIZE - (ectxt.offset + hdr_size)); va_start(arg_list, fmt); - data_size = vsnprintf((ectxt.buff + ectxt.offset + hdr_size), - avail_size, fmt, arg_list); + data_size = vscnprintf((ectxt.buff + ectxt.offset + hdr_size), + avail_size, fmt, arg_list); va_end(arg_list); tsv_write_header(&ectxt, TSV_TYPE_BYTE_ARRAY, data_size); ectxt.offset += data_size; diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 4641bdb40f8f..96c75b0e9831 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -785,6 +785,10 @@ print_graph_entry_leaf(struct trace_iterator *iter, cpu_data = per_cpu_ptr(data->cpu_data, cpu); + /* If a graph tracer ignored set_graph_notrace */ + if (call->depth < -1) + call->depth += FTRACE_NOTRACE_DEPTH; + /* * Comments display at + 1 to depth. Since * this is a leaf function, keep the comments @@ -793,7 +797,8 @@ print_graph_entry_leaf(struct trace_iterator *iter, cpu_data->depth = call->depth - 1; /* No need to keep this function around for this depth */ - if (call->depth < FTRACE_RETFUNC_DEPTH) + if (call->depth < FTRACE_RETFUNC_DEPTH && + !WARN_ON_ONCE(call->depth < 0)) cpu_data->enter_funcs[call->depth] = 0; } @@ -823,11 +828,16 @@ print_graph_entry_nested(struct trace_iterator *iter, struct fgraph_cpu_data *cpu_data; int cpu = iter->cpu; + /* If a graph tracer ignored set_graph_notrace */ + if (call->depth < -1) + call->depth += FTRACE_NOTRACE_DEPTH; + cpu_data = per_cpu_ptr(data->cpu_data, cpu); cpu_data->depth = call->depth; /* Save this function pointer to see if the exit matches */ - if (call->depth < FTRACE_RETFUNC_DEPTH) + if (call->depth < FTRACE_RETFUNC_DEPTH && + !WARN_ON_ONCE(call->depth < 0)) cpu_data->enter_funcs[call->depth] = call->func; } @@ -1057,7 +1067,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, */ cpu_data->depth = trace->depth - 1; - if (trace->depth < FTRACE_RETFUNC_DEPTH) { + if (trace->depth < FTRACE_RETFUNC_DEPTH && + !WARN_ON_ONCE(trace->depth < 0)) { if (cpu_data->enter_funcs[trace->depth] != trace->func) func_match = 0; cpu_data->enter_funcs[trace->depth] = 0; diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 91fa701b4a24..1de2ef8ec926 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -427,7 +427,6 @@ static void watchdog_overflow_callback(struct perf_event *event, */ if (is_hardlockup()) { int this_cpu = smp_processor_id(); - struct pt_regs *regs = get_irq_regs(); /* only print hardlockups once */ if (__this_cpu_read(hard_watchdog_warn) == true) diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 9ef80bf441b3..a988d4ef39da 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -757,15 +757,20 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi) if (!bdi->wb_congested) return -ENOMEM; + atomic_set(&bdi->wb_congested->refcnt, 1); + err = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL); if (err) { - kfree(bdi->wb_congested); + wb_congested_put(bdi->wb_congested); return err; } return 0; } -static void cgwb_bdi_destroy(struct backing_dev_info *bdi) { } +static void cgwb_bdi_destroy(struct backing_dev_info *bdi) +{ + wb_congested_put(bdi->wb_congested); +} #endif /* CONFIG_CGROUP_WRITEBACK */ diff --git a/mm/filemap.c b/mm/filemap.c index c46678d7d041..8b2cf0f6a529 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -867,9 +867,12 @@ void page_endio(struct page *page, int rw, int err) unlock_page(page); } else { /* rw == WRITE */ if (err) { + struct address_space *mapping; + SetPageError(page); - if (page->mapping) - mapping_set_error(page->mapping, err); + mapping = page_mapping(page); + if (mapping) + mapping_set_error(mapping, err); } end_page_writeback(page); } @@ -1561,6 +1564,11 @@ static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos, cond_resched(); find_page: + if (fatal_signal_pending(current)) { + error = -EINTR; + goto out; + } + page = find_get_page(mapping, index); if (!page) { page_cache_sync_readahead(mapping, diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4434cdd4cd9a..ea11123a9249 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1723,23 +1723,32 @@ free: } /* - * When releasing a hugetlb pool reservation, any surplus pages that were - * allocated to satisfy the reservation must be explicitly freed if they were - * never used. - * Called with hugetlb_lock held. + * This routine has two main purposes: + * 1) Decrement the reservation count (resv_huge_pages) by the value passed + * in unused_resv_pages. This corresponds to the prior adjustments made + * to the associated reservation map. + * 2) Free any unused surplus pages that may have been allocated to satisfy + * the reservation. As many as unused_resv_pages may be freed. + * + * Called with hugetlb_lock held. However, the lock could be dropped (and + * reacquired) during calls to cond_resched_lock. Whenever dropping the lock, + * we must make sure nobody else can claim pages we are in the process of + * freeing. Do this by ensuring resv_huge_page always is greater than the + * number of huge pages we plan to free when dropping the lock. */ static void return_unused_surplus_pages(struct hstate *h, unsigned long unused_resv_pages) { unsigned long nr_pages; - /* Uncommit the reservation */ - h->resv_huge_pages -= unused_resv_pages; - /* Cannot return gigantic pages currently */ if (hstate_is_gigantic(h)) - return; + goto out; + /* + * Part (or even all) of the reservation could have been backed + * by pre-allocated pages. Only free surplus pages. + */ nr_pages = min(unused_resv_pages, h->surplus_huge_pages); /* @@ -1749,12 +1758,22 @@ static void return_unused_surplus_pages(struct hstate *h, * when the nodes with surplus pages have no free pages. * free_pool_huge_page() will balance the the freed pages across the * on-line nodes with memory and will handle the hstate accounting. + * + * Note that we decrement resv_huge_pages as we free the pages. If + * we drop the lock, resv_huge_pages will still be sufficiently large + * to cover subsequent pages we may free. */ while (nr_pages--) { + h->resv_huge_pages--; + unused_resv_pages--; if (!free_pool_huge_page(h, &node_states[N_MEMORY], 1)) - break; + goto out; cond_resched_lock(&hugetlb_lock); } + +out: + /* Fully uncommit the reservation */ + h->resv_huge_pages -= unused_resv_pages; } diff --git a/mm/init-mm.c b/mm/init-mm.c index a56a851908d2..975e49f00f34 100644 --- a/mm/init-mm.c +++ b/mm/init-mm.c @@ -6,6 +6,7 @@ #include <linux/cpumask.h> #include <linux/atomic.h> +#include <linux/user_namespace.h> #include <asm/pgtable.h> #include <asm/mmu.h> @@ -21,5 +22,6 @@ struct mm_struct init_mm = { .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem), .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock), .mmlist = LIST_HEAD_INIT(init_mm.mmlist), + .user_ns = &init_user_ns, INIT_MM_CONTEXT(init_mm) }; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5d9c8a3136bc..e25b93a4267d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4150,24 +4150,6 @@ static void mem_cgroup_id_get_many(struct mem_cgroup *memcg, unsigned int n) atomic_add(n, &memcg->id.ref); } -static struct mem_cgroup *mem_cgroup_id_get_online(struct mem_cgroup *memcg) -{ - while (!atomic_inc_not_zero(&memcg->id.ref)) { - /* - * The root cgroup cannot be destroyed, so it's refcount must - * always be >= 1. - */ - if (WARN_ON_ONCE(memcg == root_mem_cgroup)) { - VM_BUG_ON(1); - break; - } - memcg = parent_mem_cgroup(memcg); - if (!memcg) - memcg = root_mem_cgroup; - } - return memcg; -} - static void mem_cgroup_id_put_many(struct mem_cgroup *memcg, unsigned int n) { if (atomic_sub_and_test(n, &memcg->id.ref)) { @@ -4496,9 +4478,9 @@ static int mem_cgroup_do_precharge(unsigned long count) return ret; } - /* Try charges one by one with reclaim */ + /* Try charges one by one with reclaim, but do not retry */ while (count--) { - ret = try_charge(mc.to, GFP_KERNEL & ~__GFP_NORETRY, 1); + ret = try_charge(mc.to, GFP_KERNEL | __GFP_NORETRY, 1); if (ret) return ret; mc.precharge++; @@ -5751,6 +5733,24 @@ static int __init mem_cgroup_init(void) subsys_initcall(mem_cgroup_init); #ifdef CONFIG_MEMCG_SWAP +static struct mem_cgroup *mem_cgroup_id_get_online(struct mem_cgroup *memcg) +{ + while (!atomic_inc_not_zero(&memcg->id.ref)) { + /* + * The root cgroup cannot be destroyed, so it's refcount must + * always be >= 1. + */ + if (WARN_ON_ONCE(memcg == root_mem_cgroup)) { + VM_BUG_ON(1); + break; + } + memcg = parent_mem_cgroup(memcg); + if (!memcg) + memcg = root_mem_cgroup; + } + return memcg; +} + /** * mem_cgroup_swapout - transfer a memsw charge to swap * @page: page whose memsw charge to transfer diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 91f7c074e385..2ad8a8888032 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1374,17 +1374,20 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages) } /* - * Confirm all pages in a range [start, end) is belongs to the same zone. + * Confirm all pages in a range [start, end) belong to the same zone. + * When true, return its valid [start, end). */ -int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn) +int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn, + unsigned long *valid_start, unsigned long *valid_end) { unsigned long pfn, sec_end_pfn; + unsigned long start, end; struct zone *zone = NULL; struct page *page; int i; - for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn); + for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn + 1); pfn < end_pfn; - pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) { + pfn = sec_end_pfn, sec_end_pfn += PAGES_PER_SECTION) { /* Make sure the memory section is present first */ if (!present_section_nr(pfn_to_section_nr(pfn))) continue; @@ -1400,10 +1403,20 @@ int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn) page = pfn_to_page(pfn + i); if (zone && page_zone(page) != zone) return 0; + if (!zone) + start = pfn + i; zone = page_zone(page); + end = pfn + MAX_ORDER_NR_PAGES; } } - return 1; + + if (zone) { + *valid_start = start; + *valid_end = end; + return 1; + } else { + return 0; + } } /* @@ -1721,6 +1734,7 @@ static int __ref __offline_pages(unsigned long start_pfn, long offlined_pages; int ret, drain, retry_max, node; unsigned long flags; + unsigned long valid_start, valid_end; struct zone *zone; struct memory_notify arg; @@ -1731,10 +1745,10 @@ static int __ref __offline_pages(unsigned long start_pfn, return -EINVAL; /* This makes hotplug much easier...and readable. we assume this for now. .*/ - if (!test_pages_in_a_zone(start_pfn, end_pfn)) + if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end)) return -EINVAL; - zone = page_zone(pfn_to_page(start_pfn)); + zone = page_zone(pfn_to_page(valid_start)); node = zone_to_nid(zone); nr_pages = end_pfn - start_pfn; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index f20eb4e8c4cc..9174ec544632 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2007,8 +2007,8 @@ retry_cpuset: nmask = policy_nodemask(gfp, pol); zl = policy_zonelist(gfp, pol, node); - mpol_cond_put(pol); page = __alloc_pages_nodemask(gfp, order, zl, nmask); + mpol_cond_put(pol); out: if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 3048c5dbda16..28f60d9ea074 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2569,7 +2569,7 @@ static bool zone_local(struct zone *local_zone, struct zone *zone) static bool zone_allows_reclaim(struct zone *local_zone, struct zone *zone) { - return node_distance(zone_to_nid(local_zone), zone_to_nid(zone)) < + return node_distance(zone_to_nid(local_zone), zone_to_nid(zone)) <= RECLAIM_DISTANCE; } #else /* CONFIG_NUMA */ @@ -5801,15 +5801,18 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn) sizeof(arch_zone_lowest_possible_pfn)); memset(arch_zone_highest_possible_pfn, 0, sizeof(arch_zone_highest_possible_pfn)); - arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions(); - arch_zone_highest_possible_pfn[0] = max_zone_pfn[0]; - for (i = 1; i < MAX_NR_ZONES; i++) { + + start_pfn = find_min_pfn_with_active_regions(); + + for (i = 0; i < MAX_NR_ZONES; i++) { if (i == ZONE_MOVABLE) continue; - arch_zone_lowest_possible_pfn[i] = - arch_zone_highest_possible_pfn[i-1]; - arch_zone_highest_possible_pfn[i] = - max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]); + + end_pfn = max(max_zone_pfn[i], start_pfn); + arch_zone_lowest_possible_pfn[i] = start_pfn; + arch_zone_highest_possible_pfn[i] = end_pfn; + + start_pfn = end_pfn; } arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0; arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0; diff --git a/mm/vmscan.c b/mm/vmscan.c index d82765ba44f4..5e9e74955bd1 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -290,6 +290,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, int nid = shrinkctl->nid; long batch_size = shrinker->batch ? shrinker->batch : SHRINK_BATCH; + long scanned = 0, next_deferred; long min_cache_size = batch_size; if (current_is_kswapd()) @@ -315,7 +316,9 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, pr_err("shrink_slab: %pF negative objects to delete nr=%ld\n", shrinker->scan_objects, total_scan); total_scan = freeable; - } + next_deferred = nr; + } else + next_deferred = total_scan; /* * We need to avoid excessive windup on filesystem shrinkers @@ -372,17 +375,22 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, count_vm_events(SLABS_SCANNED, nr_to_scan); total_scan -= nr_to_scan; + scanned += nr_to_scan; cond_resched(); } + if (next_deferred >= scanned) + next_deferred -= scanned; + else + next_deferred = 0; /* * move the unused scan count back into the shrinker in a * manner that handles concurrent updates. If we exhausted the * scan, there is no need to do an update. */ - if (total_scan > 0) - new_nr = atomic_long_add_return(total_scan, + if (next_deferred > 0) + new_nr = atomic_long_add_return(next_deferred, &shrinker->nr_deferred[nid]); else new_nr = atomic_long_read(&shrinker->nr_deferred[nid]); diff --git a/mm/zswap.c b/mm/zswap.c index 340261946fda..45476f429789 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -78,7 +78,13 @@ static u64 zswap_duplicate_entry; /* Enable/disable zswap (disabled by default) */ static bool zswap_enabled; -module_param_named(enabled, zswap_enabled, bool, 0644); +static int zswap_enabled_param_set(const char *, + const struct kernel_param *); +static struct kernel_param_ops zswap_enabled_param_ops = { + .set = zswap_enabled_param_set, + .get = param_get_bool, +}; +module_param_cb(enabled, &zswap_enabled_param_ops, &zswap_enabled, 0644); /* Crypto compressor to use */ #define ZSWAP_COMPRESSOR_DEFAULT "lzo" @@ -176,6 +182,9 @@ static atomic_t zswap_pools_count = ATOMIC_INIT(0); /* used by param callback function */ static bool zswap_init_started; +/* fatal error during init */ +static bool zswap_init_failed; + /********************************* * helpers and fwd declarations **********************************/ @@ -702,6 +711,11 @@ static int __zswap_param_set(const char *val, const struct kernel_param *kp, char *s = strstrip((char *)val); int ret; + if (zswap_init_failed) { + pr_err("can't set param, initialization failed\n"); + return -ENODEV; + } + /* no change required */ if (!strcmp(s, *(char **)kp->arg)) return 0; @@ -781,6 +795,17 @@ static int zswap_zpool_param_set(const char *val, return __zswap_param_set(val, kp, NULL, zswap_compressor); } +static int zswap_enabled_param_set(const char *val, + const struct kernel_param *kp) +{ + if (zswap_init_failed) { + pr_err("can't enable, initialization failed\n"); + return -ENODEV; + } + + return param_set_bool(val, kp); +} + /********************************* * writeback code **********************************/ @@ -1267,6 +1292,9 @@ pool_fail: dstmem_fail: zswap_entry_cache_destroy(); cache_fail: + /* if built-in, we aren't unloaded on failure; don't allow use */ + zswap_init_failed = true; + zswap_enabled = false; return -ENOMEM; } /* must be late so crypto has time to come up */ diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index 655a7d4c96e1..983f0b5e14f1 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -264,7 +264,7 @@ void ax25_disconnect(ax25_cb *ax25, int reason) { ax25_clear_queues(ax25); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) + if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY)) ax25_stop_heartbeat(ax25); ax25_stop_t1timer(ax25); ax25_stop_t2timer(ax25); diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 83b0ca27a45e..f2079acb555d 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2764,7 +2764,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv, &tvlv_tt_data, &tt_change, &tt_len); - if (!tt_len) + if (!tt_len || !tvlv_len) goto unlock; /* Copy the last orig_node's OGM buffer */ @@ -2782,7 +2782,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv, &tvlv_tt_data, &tt_change, &tt_len); - if (!tt_len) + if (!tt_len || !tvlv_len) goto out; /* fill the rest of the tvlv with the real TT entries */ diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 40197ff8918a..413d18e37083 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -773,20 +773,6 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } -static int br_dev_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[]) -{ - struct net_bridge *br = netdev_priv(dev); - - if (tb[IFLA_ADDRESS]) { - spin_lock_bh(&br->lock); - br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS])); - spin_unlock_bh(&br->lock); - } - - return register_netdevice(dev); -} - static int br_port_slave_changelink(struct net_device *brdev, struct net_device *dev, struct nlattr *tb[], @@ -1068,6 +1054,25 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], return 0; } +static int br_dev_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net_bridge *br = netdev_priv(dev); + int err; + + if (tb[IFLA_ADDRESS]) { + spin_lock_bh(&br->lock); + br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS])); + spin_unlock_bh(&br->lock); + } + + err = br_changelink(dev, tb, data); + if (err) + return err; + + return register_netdevice(dev); +} + static size_t br_get_size(const struct net_device *brdev) { return nla_total_size(sizeof(u32)) + /* IFLA_BR_FORWARD_DELAY */ diff --git a/net/can/af_can.c b/net/can/af_can.c index 166d436196c1..928f58064098 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -445,6 +445,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, * @func: callback function on filter match * @data: returned parameter for callback function * @ident: string for calling module identification + * @sk: socket pointer (might be NULL) * * Description: * Invokes the callback function with the received sk_buff and the given @@ -468,7 +469,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, */ int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, void (*func)(struct sk_buff *, void *), void *data, - char *ident) + char *ident, struct sock *sk) { struct receiver *r; struct hlist_head *rl; @@ -496,6 +497,7 @@ int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, r->func = func; r->data = data; r->ident = ident; + r->sk = sk; hlist_add_head_rcu(&r->list, rl); d->entries++; @@ -520,8 +522,11 @@ EXPORT_SYMBOL(can_rx_register); static void can_rx_delete_receiver(struct rcu_head *rp) { struct receiver *r = container_of(rp, struct receiver, rcu); + struct sock *sk = r->sk; kmem_cache_free(rcv_cache, r); + if (sk) + sock_put(sk); } /** @@ -596,8 +601,11 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask, spin_unlock(&can_rcvlists_lock); /* schedule the receiver item for deletion */ - if (r) + if (r) { + if (r->sk) + sock_hold(r->sk); call_rcu(&r->rcu, can_rx_delete_receiver); + } } EXPORT_SYMBOL(can_rx_unregister); diff --git a/net/can/af_can.h b/net/can/af_can.h index fca0fe9fc45a..b86f5129e838 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -50,13 +50,14 @@ struct receiver { struct hlist_node list; - struct rcu_head rcu; canid_t can_id; canid_t mask; unsigned long matches; void (*func)(struct sk_buff *, void *); void *data; char *ident; + struct sock *sk; + struct rcu_head rcu; }; #define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS) diff --git a/net/can/bcm.c b/net/can/bcm.c index 8ef1afacad82..4ccfd356baed 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -710,14 +710,23 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id, static void bcm_remove_op(struct bcm_op *op) { - hrtimer_cancel(&op->timer); - hrtimer_cancel(&op->thrtimer); - - if (op->tsklet.func) - tasklet_kill(&op->tsklet); + if (op->tsklet.func) { + while (test_bit(TASKLET_STATE_SCHED, &op->tsklet.state) || + test_bit(TASKLET_STATE_RUN, &op->tsklet.state) || + hrtimer_active(&op->timer)) { + hrtimer_cancel(&op->timer); + tasklet_kill(&op->tsklet); + } + } - if (op->thrtsklet.func) - tasklet_kill(&op->thrtsklet); + if (op->thrtsklet.func) { + while (test_bit(TASKLET_STATE_SCHED, &op->thrtsklet.state) || + test_bit(TASKLET_STATE_RUN, &op->thrtsklet.state) || + hrtimer_active(&op->thrtimer)) { + hrtimer_cancel(&op->thrtimer); + tasklet_kill(&op->thrtsklet); + } + } if ((op->frames) && (op->frames != &op->sframe)) kfree(op->frames); @@ -1170,7 +1179,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = can_rx_register(dev, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, - "bcm"); + "bcm", sk); op->rx_reg_dev = dev; dev_put(dev); @@ -1179,7 +1188,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } else err = can_rx_register(NULL, op->can_id, REGMASK(op->can_id), - bcm_rx_handler, op, "bcm"); + bcm_rx_handler, op, "bcm", sk); if (err) { /* this bcm rx op is broken -> remove it */ list_del(&op->list); diff --git a/net/can/gw.c b/net/can/gw.c index 455168718c2e..77c8af4047ef 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -442,7 +442,7 @@ static inline int cgw_register_filter(struct cgw_job *gwj) { return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id, gwj->ccgw.filter.can_mask, can_can_gw_rcv, - gwj, "gw"); + gwj, "gw", NULL); } static inline void cgw_unregister_filter(struct cgw_job *gwj) diff --git a/net/can/raw.c b/net/can/raw.c index 2e67b1423cd3..e9403a26a1d5 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -190,7 +190,7 @@ static int raw_enable_filters(struct net_device *dev, struct sock *sk, for (i = 0; i < count; i++) { err = can_rx_register(dev, filter[i].can_id, filter[i].can_mask, - raw_rcv, sk, "raw"); + raw_rcv, sk, "raw", sk); if (err) { /* clean up successfully registered filters */ while (--i >= 0) @@ -211,7 +211,7 @@ static int raw_enable_errfilter(struct net_device *dev, struct sock *sk, if (err_mask) err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG, - raw_rcv, sk, "raw"); + raw_rcv, sk, "raw", sk); return err; } @@ -499,6 +499,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, if (optlen % sizeof(struct can_filter) != 0) return -EINVAL; + if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter)) + return -EINVAL; + count = optlen / sizeof(struct can_filter); if (count > 1) { diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 63ae5dd24fc5..b8d927c56494 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2042,6 +2042,19 @@ static int process_connect(struct ceph_connection *con) dout("process_connect on %p tag %d\n", con, (int)con->in_tag); + if (con->auth_reply_buf) { + /* + * Any connection that defines ->get_authorizer() + * should also define ->verify_authorizer_reply(). + * See get_connect_authorizer(). + */ + ret = con->ops->verify_authorizer_reply(con, 0); + if (ret < 0) { + con->error_msg = "bad authorize reply"; + return ret; + } + } + switch (con->in_reply.tag) { case CEPH_MSGR_TAG_FEATURES: pr_err("%s%lld %s feature set mismatch," diff --git a/net/core/dev.c b/net/core/dev.c index ea08b75ad695..51aed87e8eec 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1678,24 +1678,19 @@ EXPORT_SYMBOL_GPL(net_dec_ingress_queue); static struct static_key netstamp_needed __read_mostly; #ifdef HAVE_JUMP_LABEL -/* We are not allowed to call static_key_slow_dec() from irq context - * If net_disable_timestamp() is called from irq context, defer the - * static_key_slow_dec() calls. - */ static atomic_t netstamp_needed_deferred; -#endif - -void net_enable_timestamp(void) +static void netstamp_clear(struct work_struct *work) { -#ifdef HAVE_JUMP_LABEL int deferred = atomic_xchg(&netstamp_needed_deferred, 0); - if (deferred) { - while (--deferred) - static_key_slow_dec(&netstamp_needed); - return; - } + while (deferred--) + static_key_slow_dec(&netstamp_needed); +} +static DECLARE_WORK(netstamp_work, netstamp_clear); #endif + +void net_enable_timestamp(void) +{ static_key_slow_inc(&netstamp_needed); } EXPORT_SYMBOL(net_enable_timestamp); @@ -1703,12 +1698,12 @@ EXPORT_SYMBOL(net_enable_timestamp); void net_disable_timestamp(void) { #ifdef HAVE_JUMP_LABEL - if (in_interrupt()) { - atomic_inc(&netstamp_needed_deferred); - return; - } -#endif + /* net_disable_timestamp() can be called from non process context */ + atomic_inc(&netstamp_needed_deferred); + schedule_work(&netstamp_work); +#else static_key_slow_dec(&netstamp_needed); +#endif } EXPORT_SYMBOL(net_disable_timestamp); @@ -2652,9 +2647,9 @@ static netdev_features_t harmonize_features(struct sk_buff *skb, if (skb->ip_summed != CHECKSUM_NONE && !can_checksum_protocol(features, type)) { features &= ~NETIF_F_ALL_CSUM; - } else if (illegal_highdma(skb->dev, skb)) { - features &= ~NETIF_F_SG; } + if (illegal_highdma(skb->dev, skb)) + features &= ~NETIF_F_SG; return features; } @@ -4195,7 +4190,9 @@ static void skb_gro_reset_offset(struct sk_buff *skb) pinfo->nr_frags && !PageHighMem(skb_frag_page(frag0))) { NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0); - NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0); + NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int, + skb_frag_size(frag0), + skb->end - skb->tail); } } diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 252e155c837b..a2270188b864 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -80,6 +80,7 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data) struct nlattr *nla; struct sk_buff *skb; unsigned long flags; + void *msg_header; al = sizeof(struct net_dm_alert_msg); al += dm_hit_limit * sizeof(struct net_dm_drop_point); @@ -87,21 +88,41 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data) skb = genlmsg_new(al, GFP_KERNEL); - if (skb) { - genlmsg_put(skb, 0, 0, &net_drop_monitor_family, - 0, NET_DM_CMD_ALERT); - nla = nla_reserve(skb, NLA_UNSPEC, - sizeof(struct net_dm_alert_msg)); - msg = nla_data(nla); - memset(msg, 0, al); - } else { - mod_timer(&data->send_timer, jiffies + HZ / 10); + if (!skb) + goto err; + + msg_header = genlmsg_put(skb, 0, 0, &net_drop_monitor_family, + 0, NET_DM_CMD_ALERT); + if (!msg_header) { + nlmsg_free(skb); + skb = NULL; + goto err; + } + nla = nla_reserve(skb, NLA_UNSPEC, + sizeof(struct net_dm_alert_msg)); + if (!nla) { + nlmsg_free(skb); + skb = NULL; + goto err; } + msg = nla_data(nla); + memset(msg, 0, al); + goto out; +err: + mod_timer(&data->send_timer, jiffies + HZ / 10); +out: spin_lock_irqsave(&data->lock, flags); swap(data->skb, skb); spin_unlock_irqrestore(&data->lock, flags); + if (skb) { + struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlh); + + genlmsg_end(skb, genlmsg_data(gnlh)); + } + return skb; } diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 3fbd839f6d20..cb744a352167 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -18,6 +18,11 @@ #include <net/fib_rules.h> #include <net/ip_tunnels.h> +static const struct fib_kuid_range fib_kuid_range_unset = { + KUIDT_INIT(0), + KUIDT_INIT(~0), +}; + int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table, u32 flags) { @@ -33,8 +38,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->table = table; r->flags = flags; r->fr_net = ops->fro_net; - r->uid_start = INVALID_UID; - r->uid_end = INVALID_UID; + r->uid_range = fib_kuid_range_unset; r->suppress_prefixlen = -1; r->suppress_ifgroup = -1; @@ -174,21 +178,32 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); -static inline kuid_t fib_nl_uid(struct nlattr *nla) +static int uid_range_set(struct fib_kuid_range *range) { - return make_kuid(current_user_ns(), nla_get_u32(nla)); + return uid_valid(range->start) && uid_valid(range->end); } -static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid) +static struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb) { - return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid)); + struct fib_rule_uid_range *in; + struct fib_kuid_range out; + + in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]); + + out.start = make_kuid(current_user_ns(), in->start); + out.end = make_kuid(current_user_ns(), in->end); + + return out; } -static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) +static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range) { - return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || - (uid_gte(fl->flowi_uid, rule->uid_start) && - uid_lte(fl->flowi_uid, rule->uid_end)); + struct fib_rule_uid_range out = { + from_kuid_munged(current_user_ns(), range->start), + from_kuid_munged(current_user_ns(), range->end) + }; + + return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); } static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, @@ -208,7 +223,8 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if (rule->tun_id && (rule->tun_id != fl->flowi_tun_key.tun_id)) goto out; - if (!fib_uid_range_match(fl, rule)) + if (uid_lt(fl->flowi_uid, rule->uid_range.start) || + uid_gt(fl->flowi_uid, rule->uid_range.end)) goto out; ret = ops->match(rule, fl, flags); @@ -393,17 +409,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) } else if (rule->action == FR_ACT_GOTO) goto errout_free; - /* UID start and end must either both be valid or both unspecified. */ - rule->uid_start = rule->uid_end = INVALID_UID; - if (tb[FRA_UID_START] || tb[FRA_UID_END]) { - if (tb[FRA_UID_START] && tb[FRA_UID_END]) { - rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); - rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); + if (tb[FRA_UID_RANGE]) { + if (current_user_ns() != net->user_ns) { + err = -EPERM; + goto errout_free; } - if (!uid_valid(rule->uid_start) || - !uid_valid(rule->uid_end) || - !uid_lte(rule->uid_start, rule->uid_end)) - goto errout_free; + + rule->uid_range = nla_get_kuid_range(tb); + + if (!uid_range_set(&rule->uid_range) || + !uid_lte(rule->uid_range.start, rule->uid_range.end)) + goto errout_free; + } else { + rule->uid_range = fib_kuid_range_unset; } err = ops->configure(rule, skb, frh, tb); @@ -467,6 +485,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) struct fib_rules_ops *ops = NULL; struct fib_rule *rule, *tmp; struct nlattr *tb[FRA_MAX+1]; + struct fib_kuid_range range; int err = -EINVAL; if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) @@ -486,6 +505,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) if (err < 0) goto errout; + if (tb[FRA_UID_RANGE]) { + range = nla_get_kuid_range(tb); + if (!uid_range_set(&range)) + goto errout; + } else { + range = fib_kuid_range_unset; + } + list_for_each_entry(rule, &ops->rules_list, list) { if (frh->action && (frh->action != rule->action)) continue; @@ -518,12 +545,9 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) (rule->tun_id != nla_get_be64(tb[FRA_TUN_ID]))) continue; - if (tb[FRA_UID_START] && - !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) - continue; - - if (tb[FRA_UID_END] && - !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) + if (uid_range_set(&range) && + (!uid_eq(rule->uid_range.start, range.start) || + !uid_eq(rule->uid_range.end, range.end))) continue; if (!ops->compare(rule, frh, tb)) @@ -592,9 +616,8 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4) /* FRA_FWMASK */ - + nla_total_size(8) /* FRA_TUN_ID */ - + nla_total_size(4) /* FRA_UID_START */ - + nla_total_size(4); /* FRA_UID_END */ + + nla_total_size(8); /* FRA_TUN_ID */ + + nla_total_size(sizeof(struct fib_kuid_range)); if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -653,10 +676,8 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, nla_put_u32(skb, FRA_GOTO, rule->target)) || (rule->tun_id && nla_put_be64(skb, FRA_TUN_ID, rule->tun_id)) || - (uid_valid(rule->uid_start) && - nla_put_uid(skb, FRA_UID_START, rule->uid_start)) || - (uid_valid(rule->uid_end) && - nla_put_uid(skb, FRA_UID_END, rule->uid_end))) + (uid_range_set(&rule->uid_range) && + nla_put_uid_range(skb, &rule->uid_range))) goto nla_put_failure; if (rule->suppress_ifgroup != -1) { diff --git a/net/core/sock.c b/net/core/sock.c index 5ba8a6795cd0..a84a154cdf0c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2396,8 +2396,11 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_type = sock->type; sk->sk_wq = sock->wq; sock->sk = sk; - } else + sk->sk_uid = SOCK_INODE(sock)->i_uid; + } else { sk->sk_wq = NULL; + sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0); + } rwlock_init(&sk->sk_callback_lock); lockdep_set_class_and_name(&sk->sk_callback_lock, diff --git a/net/dccp/input.c b/net/dccp/input.c index 3bd14e885396..dbe2573f6ba1 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -606,7 +606,8 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (inet_csk(sk)->icsk_af_ops->conn_request(sk, skb) < 0) return 1; - goto discard; + consume_skb(skb); + return 0; } if (dh->dccph_type == DCCP_PKT_RESET) goto discard; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 7bc787b095c8..8dfe9fb7ad36 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1101,6 +1101,8 @@ int dsa_slave_suspend(struct net_device *slave_dev) { struct dsa_slave_priv *p = netdev_priv(slave_dev); + netif_device_detach(slave_dev); + if (p->phy) { phy_stop(p->phy); p->old_pause = -1; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index de85d4e1cf43..52dcd414c2af 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -353,6 +353,7 @@ void ether_setup(struct net_device *dev) dev->header_ops = ð_header_ops; dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; + dev->min_header_len = ETH_HLEN; dev->mtu = ETH_DATA_LEN; dev->addr_len = ETH_ALEN; dev->tx_queue_len = 1000; /* Ethernet wants good queues */ diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index bdb2a07ec363..6cc3e1d602fb 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -1657,6 +1657,10 @@ int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option) goto validate_return_locked; } + if (opt_iter + 1 == opt_len) { + err_offset = opt_iter; + goto validate_return_locked; + } tag_len = tag[1]; if (tag_len > (opt_len - opt_iter)) { err_offset = opt_iter + 1; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 98c754e61024..7e30c7b50a28 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -85,7 +85,7 @@ struct fib_table *fib_new_table(struct net *net, u32 id) if (tb) return tb; - if (id == RT_TABLE_LOCAL) + if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules) alias = fib_new_table(net, RT_TABLE_MAIN); tb = fib_trie_table(id, alias); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index ffe95d954007..67d44aa9e09f 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1277,8 +1277,9 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid)) goto nla_put_failure; #endif - if (fi->fib_nh->nh_lwtstate) - lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate); + if (fi->fib_nh->nh_lwtstate && + lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate) < 0) + goto nla_put_failure; } #ifdef CONFIG_IP_ROUTE_MULTIPATH if (fi->fib_nhs > 1) { @@ -1314,8 +1315,10 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid)) goto nla_put_failure; #endif - if (nh->nh_lwtstate) - lwtunnel_fill_encap(skb, nh->nh_lwtstate); + if (nh->nh_lwtstate && + lwtunnel_fill_encap(skb, nh->nh_lwtstate) < 0) + goto nla_put_failure; + /* length of rtnetlink header + attributes */ rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh; } endfor_nexthops(fi); @@ -1588,8 +1591,13 @@ void fib_select_multipath(struct fib_result *res, int hash) void fib_select_path(struct net *net, struct fib_result *res, struct flowi4 *fl4, int mp_hash) { + bool oif_check; + + oif_check = (fl4->flowi4_oif == 0 || + fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF); + #ifdef CONFIG_IP_ROUTE_MULTIPATH - if (res->fi->fib_nhs > 1 && fl4->flowi4_oif == 0) { + if (res->fi->fib_nhs > 1 && oif_check) { if (mp_hash < 0) mp_hash = get_hash_from_flowi4(fl4) >> 1; @@ -1599,7 +1607,7 @@ void fib_select_path(struct net *net, struct fib_result *res, #endif if (!res->prefixlen && res->table->tb_num_default > 1 && - res->type == RTN_UNICAST && !fl4->flowi4_oif) + res->type == RTN_UNICAST && oif_check) fib_select_default(fl4, res); if (!fl4->saddr) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 36e26977c908..ef2d4322aba7 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -425,6 +425,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) fl4.daddr = daddr; fl4.saddr = saddr; fl4.flowi4_mark = mark; + fl4.flowi4_uid = sock_net_uid(net, NULL); fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); fl4.flowi4_proto = IPPROTO_ICMP; fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev); @@ -473,6 +474,7 @@ static struct rtable *icmp_route_lookup(struct net *net, param->replyopts.opt.opt.faddr : iph->saddr); fl4->saddr = saddr; fl4->flowi4_mark = mark; + fl4->flowi4_uid = sock_net_uid(net, NULL); fl4->flowi4_tos = RT_TOS(tos); fl4->flowi4_proto = IPPROTO_ICMP; fl4->fl4_icmp_type = type; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b3086cf27027..17adfdaf5795 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -225,9 +225,14 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) static void igmp_gq_start_timer(struct in_device *in_dev) { int tv = prandom_u32() % in_dev->mr_maxdelay; + unsigned long exp = jiffies + tv + 2; + + if (in_dev->mr_gq_running && + time_after_eq(exp, (in_dev->mr_gq_timer).expires)) + return; in_dev->mr_gq_running = 1; - if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2)) + if (!mod_timer(&in_dev->mr_gq_timer, exp)) in_dev_hold(in_dev); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index dc0b2db33f50..012aa120b090 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -427,7 +427,7 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, ireq->ir_rmt_port, - htons(ireq->ir_num), sock_i_uid((struct sock *)sk)); + htons(ireq->ir_num), sk->sk_uid); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -464,7 +464,7 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, ireq->ir_rmt_port, - htons(ireq->ir_num), sock_i_uid((struct sock *)sk)); + htons(ireq->ir_num), sk->sk_uid); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 9ce202549e7a..f300d1cbfa91 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -105,10 +105,10 @@ static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb, if (skb->ip_summed != CHECKSUM_COMPLETE) return; - if (offset != 0) - csum = csum_sub(csum, - csum_partial(skb->data + tlen, - offset, 0)); + if (offset != 0) { + int tend_off = skb_transport_offset(skb) + tlen; + csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0)); + } put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum); } @@ -1192,7 +1192,14 @@ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) pktinfo->ipi_ifindex = 0; pktinfo->ipi_spec_dst.s_addr = 0; } - skb_dst_drop(skb); + /* We need to keep the dst for __ip_options_echo() + * We could restrict the test to opt.ts_needtime || opt.srr, + * but the following is good enough as IP options are not often used. + */ + if (unlikely(IPCB(skb)->opt.optlen)) + skb_dst_force(skb); + else + skb_dst_drop(skb); } int ip_setsockopt(struct sock *sk, int level, diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 0f443831e851..3a2b21e22629 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -645,6 +645,8 @@ static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, { struct sk_buff *skb = skb_peek(&sk->sk_write_queue); + if (!skb) + return 0; pfh->wcheck = csum_partial((char *)&pfh->icmph, sizeof(struct icmphdr), pfh->wcheck); pfh->icmph.checksum = csum_fold(pfh->wcheck); @@ -796,7 +798,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, - sock_i_uid(sk)); + sk->sk_uid); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index a9b479a1c4a0..6287418c1dfe 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -601,8 +601,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), - daddr, saddr, 0, 0, - sock_i_uid(sk)); + daddr, saddr, 0, 0, sk->sk_uid); if (!saddr && ipc.oif) { err = l3mdev_get_saddr(net, ipc.oif, &fl4); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f75b5658a3a0..6ace04d14e30 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -501,7 +501,8 @@ void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) } EXPORT_SYMBOL(__ip_select_ident); -static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, +static void __build_flow_key(const struct net *net, struct flowi4 *fl4, + const struct sock *sk, const struct iphdr *iph, int oif, u8 tos, u8 prot, u32 mark, int flow_flags) @@ -518,22 +519,23 @@ static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, RT_SCOPE_UNIVERSE, prot, flow_flags, iph->daddr, iph->saddr, 0, 0, - sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID); + sock_net_uid(net, sk)); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, - struct sock *sk) + const struct sock *sk) { + const struct net *net = dev_net(skb->dev); const struct iphdr *iph = ip_hdr(skb); int oif = skb->dev->ifindex; u8 tos = RT_TOS(iph->tos); u8 prot = iph->protocol; u32 mark = skb->mark; - __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0); + __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0); } -static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk) +static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) { const struct inet_sock *inet = inet_sk(sk); const struct ip_options_rcu *inet_opt; @@ -547,12 +549,11 @@ static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk) RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk), - daddr, inet->inet_saddr, 0, 0, - sock_i_uid(sk)); + daddr, inet->inet_saddr, 0, 0, sk->sk_uid); rcu_read_unlock(); } -static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk, +static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, const struct sk_buff *skb) { if (skb) @@ -798,7 +799,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf rt = (struct rtable *) dst; - __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0); + __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0); __ip_do_redirect(rt, skb, &fl4, true); } @@ -1016,7 +1017,7 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, if (!mark) mark = IP4_REPLY_MARK(net, skb->mark); - __build_flow_key(&fl4, NULL, iph, oif, + __build_flow_key(net, &fl4, NULL, iph, oif, RT_TOS(iph->tos), protocol, mark, flow_flags); rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { @@ -1032,7 +1033,7 @@ static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) struct flowi4 fl4; struct rtable *rt; - __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); + __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0); if (!fl4.flowi4_mark) fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark); @@ -1051,6 +1052,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) struct rtable *rt; struct dst_entry *odst = NULL; bool new = false; + struct net *net = sock_net(sk); bh_lock_sock(sk); @@ -1064,7 +1066,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) goto out; } - __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); + __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0); rt = (struct rtable *)odst; if (odst->obsolete && !odst->ops->check(odst, 0)) { @@ -1104,7 +1106,7 @@ void ipv4_redirect(struct sk_buff *skb, struct net *net, struct flowi4 fl4; struct rtable *rt; - __build_flow_key(&fl4, NULL, iph, oif, + __build_flow_key(net, &fl4, NULL, iph, oif, RT_TOS(iph->tos), protocol, mark, flow_flags); rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { @@ -1119,9 +1121,10 @@ void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk) const struct iphdr *iph = (const struct iphdr *) skb->data; struct flowi4 fl4; struct rtable *rt; + struct net *net = sock_net(sk); - __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); - rt = __ip_route_output_key(sock_net(sk), &fl4); + __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0); + rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { __ip_do_redirect(rt, skb, &fl4, false); ip_rt_put(rt); @@ -2432,7 +2435,7 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id, r->rtm_dst_len = 32; r->rtm_src_len = 0; r->rtm_tos = fl4->flowi4_tos; - r->rtm_table = table_id; + r->rtm_table = table_id < 256 ? table_id : RT_TABLE_COMPAT; if (nla_put_u32(skb, RTA_TABLE, table_id)) goto nla_put_failure; r->rtm_type = rt->rt_type; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 31b6a4c9db32..2dc982b15df8 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -374,9 +374,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), - (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr, - ireq->ir_loc_addr, th->source, th->dest, - sock_i_uid(sk)); + opt->srr ? opt->faddr : ireq->ir_rmt_addr, + ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 676566faf649..4ab7735e43ab 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -789,6 +789,12 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, ret = -EAGAIN; break; } + /* if __tcp_splice_read() got nothing while we have + * an skb in receive queue, we do not want to loop. + * This might happen with URG data. + */ + if (!skb_queue_empty(&sk->sk_receive_queue)) + break; sk_wait_data(sk, &timeo, NULL); if (signal_pending(current)) { ret = sock_intr_errno(timeo); diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 55be6ac70cff..fca618272a01 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -112,7 +112,7 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req, struct tcp_fastopen_cookie tmp; if (__tcp_fastopen_cookie_gen(&ip6h->saddr, &tmp)) { - struct in6_addr *buf = (struct in6_addr *) tmp.val; + struct in6_addr *buf = &tmp.addr; int i; for (i = 0; i < 4; i++) @@ -161,6 +161,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk, * scaled. So correct it appropriately. */ tp->snd_wnd = ntohs(tcp_hdr(skb)->window); + tp->max_window = tp->snd_wnd; /* Activate the retrans timer so that SYNACK can be retransmitted. * The request socket is not added to the ehash diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b49a262b06f9..51a77e20f6c6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -687,6 +687,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) arg.bound_dev_if = sk->sk_bound_dev_if; arg.tos = ip_hdr(skb)->tos; + arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL); ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk), skb, &TCP_SKB_CB(skb)->header.h4.opt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, @@ -708,7 +709,7 @@ release_sk1: outside socket context is ugly, certainly. What can I do? */ -static void tcp_v4_send_ack(struct net *net, +static void tcp_v4_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, @@ -723,6 +724,7 @@ static void tcp_v4_send_ack(struct net *net, #endif ]; } rep; + struct net *net = sock_net(sk); struct ip_reply_arg arg; memset(&rep.th, 0, sizeof(struct tcphdr)); @@ -772,6 +774,7 @@ static void tcp_v4_send_ack(struct net *net, if (oif) arg.bound_dev_if = oif; arg.tos = tos; + arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL); ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk), skb, &TCP_SKB_CB(skb)->header.h4.opt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, @@ -785,7 +788,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); - tcp_v4_send_ack(sock_net(sk), skb, + tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp + tcptw->tw_ts_offset, @@ -813,7 +816,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, * exception of <SYN> segments, MUST be right-shifted by * Rcv.Wind.Shift bits: */ - tcp_v4_send_ack(sock_net(sk), skb, seq, + tcp_v4_send_ack(sk, skb, seq, tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, tcp_time_stamp, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ca3731721d81..8a62ad0c850b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2383,9 +2383,11 @@ u32 __tcp_select_window(struct sock *sk) int full_space = min_t(int, tp->window_clamp, allowed_space); int window; - if (mss > full_space) + if (unlikely(mss > full_space)) { mss = full_space; - + if (mss <= 0) + return 0; + } if (free_space < (full_space >> 1)) { icsk->icsk_ack.quick = 0; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 7e37ad9e1a80..254fcc7f1825 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1031,7 +1031,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) RT_SCOPE_UNIVERSE, sk->sk_protocol, flow_flags, faddr, saddr, dport, inet->inet_sport, - sock_i_uid(sk)); + sk->sk_uid); if (!saddr && ipc.oif) { err = l3mdev_get_saddr(net, ipc.oif, fl4); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3ce9df8f86c8..01455f492e17 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5289,8 +5289,7 @@ static void addrconf_disable_change(struct net *net, __s32 newf) struct net_device *dev; struct inet6_dev *idev; - rcu_read_lock(); - for_each_netdev_rcu(net, dev) { + for_each_netdev(net, dev) { idev = __in6_dev_get(dev); if (idev) { int changed = (!idev->cnf.disable_ipv6) ^ (!newf); @@ -5299,7 +5298,6 @@ static void addrconf_disable_change(struct net *net, __s32 newf) dev_disable_change(idev); } } - rcu_read_unlock(); } static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index d9b25bd17bf1..1604163c2850 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -678,7 +678,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); rcu_read_lock(); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index c52b8fc904c9..189eb10b742d 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -662,9 +662,10 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index ea2aa52578db..e9dcd7e587e2 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -161,7 +161,7 @@ ipv4_connected: fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; if (!fl6.flowi6_oif) fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 17df6bbebcff..cbcdd5db31f4 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -474,9 +474,10 @@ static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 41e5c9520c7d..3ae2fbe07b25 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -92,9 +92,10 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) - ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); else if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); if (!(type & ICMPV6_INFOMSG_MASK)) if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) @@ -478,6 +479,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) fl6.flowi6_oif = iif; fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; + fl6.flowi6_uid = sock_net_uid(net, NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); @@ -585,6 +587,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.flowi6_oif = l3mdev_fib_oif(skb->dev); fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; fl6.flowi6_mark = mark; + fl6.flowi6_uid = sock_net_uid(net, NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 897bb6eb5751..dc79ebc14189 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -86,7 +86,7 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk, fl6->flowi6_mark = ireq->ir_mark; fl6->fl6_dport = ireq->ir_rmt_port; fl6->fl6_sport = htons(ireq->ir_num); - fl6->flowi6_uid = sock_i_uid((struct sock *)sk); + fl6->flowi6_uid = sk->sk_uid; security_req_classify_flow(req, flowi6_to_flowi(fl6)); dst = ip6_dst_lookup_flow(sk, fl6, final_p); @@ -135,7 +135,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->flowi6_mark = sk->sk_mark; fl6->fl6_sport = inet->inet_sport; fl6->fl6_dport = inet->inet_dport; - fl6->flowi6_uid = sock_i_uid(sk); + fl6->flowi6_uid = sk->sk_uid; security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); rcu_read_lock(); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 17430f341073..ab0efaca4a78 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -55,6 +55,7 @@ #include <net/ip6_fib.h> #include <net/ip6_route.h> #include <net/ip6_tunnel.h> +#include <net/gre.h> static bool log_ecn_error = true; @@ -367,35 +368,37 @@ static void ip6gre_tunnel_uninit(struct net_device *dev) static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - u8 type, u8 code, int offset, __be32 info) + u8 type, u8 code, int offset, __be32 info) { - const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data; - __be16 *p = (__be16 *)(skb->data + offset); - int grehlen = offset + 4; + const struct gre_base_hdr *greh; + const struct ipv6hdr *ipv6h; + int grehlen = sizeof(*greh); struct ip6_tnl *t; + int key_off = 0; __be16 flags; + __be32 key; - flags = p[0]; - if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) { - if (flags&(GRE_VERSION|GRE_ROUTING)) - return; - if (flags&GRE_KEY) { - grehlen += 4; - if (flags&GRE_CSUM) - grehlen += 4; - } + if (!pskb_may_pull(skb, offset + grehlen)) + return; + greh = (const struct gre_base_hdr *)(skb->data + offset); + flags = greh->flags; + if (flags & (GRE_VERSION | GRE_ROUTING)) + return; + if (flags & GRE_CSUM) + grehlen += 4; + if (flags & GRE_KEY) { + key_off = grehlen + offset; + grehlen += 4; } - /* If only 8 bytes returned, keyed message will be dropped here */ - if (!pskb_may_pull(skb, grehlen)) + if (!pskb_may_pull(skb, offset + grehlen)) return; ipv6h = (const struct ipv6hdr *)skb->data; - p = (__be16 *)(skb->data + offset); + greh = (const struct gre_base_hdr *)(skb->data + offset); + key = key_off ? *(__be32 *)(skb->data + key_off) : 0; t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr, - flags & GRE_KEY ? - *(((__be32 *)p) + (grehlen / 4) - 1) : 0, - p[1]); + key, greh->protocol); if (!t) return; @@ -794,6 +797,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev) if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu); if (err != 0) { /* XXX: send ICMP error even if DF is not set. */ @@ -844,6 +849,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu); if (err != 0) { if (err == -EMSGSIZE) diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 88f3d459bcfd..3feeca6a713d 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -196,6 +196,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, ops = rcu_dereference(inet6_offloads[proto]); if (!ops || !ops->callbacks.gro_receive) { __pskb_pull(skb, skb_gro_offset(skb)); + skb_gro_frag0_invalidate(skb); proto = ipv6_gso_pull_exthdrs(skb, proto); skb_gro_pull(skb, -skb_transport_offset(skb)); skb_reset_transport_header(skb); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 2994d1f1a661..8b11a49c7dd7 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -479,18 +479,19 @@ ip6_tnl_dev_uninit(struct net_device *dev) __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) { - const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; - __u8 nexthdr = ipv6h->nexthdr; - __u16 off = sizeof(*ipv6h); + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw; + unsigned int nhoff = raw - skb->data; + unsigned int off = nhoff + sizeof(*ipv6h); + u8 next, nexthdr = ipv6h->nexthdr; while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { - __u16 optlen = 0; struct ipv6_opt_hdr *hdr; - if (raw + off + sizeof(*hdr) > skb->data && - !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) + u16 optlen; + + if (!pskb_may_pull(skb, off + sizeof(*hdr))) break; - hdr = (struct ipv6_opt_hdr *) (raw + off); + hdr = (struct ipv6_opt_hdr *)(skb->data + off); if (nexthdr == NEXTHDR_FRAGMENT) { struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; if (frag_hdr->frag_off) @@ -501,20 +502,29 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) } else { optlen = ipv6_optlen(hdr); } + /* cache hdr->nexthdr, since pskb_may_pull() might + * invalidate hdr + */ + next = hdr->nexthdr; if (nexthdr == NEXTHDR_DEST) { - __u16 i = off + 2; + u16 i = 2; + + /* Remember : hdr is no longer valid at this point. */ + if (!pskb_may_pull(skb, off + optlen)) + break; + while (1) { struct ipv6_tlv_tnl_enc_lim *tel; /* No more room for encapsulation limit */ - if (i + sizeof (*tel) > off + optlen) + if (i + sizeof(*tel) > optlen) break; - tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; + tel = (struct ipv6_tlv_tnl_enc_lim *)(skb->data + off + i); /* return index of option if found and valid */ if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && tel->length == 1) - return i; + return i + off - nhoff; /* else jump to next option */ if (tel->type) i += tel->length + 2; @@ -522,7 +532,7 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) i++; } } - nexthdr = hdr->nexthdr; + nexthdr = next; off += optlen; } return 0; @@ -1203,6 +1213,8 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_IPIP; + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + dsfield = ipv4_get_dsfield(iph); if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) @@ -1256,6 +1268,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_IPV6; + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); dsfield = ipv6_get_dsfield(ipv6h); if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index c76ebc7fc52d..24fb9c0efd00 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -597,9 +597,10 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index b247baceb797..54d165b9845a 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -74,9 +74,10 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index d11c46833d61..39970e212ad5 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -26,6 +26,7 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb) struct flowi6 fl6 = { .flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, .flowi6_mark = skb->mark, + .flowi6_uid = sock_net_uid(net, skb->sk), .daddr = iph->daddr, .saddr = iph->saddr, }; diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index fa65e92e9510..1737fc0f2988 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -141,7 +141,7 @@ int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.daddr = *daddr; fl6.flowi6_oif = oif; fl6.flowi6_mark = sk->sk_mark; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index d9ad71a01b4c..d503b7f373a3 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -589,7 +589,11 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, } offset += skb_transport_offset(skb); - BUG_ON(skb_copy_bits(skb, offset, &csum, 2)); + err = skb_copy_bits(skb, offset, &csum, 2); + if (err < 0) { + ip6_flush_pending_frames(sk); + goto out; + } /* in case cksum was not initialized */ if (unlikely(csum)) @@ -768,7 +772,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fe9e22ac065a..83ddc8074e55 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1403,7 +1403,7 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) { ip6_update_pmtu(skb, sock_net(sk), mtu, - sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk)); + sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid); } EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); @@ -1484,7 +1484,8 @@ static struct dst_entry *ip6_route_redirect(struct net *net, flags, __ip6_route_redirect); } -void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) +void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, + kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1497,6 +1498,7 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); rt6_do_redirect(dst, NULL, skb); @@ -1518,6 +1520,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, fl6.flowi6_mark = mark; fl6.daddr = msg->dest; fl6.saddr = iph->daddr; + fl6.flowi6_uid = sock_net_uid(net, NULL); dst = ip6_route_redirect(net, &fl6, &iph->saddr); rt6_do_redirect(dst, NULL, skb); @@ -1526,7 +1529,8 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) { - ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); + ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, + sk->sk_uid); } EXPORT_SYMBOL_GPL(ip6_sk_redirect); @@ -3181,7 +3185,8 @@ static int rt6_fill_node(struct net *net, if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) goto nla_put_failure; - lwtunnel_fill_encap(skb, rt->dst.lwtstate); + if (lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0) + goto nla_put_failure; nlmsg_end(skb, nlh); return 0; @@ -3253,6 +3258,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) nla_get_u32(tb[RTA_UID])); else fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); + if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 3da2b16356eb..184f0fe35dc6 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1389,6 +1389,7 @@ static int ipip6_tunnel_init(struct net_device *dev) tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst); if (!tunnel->dst_cache) { free_percpu(dev->tstats); + dev->tstats = NULL; return -ENOMEM; } diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index a22015fab95e..336843ca4e6b 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -228,7 +228,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_mark = ireq->ir_mark; fl6.fl6_dport = ireq->ir_rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3bd3d19b45a0..108b39967694 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -234,7 +234,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); final_p = fl6_update_dst(&fl6, opt, &final); @@ -814,6 +814,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark); fl6.fl6_dport = t1->dest; fl6.fl6_sport = t1->source; + fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); /* Pass a socket to ip6_dst_lookup either it is for RST @@ -975,6 +976,16 @@ drop: return 0; /* don't send reset */ } +static void tcp_v6_restore_cb(struct sk_buff *skb) +{ + /* We need to move header back to the beginning if xfrm6_policy_check() + * and tcp_v6_fill_cb() are going to be called again. + * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there. + */ + memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6, + sizeof(struct inet6_skb_parm)); +} + static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst, @@ -1164,8 +1175,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * sk_gfp_atomic(sk, GFP_ATOMIC)); consume_skb(ireq->pktopts); ireq->pktopts = NULL; - if (newnp->pktoptions) + if (newnp->pktoptions) { + tcp_v6_restore_cb(newnp->pktoptions); skb_set_owner_r(newnp->pktoptions, newsk); + } } } @@ -1180,16 +1193,6 @@ out: return NULL; } -static void tcp_v6_restore_cb(struct sk_buff *skb) -{ - /* We need to move header back to the beginning if xfrm6_policy_check() - * and tcp_v6_fill_cb() are going to be called again. - * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there. - */ - memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6, - sizeof(struct inet6_skb_parm)); -} - /* The socket must have it's spinlock held when we get * here, unless it is a TCP_LISTEN socket. * diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 1207379c1cce..003dd1d040ca 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -958,6 +958,64 @@ discard: return 0; } +static struct sock *__udp6_lib_demux_lookup(struct net *net, + __be16 loc_port, const struct in6_addr *loc_addr, + __be16 rmt_port, const struct in6_addr *rmt_addr, + int dif) +{ + struct sock *sk; + + rcu_read_lock(); + sk = __udp6_lib_lookup(net, rmt_addr, rmt_port, loc_addr, loc_port, + dif, &udp_table); + if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) + sk = NULL; + rcu_read_unlock(); + + return sk; +} + +static void udp_v6_early_demux(struct sk_buff *skb) +{ + struct net *net = dev_net(skb->dev); + const struct udphdr *uh; + struct sock *sk; + struct dst_entry *dst; + int dif = skb->dev->ifindex; + + if (!pskb_may_pull(skb, skb_transport_offset(skb) + + sizeof(struct udphdr))) + return; + + uh = udp_hdr(skb); + + if (skb->pkt_type == PACKET_HOST) + sk = __udp6_lib_demux_lookup(net, uh->dest, + &ipv6_hdr(skb)->daddr, + uh->source, &ipv6_hdr(skb)->saddr, + dif); + else + return; + + if (!sk) + return; + + skb->sk = sk; + skb->destructor = sock_efree; + dst = READ_ONCE(sk->sk_rx_dst); + + if (dst) + dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie); + if (dst) { + if (dst->flags & DST_NOCACHE) { + if (likely(atomic_inc_not_zero(&dst->__refcnt))) + skb_dst_set(skb, dst); + } else { + skb_dst_set_noref(skb, dst); + } + } +} + static __inline__ int udpv6_rcv(struct sk_buff *skb) { return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP); @@ -1244,7 +1302,7 @@ do_udp_sendmsg: fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; - fl6.flowi6_uid = sock_i_uid(sk); + fl6.flowi6_uid = sk->sk_uid; if (msg->msg_controllen) { opt = &opt_space; @@ -1461,6 +1519,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, #endif static const struct inet6_protocol udpv6_protocol = { + .early_demux = udp_v6_early_demux, .handler = udpv6_rcv, .err_handler = udpv6_err, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c index acbe61c7e683..160dc89335e2 100644 --- a/net/irda/irqueue.c +++ b/net/irda/irqueue.c @@ -383,9 +383,6 @@ EXPORT_SYMBOL(hashbin_new); * for deallocating this structure if it's complex. If not the user can * just supply kfree, which should take care of the job. */ -#ifdef CONFIG_LOCKDEP -static int hashbin_lock_depth = 0; -#endif int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) { irda_queue_t* queue; @@ -396,22 +393,27 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;); /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave_nested(&hashbin->hb_spinlock, flags, - hashbin_lock_depth++); - } + if (hashbin->hb_type & HB_LOCK) + spin_lock_irqsave(&hashbin->hb_spinlock, flags); /* * Free the entries in the hashbin, TODO: use hashbin_clear when * it has been shown to work */ for (i = 0; i < HASHBIN_SIZE; i ++ ) { - queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]); - while (queue ) { - if (free_func) - (*free_func)(queue); - queue = dequeue_first( - (irda_queue_t**) &hashbin->hb_queue[i]); + while (1) { + queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]); + + if (!queue) + break; + + if (free_func) { + if (hashbin->hb_type & HB_LOCK) + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + free_func(queue); + if (hashbin->hb_type & HB_LOCK) + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + } } } @@ -420,12 +422,8 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) hashbin->magic = ~HB_MAGIC; /* Release lock */ - if ( hashbin->hb_type & HB_LOCK) { + if (hashbin->hb_type & HB_LOCK) spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); -#ifdef CONFIG_LOCKDEP - hashbin_lock_depth--; -#endif - } /* * Free the hashbin structure diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 5871537af387..763e8e241ce3 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -273,6 +273,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops); void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type); +int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg); /* Session reference counts. Incremented when code obtains a reference * to a session. diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index d0e906d39642..445b7cd0826a 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <asm/ioctls.h> #include <linux/icmp.h> #include <linux/module.h> #include <linux/skbuff.h> @@ -555,6 +556,30 @@ out: return err ? err : copied; } +int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + struct sk_buff *skb; + int amount; + + switch (cmd) { + case SIOCOUTQ: + amount = sk_wmem_alloc_get(sk); + break; + case SIOCINQ: + spin_lock_bh(&sk->sk_receive_queue.lock); + skb = skb_peek(&sk->sk_receive_queue); + amount = skb ? skb->len : 0; + spin_unlock_bh(&sk->sk_receive_queue.lock); + break; + + default: + return -ENOIOCTLCMD; + } + + return put_user(amount, (int __user *)arg); +} +EXPORT_SYMBOL(l2tp_ioctl); + static struct proto l2tp_ip_prot = { .name = "L2TP/IP", .owner = THIS_MODULE, @@ -563,7 +588,7 @@ static struct proto l2tp_ip_prot = { .bind = l2tp_ip_bind, .connect = l2tp_ip_connect, .disconnect = l2tp_ip_disconnect, - .ioctl = udp_ioctl, + .ioctl = l2tp_ioctl, .destroy = l2tp_ip_destroy_sock, .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 3c4f867d3633..c8f483cd2ca9 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -518,6 +518,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sk->sk_uid; if (lsa) { if (addr_len < SIN6_LEN_RFC2133) @@ -714,7 +715,7 @@ static struct proto l2tp_ip6_prot = { .bind = l2tp_ip6_bind, .connect = l2tp_ip6_connect, .disconnect = l2tp_ip6_disconnect, - .ioctl = udp_ioctl, + .ioctl = l2tp_ioctl, .destroy = l2tp_ip6_destroy_sock, .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 3e821daf9dd4..8bc5a1bd2d45 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -821,7 +821,10 @@ void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb) * another trick required to cope with how the PROCOM state * machine works. -acme */ + skb_orphan(skb); + sock_hold(sk); skb->sk = sk; + skb->destructor = sock_efree; } if (!sock_owned_by_user(sk)) llc_conn_rcv(sk, skb); diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index d0e1e804ebd7..5404d0d195cc 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c @@ -290,7 +290,10 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb, ev->type = LLC_SAP_EV_TYPE_PDU; ev->reason = 0; + skb_orphan(skb); + sock_hold(sk); skb->sk = sk; + skb->destructor = sock_efree; llc_sap_state_process(sap, skb); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index f7bb6829b415..9063e8e736ad 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -355,7 +355,7 @@ int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata, /* fast-forward to vendor IEs */ offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); - if (offset) { + if (offset < ifmsh->ie_len) { len = ifmsh->ie_len - offset; data = ifmsh->ie + offset; if (skb_tailroom(skb) < len) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 83097c3832d1..23095d5e0199 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2517,7 +2517,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, } static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, - bool assoc) + bool assoc, bool abandon) { struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; @@ -2539,6 +2539,9 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, mutex_lock(&sdata->local->mtx); ieee80211_vif_release_channel(sdata); mutex_unlock(&sdata->local->mtx); + + if (abandon) + cfg80211_abandon_assoc(sdata->dev, assoc_data->bss); } kfree(assoc_data); @@ -2768,7 +2771,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, bssid, reason_code, ieee80211_get_reason_code_string(reason_code)); - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, true); cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); return; @@ -3173,14 +3176,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied association (code=%d)\n", mgmt->sa, status_code); - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); event.u.mlme.status = MLME_DENIED; event.u.mlme.reason = status_code; drv_event_callback(sdata->local, sdata, &event); } else { if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); cfg80211_assoc_timeout(sdata->dev, bss); return; } @@ -3193,7 +3196,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, * recalc after assoc_data is NULL but before associated * is set can cause the interface to go idle */ - ieee80211_destroy_assoc_data(sdata, true); + ieee80211_destroy_assoc_data(sdata, true, false); /* get uapsd queues configuration */ uapsd_queues = 0; @@ -3888,7 +3891,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) .u.mlme.status = MLME_TIMEOUT, }; - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); cfg80211_assoc_timeout(sdata->dev, bss); drv_event_callback(sdata->local, sdata, &event); } @@ -4029,7 +4032,7 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DEAUTH_LEAVING, false, frame_buf); if (ifmgd->assoc_data) - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, true); if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, @@ -4905,7 +4908,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, frame_buf); - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, true); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, req->reason_code); @@ -4980,7 +4983,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) sdata_lock(sdata); if (ifmgd->assoc_data) { struct cfg80211_bss *bss = ifmgd->assoc_data->bss; - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); cfg80211_assoc_timeout(sdata->dev, bss); } if (ifmgd->auth_data) diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 00a43a70e1fc..0402fa45b343 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -168,6 +168,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) break; } + flush_delayed_work(&sdata->dec_tailroom_needed_wk); drv_remove_interface(local, sdata); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 2022f1cf38e1..a45248a4967b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2705,7 +2705,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); int hw_headroom = sdata->local->hw.extra_tx_headroom; struct ethhdr eth; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info *info; struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; struct ieee80211_tx_data tx; ieee80211_tx_result r; @@ -2767,6 +2767,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); + info = IEEE80211_SKB_CB(skb); memset(info, 0, sizeof(*info)); info->band = fast_tx->band; info->control.vif = &sdata->vif; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index e004067ec24a..ad58d2a6284e 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -501,7 +501,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, /* The conntrack module expects to be working at L3. */ nh_ofs = skb_network_offset(skb); - skb_pull(skb, nh_ofs); + skb_pull_rcsum(skb, nh_ofs); if (key->ip.frag != OVS_FRAG_TYPE_NONE) { err = handle_fragments(net, key, info->zone.id, skb); @@ -527,6 +527,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, &info->labels.mask); err: skb_push(skb, nh_ofs); + skb_postpush_rcsum(skb, skb->data, nh_ofs); if (err) kfree_skb(skb); return err; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index f223d1c80ccf..d805cd577a60 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1497,6 +1497,8 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po) f->arr[f->num_members] = sk; smp_wmb(); f->num_members++; + if (f->num_members == 1) + dev_add_pack(&f->prot_hook); spin_unlock(&f->lock); } @@ -1513,6 +1515,8 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po) BUG_ON(i >= f->num_members); f->arr[i] = f->arr[f->num_members - 1]; f->num_members--; + if (f->num_members == 0) + __dev_remove_pack(&f->prot_hook); spin_unlock(&f->lock); } @@ -1623,6 +1627,7 @@ static void fanout_release_data(struct packet_fanout *f) static int fanout_add(struct sock *sk, u16 id, u16 type_flags) { + struct packet_rollover *rollover = NULL; struct packet_sock *po = pkt_sk(sk); struct packet_fanout *f, *match; u8 type = type_flags & 0xff; @@ -1645,23 +1650,28 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) return -EINVAL; } + mutex_lock(&fanout_mutex); + + err = -EINVAL; if (!po->running) - return -EINVAL; + goto out; + err = -EALREADY; if (po->fanout) - return -EALREADY; + goto out; if (type == PACKET_FANOUT_ROLLOVER || (type_flags & PACKET_FANOUT_FLAG_ROLLOVER)) { - po->rollover = kzalloc(sizeof(*po->rollover), GFP_KERNEL); - if (!po->rollover) - return -ENOMEM; - atomic_long_set(&po->rollover->num, 0); - atomic_long_set(&po->rollover->num_huge, 0); - atomic_long_set(&po->rollover->num_failed, 0); + err = -ENOMEM; + rollover = kzalloc(sizeof(*rollover), GFP_KERNEL); + if (!rollover) + goto out; + atomic_long_set(&rollover->num, 0); + atomic_long_set(&rollover->num_huge, 0); + atomic_long_set(&rollover->num_failed, 0); + po->rollover = rollover; } - mutex_lock(&fanout_mutex); match = NULL; list_for_each_entry(f, &fanout_list, list) { if (f->id == id && @@ -1691,7 +1701,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) match->prot_hook.func = packet_rcv_fanout; match->prot_hook.af_packet_priv = match; match->prot_hook.id_match = match_fanout_group; - dev_add_pack(&match->prot_hook); list_add(&match->list, &fanout_list); } err = -EINVAL; @@ -1708,36 +1717,40 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) } } out: - mutex_unlock(&fanout_mutex); - if (err) { - kfree(po->rollover); + if (err && rollover) { + kfree(rollover); po->rollover = NULL; } + mutex_unlock(&fanout_mutex); return err; } -static void fanout_release(struct sock *sk) +/* If pkt_sk(sk)->fanout->sk_ref is zero, this function removes + * pkt_sk(sk)->fanout from fanout_list and returns pkt_sk(sk)->fanout. + * It is the responsibility of the caller to call fanout_release_data() and + * free the returned packet_fanout (after synchronize_net()) + */ +static struct packet_fanout *fanout_release(struct sock *sk) { struct packet_sock *po = pkt_sk(sk); struct packet_fanout *f; + mutex_lock(&fanout_mutex); f = po->fanout; - if (!f) - return; + if (f) { + po->fanout = NULL; - mutex_lock(&fanout_mutex); - po->fanout = NULL; + if (atomic_dec_and_test(&f->sk_ref)) + list_del(&f->list); + else + f = NULL; - if (atomic_dec_and_test(&f->sk_ref)) { - list_del(&f->list); - dev_remove_pack(&f->prot_hook); - fanout_release_data(f); - kfree(f); + if (po->rollover) + kfree_rcu(po->rollover, rcu); } mutex_unlock(&fanout_mutex); - if (po->rollover) - kfree_rcu(po->rollover, rcu); + return f; } static bool packet_extra_vlan_len_allowed(const struct net_device *dev, @@ -2637,7 +2650,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) int vnet_hdr_len; struct packet_sock *po = pkt_sk(sk); unsigned short gso_type = 0; - int hlen, tlen; + int hlen, tlen, linear; int extra_len = 0; ssize_t n; @@ -2741,8 +2754,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) err = -ENOBUFS; hlen = LL_RESERVED_SPACE(dev); tlen = dev->needed_tailroom; - skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, - __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len), + linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len); + linear = max(linear, min_t(int, len, dev->hard_header_len)); + skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear, msg->msg_flags & MSG_DONTWAIT, &err); if (skb == NULL) goto out_unlock; @@ -2845,6 +2859,7 @@ static int packet_release(struct socket *sock) { struct sock *sk = sock->sk; struct packet_sock *po; + struct packet_fanout *f; struct net *net; union tpacket_req_u req_u; @@ -2884,9 +2899,14 @@ static int packet_release(struct socket *sock) packet_set_ring(sk, &req_u, 1, 1); } - fanout_release(sk); + f = fanout_release(sk); synchronize_net(); + + if (f) { + fanout_release_data(f); + kfree(f); + } /* * Now the socket is dead. No more input will appear. */ @@ -3860,7 +3880,6 @@ static int packet_notifier(struct notifier_block *this, } if (msg == NETDEV_UNREGISTER) { packet_cached_dev_reset(po); - fanout_release(sk); po->ifindex = -1; if (po->prot_hook.dev) dev_put(po->prot_hook.dev); diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c index fb4c60fc2203..fad084d03854 100644 --- a/net/rmnet_data/rmnet_data_config.c +++ b/net/rmnet_data/rmnet_data_config.c @@ -638,6 +638,13 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb) rmnet_header->vnd.vnd_name); break; + case RMNET_NETLINK_NEW_VND_WITH_NAME: + resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; + resp_rmnet->return_code = rmnet_create_vnd_name( + rmnet_header->vnd.id, + rmnet_header->vnd.vnd_name); + break; + case RMNET_NETLINK_FREE_VND: resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; /* Please check rmnet_vnd_free_dev documentation regarding @@ -1087,11 +1094,11 @@ int rmnet_create_vnd(int id) struct net_device *dev; ASSERT_RTNL(); LOGL("(%d);", id); - return rmnet_vnd_create_dev(id, &dev, NULL); + return rmnet_vnd_create_dev(id, &dev, NULL, 0); } /** - * rmnet_create_vnd() - Create virtual network device node + * rmnet_create_vnd_prefix() - Create virtual network device node * @id: RmNet virtual device node id * @prefix: String prefix for device name * @@ -1103,7 +1110,24 @@ int rmnet_create_vnd_prefix(int id, const char *prefix) struct net_device *dev; ASSERT_RTNL(); LOGL("(%d, \"%s\");", id, prefix); - return rmnet_vnd_create_dev(id, &dev, prefix); + return rmnet_vnd_create_dev(id, &dev, prefix, 0); +} + +/** + * rmnet_create_vnd_name() - Create virtual network device node + * @id: RmNet virtual device node id + * @prefix: String prefix for device name + * + * Return: + * - result of rmnet_vnd_create_dev() + */ +int rmnet_create_vnd_name(int id, const char *name) +{ + struct net_device *dev; + + ASSERT_RTNL(); + LOGL("(%d, \"%s\");", id, name); + return rmnet_vnd_create_dev(id, &dev, name, 1); } /** diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h index f19fbb378111..208c3a40c3ae 100644 --- a/net/rmnet_data/rmnet_data_config.h +++ b/net/rmnet_data/rmnet_data_config.h @@ -124,6 +124,7 @@ int rmnet_config_notify_cb(struct notifier_block *nb, unsigned long event, void *data); int rmnet_create_vnd(int id); int rmnet_create_vnd_prefix(int id, const char *name); +int rmnet_create_vnd_name(int id, const char *name); int rmnet_free_vnd(int id); struct rmnet_phys_ep_config *_rmnet_get_phys_ep_config diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c index 2819da9ae3f2..ede1a54661cd 100644 --- a/net/rmnet_data/rmnet_data_vnd.c +++ b/net/rmnet_data/rmnet_data_vnd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -565,7 +565,7 @@ int rmnet_vnd_init(void) * - RMNET_CONFIG_UNKNOWN_ERROR if register_netdevice() fails */ int rmnet_vnd_create_dev(int id, struct net_device **new_device, - const char *prefix) + const char *prefix, int use_name) { struct net_device *dev; char dev_prefix[IFNAMSIZ]; @@ -581,12 +581,15 @@ int rmnet_vnd_create_dev(int id, struct net_device **new_device, return RMNET_CONFIG_DEVICE_IN_USE; } - if (!prefix) + if (!prefix && !use_name) p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", RMNET_DATA_DEV_NAME_STR); + else if (prefix && use_name) + p = scnprintf(dev_prefix, IFNAMSIZ, "%s", prefix); + else if (prefix && !use_name) + p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", prefix); else - p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", - prefix); + return RMNET_CONFIG_BAD_ARGUMENTS; if (p >= (IFNAMSIZ-1)) { LOGE("Specified prefix longer than IFNAMSIZ"); return RMNET_CONFIG_BAD_ARGUMENTS; @@ -594,7 +597,7 @@ int rmnet_vnd_create_dev(int id, struct net_device **new_device, dev = alloc_netdev(sizeof(struct rmnet_vnd_private_s), dev_prefix, - NET_NAME_ENUM, + use_name ? NET_NAME_UNKNOWN : NET_NAME_ENUM, rmnet_vnd_setup); if (!dev) { LOGE("Failed to to allocate netdev for id %d", id); diff --git a/net/rmnet_data/rmnet_data_vnd.h b/net/rmnet_data/rmnet_data_vnd.h index 22ffcfc2e08e..7a1af24fa051 100644 --- a/net/rmnet_data/rmnet_data_vnd.h +++ b/net/rmnet_data/rmnet_data_vnd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,7 +27,7 @@ int rmnet_vnd_do_flow_control(struct net_device *dev, struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev); int rmnet_vnd_get_name(int id, char *name, int name_len); int rmnet_vnd_create_dev(int id, struct net_device **new_device, - const char *prefix); + const char *prefix, int use_name); int rmnet_vnd_free_dev(int id); int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index ecc1904e454f..20b2f867c5a1 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -137,13 +137,15 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n) unsigned long cl; unsigned long fh; int err; - int tp_created = 0; + int tp_created; if ((n->nlmsg_type != RTM_GETTFILTER) && !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) return -EPERM; replay: + tp_created = 0; + err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL); if (err < 0) return err; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index b5fd4ab56156..138f2d667212 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6960,7 +6960,8 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, */ release_sock(sk); current_timeo = schedule_timeout(current_timeo); - BUG_ON(sk != asoc->base.sk); + if (sk != asoc->base.sk) + goto do_error; lock_sock(sk); *timeo_p = current_timeo; diff --git a/net/socket.c b/net/socket.c index 9016cadf8b14..11a2967eaebc 100644 --- a/net/socket.c +++ b/net/socket.c @@ -533,9 +533,23 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer, return used; } +int sockfs_setattr(struct dentry *dentry, struct iattr *iattr) +{ + int err = simple_setattr(dentry, iattr); + + if (!err && (iattr->ia_valid & ATTR_UID)) { + struct socket *sock = SOCKET_I(d_inode(dentry)); + + sock->sk->sk_uid = iattr->ia_uid; + } + + return err; +} + static const struct inode_operations sockfs_inode_ops = { .getxattr = sockfs_getxattr, .listxattr = sockfs_listxattr, + .setattr = sockfs_setattr, }; /** @@ -2231,8 +2245,10 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, return err; err = sock_error(sock->sk); - if (err) + if (err) { + datagrams = err; goto out_put; + } entry = mmsg; compat_entry = (struct compat_mmsghdr __user *)mmsg; diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c index eeeba5adee6d..2410d557ae39 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.c +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c @@ -260,7 +260,7 @@ static int gssx_dec_option_array(struct xdr_stream *xdr, if (!oa->data) return -ENOMEM; - creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL); + creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL); if (!creds) { kfree(oa->data); return -ENOMEM; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 4605dc73def6..033fec307528 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1481,7 +1481,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) case RPC_GSS_PROC_DESTROY: if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) goto auth_err; - rsci->h.expiry_time = get_seconds(); + rsci->h.expiry_time = seconds_since_boot(); set_bit(CACHE_NEGATIVE, &rsci->h.flags); if (resv->iov_len + 4 > PAGE_SIZE) goto drop; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 7a93922457ff..f28aeb2cfd32 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -337,6 +337,11 @@ out: static DEFINE_IDA(rpc_clids); +void rpc_cleanup_clids(void) +{ + ida_destroy(&rpc_clids); +} + static int rpc_alloc_clid(struct rpc_clnt *clnt) { int clid; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index ee5d3d253102..3142f38d1104 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -119,6 +119,7 @@ out: static void __exit cleanup_sunrpc(void) { + rpc_cleanup_clids(); rpcauth_remove_module(); cleanup_socket_xprt(); svc_cleanup_xprt_sock(); diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index ff4f01e527ec..d4e0d648bcea 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -346,8 +346,6 @@ int rdma_read_chunk_frmr(struct svcxprt_rdma *xprt, atomic_inc(&rdma_stat_read); return ret; err: - ib_dma_unmap_sg(xprt->sc_cm_id->device, - frmr->sg, frmr->sg_nents, frmr->direction); svc_rdma_put_context(ctxt, 0); svc_rdma_put_frmr(xprt, frmr); return ret; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 73f75258ce46..b2e934ff2448 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -994,6 +994,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) unsigned int hash; struct unix_address *addr; struct hlist_head *list; + struct path path = { NULL, NULL }; err = -EINVAL; if (sunaddr->sun_family != AF_UNIX) @@ -1009,9 +1010,20 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) goto out; addr_len = err; + if (sun_path[0]) { + umode_t mode = S_IFSOCK | + (SOCK_INODE(sock)->i_mode & ~current_umask()); + err = unix_mknod(sun_path, mode, &path); + if (err) { + if (err == -EEXIST) + err = -EADDRINUSE; + goto out; + } + } + err = mutex_lock_interruptible(&u->bindlock); if (err) - goto out; + goto out_put; err = -EINVAL; if (u->addr) @@ -1028,16 +1040,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) atomic_set(&addr->refcnt, 1); if (sun_path[0]) { - struct path path; - umode_t mode = S_IFSOCK | - (SOCK_INODE(sock)->i_mode & ~current_umask()); - err = unix_mknod(sun_path, mode, &path); - if (err) { - if (err == -EEXIST) - err = -EADDRINUSE; - unix_release_addr(addr); - goto out_up; - } addr->hash = UNIX_HASH_SIZE; hash = d_real_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1); spin_lock(&unix_table_lock); @@ -1064,6 +1066,9 @@ out_unlock: spin_unlock(&unix_table_lock); out_up: mutex_unlock(&u->bindlock); +out_put: + if (err) + path_put(&path); out: return err; } diff --git a/net/wireless/core.h b/net/wireless/core.h index 2ec028ea7775..54865316358e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -400,6 +400,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev); void cfg80211_sme_deauth(struct wireless_dev *wdev); void cfg80211_sme_auth_timeout(struct wireless_dev *wdev); void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev); +void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev); /* internal helpers */ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 89130cf4db04..449e4a31a3ec 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -268,7 +268,7 @@ country CN: DFS-FCC (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (30) + (5735 - 5835 @ 80), (33) # 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm # ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf (57240 - 59400 @ 2160), (28) @@ -743,7 +743,7 @@ country KR: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (20), AUTO-BW (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5490 - 5710 @ 80), (30), DFS + (5490 - 5710 @ 160), (30), DFS (5735 - 5835 @ 80), (30) # 60 GHz band channels 1-4, # ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99 diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 36baa8d22108..1b97f978ccd6 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -150,6 +150,18 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss) } EXPORT_SYMBOL(cfg80211_assoc_timeout); +void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + + cfg80211_sme_abandon_assoc(wdev); + + cfg80211_unhold_bss(bss_from_pub(bss)); + cfg80211_put_bss(wiphy, bss); +} +EXPORT_SYMBOL(cfg80211_abandon_assoc); + void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 08bd3f3d27e3..58b45366c42c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -13577,13 +13577,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb, list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { bool schedule_destroy_work = false; - bool schedule_scan_stop = false; struct cfg80211_sched_scan_request *sched_scan_req = rcu_dereference(rdev->sched_scan_req); if (sched_scan_req && notify->portid && - sched_scan_req->owner_nlportid == notify->portid) - schedule_scan_stop = true; + sched_scan_req->owner_nlportid == notify->portid) { + sched_scan_req->owner_nlportid = 0; + + if (rdev->ops->sched_scan_stop && + rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + schedule_work(&rdev->sched_scan_stop_wk); + } list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { cfg80211_mlme_unregister_socket(wdev, notify->portid); @@ -13614,12 +13618,6 @@ static int nl80211_netlink_notify(struct notifier_block * nb, spin_unlock(&rdev->destroy_list_lock); schedule_work(&rdev->destroy_work); } - } else if (schedule_scan_stop) { - sched_scan_req->owner_nlportid = 0; - - if (rdev->ops->sched_scan_stop && - rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) - schedule_work(&rdev->sched_scan_stop_wk); } } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 9c7f0ce35a06..fe8a9062de98 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -40,6 +40,7 @@ struct cfg80211_conn { CFG80211_CONN_ASSOC_FAILED, CFG80211_CONN_ASSOC_FAILED_TIMEOUT, CFG80211_CONN_DEAUTH, + CFG80211_CONN_ABANDON, CFG80211_CONN_CONNECTED, } state; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; @@ -235,6 +236,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING, false); + /* fall through */ + case CFG80211_CONN_ABANDON: /* free directly, disconnected event already sent */ cfg80211_sme_free(wdev); return 0; @@ -456,6 +459,17 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev) schedule_work(&rdev->conn_work); } +void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + + if (!wdev->conn) + return; + + wdev->conn->state = CFG80211_CONN_ABANDON; + schedule_work(&rdev->conn_work); +} + static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev, const u8 *ies, size_t ies_len, const u8 **out_ies, size_t *out_ies_len) diff --git a/Documentation/mic/mpssd/.gitignore b/samples/mic/mpssd/.gitignore index 8b7c72f07c92..8b7c72f07c92 100644 --- a/Documentation/mic/mpssd/.gitignore +++ b/samples/mic/mpssd/.gitignore diff --git a/samples/mic/mpssd/Makefile b/samples/mic/mpssd/Makefile new file mode 100644 index 000000000000..3e3ef91fed6b --- /dev/null +++ b/samples/mic/mpssd/Makefile @@ -0,0 +1,27 @@ +ifndef CROSS_COMPILE +uname_M := $(shell uname -m 2>/dev/null || echo not) +ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) + +ifeq ($(ARCH),x86) + +PROGS := mpssd +CC = $(CROSS_COMPILE)gcc +CFLAGS := -I../../../usr/include -I../../../tools/include + +ifdef DEBUG +CFLAGS += -DDEBUG=$(DEBUG) +endif + +all: $(PROGS) +mpssd: mpssd.c sysfs.c + $(CC) $(CFLAGS) mpssd.c sysfs.c -o mpssd -lpthread + +install: + install mpssd /usr/sbin/mpssd + install micctrl /usr/sbin/micctrl + +clean: + rm -fr $(PROGS) + +endif +endif diff --git a/Documentation/mic/mpssd/micctrl b/samples/mic/mpssd/micctrl index 8f2629b41c5f..8f2629b41c5f 100755..100644 --- a/Documentation/mic/mpssd/micctrl +++ b/samples/mic/mpssd/micctrl diff --git a/Documentation/mic/mpssd/mpss b/samples/mic/mpssd/mpss index 09ea90931649..09ea90931649 100755..100644 --- a/Documentation/mic/mpssd/mpss +++ b/samples/mic/mpssd/mpss diff --git a/Documentation/mic/mpssd/mpssd.c b/samples/mic/mpssd/mpssd.c index c99a75968c01..c99a75968c01 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/samples/mic/mpssd/mpssd.c diff --git a/Documentation/mic/mpssd/mpssd.h b/samples/mic/mpssd/mpssd.h index 8bd64944aacc..8bd64944aacc 100644 --- a/Documentation/mic/mpssd/mpssd.h +++ b/samples/mic/mpssd/mpssd.h diff --git a/Documentation/mic/mpssd/sysfs.c b/samples/mic/mpssd/sysfs.c index 8dd326936083..8dd326936083 100644 --- a/Documentation/mic/mpssd/sysfs.c +++ b/samples/mic/mpssd/sysfs.c diff --git a/samples/seccomp/bpf-helper.h b/samples/seccomp/bpf-helper.h index 38ee70f3cd5b..1d8de9edd858 100644 --- a/samples/seccomp/bpf-helper.h +++ b/samples/seccomp/bpf-helper.h @@ -138,7 +138,7 @@ union arg64 { #define ARG_32(idx) \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)) -/* Loads hi into A and lo in X */ +/* Loads lo into M[0] and hi into M[1] and A */ #define ARG_64(idx) \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \ BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \ @@ -153,88 +153,107 @@ union arg64 { BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \ jt -/* Checks the lo, then swaps to check the hi. A=lo,X=hi */ +#define JA32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ + jt + +#define JGE32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ + jt + +#define JGT32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ + jt + +#define JLE32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ + jt + +#define JLT32(value, jt) \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \ + jt + +/* + * All the JXX64 checks assume lo is saved in M[0] and hi is saved in both + * A and M[1]. This invariant is kept by restoring A if necessary. + */ #define JEQ64(lo, hi, jt) \ + /* if (hi != arg.hi) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + /* if (lo != arg.lo) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) #define JNE64(lo, hi, jt) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + /* if (hi != arg.hi) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo != arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ - -#define JA32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ - jt + BPF_STMT(BPF_LD+BPF_MEM, 1) #define JA64(lo, hi, jt) \ + /* if (hi & arg.hi) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo & arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) -#define JGE32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ - jt - -#define JLT32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \ - jt - -/* Shortcut checking if hi > arg.hi. */ #define JGE64(lo, hi, jt) \ + /* if (hi > arg.hi) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo >= arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ - jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ - -#define JLT64(lo, hi, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) -#define JGT32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ - jt - -#define JLE32(value, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ - jt - -/* Check hi > args.hi first, then do the GE checking */ #define JGT64(lo, hi, jt) \ + /* if (hi > arg.hi) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo > arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) #define JLE64(lo, hi, jt) \ - BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ - BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ + /* if (hi < arg.hi) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo <= arg.lo) goto MATCH; */ \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ - BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ + jt, \ + BPF_STMT(BPF_LD+BPF_MEM, 1) + +#define JLT64(lo, hi, jt) \ + /* if (hi < arg.hi) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ + /* if (hi != arg.hi) goto NOMATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ + BPF_STMT(BPF_LD+BPF_MEM, 0), \ + /* if (lo < arg.lo) goto MATCH; */ \ + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 2, 0), \ + BPF_STMT(BPF_LD+BPF_MEM, 1), \ jt, \ - BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ + BPF_STMT(BPF_LD+BPF_MEM, 1) #define LOAD_SYSCALL_NR \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 8275f0e55106..4b2f44c20caf 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -364,12 +364,14 @@ int dialog_inputbox(WINDOW *main_window, WINDOW *prompt_win; WINDOW *form_win; PANEL *panel; - int i, x, y; + int i, x, y, lines, columns, win_lines, win_cols; int res = -1; int cursor_position = strlen(init); int cursor_form_win; char *result = *resultp; + getmaxyx(stdscr, lines, columns); + if (strlen(init)+1 > *result_len) { *result_len = strlen(init)+1; *resultp = result = realloc(result, *result_len); @@ -386,14 +388,19 @@ int dialog_inputbox(WINDOW *main_window, if (title) prompt_width = max(prompt_width, strlen(title)); + win_lines = min(prompt_lines+6, lines-2); + win_cols = min(prompt_width+7, columns-2); + prompt_lines = max(win_lines-6, 0); + prompt_width = max(win_cols-7, 0); + /* place dialog in middle of screen */ - y = (getmaxy(stdscr)-(prompt_lines+4))/2; - x = (getmaxx(stdscr)-(prompt_width+4))/2; + y = (lines-win_lines)/2; + x = (columns-win_cols)/2; strncpy(result, init, *result_len); /* create the windows */ - win = newwin(prompt_lines+6, prompt_width+7, y, x); + win = newwin(win_lines, win_cols, y, x); prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2); form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2); keypad(form_win, TRUE); diff --git a/security/inode.c b/security/inode.c index 16622aef9bde..0f1a041bf6cb 100644 --- a/security/inode.c +++ b/security/inode.c @@ -100,7 +100,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode, dir = d_inode(parent); mutex_lock(&dir->i_mutex); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len2(name, mount, parent, strlen(name)); if (IS_ERR(dentry)) goto out; diff --git a/security/pfe/pfk_ice.c b/security/pfe/pfk_ice.c index 2f09d8801a96..facecedc3827 100644 --- a/security/pfe/pfk_ice.c +++ b/security/pfe/pfk_ice.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -64,7 +64,8 @@ uint8_t ice_key[ICE_KEY_SIZE]; uint8_t ice_salt[ICE_KEY_SIZE]; -int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt) +int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt, + char *storage_type) { struct scm_desc desc = {0}; int ret; @@ -84,6 +85,9 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt) if (!tzbuf_key || !tzbuf_salt) return -ENOMEM; + if (storage_type == NULL) + return -EINVAL; + memset(tzbuf_key, 0, tzbuflen_key); memset(tzbuf_salt, 0, tzbuflen_salt); @@ -103,7 +107,8 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt) desc.args[3] = virt_to_phys(tzbuf_salt); desc.args[4] = tzbuflen_salt; - ret = qcom_ice_setup_ice_hw("ufs", true); + ret = qcom_ice_setup_ice_hw((const char *)storage_type, true); + if (ret) { pr_err("%s: could not enable clocks: 0x%x\n", __func__, ret); return ret; @@ -111,7 +116,7 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt) ret = scm_call2(smc_id, &desc); - qcom_ice_setup_ice_hw("ufs", false); + ret = qcom_ice_setup_ice_hw((const char *)storage_type, false); pr_debug(" %s , ret = %d\n", __func__, ret); if (ret) { @@ -127,7 +132,7 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt) } -int qti_pfk_ice_invalidate_key(uint32_t index) +int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type) { struct scm_desc desc = {0}; int ret; @@ -137,13 +142,17 @@ int qti_pfk_ice_invalidate_key(uint32_t index) if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX) return -EINVAL; + if (storage_type == NULL) + return -EINVAL; + smc_id = TZ_ES_INVALIDATE_ICE_KEY_ID; pr_debug(" %s , smc_id = 0x%x\n", __func__, smc_id); desc.arginfo = TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID; desc.args[0] = index; - ret = qcom_ice_setup_ice_hw("ufs", true); + ret = qcom_ice_setup_ice_hw((const char *)storage_type, true); + if (ret) { pr_err("%s: could not enable clocks: 0x%x\n", __func__, ret); return ret; @@ -151,7 +160,7 @@ int qti_pfk_ice_invalidate_key(uint32_t index) ret = scm_call2(smc_id, &desc); - qcom_ice_setup_ice_hw("ufs", false); + ret = qcom_ice_setup_ice_hw((const char *)storage_type, false); pr_debug(" %s , ret = %d\n", __func__, ret); if (ret) diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h index fe3f2ad3950c..fb7c0d142953 100644 --- a/security/pfe/pfk_ice.h +++ b/security/pfe/pfk_ice.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,8 +25,9 @@ int pfk_ice_init(void); int pfk_ice_deinit(void); -int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt); -int qti_pfk_ice_invalidate_key(uint32_t index); +int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt, + char *storage_type); +int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type); #endif /* PFK_ICE_H_ */ diff --git a/security/pfe/pfk_kc.c b/security/pfe/pfk_kc.c index 39e67569a1dd..3a12dd172d04 100644 --- a/security/pfe/pfk_kc.c +++ b/security/pfe/pfk_kc.c @@ -23,6 +23,7 @@ * Empty entries always have the oldest timestamp. */ +#include <linux/module.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include <crypto/ice.h> @@ -51,10 +52,12 @@ /** The maximum key and salt size */ #define PFK_MAX_KEY_SIZE PFK_KC_KEY_SIZE #define PFK_MAX_SALT_SIZE PFK_KC_SALT_SIZE +#define PFK_UFS "ufs" static DEFINE_SPINLOCK(kc_lock); static unsigned long flags; static bool kc_ready; +static char *s_type = "sdcc"; /** * enum pfk_kc_entry_state - state of the entry inside kc table @@ -404,7 +407,7 @@ static int kc_update_entry(struct kc_entry *entry, const unsigned char *key, kc_spin_unlock(); ret = qti_pfk_ice_set_key(entry->key_index, entry->key, - entry->salt); + entry->salt, s_type); kc_spin_lock(); return ret; @@ -524,7 +527,8 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size, kc_update_timestamp(entry); entry->state = ACTIVE_ICE_LOADED; - if (async) + if (async && (!strcmp(s_type, + (char *)PFK_UFS))) entry->loaded_ref_cnt++; break; @@ -544,7 +548,8 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size, * sync calls from within work thread do not pass * requests further to HW */ - if (async) + if (async && (!strcmp(s_type, + (char *)PFK_UFS))) entry->loaded_ref_cnt++; } @@ -556,9 +561,9 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size, case (ACTIVE_ICE_LOADED): kc_update_timestamp(entry); - if (async) + if (async && (!strcmp(s_type, + (char *)PFK_UFS))) entry->loaded_ref_cnt++; - break; case(SCM_ERROR): ret = entry->scm_error; @@ -616,24 +621,36 @@ void pfk_kc_load_key_end(const unsigned char *key, size_t key_size, return; } - ref_cnt = --entry->loaded_ref_cnt; + if (!strcmp(s_type, (char *)PFK_UFS)) { + ref_cnt = --entry->loaded_ref_cnt; - if (ref_cnt < 0) - pr_err("internal error, ref count should never be negative\n"); + if (ref_cnt < 0) + pr_err("internal error, ref count should never be negative\n"); - if (!ref_cnt) { + if (!ref_cnt) { + entry->state = INACTIVE; + /* + * wake-up invalidation if it's waiting + * for the entry to be released + */ + if (entry->thread_pending) { + tmp_pending = entry->thread_pending; + entry->thread_pending = NULL; + + kc_spin_unlock(); + wake_up_process(tmp_pending); + return; + } + } + } else { entry->state = INACTIVE; /* * wake-up invalidation if it's waiting * for the entry to be released - */ + */ if (entry->thread_pending) { - tmp_pending = entry->thread_pending; + wake_up_process(entry->thread_pending); entry->thread_pending = NULL; - - kc_spin_unlock(); - wake_up_process(tmp_pending); - return; } } @@ -689,7 +706,7 @@ int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size, kc_spin_unlock(); - qti_pfk_ice_invalidate_key(entry->key_index); + qti_pfk_ice_invalidate_key(entry->key_index, s_type); kc_spin_lock(); kc_entry_finish_invalidating(entry); @@ -771,7 +788,8 @@ int pfk_kc_remove_key(const unsigned char *key, size_t key_size) temp_indexes_size--; for (i = temp_indexes_size; i >= 0 ; i--) qti_pfk_ice_invalidate_key( - kc_entry_at_index(temp_indexes[i])->key_index); + kc_entry_at_index(temp_indexes[i])->key_index, + s_type); /* fall through */ res = 0; @@ -814,7 +832,8 @@ int pfk_kc_clear(void) kc_spin_unlock(); for (i = 0; i < PFK_KC_TABLE_SIZE; i++) - qti_pfk_ice_invalidate_key(kc_entry_at_index(i)->key_index); + qti_pfk_ice_invalidate_key(kc_entry_at_index(i)->key_index, + s_type); /* fall through */ res = 0; @@ -850,3 +869,36 @@ void pfk_kc_clear_on_reset(void) } kc_spin_unlock(); } + +static int pfk_kc_find_storage_type(char **device) +{ + char boot[20] = {'\0'}; + char *match = (char *)strnstr(saved_command_line, + "androidboot.bootdevice=", + strlen(saved_command_line)); + if (match) { + memcpy(boot, (match + strlen("androidboot.bootdevice=")), + sizeof(boot) - 1); + if (strnstr(boot, PFK_UFS, strlen(boot))) + *device = PFK_UFS; + + return 0; + } + return -EINVAL; +} + +static int __init pfk_kc_pre_init(void) +{ + return pfk_kc_find_storage_type(&s_type); +} + +static void __exit pfk_kc_exit(void) +{ + s_type = NULL; +} + +module_init(pfk_kc_pre_init); +module_exit(pfk_kc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Per-File-Key-KC driver"); diff --git a/security/pfe/pfk_kc.h b/security/pfe/pfk_kc.h index 0b0ec8825c15..dc4ad15b359d 100644 --- a/security/pfe/pfk_kc.h +++ b/security/pfe/pfk_kc.h @@ -27,7 +27,7 @@ int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size, int pfk_kc_remove_key(const unsigned char *key, size_t key_size); int pfk_kc_clear(void); void pfk_kc_clear_on_reset(void); - +extern char *saved_command_line; #endif /* PFK_KC_H_ */ diff --git a/security/security.c b/security/security.c index 8eece6cd01da..b636dd5caa77 100644 --- a/security/security.c +++ b/security/security.c @@ -498,6 +498,7 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) return 0; return call_int_hook(path_chown, 0, path, uid, gid); } +EXPORT_SYMBOL(security_path_chown); int security_path_chroot(struct path *path) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 96b2e3d521a6..5ab9d1e3e2b8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5672,7 +5672,7 @@ static int selinux_setprocattr(struct task_struct *p, return error; /* Obtain a SID for the context, if one was specified. */ - if (size && str[1] && str[1] != '\n') { + if (size && str[0] && str[0] != '\n') { if (str[size-1] == '\n') { str[size-1] = 0; size--; diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 1d5acbe0c08b..86240d02b530 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -135,6 +135,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, f->tail = cell; if (f->head == NULL) f->head = cell; + cell->next = NULL; f->cells++; spin_unlock_irqrestore(&f->lock, flags); @@ -214,6 +215,8 @@ void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, spin_lock_irqsave(&f->lock, flags); cell->next = f->head; f->head = cell; + if (!f->tail) + f->tail = cell; f->cells++; spin_unlock_irqrestore(&f->lock, flags); } diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index c850345c43b5..dfa5156f3585 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -419,7 +419,6 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) { unsigned long flags; struct snd_seq_event_cell *ptr; - int max_count = 5 * HZ; if (snd_BUG_ON(!pool)) return -EINVAL; @@ -432,14 +431,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) if (waitqueue_active(&pool->output_sleep)) wake_up(&pool->output_sleep); - while (atomic_read(&pool->counter) > 0) { - if (max_count == 0) { - pr_warn("ALSA: snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); - break; - } + while (atomic_read(&pool->counter) > 0) schedule_timeout_uninterruptible(1); - max_count--; - } /* release all resources */ spin_lock_irqsave(&pool->lock, flags); diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 0bec02e89d51..450c5187eecb 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -181,6 +181,8 @@ void __exit snd_seq_queues_delete(void) } } +static void queue_use(struct snd_seq_queue *queue, int client, int use); + /* allocate a new queue - * return queue index value or negative value for error */ @@ -192,11 +194,11 @@ int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags) if (q == NULL) return -ENOMEM; q->info_flags = info_flags; + queue_use(q, client, 1); if (queue_list_add(q) < 0) { queue_delete(q); return -ENOMEM; } - snd_seq_queue_use(q->queue, client, 1); /* use this queue */ return q->queue; } @@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client, return result; } - -/* use or unuse this queue - - * if it is the first client, starts the timer. - * if it is not longer used by any clients, stop the timer. - */ -int snd_seq_queue_use(int queueid, int client, int use) +/* use or unuse this queue */ +static void queue_use(struct snd_seq_queue *queue, int client, int use) { - struct snd_seq_queue *queue; - - queue = queueptr(queueid); - if (queue == NULL) - return -EINVAL; - mutex_lock(&queue->timer_mutex); if (use) { if (!test_and_set_bit(client, queue->clients_bitmap)) queue->clients++; @@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use) } else { snd_seq_timer_close(queue); } +} + +/* use or unuse this queue - + * if it is the first client, starts the timer. + * if it is not longer used by any clients, stop the timer. + */ +int snd_seq_queue_use(int queueid, int client, int use) +{ + struct snd_seq_queue *queue; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + mutex_lock(&queue->timer_mutex); + queue_use(queue, client, use); mutex_unlock(&queue->timer_mutex); queuefree(queue); return 0; diff --git a/sound/core/timer.c b/sound/core/timer.c index 05a31df05c00..30b28e80c6e6 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1704,9 +1704,21 @@ static int snd_timer_user_params(struct file *file, return -EBADFD; if (copy_from_user(¶ms, _params, sizeof(params))) return -EFAULT; - if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { - err = -EINVAL; - goto _end; + if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { + u64 resolution; + + if (params.ticks < 1) { + err = -EINVAL; + goto _end; + } + + /* Don't allow resolution less than 1ms */ + resolution = snd_timer_resolution(tu->timeri); + resolution *= params.ticks; + if (resolution < 1000000) { + err = -EINVAL; + goto _end; + } } if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index 0e6dd5c61f53..e4c306398b35 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -343,7 +343,7 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) if (err < 0) amdtp_stream_destroy(&tscm->rx_stream); - return 0; + return err; } /* At bus reset, streaming is stopped and some registers are clear. */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 9667cbfb0ca2..ab4cdab5cfa5 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -27,12 +27,6 @@ #include "cthw20k1.h" #include "ct20k1reg.h" -#if BITS_PER_LONG == 32 -#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ -#else -#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ -#endif - struct hw20k1 { struct hw hw; spinlock_t reg_20k1_lock; @@ -1904,19 +1898,18 @@ static int hw_card_start(struct hw *hw) { int err; struct pci_dev *pci = hw->pci; + const unsigned int dma_bits = BITS_PER_LONG; err = pci_enable_device(pci); if (err < 0) return err; /* Set DMA transfer mask */ - if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 || - dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) { - dev_err(hw->card->dev, - "architecture does not support PCI busmaster DMA with mask 0x%llx\n", - CT_XFI_DMA_MASK); - err = -ENXIO; - goto error1; + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits)); + } else { + dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); } if (!hw->io_base) { diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 9dc2950e1ab7..d86678c2a957 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -26,12 +26,6 @@ #include "cthw20k2.h" #include "ct20k2reg.h" -#if BITS_PER_LONG == 32 -#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ -#else -#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ -#endif - struct hw20k2 { struct hw hw; /* for i2c */ @@ -2029,19 +2023,18 @@ static int hw_card_start(struct hw *hw) int err = 0; struct pci_dev *pci = hw->pci; unsigned int gctl; + const unsigned int dma_bits = BITS_PER_LONG; err = pci_enable_device(pci); if (err < 0) return err; /* Set DMA transfer mask */ - if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 || - dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) { - dev_err(hw->card->dev, - "architecture does not support PCI busmaster DMA with mask 0x%llx\n", - CT_XFI_DMA_MASK); - err = -ENXIO; - goto error1; + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits)); + } else { + dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); } if (!hw->io_base) { diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7f57a145a47e..a03cf68d0bcd 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -884,6 +884,8 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) } EXPORT_SYMBOL_GPL(snd_hda_apply_fixup); +#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC)) + static bool pin_config_match(struct hda_codec *codec, const struct hda_pintbl *pins) { @@ -901,7 +903,7 @@ static bool pin_config_match(struct hda_codec *codec, for (; t_pins->nid; t_pins++) { if (t_pins->nid == nid) { found = 1; - if (t_pins->val == cfg) + if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC)) break; else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000) break; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index ad4a1e9a3ae1..8f3e5e9d8bdb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2208,9 +2208,9 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Lewisburg */ { PCI_DEVICE(0x8086, 0xa1f0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, { PCI_DEVICE(0x8086, 0xa270), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9ceb2bc36e68..c146d0de53d8 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -780,6 +780,7 @@ static const struct hda_pintbl alienware_pincfgs[] = { static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE), + SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE), {} }; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 36cd715986bc..46f7b023f69c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -262,6 +262,7 @@ enum { CXT_FIXUP_CAP_MIX_AMP_5047, CXT_FIXUP_MUTE_LED_EAPD, CXT_FIXUP_HP_SPECTRE, + CXT_FIXUP_HP_GATE_MIC, }; /* for hda_fixup_thinkpad_acpi() */ @@ -633,6 +634,17 @@ static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec, (1 << AC_AMPCAP_MUTE_SHIFT)); } +static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + /* the mic pin (0x19) doesn't give an unsolicited event; + * probe the mic pin together with the headphone pin (0x16) + */ + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_jack_set_gating_jack(codec, 0x19, 0x16); +} + /* ThinkPad X200 & co with cxt5051 */ static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ @@ -774,6 +786,10 @@ static const struct hda_fixup cxt_fixups[] = { { } } }, + [CXT_FIXUP_HP_GATE_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_hp_gate_mic_jack, + }, }; static const struct snd_pci_quirk cxt5045_fixups[] = { @@ -824,6 +840,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC), SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC), SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), + SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC), SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f0986cac82f1..cf0785ddbd14 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2230,6 +2230,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), + SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), @@ -5559,6 +5560,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -5673,6 +5675,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), @@ -5899,6 +5902,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60180}, {0x14, 0x90170120}, {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x1b, 0x01011020}, + {0x21, 0x02211010}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, {0x14, 0x90170120}, @@ -6043,6 +6049,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170150}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME, + {0x12, 0xb7a60140}, + {0x13, 0xb7a60150}, + {0x17, 0x90170110}, + {0x1a, 0x03011020}, + {0x21, 0x03211030}), {} }; @@ -6889,6 +6901,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/sound/soc/codecs/audio-ext-clk-up.c index 6de88aff0dd4..cb230f38db22 100644 --- a/sound/soc/codecs/audio-ext-clk-up.c +++ b/sound/soc/codecs/audio-ext-clk-up.c @@ -34,6 +34,7 @@ struct pinctrl_info { struct pinctrl *pinctrl; struct pinctrl_state *sleep; struct pinctrl_state *active; + char __iomem *base; }; struct audio_ext_ap_clk { @@ -192,8 +193,10 @@ static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw) pr_err("%s afe_set_digital_codec_core_clock failed\n", __func__); return ret; - } + } + if (pnctrl_info->base) + iowrite32(1, pnctrl_info->base); return 0; } @@ -219,6 +222,8 @@ static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw) if (ret < 0) pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n", __func__, ret); + if (pnctrl_info->base) + iowrite32(0, pnctrl_info->base); } static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw) @@ -381,9 +386,11 @@ static struct clk_hw *audio_msm_hws1[] = { static int audio_get_pinctrl(struct platform_device *pdev, enum audio_clk_mux mux) { + struct device *dev = &pdev->dev; struct pinctrl_info *pnctrl_info; struct pinctrl *pinctrl; int ret; + u32 reg; switch (mux) { case AP_CLK2: @@ -396,21 +403,20 @@ static int audio_get_pinctrl(struct platform_device *pdev, pnctrl_info = &audio_lpass_mclk2.pnctrl_info; break; default: - dev_err(&pdev->dev, "%s Not a valid MUX ID: %d\n", + dev_err(dev, "%s Not a valid MUX ID: %d\n", __func__, mux); return -EINVAL; } - pnctrl_info = &audio_ap_clk2.pnctrl_info; if (pnctrl_info->pinctrl) { - dev_dbg(&pdev->dev, "%s: already requested before\n", + dev_dbg(dev, "%s: already requested before\n", __func__); return -EINVAL; } - pinctrl = devm_pinctrl_get(&pdev->dev); + pinctrl = devm_pinctrl_get(dev); if (IS_ERR_OR_NULL(pinctrl)) { - dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n", + dev_dbg(dev, "%s: Unable to get pinctrl handle\n", __func__); return -EINVAL; } @@ -418,13 +424,13 @@ static int audio_get_pinctrl(struct platform_device *pdev, /* get all state handles from Device Tree */ pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); if (IS_ERR(pnctrl_info->sleep)) { - dev_err(&pdev->dev, "%s: could not get sleep pinstate\n", + dev_err(dev, "%s: could not get sleep pinstate\n", __func__); goto err; } pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); if (IS_ERR(pnctrl_info->active)) { - dev_err(&pdev->dev, "%s: could not get active pinstate\n", + dev_err(dev, "%s: could not get active pinstate\n", __func__); goto err; } @@ -432,10 +438,22 @@ static int audio_get_pinctrl(struct platform_device *pdev, ret = pinctrl_select_state(pnctrl_info->pinctrl, pnctrl_info->sleep); if (ret) { - dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n", + dev_err(dev, "%s: Disable TLMM pins failed with %d\n", __func__, ret); goto err; } + + ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", ®); + if (ret < 0) { + dev_dbg(dev, "miss mclk reg\n"); + } else { + pnctrl_info->base = ioremap(reg, sizeof(u32)); + if (pnctrl_info->base == NULL) { + dev_err(dev, "%s ioremap failed\n", __func__); + goto err; + } + } + return 0; err: diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c index 6b267122dc08..db723e5ec1f4 100644 --- a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c @@ -854,8 +854,8 @@ static int msm_sdw_config_compander(struct snd_soc_codec *codec, int comp, if (!msm_sdw->comp_enabled[comp]) return 0; - comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 8); - rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 20); + comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 0x20); + rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 0x1E0); if (SND_SOC_DAPM_EVENT_ON(event)) { /* Enable Compander Clock */ @@ -1044,7 +1044,7 @@ static int msm_sdw_swrm_read(void *handle, int reg) * Add sleep as SWR slave access read takes time. * Allow for RD_DONE to complete for previous register if any. */ - usleep_range(50, 55); + usleep_range(100, 105); /* read_lock */ mutex_lock(&msm_sdw->sdw_read_lock); @@ -1079,6 +1079,11 @@ static int msm_sdw_bulk_write(struct msm_sdw_priv *msm_sdw, sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + /* + * Add sleep as SWR slave write takes time. + * Allow for any previous pending write to complete. + */ + usleep_range(50, 55); for (i = 0; i < len; i += 2) { /* First Write the Data to register */ ret = regmap_bulk_write(msm_sdw->regmap, @@ -1656,18 +1661,24 @@ static int msm_sdw_notifier_service_cb(struct notifier_block *nb, service_nb); bool adsp_ready = false; unsigned long timeout; + static bool initial_boot = true; pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); mutex_lock(&msm_sdw->codec_mutex); switch (opcode) { case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) + break; + msm_sdw->int_mclk1_enabled = false; msm_sdw->dev_up = false; for (i = 0; i < msm_sdw->nr; i++) swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, SWR_DEVICE_DOWN, NULL); break; case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; if (!q6core_is_adsp_ready()) { dev_dbg(msm_sdw->dev, "ADSP isn't ready\n"); timeout = jiffies + diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index e5e71939f529..699e7251023f 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -37,9 +37,10 @@ #define DRV_NAME "pmic_analog_codec" #define SDM660_CDC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ - SNDRV_PCM_RATE_48000) + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) #define SDM660_CDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE) #define MSM_DIG_CDC_STRING_LEN 80 #define MSM_ANLG_CDC_VERSION_ENTRY_SIZE 32 @@ -1435,11 +1436,11 @@ static int msm_anlg_cdc_codec_enable_clock_block(struct snd_soc_codec *codec, if (enable) { snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30); + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON); snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C); - msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON); } else { snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00); @@ -3180,7 +3181,7 @@ static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = { .name = "msm_anlg_cdc_i2s_rx1", .id = AIF1_PB, .playback = { - .stream_name = "Playback", + .stream_name = "PDM Playback", .rates = SDM660_CDC_RATES, .formats = SDM660_CDC_FORMATS, .rate_max = 192000, @@ -3194,7 +3195,7 @@ static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = { .name = "msm_anlg_cdc_i2s_tx1", .id = AIF1_CAP, .capture = { - .stream_name = "Record", + .stream_name = "PDM Capture", .rates = SDM660_CDC_RATES, .formats = SDM660_CDC_FORMATS, .rate_max = 48000, @@ -3759,8 +3760,8 @@ static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec) snd_soc_write(codec, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93); - atomic_set(&pdata->int_mclk0_enabled, false); msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_DOWN); + atomic_set(&pdata->int_mclk0_enabled, false); set_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask); snd_soc_card_change_online_state(codec->component.card, 0); @@ -3796,6 +3797,9 @@ static int msm_anlg_cdc_device_up(struct snd_soc_codec *codec) msm_anlg_cdc_configure_cap(codec, false, false); wcd_mbhc_stop(&sdm660_cdc_priv->mbhc); wcd_mbhc_deinit(&sdm660_cdc_priv->mbhc); + /* Disable mechanical detection and set type to insertion */ + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, + 0xA0, 0x20); ret = wcd_mbhc_init(&sdm660_cdc_priv->mbhc, codec, &mbhc_cb, &intr_ids, wcd_mbhc_registers, true); if (ret) @@ -3818,17 +3822,22 @@ static int sdm660_cdc_notifier_service_cb(struct notifier_block *nb, bool adsp_ready = false; bool timedout; unsigned long timeout; + static bool initial_boot = true; codec = sdm660_cdc_priv->codec; dev_dbg(codec->dev, "%s: Service opcode 0x%lx\n", __func__, opcode); switch (opcode) { case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) + break; dev_dbg(codec->dev, "ADSP is about to power down. teardown/reset codec\n"); msm_anlg_cdc_device_down(codec); break; case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; dev_dbg(codec->dev, "ADSP is about to power up. bring up codec\n"); @@ -4158,6 +4167,8 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) snd_soc_dapm_ignore_suspend(dapm, "PDM Playback"); snd_soc_dapm_ignore_suspend(dapm, "PDM Capture"); + snd_soc_dapm_sync(dapm); + return 0; } diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c index 91faee1ffd32..c6074570bb50 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -1028,7 +1028,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block, break; case DIG_CDC_EVENT_PRE_RX1_INT_ON: snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x14); + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x28); snd_soc_update_bits(codec, MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10); snd_soc_update_bits(codec, @@ -1036,7 +1036,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block, break; case DIG_CDC_EVENT_PRE_RX2_INT_ON: snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x14); + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x28); snd_soc_update_bits(codec, MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10); snd_soc_update_bits(codec, @@ -1044,7 +1044,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block, break; case DIG_CDC_EVENT_POST_RX1_INT_OFF: snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x00); + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x00); snd_soc_update_bits(codec, MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF); snd_soc_update_bits(codec, @@ -1052,7 +1052,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block, break; case DIG_CDC_EVENT_POST_RX2_INT_OFF: snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x00); + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x00); snd_soc_update_bits(codec, MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF); snd_soc_update_bits(codec, @@ -1207,6 +1207,8 @@ static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX2"); snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX3"); + snd_soc_dapm_sync(dapm); + return 0; } @@ -1929,8 +1931,12 @@ static struct snd_soc_dai_driver msm_codec_dais[] = { .stream_name = "AIF1 Playback", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_max = 192000, + .rate_min = 8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, }, .ops = &msm_dig_dai_ops, }, @@ -2012,7 +2018,7 @@ static struct snd_soc_codec_driver soc_msm_dig_codec = { const struct regmap_config msm_digital_regmap_config = { .reg_bits = 32, .reg_stride = 4, - .val_bits = 32, + .val_bits = 8, .lock = enable_digital_callback, .unlock = disable_digital_callback, .cache_type = REGCACHE_FLAT, @@ -2085,10 +2091,18 @@ static int msm_dig_cdc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int msm_dig_suspend(struct device *dev) { - struct msm_asoc_mach_data *pdata = - snd_soc_card_get_drvdata(registered_digcodec->component.card); + struct msm_asoc_mach_data *pdata; struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + if (!registered_digcodec || !msm_dig_cdc) { + pr_debug("%s:digcodec 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__); + return 0; + } if (msm_dig_cdc->dapm_bias_off) { pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n", __func__, atomic_read(&pdata->int_mclk0_rsc_ref), @@ -2115,8 +2129,8 @@ static int msm_dig_resume(struct device *dev) } static const struct dev_pm_ops msm_dig_pm_ops = { - .suspend = msm_dig_suspend, - .resume = msm_dig_resume, + .suspend_late = msm_dig_suspend, + .resume_early = msm_dig_resume, }; #endif diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index e6e40d1d6a8f..4b98d1ee0ecd 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -2953,6 +2953,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc); if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier) mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); mutex_destroy(&mbhc->codec_resource_lock); mutex_destroy(&mbhc->hphl_pa_lock); mutex_destroy(&mbhc->hphr_pa_lock); diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c index b03a8a9caed7..3d2fe2c6bed9 100644 --- a/sound/soc/codecs/wcd-spi.c +++ b/sound/soc/codecs/wcd-spi.c @@ -81,8 +81,15 @@ #define WCD_SPI_WORD_BYTE_CNT (4) #define WCD_SPI_RW_MULTI_MIN_LEN (16) -/* Max size is closest multiple of 16 less than 64Kbytes */ -#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 16) +/* Max size is 32 bytes less than 64Kbytes */ +#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 32) + +/* + * Max size for the pre-allocated buffers is the max + * possible read/write length + 32 bytes for the SPI + * read/write command header itself. + */ +#define WCD_SPI_RW_MAX_BUF_SIZE (WCD_SPI_RW_MULTI_MAX_LEN + 32) /* Alignment requirements */ #define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT @@ -148,6 +155,10 @@ struct wcd_spi_priv { /* Completion object to indicate system resume completion */ struct completion resume_comp; + + /* Buffers to hold memory used for transfers */ + void *tx_buf; + void *rx_buf; }; enum xfer_request { @@ -229,17 +240,18 @@ static int wcd_spi_read_single(struct spi_device *spi, struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; - u8 *tx_buf; + u8 *tx_buf = wcd_spi->tx_buf; u32 frame = 0; int ret; dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n", __func__, remote_addr); - tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN, - GFP_KERNEL | GFP_DMA); - if (!tx_buf) + if (!tx_buf) { + dev_err(&spi->dev, "%s: tx_buf not allocated\n", + __func__); return -ENOMEM; + } frame |= WCD_SPI_READ_FRAME_OPCODE; frame |= remote_addr & WCD_CMD_ADDR_MASK; @@ -255,7 +267,6 @@ static int wcd_spi_read_single(struct spi_device *spi, rx_xfer->len = sizeof(*val); ret = spi_sync(spi, &wcd_spi->msg2); - kfree(tx_buf); return ret; } @@ -266,8 +277,8 @@ static int wcd_spi_read_multi(struct spi_device *spi, { struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); struct spi_transfer *xfer = &wcd_spi->xfer1; - u8 *tx_buf; - u8 *rx_buf; + u8 *tx_buf = wcd_spi->tx_buf; + u8 *rx_buf = wcd_spi->rx_buf; u32 frame = 0; int ret; @@ -277,15 +288,9 @@ static int wcd_spi_read_multi(struct spi_device *spi, frame |= WCD_SPI_FREAD_FRAME_OPCODE; frame |= remote_addr & WCD_CMD_ADDR_MASK; - tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, - GFP_KERNEL | GFP_DMA); - if (!tx_buf) - return -ENOMEM; - - rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, - GFP_KERNEL | GFP_DMA); - if (!rx_buf) { - kfree(tx_buf); + if (!tx_buf || !rx_buf) { + dev_err(&spi->dev, "%s: %s not allocated\n", __func__, + (!tx_buf) ? "tx_buf" : "rx_buf"); return -ENOMEM; } @@ -305,8 +310,6 @@ static int wcd_spi_read_multi(struct spi_device *spi, memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len); done: - kfree(tx_buf); - kfree(rx_buf); return ret; } @@ -343,7 +346,7 @@ static int wcd_spi_write_multi(struct spi_device *spi, struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); struct spi_transfer *xfer = &wcd_spi->xfer1; u32 frame = 0; - u8 *tx_buf; + u8 *tx_buf = wcd_spi->tx_buf; int xfer_len, ret; dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n", @@ -355,9 +358,11 @@ static int wcd_spi_write_multi(struct spi_device *spi, frame = cpu_to_be32(frame); xfer_len = len + sizeof(frame); - tx_buf = kzalloc(xfer_len, GFP_KERNEL); - if (!tx_buf) + if (!tx_buf) { + dev_err(&spi->dev, "%s: tx_buf not allocated\n", + __func__); return -ENOMEM; + } memcpy(tx_buf, &frame, sizeof(frame)); memcpy(tx_buf + sizeof(frame), data, len); @@ -371,8 +376,6 @@ static int wcd_spi_write_multi(struct spi_device *spi, dev_err(&spi->dev, "%s: Failed, addr = 0x%x, len = %zd\n", __func__, remote_addr, len); - kfree(tx_buf); - return ret; } @@ -1330,6 +1333,23 @@ static int wcd_spi_component_bind(struct device *dev, spi_message_init(&wcd_spi->msg2); spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2); spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); + + /* Pre-allocate the buffers */ + wcd_spi->tx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!wcd_spi->tx_buf) { + ret = -ENOMEM; + goto done; + } + + wcd_spi->rx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!wcd_spi->rx_buf) { + kfree(wcd_spi->tx_buf); + wcd_spi->tx_buf = NULL; + ret = -ENOMEM; + goto done; + } done: return ret; } @@ -1347,6 +1367,11 @@ static void wcd_spi_component_unbind(struct device *dev, spi_transfer_del(&wcd_spi->xfer1); spi_transfer_del(&wcd_spi->xfer2[0]); spi_transfer_del(&wcd_spi->xfer2[1]); + + kfree(wcd_spi->tx_buf); + kfree(wcd_spi->rx_buf); + wcd_spi->tx_buf = NULL; + wcd_spi->rx_buf = NULL; } static const struct component_ops wcd_spi_component_ops = { diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 676c3b0335ef..eaaca97e2b8e 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -104,6 +104,7 @@ struct wsa881x_priv { int state; struct delayed_work ocp_ctl_work; struct device_node *wsa_rst_np; + int pa_mute; }; #define SWR_SLV_MAX_REG_ADDR 0x390 @@ -171,9 +172,41 @@ static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol, return 0; } -static const struct snd_kcontrol_new wsa_analog_gain_controls[] = { +static int wsa881x_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->pa_mute; + + return 0; +} + +static int wsa881x_set_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: mute current %d, new %d\n", + __func__, wsa881x->pa_mute, value); + + if (value) + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00); + wsa881x->pa_mute = value; + + return 0; +} + + +static const struct snd_kcontrol_new wsa_snd_controls[] = { SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum, wsa_pa_gain_get, wsa_pa_gain_put), + SOC_SINGLE_EXT("WSA PA Mute", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_mute, wsa881x_set_mute), }; static int codec_debug_open(struct inode *inode, struct file *file) @@ -1050,8 +1083,8 @@ static int wsa881x_probe(struct snd_soc_codec *codec) wsa881x->tz_pdata.codec = codec; wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read; wsa881x_init_thermal(&wsa881x->tz_pdata); - snd_soc_add_codec_controls(codec, wsa_analog_gain_controls, - ARRAY_SIZE(wsa_analog_gain_controls)); + snd_soc_add_codec_controls(codec, wsa_snd_controls, + ARRAY_SIZE(wsa_snd_controls)); INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work); return 0; } diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 0487cfaac538..2b96b11fbe71 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -762,6 +762,9 @@ static int sst_soc_prepare(struct device *dev) struct sst_data *drv = dev_get_drvdata(dev); int i; + if (!drv->soc_card) + return 0; + /* suspend all pcms first */ snd_soc_suspend(drv->soc_card->dev); snd_soc_poweroff(drv->soc_card->dev); @@ -784,6 +787,9 @@ static void sst_soc_complete(struct device *dev) struct sst_data *drv = dev_get_drvdata(dev); int i; + if (!drv->soc_card) + return; + /* restart SSPs */ for (i = 0; i < drv->soc_card->num_rtd; i++) { struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index cd63b491285c..63c4e61430c4 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -13,7 +13,7 @@ snd-soc-cpe-objs := msm-cpe-lsm.o obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o # for MSM8996 sound card driver -snd-soc-msm8996-objs := msm8996.o +snd-soc-msm8996-objs := msm8996.o apq8096-auto.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o # for MSM8998 sound card driver diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c new file mode 100644 index 000000000000..138f4a02452c --- /dev/null +++ b/sound/soc/msm/apq8096-auto.c @@ -0,0 +1,4458 @@ +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/switch.h> +#include <linux/input.h> +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <sound/jack.h> +#include <sound/q6afe-v2.h> +#include <sound/q6adm-v2.h> +#include <sound/q6core.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <device_event.h> +#include "qdsp6v2/msm-pcm-routing-v2.h" + +#define DRV_NAME "apq8096-auto-asoc-snd" + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_384KHZ 384000 + +static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_auxpcm_rate = SAMPLING_RATE_8KHZ; +static int msm_hdmi_rx_ch = 2; +static int msm_proxy_rx_ch = 2; +static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int msm_sec_mi2s_tx_ch = 2; +static int msm_tert_mi2s_tx_ch = 2; +static int msm_quat_mi2s_rx_ch = 2; +static int msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +/* TDM default channels */ +static int msm_sec_tdm_tx_0_ch = 2; /* STEREO MIC */ +static int msm_sec_tdm_tx_1_ch = 2; +static int msm_sec_tdm_tx_2_ch = 2; +static int msm_sec_tdm_tx_3_ch = 2; + +static int msm_tert_tdm_rx_0_ch = 2; /* ICC STREAM */ +static int msm_tert_tdm_rx_1_ch = 2; +static int msm_tert_tdm_rx_2_ch = 2; +static int msm_tert_tdm_rx_3_ch = 2; + +static int msm_tert_tdm_tx_0_ch = 6; /* EC_REF1-EC_REF6(6 CHAN) */ +static int msm_tert_tdm_tx_1_ch = 1; +static int msm_tert_tdm_tx_2_ch = 1; +static int msm_tert_tdm_tx_3_ch; + +static int msm_quat_tdm_rx_0_ch = 6; /* ENT */ +static int msm_quat_tdm_rx_1_ch = 1; /* ANN */ +static int msm_quat_tdm_rx_2_ch = 1; /* TEL */ +static int msm_quat_tdm_rx_3_ch; + +static int msm_quat_tdm_tx_0_ch = 4; /*MIC1-MIC4*/ +static int msm_quat_tdm_tx_1_ch = 2; /*EC_REF(2 CHAN)*/ +static int msm_quat_tdm_tx_2_ch = 2; /*ENT RECORD*/ +static int msm_quat_tdm_tx_3_ch; + +/* TDM default bit format */ +static int msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +static int msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +static int msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +/* EC Reference default values are set in mixer_paths.xml */ +static int msm_ec_ref_ch = 4; +static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ; + +static void *adsp_state_notifier; +static bool dummy_device_registered; + +enum { + QUATERNARY_TDM_RX_0, + QUATERNARY_TDM_RX_1, + QUATERNARY_TDM_RX_2, + QUATERNARY_TDM_RX_3, + QUATERNARY_TDM_RX_4, + QUATERNARY_TDM_RX_5, + QUATERNARY_TDM_RX_6, + QUATERNARY_TDM_RX_7, + QUATERNARY_TDM_TX_0, + QUATERNARY_TDM_TX_1, + QUATERNARY_TDM_TX_2, + QUATERNARY_TDM_TX_3, + QUATERNARY_TDM_TX_4, + QUATERNARY_TDM_TX_5, + QUATERNARY_TDM_TX_6, + QUATERNARY_TDM_TX_7, + TERTIARY_TDM_RX_0, + TERTIARY_TDM_RX_1, + TERTIARY_TDM_RX_2, + TERTIARY_TDM_RX_3, + TERTIARY_TDM_RX_4, + TERTIARY_TDM_RX_5, + TERTIARY_TDM_RX_6, + TERTIARY_TDM_RX_7, + TERTIARY_TDM_TX_0, + TERTIARY_TDM_TX_1, + TERTIARY_TDM_TX_2, + TERTIARY_TDM_TX_3, + TERTIARY_TDM_TX_4, + TERTIARY_TDM_TX_5, + TERTIARY_TDM_TX_6, + TERTIARY_TDM_TX_7, + SECONDARY_TDM_RX_0, + SECONDARY_TDM_RX_1, + SECONDARY_TDM_RX_2, + SECONDARY_TDM_RX_3, + SECONDARY_TDM_RX_4, + SECONDARY_TDM_RX_5, + SECONDARY_TDM_RX_6, + SECONDARY_TDM_RX_7, + SECONDARY_TDM_TX_0, + SECONDARY_TDM_TX_1, + SECONDARY_TDM_TX_2, + SECONDARY_TDM_TX_3, + SECONDARY_TDM_TX_4, + SECONDARY_TDM_TX_5, + SECONDARY_TDM_TX_6, + SECONDARY_TDM_TX_7, + TDM_MAX, +}; + +#define TDM_SLOT_OFFSET_MAX 8 + +/* TDM default offset */ +static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = { + /* QUAT_TDM_RX */ + {0, 4, 8, 12, 16, 20, 0xFFFF}, + {24, 0xFFFF}, + {28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* QUAT_TDM_TX */ + {0, 4, 8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* TERT_TDM_RX */ + {0, 4, 8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* TERT_TDM_TX */ + {0, 4, 8, 12, 16, 20, 0xFFFF}, + {24, 0xFFFF}, + {28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* SEC_TDM_RX */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* SEC_TDM_TX */ + {0, 4, 0xFFFF}, + {8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ +}; + + +/*************************************************************************** +* Codec/Platform specific tdm slot offset table +* NOTE: +* each entry represents the slot offset array of one backend tdm device +* valid offset represents the starting offset in byte for the channel +* use 0xFFFF for end or unused slot offset entry +***************************************************************************/ +static unsigned int tdm_slot_offset_adp_mmxf[TDM_MAX][TDM_SLOT_OFFSET_MAX] = { + /* QUAT_TDM_RX */ + {2, 5, 8, 11, 14, 17, 20, 23}, + {26, 0xFFFF}, + {28, 0xFFFF}, + {30, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* QUAT_TDM_TX */ + {2, 0xFFFF}, + {10, 0xFFFF}, + {20, 22, 26, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* TERT_TDM_RX */ + {2, 5, 8, 11, 14, 17, 20, 23}, + {26, 0xFFFF}, + {28, 0xFFFF}, + {30, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* TERT_TDM_TX */ + {2, 0xFFFF}, + {10, 0xFFFF}, + {20, 22, 26, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* SEC_TDM_RX */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + /* SEC_TDM_TX */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ +}; + + +static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; + +static char const *hdmi_rx_bit_format_text[] = {"S16_LE", "S24_LE"}; + +static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; + +static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192"}; + +static const char *const auxpcm_rate_text[] = {"8000", "16000"}; + +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; + +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE"}; + +static char const *mi2s_bit_format_text[] = {"S16_LE", "S24_LE"}; + +static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three", + "Four", "Five", "Six", "Seven", "Eight"}; + +static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"}; + +static const char *const ec_ref_rate_text[] = {"0", "8000", "16000", + "32000", "44100", "48000", "96000", "192000", "384000"}; + +static struct afe_clk_set sec_mi2s_tx_clk = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_IBIT_CLK_DISABLE, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set mi2s_tx_clk = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_IBIT_CLK_DISABLE, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set mi2s_rx_clk = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (hdmi_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, hdmi_rx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, hdmi_rx_bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, + msm_hdmi_rx_ch); + ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2; + + return 0; +} + +static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2; + if (msm_hdmi_rx_ch > 8) { + pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n", + __func__, msm_hdmi_rx_ch); + msm_hdmi_rx_ch = 8; + } + pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch); + + return 1; +} + +static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (hdmi_rx_sample_rate) { + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__, + hdmi_rx_sample_rate); + + return 0; +} + +static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 2: + hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ; + } + + pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__, + hdmi_rx_sample_rate); + + return 0; +} + +static int msm_auxpcm_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_auxpcm_rate; + return 0; +} + +static int msm_auxpcm_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_auxpcm_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + msm_auxpcm_rate = SAMPLING_RATE_16KHZ; + break; + default: + msm_auxpcm_rate = SAMPLING_RATE_8KHZ; + break; + } + return 0; +} + +static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch); + ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1; + return 0; +} + +static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch); + return 1; +} + +static int msm_quat_mi2s_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_mi2s_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_mi2s_rx_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_mi2s_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_mi2s_rx_bit_format = %d\n", + __func__, msm_quat_mi2s_rx_bit_format); + return 0; +} + +static int msm_tert_mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_mi2s_tx_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_mi2s_tx_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_mi2s_tx_bit_format = %d\n", + __func__, msm_tert_mi2s_tx_bit_format); + return 0; +} + +static int msm_sec_mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_mi2s_tx_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_sec_mi2s_tx_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_sec_mi2s_tx_bit_format = %d\n", + __func__, msm_sec_mi2s_tx_bit_format); + return 0; +} + +static int msm_sec_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__, + msm_sec_tdm_tx_0_ch); + ucontrol->value.integer.value[0] = msm_sec_tdm_tx_0_ch - 1; + return 0; +} + +static int msm_sec_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_sec_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__, + msm_sec_tdm_tx_0_ch); + return 0; +} + +static int msm_sec_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__, + msm_sec_tdm_tx_1_ch); + ucontrol->value.integer.value[0] = msm_sec_tdm_tx_1_ch - 1; + return 0; +} + +static int msm_sec_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_sec_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__, + msm_sec_tdm_tx_1_ch); + return 0; +} + +static int msm_sec_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__, + msm_sec_tdm_tx_2_ch); + ucontrol->value.integer.value[0] = msm_sec_tdm_tx_2_ch - 1; + return 0; +} + +static int msm_sec_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_sec_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__, + msm_sec_tdm_tx_2_ch); + return 0; +} + +static int msm_sec_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__, + msm_sec_tdm_tx_3_ch); + ucontrol->value.integer.value[0] = msm_sec_tdm_tx_3_ch - 1; + return 0; +} + +static int msm_sec_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_sec_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__, + msm_sec_tdm_tx_3_ch); + return 0; +} + +static int msm_tert_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__, + msm_tert_tdm_rx_0_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_rx_0_ch - 1; + return 0; +} + +static int msm_tert_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__, + msm_tert_tdm_rx_0_ch); + return 0; +} + +static int msm_tert_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__, + msm_tert_tdm_rx_1_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_rx_1_ch - 1; + return 0; +} + +static int msm_tert_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__, + msm_tert_tdm_rx_1_ch); + return 0; +} + +static int msm_tert_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__, + msm_tert_tdm_rx_2_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_rx_2_ch - 1; + return 0; +} + +static int msm_tert_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__, + msm_tert_tdm_rx_2_ch); + return 0; +} + +static int msm_tert_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__, + msm_tert_tdm_rx_3_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_rx_3_ch - 1; + return 0; +} + +static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__, + msm_tert_tdm_rx_3_ch); + return 0; +} + +static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__, + msm_tert_tdm_tx_0_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_tx_0_ch - 1; + return 0; +} + +static int msm_tert_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__, + msm_tert_tdm_tx_0_ch); + return 0; +} + +static int msm_tert_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__, + msm_tert_tdm_tx_1_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_tx_1_ch - 1; + return 0; +} + +static int msm_tert_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__, + msm_tert_tdm_tx_1_ch); + return 0; +} + +static int msm_tert_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__, + msm_tert_tdm_tx_2_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_tx_2_ch - 1; + return 0; +} + +static int msm_tert_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__, + msm_tert_tdm_tx_2_ch); + return 0; +} + +static int msm_tert_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__, + msm_tert_tdm_tx_3_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_tx_3_ch - 1; + return 0; +} + +static int msm_tert_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__, + msm_tert_tdm_tx_3_ch); + return 0; +} + +static int msm_quat_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__, + msm_quat_tdm_rx_0_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_rx_0_ch - 1; + return 0; +} + +static int msm_quat_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__, + msm_quat_tdm_rx_0_ch); + return 0; +} + +static int msm_quat_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__, + msm_quat_tdm_rx_1_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_rx_1_ch - 1; + return 0; +} + +static int msm_quat_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__, + msm_quat_tdm_rx_1_ch); + return 0; +} + +static int msm_quat_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__, + msm_quat_tdm_rx_2_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_rx_2_ch - 1; + return 0; +} + +static int msm_quat_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__, + msm_quat_tdm_rx_2_ch); + return 0; +} + +static int msm_quat_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__, + msm_quat_tdm_rx_3_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_rx_3_ch - 1; + return 0; +} + +static int msm_quat_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__, + msm_quat_tdm_rx_3_ch); + return 0; +} + +static int msm_quat_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__, + msm_quat_tdm_tx_0_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_tx_0_ch - 1; + return 0; +} + +static int msm_quat_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__, + msm_quat_tdm_tx_0_ch); + return 0; +} + +static int msm_quat_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__, + msm_quat_tdm_tx_1_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_tx_1_ch - 1; + return 0; +} + +static int msm_quat_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__, + msm_quat_tdm_tx_1_ch); + return 0; +} + +static int msm_quat_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__, + msm_quat_tdm_tx_2_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_tx_2_ch - 1; + return 0; +} + +static int msm_quat_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__, + msm_quat_tdm_tx_2_ch); + return 0; +} + +static int msm_quat_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__, + msm_quat_tdm_tx_3_ch); + ucontrol->value.integer.value[0] = msm_quat_tdm_tx_3_ch - 1; + return 0; +} + +static int msm_quat_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_quat_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__, + msm_quat_tdm_tx_3_ch); + return 0; +} + +static int msm_sec_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_tx_0_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %d\n", + __func__, msm_sec_tdm_tx_0_bit_format); + return 0; +} + +static int msm_sec_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_tx_1_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %d\n", + __func__, msm_sec_tdm_tx_1_bit_format); + return 0; +} + +static int msm_sec_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_tx_2_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %d\n", + __func__, msm_sec_tdm_tx_2_bit_format); + return 0; +} + +static int msm_sec_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_tx_3_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %d\n", + __func__, msm_sec_tdm_tx_3_bit_format); + return 0; +} + +static int msm_tert_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_rx_0_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %d\n", + __func__, msm_tert_tdm_rx_0_bit_format); + return 0; +} + +static int msm_tert_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_rx_1_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %d\n", + __func__, msm_tert_tdm_rx_1_bit_format); + return 0; +} + +static int msm_tert_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_rx_2_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %d\n", + __func__, msm_tert_tdm_rx_2_bit_format); + return 0; +} + +static int msm_tert_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_rx_3_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %d\n", + __func__, msm_tert_tdm_rx_3_bit_format); + return 0; +} + +static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_tx_0_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %d\n", + __func__, msm_tert_tdm_tx_0_bit_format); + return 0; +} + +static int msm_tert_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_tx_1_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %d\n", + __func__, msm_tert_tdm_tx_1_bit_format); + return 0; +} + +static int msm_tert_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_tx_2_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %d\n", + __func__, msm_tert_tdm_tx_2_bit_format); + return 0; +} + +static int msm_tert_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_tx_3_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %d\n", + __func__, msm_tert_tdm_tx_3_bit_format); + return 0; +} + +static int msm_quat_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_rx_0_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %d\n", + __func__, msm_quat_tdm_rx_0_bit_format); + return 0; +} + +static int msm_quat_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_rx_1_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %d\n", + __func__, msm_quat_tdm_rx_1_bit_format); + return 0; +} + +static int msm_quat_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_rx_2_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %d\n", + __func__, msm_quat_tdm_rx_2_bit_format); + return 0; +} + +static int msm_quat_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_rx_3_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %d\n", + __func__, msm_quat_tdm_rx_3_bit_format); + return 0; +} + +static int msm_quat_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_tx_0_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %d\n", + __func__, msm_quat_tdm_tx_0_bit_format); + return 0; +} + +static int msm_quat_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_tx_1_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %d\n", + __func__, msm_quat_tdm_tx_1_bit_format); + return 0; +} + +static int msm_quat_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_tx_2_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %d\n", + __func__, msm_quat_tdm_tx_2_bit_format); + return 0; +} + +static int msm_quat_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_quat_tdm_tx_3_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_quat_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %d\n", + __func__, msm_quat_tdm_tx_3_bit_format); + return 0; +} + +static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_ec_ref_ch; + pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_ec_ref_ch = ucontrol->value.integer.value[0]; + pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch); + adm_num_ec_ref_rx_chans(msm_ec_ref_ch); + return 0; +} + +static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_ec_ref_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S16_LE: + ucontrol->value.integer.value[0] = 1; + break; + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_ec_ref_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 1: + msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + default: + msm_ec_ref_bit_format = 0; + break; + } + pr_debug("%s: msm_ec_ref_bit_format = %d\n", + __func__, msm_ec_ref_bit_format); + adm_ec_ref_rx_bit_width(msm_ec_ref_bit_format); + return 0; +} + +static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate; + pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_ec_ref_sampling_rate = 0; + break; + case 1: + msm_ec_ref_sampling_rate = SAMPLING_RATE_8KHZ; + break; + case 2: + msm_ec_ref_sampling_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + msm_ec_ref_sampling_rate = SAMPLING_RATE_32KHZ; + break; + case 4: + msm_ec_ref_sampling_rate = SAMPLING_RATE_44P1KHZ; + break; + case 5: + msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ; + break; + case 6: + msm_ec_ref_sampling_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + msm_ec_ref_sampling_rate = SAMPLING_RATE_192KHZ; + break; + case 8: + msm_ec_ref_sampling_rate = SAMPLING_RATE_384KHZ; + break; + default: + msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("%s: msm_ec_ref_sampling_rate = %d\n", + __func__, msm_ec_ref_sampling_rate); + adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate); + return 0; +} + +static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = msm_auxpcm_rate; + channels->min = channels->max = 1; + + return 0; +} + +static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch); + + if (channels->max < 2) + channels->min = channels->max = 2; + channels->min = channels->max = msm_proxy_rx_ch; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + return 0; +} + +static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + rate->min = rate->max = SAMPLING_RATE_48KHZ; + return 0; +} + +static int msm_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s channels->min %u channels->max %u ()\n", __func__, + channels->min, channels->max); + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + hdmi_rx_bit_format); + if (channels->max < 2) + channels->min = channels->max = 2; + rate->min = rate->max = hdmi_rx_sample_rate; + channels->min = channels->max = msm_hdmi_rx_ch; + + return 0; +} + +static int msm_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: channel:%d\n", __func__, msm_quat_mi2s_rx_ch); + rate->min = rate->max = SAMPLING_RATE_48KHZ; + channels->min = channels->max = msm_quat_mi2s_rx_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_mi2s_rx_bit_format); + + return 0; +} + +static int msm_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (cpu_dai->id) { + case 0: /*MSM_PRIM_MI2S*/ + break; + case 1: /*MSM_SEC_MI2S*/ + pr_debug("%s: channel:%d\n", __func__, msm_sec_mi2s_tx_ch); + channels->min = channels->max = msm_sec_mi2s_tx_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_mi2s_tx_bit_format); + break; + case 2: /*MSM_TERT_MI2S*/ + pr_debug("%s: channel:%d\n", __func__, msm_tert_mi2s_tx_ch); + channels->min = channels->max = msm_tert_mi2s_tx_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_mi2s_tx_bit_format); + break; + case 3: /*MSM_QUAT_MI2S*/ + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + rate->min = rate->max = SAMPLING_RATE_48KHZ; + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + pr_debug("%s:\n", __func__); + rate->min = rate->max = SAMPLING_RATE_48KHZ; + return 0; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (cpu_dai->id) { + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels->min = channels->max = msm_sec_tdm_tx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_tdm_tx_0_bit_format); + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + channels->min = channels->max = msm_sec_tdm_tx_1_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_tdm_tx_1_bit_format); + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + channels->min = channels->max = msm_sec_tdm_tx_2_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_tdm_tx_2_bit_format); + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + channels->min = channels->max = msm_sec_tdm_tx_3_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_tdm_tx_3_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + channels->min = channels->max = msm_tert_tdm_rx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_rx_0_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + channels->min = channels->max = msm_tert_tdm_rx_1_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_rx_1_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + channels->min = channels->max = msm_tert_tdm_rx_2_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_rx_2_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + channels->min = channels->max = msm_tert_tdm_rx_3_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_rx_3_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + channels->min = channels->max = msm_tert_tdm_tx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_tx_0_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + channels->min = channels->max = msm_tert_tdm_tx_1_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_tx_1_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + channels->min = channels->max = msm_tert_tdm_tx_2_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_tx_2_bit_format); + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + channels->min = channels->max = msm_tert_tdm_tx_3_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_tx_3_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + channels->min = channels->max = msm_quat_tdm_rx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_rx_0_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + channels->min = channels->max = msm_quat_tdm_rx_1_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_rx_1_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + channels->min = channels->max = msm_quat_tdm_rx_2_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_rx_2_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + channels->min = channels->max = msm_quat_tdm_rx_3_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_rx_3_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + channels->min = channels->max = msm_quat_tdm_tx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_tx_0_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + channels->min = channels->max = msm_quat_tdm_tx_1_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_tx_1_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + channels->min = channels->max = msm_quat_tdm_tx_2_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_tx_2_bit_format); + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + channels->min = channels->max = msm_quat_tdm_tx_3_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_quat_tdm_tx_3_bit_format); + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + rate->min = rate->max = SAMPLING_RATE_48KHZ; + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int apq8096_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("%s: substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + switch (cpu_dai->id) { + case 0: /*MSM_PRIM_MI2S*/ + break; + case 1: /*MSM_SEC_MI2S*/ + sec_mi2s_tx_clk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_SECONDARY_MI2S_TX, + &sec_mi2s_tx_clk); + if (ret < 0) { + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed, err:%d\n", + __func__, ret); + break; + case 2: /*MSM_TERT_MI2S*/ + mi2s_tx_clk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX, + &mi2s_tx_clk); + if (ret < 0) { + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed, err:%d\n", + __func__, ret); + break; + case 3: /*MSM_QUAT_MI2S*/ + mi2s_rx_clk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_QUATERNARY_MI2S_RX, + &mi2s_rx_clk); + if (ret < 0) { + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed, err:%d\n", + __func__, ret); + break; + default: + pr_err("%s: invalid cpu_dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + +err: + return ret; +} + +static void apq8096_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("%s: substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + switch (cpu_dai->id) { + case 0: /*MSM_PRIM_MI2S*/ + break; + case 1: /*MSM_SEC_MI2S*/ + sec_mi2s_tx_clk.enable = 0; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_SECONDARY_MI2S_TX, + &sec_mi2s_tx_clk); + if (ret < 0) + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, ret); + break; + case 2: /*MSM_TERT_MI2S*/ + mi2s_tx_clk.enable = 0; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX, + &mi2s_tx_clk); + if (ret < 0) + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, ret); + break; + case 3: /*MSM_QUAT_MI2S*/ + mi2s_rx_clk.enable = 0; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_QUATERNARY_MI2S_RX, + &mi2s_rx_clk); + if (ret < 0) + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, ret); + break; + default: + pr_err("%s: invalid cpu_dai id 0x%x\n", __func__, cpu_dai->id); + break; + } +} + +static struct snd_soc_ops apq8096_mi2s_be_ops = { + .startup = apq8096_mi2s_snd_startup, + .shutdown = apq8096_mi2s_snd_shutdown, +}; + +static unsigned int tdm_param_set_slot_mask(u16 port_id, + int slot_width, int slots) +{ + unsigned int slot_mask = 0; + int upper, lower, i, j; + unsigned int *slot_offset; + + switch (port_id) { + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + lower = SECONDARY_TDM_RX_0; + upper = SECONDARY_TDM_RX_7; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + lower = SECONDARY_TDM_TX_0; + upper = SECONDARY_TDM_TX_7; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + lower = TERTIARY_TDM_RX_0; + upper = TERTIARY_TDM_RX_7; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + lower = TERTIARY_TDM_TX_0; + upper = TERTIARY_TDM_TX_7; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + lower = QUATERNARY_TDM_RX_0; + upper = QUATERNARY_TDM_RX_7; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + lower = QUATERNARY_TDM_TX_0; + upper = QUATERNARY_TDM_TX_7; + break; + default: + return slot_mask; + } + + for (i = lower; i <= upper; i++) { + slot_offset = tdm_slot_offset[i]; + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + /* + * set the mask of active slot according to + * the offset table for the group of devices + */ + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int apq8096_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channel HW configuration should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + + switch (cpu_dai->id) { + case AFE_PORT_ID_SECONDARY_TDM_RX: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_0]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_1]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_2]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_3]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_4]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_5]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_6]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_7]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_0]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_1]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_2]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_3]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_4]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_5]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_6]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_7]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_0]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_1]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_2]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_4]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_5]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_6]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_7]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_0]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_1]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_2]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_4]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_5]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_6]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_7]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_0]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_1]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_2]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_3]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_4]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_5]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_6]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_7]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_0]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_1]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_2]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_3]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_4]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_5]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_6]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_7]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } + +end: + return ret; +} + +static struct snd_soc_ops apq8096_tdm_be_ops = { + .hw_params = apq8096_tdm_snd_hw_params, +}; + +static const struct soc_enum msm_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text), + SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text), + SOC_ENUM_SINGLE_EXT(2, hdmi_rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text), + SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text), + SOC_ENUM_SINGLE_EXT(8, tdm_ch_text), + SOC_ENUM_SINGLE_EXT(2, tdm_bit_format_text), + SOC_ENUM_SINGLE_EXT(2, mi2s_bit_format_text), + SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text), + SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text), + SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text), +}; + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("AUX PCM SampleRate", msm_snd_enum[0], + msm_auxpcm_rate_get, msm_auxpcm_rate_put), + SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[1], + msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put), + SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[2], + hdmi_rx_bit_format_get, hdmi_rx_bit_format_put), + SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[3], + msm_proxy_rx_ch_get, msm_proxy_rx_ch_put), + SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[4], + hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", msm_snd_enum[5], + msm_sec_tdm_tx_0_ch_get, msm_sec_tdm_tx_0_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Channels", msm_snd_enum[5], + msm_sec_tdm_tx_1_ch_get, msm_sec_tdm_tx_1_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Channels", msm_snd_enum[5], + msm_sec_tdm_tx_2_ch_get, msm_sec_tdm_tx_2_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Channels", msm_snd_enum[5], + msm_sec_tdm_tx_3_ch_get, msm_sec_tdm_tx_3_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", msm_snd_enum[5], + msm_tert_tdm_rx_0_ch_get, msm_tert_tdm_rx_0_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Channels", msm_snd_enum[5], + msm_tert_tdm_rx_1_ch_get, msm_tert_tdm_rx_1_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Channels", msm_snd_enum[5], + msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[5], + msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[5], + msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[5], + msm_tert_tdm_tx_1_ch_get, msm_tert_tdm_tx_1_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Channels", msm_snd_enum[5], + msm_tert_tdm_tx_2_ch_get, msm_tert_tdm_tx_2_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Channels", msm_snd_enum[5], + msm_tert_tdm_tx_3_ch_get, msm_tert_tdm_tx_3_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", msm_snd_enum[5], + msm_quat_tdm_rx_0_ch_get, msm_quat_tdm_rx_0_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", msm_snd_enum[5], + msm_quat_tdm_rx_1_ch_get, msm_quat_tdm_rx_1_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Channels", msm_snd_enum[5], + msm_quat_tdm_rx_2_ch_get, msm_quat_tdm_rx_2_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Channels", msm_snd_enum[5], + msm_quat_tdm_rx_3_ch_get, msm_quat_tdm_rx_3_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", msm_snd_enum[5], + msm_quat_tdm_tx_0_ch_get, msm_quat_tdm_tx_0_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Channels", msm_snd_enum[5], + msm_quat_tdm_tx_1_ch_get, msm_quat_tdm_tx_1_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Channels", msm_snd_enum[5], + msm_quat_tdm_tx_2_ch_get, msm_quat_tdm_tx_2_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Channels", msm_snd_enum[5], + msm_quat_tdm_tx_3_ch_get, msm_quat_tdm_tx_3_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Bit Format", msm_snd_enum[6], + msm_sec_tdm_tx_0_bit_format_get, + msm_sec_tdm_tx_0_bit_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Bit Format", msm_snd_enum[6], + msm_sec_tdm_tx_1_bit_format_get, + msm_sec_tdm_tx_1_bit_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Bit Format", msm_snd_enum[6], + msm_sec_tdm_tx_2_bit_format_get, + msm_sec_tdm_tx_2_bit_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Bit Format", msm_snd_enum[6], + msm_sec_tdm_tx_3_bit_format_get, + msm_sec_tdm_tx_3_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Bit Format", msm_snd_enum[6], + msm_tert_tdm_rx_0_bit_format_get, + msm_tert_tdm_rx_0_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Bit Format", msm_snd_enum[6], + msm_tert_tdm_rx_1_bit_format_get, + msm_tert_tdm_rx_1_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Bit Format", msm_snd_enum[6], + msm_tert_tdm_rx_2_bit_format_get, + msm_tert_tdm_rx_2_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[6], + msm_tert_tdm_rx_3_bit_format_get, + msm_tert_tdm_rx_3_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[6], + msm_tert_tdm_tx_0_bit_format_get, + msm_tert_tdm_tx_0_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Bit Format", msm_snd_enum[6], + msm_tert_tdm_tx_1_bit_format_get, + msm_tert_tdm_tx_1_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Bit Format", msm_snd_enum[6], + msm_tert_tdm_tx_2_bit_format_get, + msm_tert_tdm_tx_2_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Bit Format", msm_snd_enum[6], + msm_tert_tdm_tx_3_bit_format_get, + msm_tert_tdm_tx_3_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Bit Format", msm_snd_enum[6], + msm_quat_tdm_rx_0_bit_format_get, + msm_quat_tdm_rx_0_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Bit Format", msm_snd_enum[6], + msm_quat_tdm_rx_1_bit_format_get, + msm_quat_tdm_rx_1_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Bit Format", msm_snd_enum[6], + msm_quat_tdm_rx_2_bit_format_get, + msm_quat_tdm_rx_2_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Bit Format", msm_snd_enum[6], + msm_quat_tdm_rx_3_bit_format_get, + msm_quat_tdm_rx_3_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Bit Format", msm_snd_enum[6], + msm_quat_tdm_tx_0_bit_format_get, + msm_quat_tdm_tx_0_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Bit Format", msm_snd_enum[6], + msm_quat_tdm_tx_1_bit_format_get, + msm_quat_tdm_tx_1_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Bit Format", msm_snd_enum[6], + msm_quat_tdm_tx_2_bit_format_get, + msm_quat_tdm_tx_2_bit_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Bit Format", msm_snd_enum[6], + msm_quat_tdm_tx_3_bit_format_get, + msm_quat_tdm_tx_3_bit_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Bit Format", msm_snd_enum[7], + msm_quat_mi2s_rx_bit_format_get, + msm_quat_mi2s_rx_bit_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Bit Format", msm_snd_enum[7], + msm_tert_mi2s_tx_bit_format_get, + msm_tert_mi2s_tx_bit_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Bit Format", msm_snd_enum[7], + msm_sec_mi2s_tx_bit_format_get, + msm_sec_mi2s_tx_bit_format_put), + SOC_ENUM_EXT("EC Reference Channels", msm_snd_enum[8], + msm_ec_ref_ch_get, msm_ec_ref_ch_put), + SOC_ENUM_EXT("EC Reference Bit Format", msm_snd_enum[9], + msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put), + SOC_ENUM_EXT("EC Reference SampleRate", msm_snd_enum[10], + msm_ec_ref_rate_get, msm_ec_ref_rate_put), +}; + +static int apq8096_get_ll_qos_val(struct snd_pcm_runtime *runtime) +{ + int usecs; + + /* take 10% of period time as the deadline */ + usecs = (100000 / runtime->rate) * runtime->period_size; + usecs += ((100000 % runtime->rate) * runtime->period_size) / + runtime->rate; + + return usecs; +} + +static int apq8096_mm5_prepare(struct snd_pcm_substream *substream) +{ + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + apq8096_get_ll_qos_val(substream->runtime)); + return 0; +} + +static struct snd_soc_ops apq8096_mm5_ops = { + .prepare = apq8096_mm5_prepare, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link apq8096_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = "MSM8996 Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = "MSM8996 Media2", + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = "MSM8996 ULL", + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary MI2S TX_Hostless", + .stream_name = "Tertiary MI2S_TX Hostless Capture", + .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + { + .name = "MSM8996 Compress1", + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "VoLTE", + .stream_name = "VoLTE", + .cpu_dai_name = "VoLTE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOLTE, + }, + { + .name = "MSM8996 LowLatency", + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &apq8096_mm5_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = "MSM8996 Compress2", + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = "MSM8996 Compress3", + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = "MSM8996 Compr8", + .stream_name = "COMPR8", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-compr-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + { + .name = "QCHAT", + .stream_name = "QCHAT", + .cpu_dai_name = "QCHAT", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_QCHAT, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + { + .name = "INT_HFP_BT Hostless", + .stream_name = "INT_HFP_BT Hostless", + .cpu_dai_name = "INT_HFP_BT_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM8996 HFP TX", + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = "MSM8996 LowLatency Loopback", + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-loopback.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = "VoWLAN", + .stream_name = "VoWLAN", + .cpu_dai_name = "VoWLAN", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOWLAN, + }, + { + .name = "MSM8996 Compress4", + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = "MSM8996 Compress5", + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = "MSM8996 Compress6", + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = "MSM8996 Compress7", + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = "MSM8996 Compress8", + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = "MSM8996 Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "Circuit-Switch Voice", + .stream_name = "CS-Voice", + .cpu_dai_name = "CS-VOICE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_CS_VOICE, + }, + { + .name = "Voice2", + .stream_name = "Voice2", + .cpu_dai_name = "Voice2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICE2, + }, +}; + +static struct snd_soc_dai_link apq8096_auto_fe_dai_links[] = { + { + .name = "Tertiary TDM RX 0 Hostless", + .stream_name = "Tertiary TDM RX 0 Hostless", + .cpu_dai_name = "TERT_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM RX 1 Hostless", + .stream_name = "Tertiary TDM RX 1 Hostless", + .cpu_dai_name = "TERT_TDM_RX_1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM RX 2 Hostless", + .stream_name = "Tertiary TDM RX 2 Hostless", + .cpu_dai_name = "TERT_TDM_RX_2_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM RX 3 Hostless", + .stream_name = "Tertiary TDM RX 3 Hostless", + .cpu_dai_name = "TERT_TDM_RX_3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM TX 0 Hostless", + .stream_name = "Tertiary TDM TX 0 Hostless", + .cpu_dai_name = "TERT_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM TX 1 Hostless", + .stream_name = "Tertiary TDM TX 1 Hostless", + .cpu_dai_name = "TERT_TDM_TX_1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM TX 2 Hostless", + .stream_name = "Tertiary TDM TX 2 Hostless", + .cpu_dai_name = "TERT_TDM_TX_2_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Tertiary TDM TX 3 Hostless", + .stream_name = "Tertiary TDM TX 3 Hostless", + .cpu_dai_name = "TERT_TDM_TX_3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM RX 0 Hostless", + .stream_name = "Quaternary TDM RX 0 Hostless", + .cpu_dai_name = "QUAT_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM RX 1 Hostless", + .stream_name = "Quaternary TDM RX 1 Hostless", + .cpu_dai_name = "QUAT_TDM_RX_1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM RX 2 Hostless", + .stream_name = "Quaternary TDM RX 2 Hostless", + .cpu_dai_name = "QUAT_TDM_RX_2_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM RX 3 Hostless", + .stream_name = "Quaternary TDM RX 3 Hostless", + .cpu_dai_name = "QUAT_TDM_RX_3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM TX 0 Hostless", + .stream_name = "Quaternary TDM TX 0 Hostless", + .cpu_dai_name = "QUAT_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM TX 1 Hostless", + .stream_name = "Quaternary TDM TX 1 Hostless", + .cpu_dai_name = "QUAT_TDM_TX_1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM TX 2 Hostless", + .stream_name = "Quaternary TDM TX 2 Hostless", + .cpu_dai_name = "QUAT_TDM_TX_2_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary TDM TX 3 Hostless", + .stream_name = "Quaternary TDM TX 3 Hostless", + .cpu_dai_name = "QUAT_TDM_TX_3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Quaternary MI2S_RX Hostless Playback", + .stream_name = "Quaternary MI2S_RX Hostless Playback", + .cpu_dai_name = "QUAT_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary MI2S_TX Hostless Capture", + .stream_name = "Secondary MI2S_TX Hostless Capture", + .cpu_dai_name = "SEC_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link apq8096_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_auxpcm_be_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_auxpcm_be_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + } +}; + +static struct snd_soc_dai_link apq8096_auto_be_dai_links[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_mi2s_tx_be_hw_params_fixup, + .ops = &apq8096_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_mi2s_tx_be_hw_params_fixup, + .ops = &apq8096_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup, + .ops = &apq8096_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_1, + .stream_name = "Secondary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36883", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_2, + .stream_name = "Secondary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36885", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_3, + .stream_name = "Secondary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36887", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_1, + .stream_name = "Tertiary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36898", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_2, + .stream_name = "Tertiary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36900", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_3, + .stream_name = "Tertiary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36902", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_1, + .stream_name = "Tertiary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36899", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_2, + .stream_name = "Tertiary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36901", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_3, + .stream_name = "Tertiary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36903", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_1, + .stream_name = "Quaternary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36914", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_2, + .stream_name = "Quaternary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36916", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_3, + .stream_name = "Quaternary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36918", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_1, + .stream_name = "Quaternary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36915", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_2, + .stream_name = "Quaternary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36917", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_3, + .stream_name = "Quaternary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36919", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link apq8096_hdmi_dai_link[] = { + /* HDMI BACK END DAI Link */ + { + .name = LPASS_BE_HDMI, + .stream_name = "HDMI Playback", + .cpu_dai_name = "msm-dai-q6-hdmi.8", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_HDMI_RX, + .be_hw_params_fixup = msm_hdmi_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link apq8096_auto_dai_links[ + ARRAY_SIZE(apq8096_common_dai_links) + + ARRAY_SIZE(apq8096_auto_fe_dai_links) + + ARRAY_SIZE(apq8096_common_be_dai_links) + + ARRAY_SIZE(apq8096_auto_be_dai_links) + + ARRAY_SIZE(apq8096_hdmi_dai_link)]; + +struct snd_soc_card snd_soc_card_auto_apq8096 = { + .name = "apq8096-auto-snd-card", +}; + +struct snd_soc_card snd_soc_card_adp_agave_apq8096 = { + .name = "apq8096-adp-agave-snd-card", +}; + +struct snd_soc_card snd_soc_card_adp_mmxf_apq8096 = { + .name = "apq8096-adp-mmxf-snd-card", +}; + +static int apq8096_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static const struct of_device_id apq8096_asoc_machine_of_match[] = { + { .compatible = "qcom,apq8096-asoc-snd-auto", + .data = "auto_codec"}, + { .compatible = "qcom,apq8096-asoc-snd-adp-agave", + .data = "adp_agave_codec"}, + { .compatible = "qcom,apq8096-asoc-snd-adp-mmxf", + .data = "adp_mmxf_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3, len_4; + const struct of_device_id *match; + + match = of_match_node(apq8096_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "auto_codec")) + card = &snd_soc_card_auto_apq8096; + else if (!strcmp(match->data, "adp_agave_codec")) + card = &snd_soc_card_adp_agave_apq8096; + else if (!strcmp(match->data, "adp_mmxf_codec")) + card = &snd_soc_card_adp_mmxf_apq8096; + else { + dev_err(dev, "%s: Codec not supported\n", + __func__); + return NULL; + } + + /* same FE and BE used for all codec */ + len_1 = ARRAY_SIZE(apq8096_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(apq8096_auto_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(apq8096_common_be_dai_links); + + memcpy(apq8096_auto_dai_links, + apq8096_common_dai_links, + sizeof(apq8096_common_dai_links)); + memcpy(apq8096_auto_dai_links + len_1, + apq8096_auto_fe_dai_links, + sizeof(apq8096_auto_fe_dai_links)); + memcpy(apq8096_auto_dai_links + len_2, + apq8096_common_be_dai_links, + sizeof(apq8096_common_be_dai_links)); + memcpy(apq8096_auto_dai_links + len_3, + apq8096_auto_be_dai_links, + sizeof(apq8096_auto_be_dai_links)); + + dailink = apq8096_auto_dai_links; + len_4 = len_3 + ARRAY_SIZE(apq8096_auto_be_dai_links); + + if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) { + dev_dbg(dev, "%s(): hdmi audio support present\n", + __func__); + memcpy(dailink + len_4, apq8096_hdmi_dai_link, + sizeof(apq8096_hdmi_dai_link)); + len_4 += ARRAY_SIZE(apq8096_hdmi_dai_link); + } else { + dev_dbg(dev, "%s(): No hdmi audio support\n", __func__); + } + + if (card) { + card->dai_link = dailink; + card->num_links = len_4; + } + + return card; +} + +/* + * TDM offset mapping is per platform/codec specific. + * TO BE UPDATED if new platform/codec is introduced. + */ +static int apq8096_init_tdm_dev(struct device *dev) +{ + const struct of_device_id *match; + + match = of_match_node(apq8096_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return -EINVAL; + } + + if (!strcmp(match->data, "adp_mmxf_codec")) { + dev_dbg(dev, "%s: ADP MMXF tdm slot offset\n", __func__); + memcpy(tdm_slot_offset, + tdm_slot_offset_adp_mmxf, + sizeof(tdm_slot_offset_adp_mmxf)); + } else { + dev_dbg(dev, "%s: DEFAULT tdm slot offset\n", __func__); + } + + return 0; +} + +static int apq8096_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + const struct of_device_id *match; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "Parse card name failed, err:%d\n", + ret); + goto err; + } + + match = of_match_node(apq8096_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: No matched codec is found.\n", + __func__); + goto err; + } + + ret = apq8096_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + /* populate controls of snd card */ + card->controls = msm_snd_controls; + card->num_controls = ARRAY_SIZE(msm_snd_controls); + + ret = apq8096_init_tdm_dev(&pdev->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = snd_soc_register_card(card); + if (ret == -EPROBE_DEFER) { + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + + return 0; + +err: + return ret; +} + +static int apq8096_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver apq8096_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = apq8096_asoc_machine_of_match, + }, + .probe = apq8096_asoc_machine_probe, + .remove = apq8096_asoc_machine_remove, +}; + +static int dummy_machine_probe(struct platform_device *pdev) +{ + return 0; +} + +static int dummy_machine_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_device dummy_machine_device = { + .name = "apq8096-auto-asoc-dummy", +}; + +static struct platform_driver apq8096_asoc_machine_dummy_driver = { + .driver = { + .name = "apq8096-auto-asoc-dummy", + .owner = THIS_MODULE, + }, + .probe = dummy_machine_probe, + .remove = dummy_machine_remove, +}; + +static int apq8096_adsp_state_callback(struct notifier_block *nb, + unsigned long value, void *priv) +{ + if (!dummy_device_registered && SUBSYS_AFTER_POWERUP == value) { + platform_driver_register(&apq8096_asoc_machine_dummy_driver); + platform_device_register(&dummy_machine_device); + dummy_device_registered = true; + } + + return NOTIFY_OK; +} + +static struct notifier_block adsp_state_notifier_block = { + .notifier_call = apq8096_adsp_state_callback, + .priority = -INT_MAX, +}; + +static int __init apq8096_soc_platform_init(void) +{ + adsp_state_notifier = subsys_notif_register_notifier("adsp", + &adsp_state_notifier_block); + return 0; +} + +module_init(apq8096_soc_platform_init); +module_platform_driver(apq8096_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, apq8096_asoc_machine_of_match); diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index 041a91e28988..ec02cdf3ca3c 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -496,6 +496,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); static struct platform_device *spdev; @@ -2263,6 +2265,54 @@ static int mi2s_get_sample_rate(int value) return sample_rate; } +static int mi2s_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2395,6 +2445,78 @@ static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, return 1; } +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + static int msm_hifi_ctrl(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); @@ -2647,6 +2769,22 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, msm_hifi_put), }; @@ -3111,48 +3249,64 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; channels->min = channels->max = mi2s_rx_cfg[PRIM_MI2S].channels; break; case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; channels->min = channels->max = mi2s_tx_cfg[PRIM_MI2S].channels; break; case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; channels->min = channels->max = mi2s_rx_cfg[SEC_MI2S].channels; break; case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; channels->min = channels->max = mi2s_tx_cfg[SEC_MI2S].channels; break; case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; channels->min = channels->max = mi2s_rx_cfg[TERT_MI2S].channels; break; case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; channels->min = channels->max = mi2s_tx_cfg[TERT_MI2S].channels; break; case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; channels->min = channels->max = mi2s_rx_cfg[QUAT_MI2S].channels; break; case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; channels->min = channels->max = mi2s_tx_cfg[QUAT_MI2S].channels; @@ -3972,6 +4126,7 @@ static u32 get_mi2s_bits_per_sample(u32 bit_format) u32 bit_per_sample; switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S24_3LE: case SNDRV_PCM_FORMAT_S24_LE: bit_per_sample = 32; @@ -5239,7 +5394,6 @@ static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = { .platform_name = "msm-pcm-hostless", .dynamic = 1, .dpcm_playback = 1, - .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 8e986a74ffff..2929ea0d735b 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -45,6 +45,7 @@ #include <sound/msm-dts-eagle.h> #include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" #define DSP_PP_BUFFERING_IN_MSEC 25 #define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 @@ -543,12 +544,19 @@ static void compr_event_handler(uint32_t opcode, unsigned long flags; uint64_t read_size; uint32_t *buff_addr; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; if (!prtd) { pr_err("%s: prtd is NULL\n", __func__); return; } cstream = prtd->cstream; + if (!cstream) { + pr_err("%s: cstream is NULL\n", __func__); + return; + } + ac = prtd->audio_client; /* @@ -716,6 +724,23 @@ static void compr_event_handler(uint32_t opcode, prtd->gapless_state.gapless_transition = 0; spin_unlock_irqrestore(&prtd->lock, flags); break; + case ASM_STREAM_PP_EVENT: + pr_debug("%s: ASM_STREAM_PP_EVENT\n", __func__); + rtd = cstream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, DSP_STREAM_CALLBACK, + payload); + if (ret) { + pr_err("%s: failed to inform mixer ctrl. err = %d\n", + __func__, ret); + return; + } + + break; case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: { pr_debug("ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY\n"); @@ -815,6 +840,10 @@ static void compr_event_handler(uint32_t opcode, } atomic_set(&prtd->close, 0); break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:", + __func__); + break; default: break; } @@ -3578,6 +3607,65 @@ end: return rc; } +static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int ret = 0, param_length = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(¶m_length, ucontrol->value.bytes.data, + sizeof(param_length)); + if ((param_length + sizeof(param_length)) + >= sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, param_length); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, + ASM_STREAM_CMD_REGISTER_PP_EVENTS, + ucontrol->value.bytes.data + sizeof(param_length), + param_length); + if (ret < 0) + pr_err("%s: failed to register pp event. err = %d\n", + __func__, ret); +done: + return ret; +} + static int msm_compr_gapless_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -3854,6 +3942,117 @@ static int msm_compr_add_query_audio_effect_control( return 0; } +static int msm_compr_add_audio_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_audio_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_compr_adsp_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str; + fe_audio_adsp_stream_cmd_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_audio_adsp_stream_cmd_config_control, + ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_audio_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_audio_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, + .put = msm_adsp_stream_callback_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_callback_config_control[0].name = mixer_str; + fe_audio_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_audio_adsp_callback_config_control, + ARRAY_SIZE(fe_audio_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); +done: + return ret; +} + static int msm_compr_add_dec_runtime_params_control( struct snd_soc_pcm_runtime *rtd) { @@ -4048,6 +4247,16 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: Could not add Compr Audio Effects Control\n", __func__); + rc = msm_compr_add_audio_adsp_stream_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ADSP Stream Cmd Control\n", + __func__); + + rc = msm_compr_add_audio_adsp_stream_callback_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ADSP Stream Callback Control\n", + __func__); + rc = msm_compr_add_query_audio_effect_control(rtd); if (rc) pr_err("%s: Could not add Compr Query Audio Effect Control\n", diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index d7efefdb3a04..201ca652c10b 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -4143,12 +4143,13 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "INT0 MI2S Playback", .aif_name = "INT0_MI2S_RX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .capture = { .stream_name = "INT0 MI2S Capture", @@ -4247,12 +4248,13 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "INT4 MI2S Playback", .aif_name = "INT4_MI2S_RX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .capture = { .stream_name = "INT4 MI2S Capture", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index 7928c3791f96..73eadfa4eebb 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -37,6 +37,7 @@ #include "msm-pcm-q6-v2.h" #include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" enum stream_state { IDLE = 0, @@ -147,6 +148,8 @@ static void event_handler(uint32_t opcode, uint32_t idx = 0; uint32_t size = 0; uint8_t buf_index; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; switch (opcode) { case ASM_DATA_EVENT_WRITE_DONE_V2: { @@ -223,6 +226,29 @@ static void event_handler(uint32_t opcode, } break; } + case ASM_STREAM_PP_EVENT: { + pr_debug("%s: ASM_STREAM_PP_EVENT\n", __func__); + if (!substream) { + pr_err("%s: substream is NULL.\n", __func__); + return; + } + + rtd = substream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, DSP_STREAM_CALLBACK, + payload); + if (ret) { + pr_err("%s: failed to inform mixer ctl. err = %d\n", + __func__, ret); + return; + } + + break; + } case APR_BASIC_RSP_RESULT: { switch (payload[0]) { case ASM_SESSION_CMD_RUN_V2: @@ -252,6 +278,10 @@ static void event_handler(uint32_t opcode, } atomic_set(&prtd->start, 1); break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:", + __func__); + break; default: pr_debug("%s:Payload = [0x%x]stat[0x%x]\n", __func__, payload[0], payload[1]); @@ -1036,6 +1066,177 @@ static struct snd_pcm_ops msm_pcm_ops = { .mmap = msm_pcm_mmap, }; +static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(pcm); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + int ret = 0, param_length = 0; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + ret = -ENODEV; + goto done; + } + + substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + ret = -EINVAL; + goto done; + } + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = substream->runtime->private_data; + if (prtd->audio_client == NULL) { + pr_err("%s prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(¶m_length, ucontrol->value.bytes.data, + sizeof(param_length)); + if ((param_length + sizeof(param_length)) + >= sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, param_length); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, + ASM_STREAM_CMD_REGISTER_PP_EVENTS, + ucontrol->value.bytes.data + sizeof(param_length), + param_length); + if (ret < 0) + pr_err("%s: failed to register pp event. err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_pcm_add_audio_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_audio_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__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str; + fe_audio_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_audio_adsp_stream_cmd_config_control, + ARRAY_SIZE(fe_audio_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); +done: + return ret; +} + +static int msm_pcm_add_audio_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_audio_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, + .put = msm_adsp_stream_callback_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + 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) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_callback_config_control[0].name = mixer_str; + fe_audio_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_audio_adsp_callback_config_control, + ARRAY_SIZE(fe_audio_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); +done: + return ret; +} + static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume) { int rc = 0; @@ -1549,6 +1750,16 @@ static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: Could not add pcm Compress Control %d\n", __func__, ret); + ret = msm_pcm_add_audio_adsp_stream_cmd_control(rtd); + if (ret) + pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n", + __func__); + + ret = msm_pcm_add_audio_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-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index e8bf562acc4f..837a08488991 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -12343,6 +12343,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c index 6f463c079f19..d4e78604f868 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c @@ -817,6 +817,136 @@ static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol, return 0; } + +int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + const char *mixer_ctl_name, + uint32_t *payload) +{ + /* adsp pp event notifier */ + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value control; + uint32_t payload_size = 0; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + + if (!rtd || !payload) { + pr_err("%s: %s is NULL\n", __func__, + (!rtd) ? "rtd" : "payload"); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_ATOMIC); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + control.id = kctl->id; + payload_size = payload[0]; + /* Copy complete payload */ + memcpy(control.value.bytes.data, (void *)payload, + sizeof(payload_size) + payload_size); + kctl->put(kctl, &control); + if (rtd->card->snd_card == NULL) { + pr_err("%s: snd_card is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + snd_ctl_notify(rtd->card->snd_card, + SNDRV_CTL_EVENT_MASK_INFO, + &control.id); +done: + return ret; +} + +int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 512; + + return 0; +} + +int msm_adsp_stream_callback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t payload_size = 0, last_payload_size = 0; + + /* fetch payload size in first four bytes */ + memcpy(&payload_size, ucontrol->value.bytes.data, sizeof(uint32_t)); + + if (kcontrol->private_data == NULL) { + /* buffer is empty */ + kcontrol->private_data = + kzalloc(payload_size + sizeof(payload_size), + GFP_ATOMIC); + if (kcontrol->private_data == NULL) + return -ENOMEM; + } else { + memcpy(&last_payload_size, kcontrol->private_data, + sizeof(uint32_t)); + if (last_payload_size < payload_size) { + /* new payload size exceeds old one. + * reallocate buffer + */ + kfree(kcontrol->private_data); + kcontrol->private_data = + kzalloc(payload_size + sizeof(payload_size), + GFP_ATOMIC); + if (kcontrol->private_data == NULL) + return -ENOMEM; + } + } + + memcpy(kcontrol->private_data, ucontrol->value.bytes.data, + sizeof(uint32_t) + payload_size); + + return 0; +} + +int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t payload_size = 0; + + if (kcontrol->private_data == NULL) { + pr_err("%s: ASM Stream PP Event Data Unavailable\n", __func__); + return -EINVAL; + } + + memcpy(&payload_size, kcontrol->private_data, sizeof(uint32_t)); + memcpy(ucontrol->value.bytes.data, kcontrol->private_data, + sizeof(uint32_t) + payload_size); + kfree(kcontrol->private_data); + kcontrol->private_data = NULL; + + return 0; +} + +int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 512; + + return 0; +} + static int msm_multichannel_ec_primary_mic_ch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h index f8a1da5e7702..70ce20fbd8f8 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. @@ -13,7 +13,17 @@ #define _MSM_QTI_PP_H_ #include <sound/soc.h> - +int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + const char *mixer_ctl_name, + uint32_t *payload); +int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int msm_adsp_stream_callback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); #ifdef CONFIG_QTI_PP void msm_qti_pp_send_eq_values(int fedai_id); int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx, diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 80729b3052b7..6616edcd3347 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -6692,8 +6692,6 @@ static int afe_set_cal_fb_spkr_prot(int32_t cal_type, size_t data_size, mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); memcpy(&this_afe.prot_cfg, &cal_data->cal_info, sizeof(this_afe.prot_cfg)); - this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode; - this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode; mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); done: return ret; @@ -6835,8 +6833,6 @@ static int afe_get_cal_fb_spkr_prot(int32_t cal_type, size_t data_size, cal_data->cal_info.r0[SP_V2_SPKR_1] = -1; cal_data->cal_info.r0[SP_V2_SPKR_2] = -1; } - this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode; - this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode; mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); __pm_relax(&wl.ws); done: diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index f38108258306..1ca99c3f9115 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -1094,6 +1094,65 @@ fail: return NULL; } +int q6asm_send_stream_cmd(struct audio_client *ac, uint32_t opcode, + void *param, uint32_t params_length) +{ + char *asm_params = NULL; + struct apr_hdr hdr; + int sz, rc; + + if (!param || !ac) { + pr_err("%s: %s is NULL\n", __func__, + (!param) ? "param" : "ac"); + rc = -EINVAL; + goto done; + } + + sz = sizeof(struct apr_hdr) + params_length; + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + rc = -ENOMEM; + goto done; + } + + q6asm_add_hdr_async(ac, &hdr, sizeof(struct apr_hdr) + + params_length, TRUE); + atomic_set(&ac->cmd_state_pp, -1); + hdr.opcode = opcode; + memcpy(asm_params, &hdr, sizeof(struct apr_hdr)); + memcpy(asm_params + sizeof(struct apr_hdr), + param, params_length); + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: audio adsp pp register failed\n", __func__); + rc = -EINVAL; + goto fail_send_param; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state_pp) >= 0), 1 * HZ); + if (!rc) { + pr_err("%s: timeout, adsp pp register\n", __func__); + rc = -ETIMEDOUT; + goto fail_send_param; + } + + if (atomic_read(&ac->cmd_state_pp) > 0) { + pr_err("%s: DSP returned error[%s] adsp pp register\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state_pp))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + goto fail_send_param; + } + + rc = 0; +fail_send_param: + kfree(asm_params); +done: + return rc; +} + struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) { struct audio_client *ac; @@ -1167,6 +1226,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) spin_lock_init(&ac->port[lcnt].dsp_lock); } atomic_set(&ac->cmd_state, 0); + atomic_set(&ac->cmd_state_pp, 0); atomic_set(&ac->mem_state, 0); rc = send_asm_custom_topology(ac); @@ -1614,6 +1674,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) int32_t ret = 0; union asm_token_struct asm_token; uint8_t buf_index; + char *pp_event_package = NULL; + uint32_t payload_size = 0; if (ac == NULL) { pr_err("%s: ac NULL\n", __func__); @@ -1659,6 +1721,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->time_flag, 0); atomic_set(&ac->cmd_state, 0); atomic_set(&ac->mem_state, 0); + atomic_set(&ac->cmd_state_pp, 0); wake_up(&ac->time_wait); wake_up(&ac->cmd_wait); wake_up(&ac->mem_wait); @@ -1726,14 +1789,29 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pr_err("%s: cmd = 0x%x returned error = 0x%x\n", __func__, payload[0], payload[1]); if (wakeup_flag) { - atomic_set(&ac->cmd_state, payload[1]); + if (payload[0] == + ASM_STREAM_CMD_SET_PP_PARAMS_V2) + atomic_set(&ac->cmd_state_pp, + payload[1]); + else + atomic_set(&ac->cmd_state, + payload[1]); wake_up(&ac->cmd_wait); } return 0; } - if (atomic_read(&ac->cmd_state) && wakeup_flag) { - atomic_set(&ac->cmd_state, 0); - wake_up(&ac->cmd_wait); + if (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) { + if (atomic_read(&ac->cmd_state_pp) && + wakeup_flag) { + atomic_set(&ac->cmd_state_pp, 0); + wake_up(&ac->cmd_wait); + } + } else { + if (atomic_read(&ac->cmd_state) && + wakeup_flag) { + atomic_set(&ac->cmd_state, 0); + wake_up(&ac->cmd_wait); + } } if (ac->cb) ac->cb(data->opcode, data->token, @@ -1780,6 +1858,17 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) data->payload_size); } break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + if (payload[1] != 0) + pr_err("%s: ASM get param error = %d, resuming\n", + __func__, payload[1]); + atomic_set(&ac->cmd_state_pp, payload[1]); + wake_up(&ac->cmd_wait); + break; default: pr_debug("%s: command[0x%x] not expecting rsp\n", __func__, payload[0]); @@ -1950,6 +2039,26 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2: q6asm_process_mtmx_get_param_rsp(ac, (void *) payload); break; + case ASM_STREAM_PP_EVENT: + pr_debug("%s: ASM_STREAM_PP_EVENT payload[0][0x%x] payload[1][0x%x]", + __func__, payload[0], payload[1]); + /* repack payload for asm_stream_pp_event + * package is composed of size + actual payload + */ + payload_size = data->payload_size; + pp_event_package = + kzalloc(payload_size + sizeof(payload_size), + GFP_ATOMIC); + if (!pp_event_package) + return -ENOMEM; + memcpy((void *)pp_event_package, + &payload_size, sizeof(payload_size)); + memcpy((void *)pp_event_package + sizeof(payload_size), + data->payload, payload_size); + ac->cb(data->opcode, data->token, + (void *)pp_event_package, ac->priv); + kfree(pp_event_package); + return 0; case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2: pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n", __func__, ac->session, payload[0], payload[2], @@ -6417,7 +6526,7 @@ int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain) memset(&multi_ch_gain, 0, sizeof(multi_ch_gain)); sz = sizeof(struct asm_volume_ctrl_multichannel_gain); q6asm_add_hdr_async(ac, &multi_ch_gain.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); multi_ch_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; multi_ch_gain.param.data_payload_addr_lsw = 0; multi_ch_gain.param.data_payload_addr_msw = 0; @@ -6443,20 +6552,20 @@ int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain) } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, multi_ch_gain.data.param_id); rc = -ETIMEDOUT; goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] , set-params paramid[0x%x]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state)), + atomic_read(&ac->cmd_state_pp)), multi_ch_gain.data.param_id); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } rc = 0; @@ -6511,7 +6620,7 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, memset(&multich_gain, 0, sizeof(multich_gain)); sz = sizeof(struct asm_volume_ctrl_multichannel_gain); q6asm_add_hdr_async(ac, &multich_gain.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, 1); + atomic_set(&ac->cmd_state_pp, -1); multich_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; multich_gain.param.data_payload_addr_lsw = 0; multich_gain.param.data_payload_addr_msw = 0; @@ -6549,17 +6658,17 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) <= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, multich_gain.data.param_id); rc = -EINVAL; goto done; } - if (atomic_read(&ac->cmd_state) < 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%d] , set-params paramid[0x%x]\n", - __func__, atomic_read(&ac->cmd_state), - multich_gain.data.param_id); + __func__, atomic_read(&ac->cmd_state_pp), + multich_gain.data.param_id); rc = -EINVAL; goto done; } @@ -6587,7 +6696,7 @@ int q6asm_set_mute(struct audio_client *ac, int muteflag) sz = sizeof(struct asm_volume_ctrl_mute_config); q6asm_add_hdr_async(ac, &mute.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); mute.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; mute.param.data_payload_addr_lsw = 0; mute.param.data_payload_addr_msw = 0; @@ -6609,20 +6718,20 @@ int q6asm_set_mute(struct audio_client *ac, int muteflag) } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, mute.data.param_id); rc = -ETIMEDOUT; goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state)), + atomic_read(&ac->cmd_state_pp)), mute.data.param_id); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } rc = 0; @@ -6663,7 +6772,7 @@ int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, ad->data.param_id = param_id; ad->data.param_size = size; ad->data.reserved = 0; - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); if (po) { struct list_head *ptr, *next; @@ -6714,7 +6823,7 @@ int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 1*HZ); if (!rc) { pr_err("DTS_EAGLE_ASM - %s: timeout, set-params paramid[0x%x]\n", __func__, ad->data.param_id); @@ -6722,12 +6831,12 @@ int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state))); + atomic_read(&ac->cmd_state_pp))); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } rc = 0; @@ -6887,7 +6996,7 @@ static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) sz = sizeof(struct asm_volume_ctrl_master_gain); q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; vol.param.data_payload_addr_lsw = 0; vol.param.data_payload_addr_msw = 0; @@ -6909,20 +7018,20 @@ static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, vol.data.param_id); rc = -ETIMEDOUT; goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state)), + atomic_read(&ac->cmd_state_pp)), vol.data.param_id); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } @@ -7024,7 +7133,7 @@ int q6asm_set_softpause(struct audio_client *ac, sz = sizeof(struct asm_soft_pause_params); q6asm_add_hdr_async(ac, &softpause.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); softpause.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; softpause.param.data_payload_addr_lsw = 0; @@ -7051,20 +7160,20 @@ int q6asm_set_softpause(struct audio_client *ac, } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, softpause.data.param_id); rc = -ETIMEDOUT; goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state)), + atomic_read(&ac->cmd_state_pp)), softpause.data.param_id); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } rc = 0; @@ -7104,7 +7213,7 @@ static int __q6asm_set_softvolume(struct audio_client *ac, sz = sizeof(struct asm_soft_step_volume_params); q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; softvol.param.data_payload_addr_lsw = 0; softvol.param.data_payload_addr_msw = 0; @@ -7129,20 +7238,20 @@ static int __q6asm_set_softvolume(struct audio_client *ac, } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, softvol.data.param_id); rc = -ETIMEDOUT; goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state)), + atomic_read(&ac->cmd_state_pp)), softvol.data.param_id); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } rc = 0; @@ -7191,7 +7300,7 @@ int q6asm_equalizer(struct audio_client *ac, void *eq_p) sz = sizeof(struct asm_eq_params); eq_params = (struct msm_audio_eq_stream_config *) eq_p; q6asm_add_hdr(ac, &eq.hdr, sz, TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); eq.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; eq.param.data_payload_addr_lsw = 0; @@ -7236,20 +7345,20 @@ int q6asm_equalizer(struct audio_client *ac, void *eq_p) } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ); if (!rc) { pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, eq.data.param_id); rc = -ETIMEDOUT; goto fail_cmd; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state)), + atomic_read(&ac->cmd_state_pp)), eq.data.param_id); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_cmd; } rc = 0; @@ -7867,7 +7976,7 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) + sizeof(struct asm_stream_cmd_set_pp_params_v2) + params_length), TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; payload_params.data_payload_addr_lsw = 0; payload_params.data_payload_addr_msw = 0; @@ -7886,18 +7995,18 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, goto fail_send_param; } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 1*HZ); if (!rc) { pr_err("%s: timeout, audio effects set-params\n", __func__); rc = -ETIMEDOUT; goto fail_send_param; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%s] set-params\n", __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state))); + atomic_read(&ac->cmd_state_pp))); rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); + atomic_read(&ac->cmd_state_pp)); goto fail_send_param; } @@ -8749,7 +8858,7 @@ int q6asm_send_cal(struct audio_client *ac) q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) + sizeof(struct asm_stream_cmd_set_pp_params_v2)), TRUE); - atomic_set(&ac->cmd_state, -1); + atomic_set(&ac->cmd_state_pp, -1); hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; payload_params.data_payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr); @@ -8775,15 +8884,15 @@ int q6asm_send_cal(struct audio_client *ac) goto free; } rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 5 * HZ); + (atomic_read(&ac->cmd_state_pp) >= 0), 5 * HZ); if (!rc) { pr_err("%s: timeout, audio audstrm cal send\n", __func__); rc = -ETIMEDOUT; goto free; } - if (atomic_read(&ac->cmd_state) > 0) { + if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%d] audio audstrm cal send\n", - __func__, atomic_read(&ac->cmd_state)); + __func__, atomic_read(&ac->cmd_state_pp)); rc = -EINVAL; goto free; } diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c index f64074d442dc..1c03d8c9e797 100644 --- a/sound/soc/msm/sdm660-ext-dai-links.c +++ b/sound/soc/msm/sdm660-ext-dai-links.c @@ -335,7 +335,6 @@ static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { .platform_name = "msm-pcm-hostless", .dynamic = 1, .dpcm_playback = 1, - .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, diff --git a/sound/soc/msm/sdm660-external.c b/sound/soc/msm/sdm660-external.c index 191db6c2fa9d..47c988618398 100644 --- a/sound/soc/msm/sdm660-external.c +++ b/sound/soc/msm/sdm660-external.c @@ -1609,6 +1609,9 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + } else { + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); } snd_soc_dapm_sync(dapm); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index e163b0148c4b..fd6e247d9fd8 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1027,12 +1027,13 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) { struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); + unsigned long flags; if (!is_secondary(i2s)) { if (i2s->quirks & QUIRK_NEED_RSTCLR) { - spin_lock(i2s->lock); + spin_lock_irqsave(i2s->lock, flags); writel(0, i2s->addr + I2SCON); - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); } } diff --git a/sound/usb/card.c b/sound/usb/card.c index 76720b2a1f0e..7bc935bab369 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -267,7 +267,6 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int if (! snd_usb_parse_audio_interface(chip, interface)) { usb_set_interface(dev, interface, 0); /* reset the current interface */ usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); - return -EINVAL; } return 0; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 252dd6df5ef1..2bba7dbc35f9 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -538,6 +538,11 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) alive, ep->ep_num); clear_bit(EP_FLAG_STOPPING, &ep->flags); + ep->data_subs = NULL; + ep->sync_slave = NULL; + ep->retire_data_urb = NULL; + ep->prepare_data_urb = NULL; + return 0; } @@ -902,9 +907,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, /** * snd_usb_endpoint_start: start an snd_usb_endpoint * - * @ep: the endpoint to start - * @can_sleep: flag indicating whether the operation is executed in - * non-atomic context + * @ep: the endpoint to start * * A call to this function will increment the use count of the endpoint. * In case it is not already running, the URBs for this endpoint will be @@ -914,7 +917,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * Returns an error if the URB submission failed, 0 in all other cases. */ -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep) +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) { int err; unsigned int i; @@ -928,8 +931,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep) /* just to be sure */ deactivate_urbs(ep, false); - if (can_sleep) - wait_clear_urbs(ep); ep->active_mask = 0; ep->unlink_mask = 0; @@ -1010,10 +1011,6 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (--ep->use_count == 0) { deactivate_urbs(ep, false); - ep->data_subs = NULL; - ep->sync_slave = NULL; - ep->retire_data_urb = NULL; - ep->prepare_data_urb = NULL; set_bit(EP_FLAG_STOPPING, &ep->flags); } } diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 6428392d8f62..584f295d7c77 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -18,7 +18,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep); +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c index 2c44139b4041..33db205dd12b 100644 --- a/sound/usb/hiface/pcm.c +++ b/sound/usb/hiface/pcm.c @@ -445,6 +445,8 @@ static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub) mutex_lock(&rt->stream_mutex); + hiface_pcm_stream_stop(rt); + sub->dma_off = 0; sub->period_off = 0; diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 606fd473454b..157c0704817c 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -931,9 +931,10 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */ case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */ case USB_ID(0x046d, 0x0991): + case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */ /* Most audio usb devices lie about volume resolution. * Most Logitech webcams have res = 384. - * Proboly there is some logitech magic behind this number --fishor + * Probably there is some logitech magic behind this number --fishor */ if (!strcmp(kctl->id.name, "Mic Capture Volume")) { usb_audio_info(chip, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index a0c0a184d02b..4c929106a1bf 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -218,7 +218,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } -static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) +static int start_endpoints(struct snd_usb_substream *subs) { int err; @@ -231,7 +231,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) dev_dbg(&subs->dev->dev, "Starting data EP @%pK\n", ep); ep->data_subs = subs; - err = snd_usb_endpoint_start(ep, can_sleep); + err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); return err; @@ -260,7 +260,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) dev_dbg(&subs->dev->dev, "Starting sync EP @%pK\n", ep); ep->sync_slave = subs->data_endpoint; - err = snd_usb_endpoint_start(ep, can_sleep); + err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); return err; @@ -897,7 +897,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) - ret = start_endpoints(subs, true); + ret = start_endpoints(subs); unlock: snd_usb_unlock_shutdown(subs->stream->chip); @@ -1713,7 +1713,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream switch (cmd) { case SNDRV_PCM_TRIGGER_START: - err = start_endpoints(subs, false); + err = start_endpoints(subs); if (err < 0) return err; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 3039e907f1f8..29f38e2b4ca9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1136,6 +1136,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */ case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */ case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */ + case USB_ID(0x047F, 0x02F7): /* Plantronics BT-600 */ case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */ case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 9df61059a85d..a2fd6e79d5a5 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -95,7 +95,8 @@ static void register_python_scripting(struct scripting_ops *scripting_ops) if (err) die("error registering py script extension"); - scripting_context = malloc(sizeof(struct scripting_context)); + if (scripting_context == NULL) + scripting_context = malloc(sizeof(*scripting_context)); } #ifdef NO_LIBPYTHON @@ -159,7 +160,8 @@ static void register_perl_scripting(struct scripting_ops *scripting_ops) if (err) die("error registering pl script extension"); - scripting_context = malloc(sizeof(struct scripting_context)); + if (scripting_context == NULL) + scripting_context = malloc(sizeof(*scripting_context)); } #ifdef NO_LIBPERL diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index d08e214ec6e7..223d88e25e05 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -2629,7 +2629,7 @@ sub do_run_test { } waitpid $child_pid, 0; - $child_exit = $?; + $child_exit = $? >> 8; my $end_time = time; $test_time = $end_time - $start_time; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c8edff6803d1..24ebd3e3eb7d 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -83,7 +83,7 @@ ifdef INSTALL_PATH done; @# Ask all targets to emit their test scripts - echo "#!/bin/bash" > $(ALL_SCRIPT) + echo "#!/bin/sh" > $(ALL_SCRIPT) echo "cd \$$(dirname \$$0)" >> $(ALL_SCRIPT) echo "ROOT=\$$PWD" >> $(ALL_SCRIPT) diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests index c09a682df56a..16058bbea7a8 100755 --- a/tools/testing/selftests/net/run_netsocktests +++ b/tools/testing/selftests/net/run_netsocktests @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh echo "--------------------" echo "running socket test" diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c index c22860ab9733..30e1ac62e8cb 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c @@ -66,7 +66,7 @@ int pmc56_overflow(void) FAIL_IF(ebb_event_enable(&event)); - mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); + mtspr(SPRN_PMC2, pmc_sample_period(sample_period)); mtspr(SPRN_PMC5, 0); mtspr(SPRN_PMC6, 0); diff --git a/virt/lib/irqbypass.c b/virt/lib/irqbypass.c index 09a03b5a21ff..e5d5dde6bf75 100644 --- a/virt/lib/irqbypass.c +++ b/virt/lib/irqbypass.c @@ -188,7 +188,7 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer) mutex_lock(&lock); list_for_each_entry(tmp, &consumers, node) { - if (tmp->token == consumer->token) { + if (tmp->token == consumer->token || tmp == consumer) { mutex_unlock(&lock); module_put(THIS_MODULE); return -EBUSY; @@ -235,7 +235,7 @@ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer) mutex_lock(&lock); list_for_each_entry(tmp, &consumers, node) { - if (tmp->token != consumer->token) + if (tmp != consumer) continue; list_for_each_entry(producer, &producers, node) { |
