diff options
884 files changed, 22123 insertions, 10027 deletions
@@ -1637,6 +1637,10 @@ S: Panoramastrasse 18 S: D-69126 Heidelberg S: Germany +N: Simon Horman +M: horms@verge.net.au +D: Renesas ARM/ARM64 SoC maintainer + N: Christopher Horn E: chorn@warwick.net D: Miscellaneous sysctl hacks diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 new file mode 100644 index 000000000000..7627d3be08f5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 @@ -0,0 +1,39 @@ +What: /sys/bus/iio/devices/iio:deviceX/ac_excitation_en +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Reading gives the state of AC excitation. + Writing '1' enables AC excitation. + +What: /sys/bus/iio/devices/iio:deviceX/bridge_switch_en +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + This bridge switch is used to disconnect it when there is a + need to minimize the system current consumption. + Reading gives the state of the bridge switch. + Writing '1' enables the bridge switch. + +What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Initiates the system calibration procedure. This is done on a + single channel at a time. Write '1' to start the calibration. + +What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode_available +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Reading returns a list with the possible calibration modes. + There are two available options: + "zero_scale" - calibrate to zero scale + "full_scale" - calibrate to full scale + +What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Sets up the calibration mode used in the system calibration + procedure. Reading returns the current calibration mode. + Writing sets the system calibration mode. diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 0fa8c0e615c2..5361ebec3361 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -615,8 +615,8 @@ on an IO device and is an example of this type. Protections ----------- -A cgroup is protected to be allocated upto the configured amount of -the resource if the usages of all its ancestors are under their +A cgroup is protected upto the configured amount of the resource +as long as the usages of all its ancestors are under their protected levels. Protections can be hard guarantees or best effort soft boundaries. Protections can also be over-committed in which case only upto the amount available to the parent is protected among @@ -1096,7 +1096,10 @@ PAGE_SIZE multiple when read back. is within its effective min boundary, the cgroup's memory won't be reclaimed under any conditions. If there is no unprotected reclaimable memory available, OOM killer - is invoked. + is invoked. Above the effective min boundary (or + effective low boundary if it is higher), pages are reclaimed + proportionally to the overage, reducing reclaim pressure for + smaller overages. Effective min boundary is limited by memory.min values of all ancestor cgroups. If there is memory.min overcommitment @@ -1118,7 +1121,10 @@ PAGE_SIZE multiple when read back. Best-effort memory protection. If the memory usage of a cgroup is within its effective low boundary, the cgroup's memory won't be reclaimed unless memory can be reclaimed - from unprotected cgroups. + from unprotected cgroups. Above the effective low boundary (or + effective min boundary if it is higher), pages are reclaimed + proportionally to the overage, reducing reclaim pressure for + smaller overages. Effective low boundary is limited by memory.low values of all ancestor cgroups. If there is memory.low overcommitment @@ -2482,8 +2488,10 @@ system performance due to overreclaim, to the point where the feature becomes self-defeating. The memory.low boundary on the other hand is a top-down allocated -reserve. A cgroup enjoys reclaim protection when it's within its low, -which makes delegation of subtrees possible. +reserve. A cgroup enjoys reclaim protection when it's within its +effective low, which makes delegation of subtrees possible. It also +enjoys having reclaim pressure proportional to its overage when +above its effective low. The original high boundary, the hard limit, is defined as a strict limit that can not budge, even if the OOM killer has to be called. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index c7ac2f3ac99f..a84a83f8881e 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5302,6 +5302,10 @@ the unplug protocol never -- do not unplug even if version check succeeds + xen_legacy_crash [X86,XEN] + Crash from Xen panic notifier, without executing late + panic() code such as dumping handler. + xen_nopvspin [X86,XEN] Disables the ticketlock slowpath using Xen PV optimizations. diff --git a/Documentation/arm64/memory.rst b/Documentation/arm64/memory.rst index b040909e45f8..02e02175e6f5 100644 --- a/Documentation/arm64/memory.rst +++ b/Documentation/arm64/memory.rst @@ -154,11 +154,18 @@ return virtual addresses to userspace from a 48-bit range. Software can "opt-in" to receiving VAs from a 52-bit space by specifying an mmap hint parameter that is larger than 48-bit. + For example: - maybe_high_address = mmap(~0UL, size, prot, flags,...); + +.. code-block:: c + + maybe_high_address = mmap(~0UL, size, prot, flags,...); It is also possible to build a debug kernel that returns addresses from a 52-bit space by enabling the following kernel config options: + +.. code-block:: sh + CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y Note that this option is only intended for debugging applications diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst index fa16a0538dcb..ab0eae1c153a 100644 --- a/Documentation/core-api/index.rst +++ b/Documentation/core-api/index.rst @@ -38,6 +38,7 @@ Core utilities protection-keys ../RCU/index gcc-plugins + symbol-namespaces Interfaces for kernel debugging diff --git a/Documentation/core-api/memory-allocation.rst b/Documentation/core-api/memory-allocation.rst index 7744aa3bf2e0..939e3dfc86e9 100644 --- a/Documentation/core-api/memory-allocation.rst +++ b/Documentation/core-api/memory-allocation.rst @@ -98,6 +98,10 @@ limited. The actual limit depends on the hardware and the kernel configuration, but it is a good practice to use `kmalloc` for objects smaller than page size. +The address of a chunk allocated with `kmalloc` is aligned to at least +ARCH_KMALLOC_MINALIGN bytes. For sizes which are a power of two, the +alignment is also guaranteed to be at least the respective size. + For large allocations you can use :c:func:`vmalloc` and :c:func:`vzalloc`, or directly request pages from the page allocator. The memory allocated by `vmalloc` and related functions is diff --git a/Documentation/kbuild/namespaces.rst b/Documentation/core-api/symbol-namespaces.rst index 982ed7b568ac..982ed7b568ac 100644 --- a/Documentation/kbuild/namespaces.rst +++ b/Documentation/core-api/symbol-namespaces.rst diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst index 25604904fa6e..ecdfdc9d4b03 100644 --- a/Documentation/dev-tools/kselftest.rst +++ b/Documentation/dev-tools/kselftest.rst @@ -89,6 +89,22 @@ To build, save output files in a separate directory with KBUILD_OUTPUT :: $ export KBUILD_OUTPUT=/tmp/kselftest; make TARGETS="size timers" kselftest +Additionally you can use the "SKIP_TARGETS" variable on the make command +line to specify one or more targets to exclude from the TARGETS list. + +To run all tests but a single subsystem:: + + $ make -C tools/testing/selftests SKIP_TARGETS=ptrace run_tests + +You can specify multiple tests to skip:: + + $ make SKIP_TARGETS="size timers" kselftest + +You can also specify a restricted list of tests to run together with a +dedicated skiplist:: + + $ make TARGETS="bpf breakpoints size timers" SKIP_TARGETS=bpf kselftest + See the top-level tools/testing/selftests/Makefile for the list of all possible targets. diff --git a/Documentation/devicetree/bindings/counter/ti-eqep.yaml b/Documentation/devicetree/bindings/counter/ti-eqep.yaml new file mode 100644 index 000000000000..85f1ff83afe7 --- /dev/null +++ b/Documentation/devicetree/bindings/counter/ti-eqep.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/ti-eqep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module + +maintainers: + - David Lechner <david@lechnology.com> + +properties: + compatible: + const: ti,am3352-eqep + + reg: + maxItems: 1 + + interrupts: + description: The eQEP event interrupt + maxItems: 1 + + clocks: + description: The clock that determines the SYSCLKOUT rate for the eQEP + peripheral. + maxItems: 1 + + clock-names: + const: sysclkout + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + eqep0: counter@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <79>; + }; + +... diff --git a/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml b/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml index 3248595dc93c..f04870d84542 100644 --- a/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml +++ b/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml @@ -85,4 +85,5 @@ examples: <&pd IMX_SC_R_DSP_RAM>; mbox-names = "txdb0", "txdb1", "rxdb0", "rxdb1"; mboxes = <&lsio_mu13 2 0>, <&lsio_mu13 2 1>, <&lsio_mu13 3 0>, <&lsio_mu13 3 1>; + memory-region = <&dsp_reserved>; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml index 676ec42e1438..567a33a83dce 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml @@ -43,13 +43,9 @@ properties: dvdd-supply: description: DVdd voltage supply - items: - - const: dvdd avdd-supply: description: AVdd voltage supply - items: - - const: avdd adi,rejection-60-Hz-enable: description: | @@ -99,6 +95,9 @@ required: examples: - | spi0 { + #address-cells = <1>; + #size-cells = <0>; + adc@0 { compatible = "adi,ad7192"; reg = <0>; diff --git a/Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt b/Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt index f01159f20d87..cd9048cf9dcf 100644 --- a/Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Should be one of: * ingenic,jz4725b-adc * ingenic,jz4740-adc + * ingenic,jz4770-adc - reg: ADC controller registers location and length. - clocks: phandle to the SoC's ADC clock. - clock-names: Must be set to "adc". diff --git a/Documentation/devicetree/bindings/iio/adc/max1027-adc.txt b/Documentation/devicetree/bindings/iio/adc/max1027-adc.txt deleted file mode 100644 index e680c61dfb84..000000000000 --- a/Documentation/devicetree/bindings/iio/adc/max1027-adc.txt +++ /dev/null @@ -1,20 +0,0 @@ -* Maxim 1027/1029/1031 Analog to Digital Converter (ADC) - -Required properties: - - compatible: Should be "maxim,max1027" or "maxim,max1029" or "maxim,max1031" - - reg: SPI chip select number for the device - - interrupts: IRQ line for the ADC - see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt - -Recommended properties: -- spi-max-frequency: Definition as per - Documentation/devicetree/bindings/spi/spi-bus.txt - -Example: -adc@0 { - compatible = "maxim,max1027"; - reg = <0>; - interrupt-parent = <&gpio5>; - interrupts = <15 IRQ_TYPE_EDGE_RISING>; - spi-max-frequency = <1000000>; -}; diff --git a/Documentation/devicetree/bindings/iio/iio-bindings.txt b/Documentation/devicetree/bindings/iio/iio-bindings.txt index 68d6f8ce063b..af33267727f4 100644 --- a/Documentation/devicetree/bindings/iio/iio-bindings.txt +++ b/Documentation/devicetree/bindings/iio/iio-bindings.txt @@ -18,12 +18,17 @@ Required properties: with a single IIO output and 1 for nodes with multiple IIO outputs. +Optional properties: +label: A symbolic name for the device. + + Example for a simple configuration with no trigger: adc: voltage-sensor@35 { compatible = "maxim,max1139"; reg = <0x35>; #io-channel-cells = <1>; + label = "voltage_feedback_group1"; }; Example for a configuration with trigger: diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt index 6d0c050d89fe..fc018ecba086 100644 --- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -14,6 +14,7 @@ Required properties: "st,lsm6ds3tr-c" "st,ism330dhcx" "st,lsm9ds1-imu" + "st,lsm6ds0" - reg: i2c address of the sensor / spi cs line Optional properties: @@ -31,6 +32,7 @@ Optional properties: - interrupts: interrupt mapping for IRQ. It should be configured with flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_EDGE_FALLING. +- wakeup-source: Enables wake up of host system on event. Refer to interrupt-controller/interrupts.txt for generic interrupt client node bindings. diff --git a/Documentation/devicetree/bindings/iio/light/adux1020.yaml b/Documentation/devicetree/bindings/iio/light/adux1020.yaml new file mode 100644 index 000000000000..69bd5c06319d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/adux1020.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/adux1020.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADUX1020 Photometric sensor + +maintainers: + - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + +description: | + Photometric sensor over an i2c interface. + https://www.analog.com/media/en/technical-documentation/data-sheets/ADUX1020.pdf + +properties: + compatible: + enum: + - adi,adux1020 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + + #address-cells = <1>; + #size-cells = <0>; + + adux1020@64 { + compatible = "adi,adux1020"; + reg = <0x64>; + interrupt-parent = <&msmgpio>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/light/bh1750.txt b/Documentation/devicetree/bindings/iio/light/bh1750.txt deleted file mode 100644 index 1e7685797d7a..000000000000 --- a/Documentation/devicetree/bindings/iio/light/bh1750.txt +++ /dev/null @@ -1,18 +0,0 @@ -ROHM BH1750 - ALS, Ambient light sensor - -Required properties: - -- compatible: Must be one of: - "rohm,bh1710" - "rohm,bh1715" - "rohm,bh1721" - "rohm,bh1750" - "rohm,bh1751" -- reg: the I2C address of the sensor - -Example: - -light-sensor@23 { - compatible = "rohm,bh1750"; - reg = <0x23>; -}; diff --git a/Documentation/devicetree/bindings/iio/light/bh1750.yaml b/Documentation/devicetree/bindings/iio/light/bh1750.yaml new file mode 100644 index 000000000000..1cc60d7ecfa0 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/bh1750.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/bh1750.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BH1750 ambient light sensor + +maintainers: + - Tomasz Duszynski <tduszyns@gmail.com> + +description: | + Ambient light sensor with an i2c interface. + +properties: + compatible: + enum: + - rohm,bh1710 + - rohm,bh1715 + - rohm,bh1721 + - rohm,bh1750 + - rohm,bh1751 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@23 { + compatible = "rohm,bh1750"; + reg = <0x23>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt b/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt deleted file mode 100644 index dd1058fbe9c3..000000000000 --- a/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt +++ /dev/null @@ -1,29 +0,0 @@ -* MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202, - mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface - for ranging - -Required properties: - - compatible: "maxbotix,mb1202", - "maxbotix,mb1212", - "maxbotix,mb1222", - "maxbotix,mb1232", - "maxbotix,mb1242", - "maxbotix,mb7040" or - "maxbotix,mb7137" - - - reg: i2c address of the device, see also i2c/i2c.txt - -Optional properties: - - interrupts: Interrupt used to announce the preceding reading - request has finished and that data is available. - If no interrupt is specified the device driver - falls back to wait a fixed amount of time until - data can be retrieved. - -Example: -proximity@70 { - compatible = "maxbotix,mb1232"; - reg = <0x70>; - interrupt-parent = <&gpio2>; - interrupts = <2 IRQ_TYPE_EDGE_FALLING>; -}; diff --git a/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml b/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml new file mode 100644 index 000000000000..3eac248f291d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/maxbotix,mb1232.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MaxBotix I2CXL-MaxSonar ultrasonic distance sensor + +maintainers: + - Andreas Klinger <ak@it-klinger.de> + +description: | + MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202, + mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface + for ranging + + Specifications about the devices can be found at: + https://www.maxbotix.com/documents/I2CXL-MaxSonar-EZ_Datasheet.pdf + +properties: + compatible: + enum: + - maxbotix,mb1202 + - maxbotix,mb1212 + - maxbotix,mb1222 + - maxbotix,mb1232 + - maxbotix,mb1242 + - maxbotix,mb7040 + - maxbotix,mb7137 + + reg: + maxItems: 1 + + interrupts: + description: + Interrupt used to announce the preceding reading request has finished + and that data is available. If no interrupt is specified the device + driver falls back to wait a fixed amount of time until data can be + retrieved. + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + proximity@70 { + compatible = "maxbotix,mb1232"; + reg = <0x70>; + interrupt-parent = <&gpio2>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/rc.yaml b/Documentation/devicetree/bindings/media/rc.yaml index 3d5c154fd230..9054555e6608 100644 --- a/Documentation/devicetree/bindings/media/rc.yaml +++ b/Documentation/devicetree/bindings/media/rc.yaml @@ -73,7 +73,6 @@ properties: - rc-genius-tvgo-a11mce - rc-gotview7135 - rc-hauppauge - - rc-hauppauge - rc-hisi-poplar - rc-hisi-tv-demo - rc-imon-mce diff --git a/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml index 8a56a8526cef..a97482179cf5 100644 --- a/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml @@ -37,7 +37,7 @@ properties: - description: exclusive PHY reset line - description: shared reset line between the PCIe PHY and PCIe controller - resets-names: + reset-names: items: - const: phy - const: pcie diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index dd63151dc8b6..b143d9a21b2d 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -26,6 +26,8 @@ Required properties: - "renesas,hscif-r8a77470" for R8A77470 (RZ/G1C) HSCIF compatible UART. - "renesas,scif-r8a774a1" for R8A774A1 (RZ/G2M) SCIF compatible UART. - "renesas,hscif-r8a774a1" for R8A774A1 (RZ/G2M) HSCIF compatible UART. + - "renesas,scif-r8a774b1" for R8A774B1 (RZ/G2N) SCIF compatible UART. + - "renesas,hscif-r8a774b1" for R8A774B1 (RZ/G2N) HSCIF compatible UART. - "renesas,scif-r8a774c0" for R8A774C0 (RZ/G2E) SCIF compatible UART. - "renesas,hscif-r8a774c0" for R8A774C0 (RZ/G2E) HSCIF compatible UART. - "renesas,scif-r8a7778" for R8A7778 (R-Car M1) SCIF compatible UART. diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 870ac52d2225..765fd1c170df 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -114,6 +114,18 @@ properties: - isil,isl68137 # 5 Bit Programmable, Pulse-Width Modulator - maxim,ds1050 + # 10-bit 8 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1027 + # 10-bit 12 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1029 + # 10-bit 16 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1031 + # 12-bit 8 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1227 + # 12-bit 12 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1229 + # 12-bit 16 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1231 # Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs - maxim,max1237 # PECI-to-I2C translator for PECI-to-SMBus/I2C protocol conversion diff --git a/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt index b9f04e617eb7..6ffb09be7a76 100644 --- a/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt +++ b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt @@ -85,8 +85,8 @@ A child node must exist to represent the core DWC2 IP block. The name of the node is not important. The content of the node is defined in dwc2.txt. PHY documentation is provided in the following places: -- Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt -- Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt +- Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb2-phy.yaml +- Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb3-pcie-phy.yaml Example device nodes: usb: usb@ffe09000 { diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 059f6ef1ad4a..1ca64c85191a 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -63,7 +63,11 @@ properties: description: Set this flag to force EHCI reset after resume. - phys: true + phys: + description: PHY specifier for the USB PHY + + phy-names: + const: usb required: - compatible @@ -89,6 +93,7 @@ examples: interrupts = <39>; clocks = <&ahb_gates 1>; phys = <&usbphy 1>; + phy-names = "usb"; }; ... diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index da5a14becbe5..bcffec1f1341 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -67,7 +67,11 @@ properties: description: Overrides the detected port count - phys: true + phys: + description: PHY specifier for the USB PHY + + phy-names: + const: usb required: - compatible @@ -84,6 +88,7 @@ examples: interrupts = <64>; clocks = <&usb_clk 6>, <&ahb_gates 2>; phys = <&usbphy 1>; + phy-names = "usb"; }; ... diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt index f3e4acecabe8..42d8814f903a 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt @@ -33,7 +33,7 @@ Required properties: "dma_ck": dma_bus clock for data transfer by DMA, "xhci_ck": controller clock - - phys : see usb-hcd.txt in the current directory + - phys : see usb-hcd.yaml in the current directory Optional properties: - wakeup-source : enable USB remote wakeup; @@ -53,7 +53,7 @@ Optional properties: See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt - imod-interval-ns: default interrupt moderation interval is 5000ns -additionally the properties from usb-hcd.txt (in the current directory) are +additionally the properties from usb-hcd.yaml (in the current directory) are supported. Example: diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt index b9af7f5ee91d..e0ae6096f7ac 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt @@ -17,7 +17,7 @@ Required properties: - clock-names : must contain "sys_ck" for clock of controller, the following clocks are optional: "ref_ck", "mcu_ck" and "dma_ck"; - - phys : see usb-hcd.txt in the current directory + - phys : see usb-hcd.yaml in the current directory - dr_mode : should be one of "host", "peripheral" or "otg", refer to usb/generic.txt @@ -60,7 +60,7 @@ Optional properties: - mediatek,u3p-dis-msk : mask to disable u3ports, bit0 for u3port0, bit1 for u3port1, ... etc; -additionally the properties from usb-hcd.txt (in the current directory) are +additionally the properties from usb-hcd.yaml (in the current directory) are supported. Sub-nodes: diff --git a/Documentation/devicetree/bindings/usb/usb-hcd.yaml b/Documentation/devicetree/bindings/usb/usb-hcd.yaml index 9c8c56d3a792..7263b7f2b510 100644 --- a/Documentation/devicetree/bindings/usb/usb-hcd.yaml +++ b/Documentation/devicetree/bindings/usb/usb-hcd.yaml @@ -18,8 +18,13 @@ properties: description: List of all the USB PHYs on this HCD + phy-names: + description: + Name specifier for the USB PHY + examples: - | usb { phys = <&usb2_phy1>, <&usb3_phy1>; + phy-names = "usb"; }; diff --git a/Documentation/devicetree/bindings/usb/usb-uhci.txt b/Documentation/devicetree/bindings/usb/usb-uhci.txt index cc2e6f7d602e..d1702eb2c8bd 100644 --- a/Documentation/devicetree/bindings/usb/usb-uhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-uhci.txt @@ -6,7 +6,7 @@ Required properties: - reg : Should contain 1 register ranges(address and length) - interrupts : UHCI controller interrupt -additionally the properties from usb-hcd.txt (in the current directory) are +additionally the properties from usb-hcd.yaml (in the current directory) are supported. Example: diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index 97400e8f8605..b49b819571f9 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -41,9 +41,9 @@ Optional properties: - usb3-lpm-capable: determines if platform is USB3 LPM capable - quirk-broken-port-ped: set if the controller has broken port disable mechanism - imod-interval-ns: default interrupt moderation interval is 5000ns - - phys : see usb-hcd.txt in the current directory + - phys : see usb-hcd.yaml in the current directory -additionally the properties from usb-hcd.txt (in the current directory) are +additionally the properties from usb-hcd.yaml (in the current directory) are supported. diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 8147c3f218bf..230ad59b462b 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -7,6 +7,7 @@ Linux Hardware Monitoring hwmon-kernel-api pmbus-core + inspur-ipsps1 submitting-patches sysfs-interface userspace-tools diff --git a/Documentation/hwmon/inspur-ipsps1.rst b/Documentation/hwmon/inspur-ipsps1.rst index 2b871ae3448f..292c0c26bdd1 100644 --- a/Documentation/hwmon/inspur-ipsps1.rst +++ b/Documentation/hwmon/inspur-ipsps1.rst @@ -1,5 +1,5 @@ Kernel driver inspur-ipsps1 -======================= +=========================== Supported chips: diff --git a/Documentation/hwmon/k10temp.rst b/Documentation/hwmon/k10temp.rst index 12a86ba17de9..4451d59b9425 100644 --- a/Documentation/hwmon/k10temp.rst +++ b/Documentation/hwmon/k10temp.rst @@ -21,10 +21,17 @@ Supported chips: * AMD Family 14h processors: "Brazos" (C/E/G/Z-Series) -* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri", "Carrizo" +* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri", + "Carrizo", "Stoney Ridge", "Bristol Ridge" * AMD Family 16h processors: "Kabini", "Mullins" +* AMD Family 17h processors: "Zen", "Zen 2" + +* AMD Family 18h processors: "Hygon Dhyana" + +* AMD Family 19h processors: "Zen 3" + Prefix: 'k10temp' Addresses scanned: PCI space @@ -110,3 +117,12 @@ The maximum value for Tctl is available in the file temp1_max. If the BIOS has enabled hardware temperature control, the threshold at which the processor will throttle itself to avoid damage is available in temp1_crit and temp1_crit_hyst. + +On some AMD CPUs, there is a difference between the die temperature (Tdie) and +the reported temperature (Tctl). Tdie is the real measured temperature, and +Tctl is used for fan control. While Tctl is always available as temp1_input, +the driver exports Tdie temperature as temp2_input for those CPUs which support +it. + +Models from 17h family report relative temperature, the driver aims to +compensate and report the real temperature. diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 6ba9d5365ff3..b89c88168d6a 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -954,11 +954,6 @@ When kbuild executes, the following steps are followed (roughly): From commandline LDFLAGS_MODULE shall be used (see kbuild.txt). - KBUILD_ARFLAGS Options for $(AR) when creating archives - - $(KBUILD_ARFLAGS) set by the top level Makefile to "D" (deterministic - mode) if this option is supported by $(AR). - KBUILD_LDS The linker script with full path. Assigned by the top-level Makefile. diff --git a/Documentation/kbuild/modules.rst b/Documentation/kbuild/modules.rst index d2ae799237fd..774a998dcf37 100644 --- a/Documentation/kbuild/modules.rst +++ b/Documentation/kbuild/modules.rst @@ -498,10 +498,11 @@ build. will be written containing all exported symbols that were not defined in the kernel. ---- 6.3 Symbols From Another External Module +6.3 Symbols From Another External Module +---------------------------------------- Sometimes, an external module uses exported symbols from - another external module. kbuild needs to have full knowledge of + another external module. Kbuild needs to have full knowledge of all symbols to avoid spitting out warnings about undefined symbols. Three solutions exist for this situation. @@ -521,7 +522,7 @@ build. The top-level kbuild file would then look like:: #./Kbuild (or ./Makefile): - obj-y := foo/ bar/ + obj-m := foo/ bar/ And executing:: diff --git a/Documentation/kbuild/reproducible-builds.rst b/Documentation/kbuild/reproducible-builds.rst index ab92e98c89c8..503393854e2e 100644 --- a/Documentation/kbuild/reproducible-builds.rst +++ b/Documentation/kbuild/reproducible-builds.rst @@ -16,16 +16,21 @@ the kernel may be unreproducible, and how to avoid them. Timestamps ---------- -The kernel embeds a timestamp in two places: +The kernel embeds timestamps in three places: * The version string exposed by ``uname()`` and included in ``/proc/version`` * File timestamps in the embedded initramfs -By default the timestamp is the current time. This must be overridden -using the `KBUILD_BUILD_TIMESTAMP`_ variable. If you are building -from a git commit, you could use its commit date. +* If enabled via ``CONFIG_IKHEADERS``, file timestamps of kernel + headers embedded in the kernel or respective module, + exposed via ``/sys/kernel/kheaders.tar.xz`` + +By default the timestamp is the current time and in the case of +``kheaders`` the various files' modification times. This must +be overridden using the `KBUILD_BUILD_TIMESTAMP`_ variable. +If you are building from a git commit, you could use its commit date. The kernel does *not* use the ``__DATE__`` and ``__TIME__`` macros, and enables warnings if they are used. If you incorporate external diff --git a/Documentation/networking/device_drivers/index.rst b/Documentation/networking/device_drivers/index.rst index f51f92571e39..c1f7f75e5fd9 100644 --- a/Documentation/networking/device_drivers/index.rst +++ b/Documentation/networking/device_drivers/index.rst @@ -23,6 +23,7 @@ Contents: intel/ice google/gve mellanox/mlx5 + netronome/nfp pensando/ionic .. only:: subproject and html diff --git a/Documentation/networking/j1939.rst b/Documentation/networking/j1939.rst index ce7e7a044e08..dc60b13fcd09 100644 --- a/Documentation/networking/j1939.rst +++ b/Documentation/networking/j1939.rst @@ -272,7 +272,7 @@ supported flags are: * MSG_DONTWAIT, i.e. non-blocking operation. recvmsg(2) -^^^^^^^^^ +^^^^^^^^^^ In most cases recvmsg(2) is needed if you want to extract more information than recvfrom(2) can provide. For example package priority and timestamp. The diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index f4a2198187f9..ada573b7d703 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -56,7 +56,7 @@ instead of ``double-indenting`` the ``case`` labels. E.g.: case 'K': case 'k': mem <<= 10; - /* fall through */ + fallthrough; default: break; } diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst index 053b24a6dd38..179f2a5625a0 100644 --- a/Documentation/process/deprecated.rst +++ b/Documentation/process/deprecated.rst @@ -122,14 +122,27 @@ memory adjacent to the stack (when built without `CONFIG_VMAP_STACK=y`) Implicit switch case fall-through --------------------------------- -The C language allows switch cases to "fall through" when -a "break" statement is missing at the end of a case. This, -however, introduces ambiguity in the code, as it's not always -clear if the missing break is intentional or a bug. As there -have been a long list of flaws `due to missing "break" statements +The C language allows switch cases to "fall-through" when a "break" statement +is missing at the end of a case. This, however, introduces ambiguity in the +code, as it's not always clear if the missing break is intentional or a bug. + +As there have been a long list of flaws `due to missing "break" statements <https://cwe.mitre.org/data/definitions/484.html>`_, we no longer allow -"implicit fall-through". In order to identify an intentional fall-through -case, we have adopted the marking used by static analyzers: a comment -saying `/* Fall through */`. Once the C++17 `__attribute__((fallthrough))` -is more widely handled by C compilers, static analyzers, and IDEs, we can -switch to using that instead. +"implicit fall-through". + +In order to identify intentional fall-through cases, we have adopted a +pseudo-keyword macro 'fallthrough' which expands to gcc's extension +__attribute__((__fallthrough__)). `Statement Attributes +<https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html>`_ + +When the C17/C18 [[fallthrough]] syntax is more commonly supported by +C compilers, static analyzers, and IDEs, we can switch to using that syntax +for the macro pseudo-keyword. + +All switch/case blocks must end in one of: + + break; + fallthrough; + continue; + goto <label>; + return [expression]; diff --git a/Documentation/usb/rio.rst b/Documentation/usb/rio.rst deleted file mode 100644 index ea73475471db..000000000000 --- a/Documentation/usb/rio.rst +++ /dev/null @@ -1,109 +0,0 @@ -============ -Diamonds Rio -============ - -Copyright (C) 1999, 2000 Bruce Tenison - -Portions Copyright (C) 1999, 2000 David Nelson - -Thanks to David Nelson for guidance and the usage of the scanner.txt -and scanner.c files to model our driver and this informative file. - -Mar. 2, 2000 - -Changes -======= - -- Initial Revision - - -Overview -======== - -This README will address issues regarding how to configure the kernel -to access a RIO 500 mp3 player. -Before I explain how to use this to access the Rio500 please be warned: - -.. warning:: - - Please note that this software is still under development. The authors - are in no way responsible for any damage that may occur, no matter how - inconsequential. - -It seems that the Rio has a problem when sending .mp3 with low batteries. -I suggest when the batteries are low and you want to transfer stuff that you -replace it with a fresh one. In my case, what happened is I lost two 16kb -blocks (they are no longer usable to store information to it). But I don't -know if that's normal or not; it could simply be a problem with the flash -memory. - -In an extreme case, I left my Rio playing overnight and the batteries wore -down to nothing and appear to have corrupted the flash memory. My RIO -needed to be replaced as a result. Diamond tech support is aware of the -problem. Do NOT allow your batteries to wear down to nothing before -changing them. It appears RIO 500 firmware does not handle low battery -power well at all. - -On systems with OHCI controllers, the kernel OHCI code appears to have -power on problems with some chipsets. If you are having problems -connecting to your RIO 500, try turning it on first and then plugging it -into the USB cable. - -Contact Information -------------------- - - The main page for the project is hosted at sourceforge.net in the following - URL: <http://rio500.sourceforge.net>. You can also go to the project's - sourceforge home page at: <http://sourceforge.net/projects/rio500/>. - There is also a mailing list: rio500-users@lists.sourceforge.net - -Authors -------- - -Most of the code was written by Cesar Miquel <miquel@df.uba.ar>. Keith -Clayton <kclayton@jps.net> is incharge of the PPC port and making sure -things work there. Bruce Tenison <btenison@dibbs.net> is adding support -for .fon files and also does testing. The program will mostly sure be -re-written and Pete Ikusz along with the rest will re-design it. I would -also like to thank Tri Nguyen <tmn_3022000@hotmail.com> who provided use -with some important information regarding the communication with the Rio. - -Additional Information and userspace tools - - http://rio500.sourceforge.net/ - - -Requirements -============ - -A host with a USB port running a Linux kernel with RIO 500 support enabled. - -The driver is a module called rio500, which should be automatically loaded -as you plug in your device. If that fails you can manually load it with - - modprobe rio500 - -Udev should automatically create a device node as soon as plug in your device. -If that fails, you can manually add a device for the USB rio500:: - - mknod /dev/usb/rio500 c 180 64 - -In that case, set appropriate permissions for /dev/usb/rio500 (don't forget -about group and world permissions). Both read and write permissions are -required for proper operation. - -That's it. The Rio500 Utils at: http://rio500.sourceforge.net should -be able to access the rio500. - -Limits -====== - -You can use only a single rio500 device at a time with your computer. - -Bugs -==== - -If you encounter any problems feel free to drop me an email. - -Bruce Tenison -btenison@dibbs.net diff --git a/MAINTAINERS b/MAINTAINERS index 19993e872f26..31f2e621f972 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2166,12 +2166,10 @@ F: arch/arm64/boot/dts/realtek/ F: Documentation/devicetree/bindings/arm/realtek.yaml ARM/RENESAS ARM64 ARCHITECTURE -M: Simon Horman <horms@verge.net.au> M: Geert Uytterhoeven <geert+renesas@glider.be> M: Magnus Damm <magnus.damm@gmail.com> L: linux-renesas-soc@vger.kernel.org Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git next T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next S: Supported F: arch/arm64/boot/dts/renesas/ @@ -2283,12 +2281,10 @@ S: Maintained F: drivers/media/platform/s5p-mfc/ ARM/SHMOBILE ARM ARCHITECTURE -M: Simon Horman <horms@verge.net.au> M: Geert Uytterhoeven <geert+renesas@glider.be> M: Magnus Damm <magnus.damm@gmail.com> L: linux-renesas-soc@vger.kernel.org Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git next T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next S: Supported F: arch/arm/boot/dts/emev2* @@ -6113,7 +6109,10 @@ M: Gao Xiang <gaoxiang25@huawei.com> M: Chao Yu <yuchao0@huawei.com> L: linux-erofs@lists.ozlabs.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git +F: Documentation/filesystems/erofs.txt F: fs/erofs/ +F: include/trace/events/erofs.h ERRSEQ ERROR TRACKING INFRASTRUCTURE M: Jeff Layton <jlayton@kernel.org> @@ -9076,6 +9075,7 @@ F: security/keys/ KGDB / KDB /debug_core M: Jason Wessel <jason.wessel@windriver.com> M: Daniel Thompson <daniel.thompson@linaro.org> +R: Douglas Anderson <dianders@chromium.org> W: http://kgdb.wiki.kernel.org/ L: kgdb-bugreport@lists.sourceforge.net T: git git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb.git @@ -9184,6 +9184,7 @@ M: Pavel Machek <pavel@ucw.cz> R: Dan Murphy <dmurphy@ti.com> L: linux-leds@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git S: Maintained F: Documentation/devicetree/bindings/leds/ F: drivers/leds/ @@ -11544,6 +11545,7 @@ NSDEPS M: Matthias Maennich <maennich@google.com> S: Maintained F: scripts/nsdeps +F: Documentation/core-api/symbol-namespaces.rst NTB AMD DRIVER M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> @@ -13952,6 +13954,12 @@ L: linux-serial@vger.kernel.org S: Odd Fixes F: drivers/tty/serial/rp2.* +ROHM BH1750 AMBIENT LIGHT SENSOR DRIVER +M: Tomasz Duszynski <tduszyns@gmail.com> +S: Maintained +F: drivers/iio/light/bh1750.c +F: Documentation/devicetree/bindings/iio/light/bh1750.yaml + ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS M: Marek Vasut <marek.vasut+renesas@gmail.com> L: linux-kernel@vger.kernel.org @@ -14803,6 +14811,11 @@ S: Maintained F: drivers/input/touchscreen/silead.c F: drivers/platform/x86/touchscreen_dmi.c +SILICON LABS WIRELESS DRIVERS (for WFxxx series) +M: Jérôme Pouiller <jerome.pouiller@silabs.com> +S: Supported +F: drivers/staging/wfx/ + SILICON MOTION SM712 FRAME BUFFER DRIVER M: Sudip Mukherjee <sudipm.mukherjee@gmail.com> M: Teddy Wang <teddy.wang@siliconmotion.com> @@ -16203,6 +16216,12 @@ S: Maintained F: drivers/media/platform/davinci/ F: include/media/davinci/ +TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER +R: David Lechner <david@lechnology.com> +L: linux-iio@vger.kernel.org +F: Documentation/devicetree/bindings/counter/ti-eqep.yaml +F: drivers/counter/ti-eqep.c + TI ETHERNET SWITCH DRIVER (CPSW) R: Grygorii Strashko <grygorii.strashko@ti.com> L: linux-omap@vger.kernel.org @@ -16763,13 +16782,6 @@ W: http://www.linux-usb.org/usbnet S: Maintained F: drivers/net/usb/dm9601.c -USB DIAMOND RIO500 DRIVER -M: Cesar Miquel <miquel@df.uba.ar> -L: rio500-users@lists.sourceforge.net -W: http://rio500.sourceforge.net -S: Maintained -F: drivers/usb/misc/rio500* - USB EHCI DRIVER M: Alan Stern <stern@rowland.harvard.edu> L: linux-usb@vger.kernel.org @@ -2,8 +2,8 @@ VERSION = 5 PATCHLEVEL = 4 SUBLEVEL = 0 -EXTRAVERSION = -rc1 -NAME = Bobtail Squid +EXTRAVERSION = -rc3 +NAME = Nesting Opossum # *DOCUMENTATION* # To see a list of typical targets execute "make help" @@ -206,24 +206,8 @@ ifndef KBUILD_CHECKSRC KBUILD_CHECKSRC = 0 endif -# Use make M=dir to specify directory of external module to build -# Old syntax make ... SUBDIRS=$PWD is still supported -# Setting the environment variable KBUILD_EXTMOD take precedence -ifdef SUBDIRS - $(warning ================= WARNING ================) - $(warning 'SUBDIRS' will be removed after Linux 5.3) - $(warning ) - $(warning If you are building an individual subdirectory) - $(warning in the kernel tree, you can do like this:) - $(warning $$ make path/to/dir/you/want/to/build/) - $(warning (Do not forget the trailing slash)) - $(warning ) - $(warning If you are building an external module,) - $(warning Please use 'M=' or 'KBUILD_EXTMOD' instead) - $(warning ==========================================) - KBUILD_EXTMOD ?= $(SUBDIRS) -endif - +# Use make M=dir or set the environment variable KBUILD_EXTMOD to specify the +# directory of external module to build. Setting M= takes precedence. ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif @@ -498,7 +482,6 @@ export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL -export KBUILD_ARFLAGS # Files to ignore in find ... statements @@ -616,7 +599,7 @@ endif # in addition to whatever we do anyway. # Just "make" or "make all" shall build modules as well -ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) +ifneq ($(filter all _all modules nsdeps,$(MAKECMDGOALS)),) KBUILD_MODULES := 1 endif @@ -914,9 +897,6 @@ ifdef CONFIG_RETPOLINE KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none) endif -# use the deterministic mode of AR if available -KBUILD_ARFLAGS := $(call ar-option,D) - include scripts/Makefile.kasan include scripts/Makefile.extrawarn include scripts/Makefile.ubsan @@ -1237,9 +1217,8 @@ PHONY += kselftest kselftest: $(Q)$(MAKE) -C $(srctree)/tools/testing/selftests run_tests -PHONY += kselftest-clean -kselftest-clean: - $(Q)$(MAKE) -C $(srctree)/tools/testing/selftests clean +kselftest-%: FORCE + $(Q)$(MAKE) -C $(srctree)/tools/testing/selftests $* PHONY += kselftest-merge kselftest-merge: diff --git a/arch/arm/boot/dts/am335x-icev2.dts b/arch/arm/boot/dts/am335x-icev2.dts index 18f70b35da4c..204bccfcc110 100644 --- a/arch/arm/boot/dts/am335x-icev2.dts +++ b/arch/arm/boot/dts/am335x-icev2.dts @@ -432,7 +432,7 @@ pinctrl-0 = <&mmc0_pins_default>; }; -&gpio0 { +&gpio0_target { /* Do not idle the GPIO used for holding the VTT regulator */ ti,no-reset-on-init; ti,no-idle-on-init; diff --git a/arch/arm/boot/dts/am33xx-l4.dtsi b/arch/arm/boot/dts/am33xx-l4.dtsi index 9915c891e05f..7a9eb2b0d45b 100644 --- a/arch/arm/boot/dts/am33xx-l4.dtsi +++ b/arch/arm/boot/dts/am33xx-l4.dtsi @@ -127,7 +127,7 @@ ranges = <0x0 0x5000 0x1000>; }; - target-module@7000 { /* 0x44e07000, ap 14 20.0 */ + gpio0_target: target-module@7000 { /* 0x44e07000, ap 14 20.0 */ compatible = "ti,sysc-omap2", "ti,sysc"; ti,hwmods = "gpio1"; reg = <0x7000 0x4>, @@ -2038,7 +2038,9 @@ reg = <0xe000 0x4>, <0xe054 0x4>; reg-names = "rev", "sysc"; - ti,sysc-midle ; + ti,sysc-midle = <SYSC_IDLE_FORCE>, + <SYSC_IDLE_NO>, + <SYSC_IDLE_SMART>; ti,sysc-sidle = <SYSC_IDLE_FORCE>, <SYSC_IDLE_NO>, <SYSC_IDLE_SMART>; diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index 848e2a8884e2..14bbc438055f 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -337,6 +337,8 @@ ti,hwmods = "dss_dispc"; clocks = <&disp_clk>; clock-names = "fck"; + + max-memory-bandwidth = <230000000>; }; rfbi: rfbi@4832a800 { diff --git a/arch/arm/boot/dts/dra7-l4.dtsi b/arch/arm/boot/dts/dra7-l4.dtsi index ea0e7c19eb4e..5cac2dd58241 100644 --- a/arch/arm/boot/dts/dra7-l4.dtsi +++ b/arch/arm/boot/dts/dra7-l4.dtsi @@ -2732,7 +2732,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 129 1>, <&edma_xbar 128 1>; dma-names = "tx", "rx"; - clocks = <&ipu_clkctrl DRA7_IPU_MCASP1_CLKCTRL 22>, + clocks = <&ipu_clkctrl DRA7_IPU_MCASP1_CLKCTRL 0>, <&ipu_clkctrl DRA7_IPU_MCASP1_CLKCTRL 24>, <&ipu_clkctrl DRA7_IPU_MCASP1_CLKCTRL 28>; clock-names = "fck", "ahclkx", "ahclkr"; @@ -2768,8 +2768,8 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 131 1>, <&edma_xbar 130 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP2_CLKCTRL 22>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP2_CLKCTRL 24>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP2_CLKCTRL 0>, + <&ipu_clkctrl DRA7_IPU_MCASP1_CLKCTRL 24>, <&l4per2_clkctrl DRA7_L4PER2_MCASP2_CLKCTRL 28>; clock-names = "fck", "ahclkx", "ahclkr"; status = "disabled"; @@ -2786,9 +2786,8 @@ <SYSC_IDLE_SMART>; /* Domains (P, C): l4per_pwrdm, l4per2_clkdm */ clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 0>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 24>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 28>; - clock-names = "fck", "ahclkx", "ahclkr"; + <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 24>; + clock-names = "fck", "ahclkx"; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x68000 0x2000>, @@ -2804,7 +2803,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 133 1>, <&edma_xbar 132 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 22>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 0>, <&l4per2_clkctrl DRA7_L4PER2_MCASP3_CLKCTRL 24>; clock-names = "fck", "ahclkx"; status = "disabled"; @@ -2821,9 +2820,8 @@ <SYSC_IDLE_SMART>; /* Domains (P, C): l4per_pwrdm, l4per2_clkdm */ clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 0>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 24>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 28>; - clock-names = "fck", "ahclkx", "ahclkr"; + <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 24>; + clock-names = "fck", "ahclkx"; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x6c000 0x2000>, @@ -2839,7 +2837,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 135 1>, <&edma_xbar 134 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 22>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 0>, <&l4per2_clkctrl DRA7_L4PER2_MCASP4_CLKCTRL 24>; clock-names = "fck", "ahclkx"; status = "disabled"; @@ -2856,9 +2854,8 @@ <SYSC_IDLE_SMART>; /* Domains (P, C): l4per_pwrdm, l4per2_clkdm */ clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 0>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 24>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 28>; - clock-names = "fck", "ahclkx", "ahclkr"; + <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 24>; + clock-names = "fck", "ahclkx"; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x70000 0x2000>, @@ -2874,7 +2871,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 137 1>, <&edma_xbar 136 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 22>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 0>, <&l4per2_clkctrl DRA7_L4PER2_MCASP5_CLKCTRL 24>; clock-names = "fck", "ahclkx"; status = "disabled"; @@ -2891,9 +2888,8 @@ <SYSC_IDLE_SMART>; /* Domains (P, C): l4per_pwrdm, l4per2_clkdm */ clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 0>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 24>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 28>; - clock-names = "fck", "ahclkx", "ahclkr"; + <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 24>; + clock-names = "fck", "ahclkx"; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x74000 0x2000>, @@ -2909,7 +2905,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 139 1>, <&edma_xbar 138 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 22>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 0>, <&l4per2_clkctrl DRA7_L4PER2_MCASP6_CLKCTRL 24>; clock-names = "fck", "ahclkx"; status = "disabled"; @@ -2926,9 +2922,8 @@ <SYSC_IDLE_SMART>; /* Domains (P, C): l4per_pwrdm, l4per2_clkdm */ clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 0>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 24>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 28>; - clock-names = "fck", "ahclkx", "ahclkr"; + <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 24>; + clock-names = "fck", "ahclkx"; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x78000 0x2000>, @@ -2944,7 +2939,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 141 1>, <&edma_xbar 140 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 22>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 0>, <&l4per2_clkctrl DRA7_L4PER2_MCASP7_CLKCTRL 24>; clock-names = "fck", "ahclkx"; status = "disabled"; @@ -2961,9 +2956,8 @@ <SYSC_IDLE_SMART>; /* Domains (P, C): l4per_pwrdm, l4per2_clkdm */ clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 0>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 24>, - <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 28>; - clock-names = "fck", "ahclkx", "ahclkr"; + <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 24>; + clock-names = "fck", "ahclkx"; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x7c000 0x2000>, @@ -2979,7 +2973,7 @@ interrupt-names = "tx", "rx"; dmas = <&edma_xbar 143 1>, <&edma_xbar 142 1>; dma-names = "tx", "rx"; - clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 22>, + clocks = <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 0>, <&l4per2_clkctrl DRA7_L4PER2_MCASP8_CLKCTRL 24>; clock-names = "fck", "ahclkx"; status = "disabled"; diff --git a/arch/arm/boot/dts/omap3-gta04.dtsi b/arch/arm/boot/dts/omap3-gta04.dtsi index d01fc8744fd7..b6ef1a7ac8a4 100644 --- a/arch/arm/boot/dts/omap3-gta04.dtsi +++ b/arch/arm/boot/dts/omap3-gta04.dtsi @@ -124,6 +124,7 @@ spi-max-frequency = <100000>; spi-cpol; spi-cpha; + spi-cs-high; backlight= <&backlight>; label = "lcd"; diff --git a/arch/arm/boot/dts/ste-dbx5x0.dtsi b/arch/arm/boot/dts/ste-dbx5x0.dtsi index a53657b83288..bda454d12150 100644 --- a/arch/arm/boot/dts/ste-dbx5x0.dtsi +++ b/arch/arm/boot/dts/ste-dbx5x0.dtsi @@ -8,6 +8,7 @@ #include <dt-bindings/mfd/dbx500-prcmu.h> #include <dt-bindings/arm/ux500_pm_domains.h> #include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/thermal/thermal.h> / { #address-cells = <1>; @@ -59,8 +60,12 @@ * cooling. */ cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <1000>; + polling-delay-passive = <250>; + /* + * This sensor fires interrupts to update the thermal + * zone, so no polling is needed. + */ + polling-delay = <0>; thermal-sensors = <&thermal>; @@ -79,7 +84,7 @@ cooling-maps { trip = <&cpu_alert>; - cooling-device = <&CPU0 0 2>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; contribution = <100>; }; }; diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index ce823c44e98a..4c268b70b735 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -520,6 +520,7 @@ interrupts = <39>; clocks = <&ccu CLK_AHB_EHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -529,6 +530,7 @@ interrupts = <64>; clocks = <&ccu CLK_USB_OHCI0>, <&ccu CLK_AHB_OHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -608,6 +610,7 @@ interrupts = <40>; clocks = <&ccu CLK_AHB_EHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; @@ -617,6 +620,7 @@ interrupts = <65>; clocks = <&ccu CLK_USB_OHCI1>, <&ccu CLK_AHB_OHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun5i.dtsi b/arch/arm/boot/dts/sun5i.dtsi index cfb1efc8828c..6befa236ba99 100644 --- a/arch/arm/boot/dts/sun5i.dtsi +++ b/arch/arm/boot/dts/sun5i.dtsi @@ -391,6 +391,7 @@ interrupts = <39>; clocks = <&ccu CLK_AHB_EHCI>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -400,6 +401,7 @@ interrupts = <40>; clocks = <&ccu CLK_USB_OHCI>, <&ccu CLK_AHB_OHCI>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index bbeb743633c6..ac7638078420 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -545,6 +545,7 @@ clocks = <&ccu CLK_AHB1_EHCI0>; resets = <&ccu RST_AHB1_EHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -555,6 +556,7 @@ clocks = <&ccu CLK_AHB1_OHCI0>, <&ccu CLK_USB_OHCI0>; resets = <&ccu RST_AHB1_OHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -565,6 +567,7 @@ clocks = <&ccu CLK_AHB1_EHCI1>; resets = <&ccu RST_AHB1_EHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; @@ -575,6 +578,7 @@ clocks = <&ccu CLK_AHB1_OHCI1>, <&ccu CLK_USB_OHCI1>; resets = <&ccu RST_AHB1_OHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 49380de754a9..874231be04e4 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -623,6 +623,7 @@ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ccu CLK_AHB_EHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -632,6 +633,7 @@ interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ccu CLK_USB_OHCI0>, <&ccu CLK_AHB_OHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -714,6 +716,7 @@ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ccu CLK_AHB_EHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; @@ -723,6 +726,7 @@ interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ccu CLK_USB_OHCI1>, <&ccu CLK_AHB_OHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi b/arch/arm/boot/dts/sun8i-a23-a33.dtsi index 52eed0ae3607..f292f96ab39b 100644 --- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi +++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi @@ -307,6 +307,7 @@ clocks = <&ccu CLK_BUS_EHCI>; resets = <&ccu RST_BUS_EHCI>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -317,6 +318,7 @@ clocks = <&ccu CLK_BUS_OHCI>, <&ccu CLK_USB_OHCI>; resets = <&ccu RST_BUS_OHCI>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi index 523be6611c50..74bb053cf23c 100644 --- a/arch/arm/boot/dts/sun8i-a83t.dtsi +++ b/arch/arm/boot/dts/sun8i-a83t.dtsi @@ -632,6 +632,7 @@ clocks = <&ccu CLK_BUS_EHCI0>; resets = <&ccu RST_BUS_EHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -643,6 +644,7 @@ clocks = <&ccu CLK_BUS_OHCI0>, <&ccu CLK_USB_OHCI0>; resets = <&ccu RST_BUS_OHCI0>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -654,6 +656,7 @@ clocks = <&ccu CLK_BUS_EHCI1>; resets = <&ccu RST_BUS_EHCI1>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun8i-r40.dtsi b/arch/arm/boot/dts/sun8i-r40.dtsi index bde068111b85..c9c2688db66d 100644 --- a/arch/arm/boot/dts/sun8i-r40.dtsi +++ b/arch/arm/boot/dts/sun8i-r40.dtsi @@ -273,6 +273,7 @@ clocks = <&ccu CLK_BUS_EHCI1>; resets = <&ccu RST_BUS_EHCI1>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -284,6 +285,7 @@ <&ccu CLK_USB_OHCI1>; resets = <&ccu RST_BUS_OHCI1>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -294,6 +296,7 @@ clocks = <&ccu CLK_BUS_EHCI2>; resets = <&ccu RST_BUS_EHCI2>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; @@ -305,6 +308,7 @@ <&ccu CLK_USB_OHCI2>; resets = <&ccu RST_BUS_OHCI2>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi index c34d505c7efe..b9b6fb00be28 100644 --- a/arch/arm/boot/dts/sun9i-a80.dtsi +++ b/arch/arm/boot/dts/sun9i-a80.dtsi @@ -346,6 +346,7 @@ clocks = <&usb_clocks CLK_BUS_HCI0>; resets = <&usb_clocks RST_USB0_HCI>; phys = <&usbphy1>; + phy-names = "usb"; status = "disabled"; }; @@ -357,6 +358,7 @@ <&usb_clocks CLK_USB_OHCI0>; resets = <&usb_clocks RST_USB0_HCI>; phys = <&usbphy1>; + phy-names = "usb"; status = "disabled"; }; @@ -378,6 +380,7 @@ clocks = <&usb_clocks CLK_BUS_HCI1>; resets = <&usb_clocks RST_USB1_HCI>; phys = <&usbphy2>; + phy-names = "usb"; status = "disabled"; }; @@ -407,6 +410,7 @@ clocks = <&usb_clocks CLK_BUS_HCI2>; resets = <&usb_clocks RST_USB2_HCI>; phys = <&usbphy3>; + phy-names = "usb"; status = "disabled"; }; @@ -418,6 +422,7 @@ <&usb_clocks CLK_USB_OHCI2>; resets = <&usb_clocks RST_USB2_HCI>; phys = <&usbphy3>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi index eba190b3f9de..107eeafad20a 100644 --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi @@ -304,6 +304,7 @@ clocks = <&ccu CLK_BUS_EHCI1>, <&ccu CLK_BUS_OHCI1>; resets = <&ccu RST_BUS_EHCI1>, <&ccu RST_BUS_OHCI1>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -315,6 +316,7 @@ <&ccu CLK_USB_OHCI1>; resets = <&ccu RST_BUS_EHCI1>, <&ccu RST_BUS_OHCI1>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -325,6 +327,7 @@ clocks = <&ccu CLK_BUS_EHCI2>, <&ccu CLK_BUS_OHCI2>; resets = <&ccu RST_BUS_EHCI2>, <&ccu RST_BUS_OHCI2>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; @@ -336,6 +339,7 @@ <&ccu CLK_USB_OHCI2>; resets = <&ccu RST_BUS_EHCI2>, <&ccu RST_BUS_OHCI2>; phys = <&usbphy 2>; + phy-names = "usb"; status = "disabled"; }; @@ -346,6 +350,7 @@ clocks = <&ccu CLK_BUS_EHCI3>, <&ccu CLK_BUS_OHCI3>; resets = <&ccu RST_BUS_EHCI3>, <&ccu RST_BUS_OHCI3>; phys = <&usbphy 3>; + phy-names = "usb"; status = "disabled"; }; @@ -357,6 +362,7 @@ <&ccu CLK_USB_OHCI3>; resets = <&ccu RST_BUS_EHCI3>, <&ccu RST_BUS_OHCI3>; phys = <&usbphy 3>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm/configs/badge4_defconfig b/arch/arm/configs/badge4_defconfig index 5ae5b5228467..ef484c4cfd1a 100644 --- a/arch/arm/configs/badge4_defconfig +++ b/arch/arm/configs/badge4_defconfig @@ -91,7 +91,6 @@ CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_SERIAL_CYBERJACK=m CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m -CONFIG_USB_RIO500=m CONFIG_EXT2_FS=m CONFIG_EXT3_FS=m CONFIG_MSDOS_FS=y diff --git a/arch/arm/configs/corgi_defconfig b/arch/arm/configs/corgi_defconfig index e4f6442588e7..4fec2ec379ad 100644 --- a/arch/arm/configs/corgi_defconfig +++ b/arch/arm/configs/corgi_defconfig @@ -195,7 +195,6 @@ CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m -CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYTHERM=m diff --git a/arch/arm/configs/davinci_all_defconfig b/arch/arm/configs/davinci_all_defconfig index b34970ce6b31..01e3c0f4be92 100644 --- a/arch/arm/configs/davinci_all_defconfig +++ b/arch/arm/configs/davinci_all_defconfig @@ -228,7 +228,7 @@ CONFIG_RTC_DRV_OMAP=m CONFIG_DMADEVICES=y CONFIG_TI_EDMA=y CONFIG_COMMON_CLK_PWM=m -CONFIG_REMOTEPROC=m +CONFIG_REMOTEPROC=y CONFIG_DA8XX_REMOTEPROC=m CONFIG_MEMORY=y CONFIG_TI_AEMIF=m diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 13ba53286901..e4c8def9a0a5 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -415,7 +415,7 @@ CONFIG_SPI_SH_MSIOF=m CONFIG_SPI_SH_HSPI=y CONFIG_SPI_SIRF=y CONFIG_SPI_STM32=m -CONFIG_SPI_STM32_QSPI=m +CONFIG_SPI_STM32_QSPI=y CONFIG_SPI_SUN4I=y CONFIG_SPI_SUN6I=y CONFIG_SPI_TEGRA114=y @@ -933,7 +933,7 @@ CONFIG_BCM2835_MBOX=y CONFIG_ROCKCHIP_IOMMU=y CONFIG_TEGRA_IOMMU_GART=y CONFIG_TEGRA_IOMMU_SMMU=y -CONFIG_REMOTEPROC=m +CONFIG_REMOTEPROC=y CONFIG_ST_REMOTEPROC=m CONFIG_RPMSG_VIRTIO=m CONFIG_ASPEED_LPC_CTRL=m diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index 64eb896907bf..d3f50971e451 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -364,6 +364,7 @@ CONFIG_DRM_OMAP_PANEL_TPO_TD043MTEA1=m CONFIG_DRM_OMAP_PANEL_NEC_NL8048HL11=m CONFIG_DRM_TILCDC=m CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_TI_TFP410=m CONFIG_FB=y CONFIG_FIRMWARE_EDID=y CONFIG_FB_MODE_HELPERS=y @@ -423,6 +424,7 @@ CONFIG_USB_SERIAL_GENERIC=y CONFIG_USB_SERIAL_SIMPLE=m CONFIG_USB_SERIAL_FTDI_SIO=m CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_TEST=m CONFIG_NOP_USB_XCEIV=m CONFIG_AM335X_PHY_USB=m @@ -460,6 +462,7 @@ CONFIG_MMC_SDHCI_OMAP=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=m CONFIG_LEDS_CPCAP=m +CONFIG_LEDS_LM3532=m CONFIG_LEDS_GPIO=m CONFIG_LEDS_PCA963X=m CONFIG_LEDS_PWM=m @@ -481,7 +484,7 @@ CONFIG_RTC_DRV_OMAP=m CONFIG_RTC_DRV_CPCAP=m CONFIG_DMADEVICES=y CONFIG_OMAP_IOMMU=y -CONFIG_REMOTEPROC=m +CONFIG_REMOTEPROC=y CONFIG_OMAP_REMOTEPROC=m CONFIG_WKUP_M3_RPROC=m CONFIG_SOC_TI=y diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index 787c3f9be414..b817c57f05f1 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -581,7 +581,6 @@ CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m -CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYTHERM=m diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig index 95b5a4ffddea..73ed73a8785a 100644 --- a/arch/arm/configs/s3c2410_defconfig +++ b/arch/arm/configs/s3c2410_defconfig @@ -327,7 +327,6 @@ CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m CONFIG_USB_ADUTUX=m CONFIG_USB_SEVSEG=m -CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYPRESS_CY7C63=m diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig index 4fb51d665abb..a1cdbfa064c5 100644 --- a/arch/arm/configs/spitz_defconfig +++ b/arch/arm/configs/spitz_defconfig @@ -189,7 +189,6 @@ CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m -CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYTHERM=m diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig index b24df84a1d7a..043b0b18bf7e 100644 --- a/arch/arm/crypto/Kconfig +++ b/arch/arm/crypto/Kconfig @@ -98,6 +98,7 @@ config CRYPTO_AES_ARM_CE tristate "Accelerated AES using ARMv8 Crypto Extensions" depends on KERNEL_MODE_NEON select CRYPTO_BLKCIPHER + select CRYPTO_LIB_AES select CRYPTO_SIMD help Use an implementation of AES in CBC, CTR and XTS modes that uses diff --git a/arch/arm/crypto/aes-ce-core.S b/arch/arm/crypto/aes-ce-core.S index b978cdf133af..4d1707388d94 100644 --- a/arch/arm/crypto/aes-ce-core.S +++ b/arch/arm/crypto/aes-ce-core.S @@ -9,6 +9,7 @@ #include <asm/assembler.h> .text + .arch armv8-a .fpu crypto-neon-fp-armv8 .align 3 diff --git a/arch/arm/include/asm/xen/xen-ops.h b/arch/arm/include/asm/xen/xen-ops.h deleted file mode 100644 index ec154e719b11..000000000000 --- a/arch/arm/include/asm/xen/xen-ops.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_XEN_OPS_H -#define _ASM_XEN_OPS_H - -void xen_efi_runtime_setup(void); - -#endif /* _ASM_XEN_OPS_H */ diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c index dd939e1325c6..29fd13684a68 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c @@ -763,7 +763,8 @@ static struct omap_hwmod_class_sysconfig am33xx_timer_sysc = { .rev_offs = 0x0000, .sysc_offs = 0x0010, .syss_offs = 0x0014, - .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET), + .sysc_flags = SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET | + SYSC_HAS_RESET_STATUS, .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | SIDLE_SMART_WKUP), .sysc_fields = &omap_hwmod_sysc_type2, diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 2bcb6345b873..54524775f278 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -231,8 +231,9 @@ static struct omap_hwmod am33xx_control_hwmod = { static struct omap_hwmod_class_sysconfig lcdc_sysc = { .rev_offs = 0x0, .sysc_offs = 0x54, - .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE), - .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_flags = SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART, .sysc_fields = &omap_hwmod_sysc_type2, }; diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 1fde1bf53fb6..7ac9af56762d 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -74,83 +74,6 @@ int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) return 0; } -/* - * This API is to be called during init to set the various voltage - * domains to the voltage as per the opp table. Typically we boot up - * at the nominal voltage. So this function finds out the rate of - * the clock associated with the voltage domain, finds out the correct - * opp entry and sets the voltage domain to the voltage specified - * in the opp entry - */ -static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, - const char *oh_name) -{ - struct voltagedomain *voltdm; - struct clk *clk; - struct dev_pm_opp *opp; - unsigned long freq, bootup_volt; - struct device *dev; - - if (!vdd_name || !clk_name || !oh_name) { - pr_err("%s: invalid parameters\n", __func__); - goto exit; - } - - if (!strncmp(oh_name, "mpu", 3)) - /* - * All current OMAPs share voltage rail and clock - * source, so CPU0 is used to represent the MPU-SS. - */ - dev = get_cpu_device(0); - else - dev = omap_device_get_by_hwmod_name(oh_name); - - if (IS_ERR(dev)) { - pr_err("%s: Unable to get dev pointer for hwmod %s\n", - __func__, oh_name); - goto exit; - } - - voltdm = voltdm_lookup(vdd_name); - if (!voltdm) { - pr_err("%s: unable to get vdd pointer for vdd_%s\n", - __func__, vdd_name); - goto exit; - } - - clk = clk_get(NULL, clk_name); - if (IS_ERR(clk)) { - pr_err("%s: unable to get clk %s\n", __func__, clk_name); - goto exit; - } - - freq = clk_get_rate(clk); - clk_put(clk); - - opp = dev_pm_opp_find_freq_ceil(dev, &freq); - if (IS_ERR(opp)) { - pr_err("%s: unable to find boot up OPP for vdd_%s\n", - __func__, vdd_name); - goto exit; - } - - bootup_volt = dev_pm_opp_get_voltage(opp); - dev_pm_opp_put(opp); - - if (!bootup_volt) { - pr_err("%s: unable to find voltage corresponding to the bootup OPP for vdd_%s\n", - __func__, vdd_name); - goto exit; - } - - voltdm_scale(voltdm, bootup_volt); - return 0; - -exit: - pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name); - return -EINVAL; -} - #ifdef CONFIG_SUSPEND static int omap_pm_enter(suspend_state_t suspend_state) { @@ -208,25 +131,6 @@ void omap_common_suspend_init(void *pm_suspend) } #endif /* CONFIG_SUSPEND */ -static void __init omap3_init_voltages(void) -{ - if (!soc_is_omap34xx()) - return; - - omap2_set_init_voltage("mpu_iva", "dpll1_ck", "mpu"); - omap2_set_init_voltage("core", "l3_ick", "l3_main"); -} - -static void __init omap4_init_voltages(void) -{ - if (!soc_is_omap44xx()) - return; - - omap2_set_init_voltage("mpu", "dpll_mpu_ck", "mpu"); - omap2_set_init_voltage("core", "l3_div_ck", "l3_main_1"); - omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva"); -} - int __maybe_unused omap_pm_nop_init(void) { return 0; @@ -246,10 +150,6 @@ int __init omap2_common_pm_late_init(void) omap4_twl_init(); omap_voltage_late_init(); - /* Initialize the voltages */ - omap3_init_voltages(); - omap4_init_voltages(); - /* Smartreflex device init */ omap_devinit_smartreflex(); diff --git a/arch/arm/xen/Makefile b/arch/arm/xen/Makefile index 7ed28982c4c3..c32d04713ba0 100644 --- a/arch/arm/xen/Makefile +++ b/arch/arm/xen/Makefile @@ -1,3 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y := enlighten.o hypercall.o grant-table.o p2m.o mm.o -obj-$(CONFIG_XEN_EFI) += efi.o diff --git a/arch/arm/xen/efi.c b/arch/arm/xen/efi.c deleted file mode 100644 index d687a73044bf..000000000000 --- a/arch/arm/xen/efi.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2015, Linaro Limited, Shannon Zhao - */ - -#include <linux/efi.h> -#include <xen/xen-ops.h> -#include <asm/xen/xen-ops.h> - -/* Set XEN EFI runtime services function pointers. Other fields of struct efi, - * e.g. efi.systab, will be set like normal EFI. - */ -void __init xen_efi_runtime_setup(void) -{ - efi.get_time = xen_efi_get_time; - efi.set_time = xen_efi_set_time; - efi.get_wakeup_time = xen_efi_get_wakeup_time; - efi.set_wakeup_time = xen_efi_set_wakeup_time; - efi.get_variable = xen_efi_get_variable; - efi.get_next_variable = xen_efi_get_next_variable; - efi.set_variable = xen_efi_set_variable; - efi.query_variable_info = xen_efi_query_variable_info; - efi.update_capsule = xen_efi_update_capsule; - efi.query_capsule_caps = xen_efi_query_capsule_caps; - efi.get_next_high_mono_count = xen_efi_get_next_high_mono_count; - efi.reset_system = xen_efi_reset_system; -} -EXPORT_SYMBOL_GPL(xen_efi_runtime_setup); diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 1e57692552d9..dd6804a64f1a 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -15,7 +15,6 @@ #include <xen/xen-ops.h> #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> -#include <asm/xen/xen-ops.h> #include <asm/system_misc.h> #include <asm/efi.h> #include <linux/interrupt.h> @@ -437,7 +436,7 @@ EXPORT_SYMBOL_GPL(HYPERVISOR_memory_op); EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op); EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op); EXPORT_SYMBOL_GPL(HYPERVISOR_tmem_op); -EXPORT_SYMBOL_GPL(HYPERVISOR_platform_op); +EXPORT_SYMBOL_GPL(HYPERVISOR_platform_op_raw); EXPORT_SYMBOL_GPL(HYPERVISOR_multicall); EXPORT_SYMBOL_GPL(HYPERVISOR_vm_assist); EXPORT_SYMBOL_GPL(HYPERVISOR_dm_op); diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index 2b2c208408bb..38fa917c8585 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -28,7 +28,10 @@ unsigned long xen_get_swiotlb_free_pages(unsigned int order) for_each_memblock(memory, reg) { if (reg->base < (phys_addr_t)0xffffffff) { - flags |= __GFP_DMA; + if (IS_ENABLED(CONFIG_ZONE_DMA32)) + flags |= __GFP_DMA32; + else + flags |= __GFP_DMA; break; } } diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 41a9b4257b72..950a56b71ff0 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -110,7 +110,6 @@ config ARM64 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY - select GENERIC_COMPAT_VDSO if (!CPU_BIG_ENDIAN && COMPAT) select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_PCI @@ -1159,7 +1158,7 @@ menuconfig COMPAT if COMPAT config KUSER_HELPERS - bool "Enable kuser helpers page for 32 bit applications" + bool "Enable kuser helpers page for 32-bit applications" default y help Warning: disabling this option may break 32-bit user programs. @@ -1185,6 +1184,18 @@ config KUSER_HELPERS Say N here only if you are absolutely certain that you do not need these helpers; otherwise, the safe option is to say Y. +config COMPAT_VDSO + bool "Enable vDSO for 32-bit applications" + depends on !CPU_BIG_ENDIAN && "$(CROSS_COMPILE_COMPAT)" != "" + select GENERIC_COMPAT_VDSO + default y + help + Place in the process address space of 32-bit applications an + ELF shared object providing fast implementations of gettimeofday + and clock_gettime. + + You must have a 32-bit build of glibc 2.22 or later for programs + to seamlessly take advantage of this. menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 84a3d502c5a5..2c0238ce0551 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -53,22 +53,6 @@ $(warning Detected assembler with broken .inst; disassembly will be unreliable) endif endif -ifeq ($(CONFIG_GENERIC_COMPAT_VDSO), y) - CROSS_COMPILE_COMPAT ?= $(CONFIG_CROSS_COMPILE_COMPAT_VDSO:"%"=%) - - ifeq ($(CONFIG_CC_IS_CLANG), y) - $(warning CROSS_COMPILE_COMPAT is clang, the compat vDSO will not be built) - else ifeq ($(strip $(CROSS_COMPILE_COMPAT)),) - $(warning CROSS_COMPILE_COMPAT not defined or empty, the compat vDSO will not be built) - else ifeq ($(shell which $(CROSS_COMPILE_COMPAT)gcc 2> /dev/null),) - $(error $(CROSS_COMPILE_COMPAT)gcc not found, check CROSS_COMPILE_COMPAT) - else - export CROSS_COMPILE_COMPAT - export CONFIG_COMPAT_VDSO := y - compat_vdso := -DCONFIG_COMPAT_VDSO=1 - endif -endif - KBUILD_CFLAGS += -mgeneral-regs-only $(lseinstr) $(brokengasinst) \ $(compat_vdso) $(cc_has_k_constraint) KBUILD_CFLAGS += -fno-asynchronous-unwind-tables diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts index 2b6345db7dc0..78c82a665c84 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts @@ -104,6 +104,7 @@ &ehci0 { phys = <&usbphy 0>; + phy-names = "usb"; status = "okay"; }; @@ -150,6 +151,7 @@ &ohci0 { phys = <&usbphy 0>; + phy-names = "usb"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index 69128a6dfc46..3eccbdba7154 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -553,6 +553,7 @@ resets = <&ccu RST_BUS_OHCI1>, <&ccu RST_BUS_EHCI1>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; @@ -564,6 +565,7 @@ <&ccu CLK_USB_OHCI1>; resets = <&ccu RST_BUS_OHCI1>; phys = <&usbphy 1>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi index 4020a1aafa3e..0d5ea19336a1 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi @@ -547,6 +547,7 @@ resets = <&ccu RST_BUS_OHCI3>, <&ccu RST_BUS_EHCI3>; phys = <&usb2phy 3>; + phy-names = "usb"; status = "disabled"; }; @@ -558,6 +559,7 @@ <&ccu CLK_USB_OHCI3>; resets = <&ccu RST_BUS_OHCI3>; phys = <&usb2phy 3>; + phy-names = "usb"; status = "disabled"; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 8e05c39eab08..c9a867ac32d4 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -723,7 +723,7 @@ CONFIG_TEGRA_IOMMU_SMMU=y CONFIG_ARM_SMMU=y CONFIG_ARM_SMMU_V3=y CONFIG_QCOM_IOMMU=y -CONFIG_REMOTEPROC=m +CONFIG_REMOTEPROC=y CONFIG_QCOM_Q6V5_MSS=m CONFIG_QCOM_Q6V5_PAS=m CONFIG_QCOM_SYSMON=m diff --git a/arch/arm64/include/asm/atomic_lse.h b/arch/arm64/include/asm/atomic_lse.h index c6bd87d2915b..574808b9df4c 100644 --- a/arch/arm64/include/asm/atomic_lse.h +++ b/arch/arm64/include/asm/atomic_lse.h @@ -321,7 +321,8 @@ static inline s64 __lse_atomic64_dec_if_positive(atomic64_t *v) } #define __CMPXCHG_CASE(w, sfx, name, sz, mb, cl...) \ -static inline u##sz __lse__cmpxchg_case_##name##sz(volatile void *ptr, \ +static __always_inline u##sz \ +__lse__cmpxchg_case_##name##sz(volatile void *ptr, \ u##sz old, \ u##sz new) \ { \ @@ -362,7 +363,8 @@ __CMPXCHG_CASE(x, , mb_, 64, al, "memory") #undef __CMPXCHG_CASE #define __CMPXCHG_DBL(name, mb, cl...) \ -static inline long __lse__cmpxchg_double##name(unsigned long old1, \ +static __always_inline long \ +__lse__cmpxchg_double##name(unsigned long old1, \ unsigned long old2, \ unsigned long new1, \ unsigned long new2, \ diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 86825aa20852..97f21cc66657 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -47,30 +47,6 @@ #define read_sysreg_el2(r) read_sysreg_elx(r, _EL2, _EL1) #define write_sysreg_el2(v,r) write_sysreg_elx(v, r, _EL2, _EL1) -/** - * hyp_alternate_select - Generates patchable code sequences that are - * used to switch between two implementations of a function, depending - * on the availability of a feature. - * - * @fname: a symbol name that will be defined as a function returning a - * function pointer whose type will match @orig and @alt - * @orig: A pointer to the default function, as returned by @fname when - * @cond doesn't hold - * @alt: A pointer to the alternate function, as returned by @fname - * when @cond holds - * @cond: a CPU feature (as described in asm/cpufeature.h) - */ -#define hyp_alternate_select(fname, orig, alt, cond) \ -typeof(orig) * __hyp_text fname(void) \ -{ \ - typeof(alt) *val = orig; \ - asm volatile(ALTERNATIVE("nop \n", \ - "mov %0, %1 \n", \ - cond) \ - : "+r" (val) : "r" (alt)); \ - return val; \ -} - int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu); void __vgic_v3_save_state(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/include/asm/vdso/compat_barrier.h b/arch/arm64/include/asm/vdso/compat_barrier.h index fb60a88b5ed4..3fd8fd6d8fc2 100644 --- a/arch/arm64/include/asm/vdso/compat_barrier.h +++ b/arch/arm64/include/asm/vdso/compat_barrier.h @@ -20,7 +20,7 @@ #define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory") -#if __LINUX_ARM_ARCH__ >= 8 +#if __LINUX_ARM_ARCH__ >= 8 && defined(CONFIG_AS_DMB_ISHLD) #define aarch32_smp_mb() dmb(ish) #define aarch32_smp_rmb() dmb(ishld) #define aarch32_smp_wmb() dmb(ishst) diff --git a/arch/arm64/include/asm/vdso_datapage.h b/arch/arm64/include/asm/vdso_datapage.h deleted file mode 100644 index 1f38bf330a6e..000000000000 --- a/arch/arm64/include/asm/vdso_datapage.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2012 ARM Limited - */ -#ifndef __ASM_VDSO_DATAPAGE_H -#define __ASM_VDSO_DATAPAGE_H - -#ifndef __ASSEMBLY__ - -struct vdso_data { - __u64 cs_cycle_last; /* Timebase at clocksource init */ - __u64 raw_time_sec; /* Raw time */ - __u64 raw_time_nsec; - __u64 xtime_clock_sec; /* Kernel time */ - __u64 xtime_clock_nsec; - __u64 xtime_coarse_sec; /* Coarse time */ - __u64 xtime_coarse_nsec; - __u64 wtm_clock_sec; /* Wall to monotonic time */ - __u64 wtm_clock_nsec; - __u32 tb_seq_count; /* Timebase sequence counter */ - /* cs_* members must be adjacent and in this order (ldp accesses) */ - __u32 cs_mono_mult; /* NTP-adjusted clocksource multiplier */ - __u32 cs_shift; /* Clocksource shift (mono = raw) */ - __u32 cs_raw_mult; /* Raw clocksource multiplier */ - __u32 tz_minuteswest; /* Whacky timezone stuff */ - __u32 tz_dsttime; - __u32 use_syscall; - __u32 hrtimer_res; -}; - -#endif /* !__ASSEMBLY__ */ - -#endif /* __ASM_VDSO_DATAPAGE_H */ diff --git a/arch/arm64/include/asm/xen/xen-ops.h b/arch/arm64/include/asm/xen/xen-ops.h deleted file mode 100644 index e6e784051932..000000000000 --- a/arch/arm64/include/asm/xen/xen-ops.h +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_XEN_OPS_H -#define _ASM_XEN_OPS_H - -void xen_efi_runtime_setup(void); - -#endif /* _ASM_XEN_OPS_H */ diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index 2ec09debc2bb..ca158be21f83 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -174,6 +174,9 @@ static void __init register_insn_emulation(struct insn_emulation_ops *ops) struct insn_emulation *insn; insn = kzalloc(sizeof(*insn), GFP_KERNEL); + if (!insn) + return; + insn->ops = ops; insn->min = INSN_UNDEF; @@ -233,6 +236,8 @@ static void __init register_insn_emulation_sysctl(void) insns_sysctl = kcalloc(nr_insn_emulated + 1, sizeof(*sysctl), GFP_KERNEL); + if (!insns_sysctl) + return; raw_spin_lock_irqsave(&insn_emulation_lock, flags); list_for_each_entry(insn, &insn_emulation, node) { diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 1e43ba5c79b7..f593f4cffc0d 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -128,8 +128,8 @@ static void install_bp_hardening_cb(bp_hardening_cb_t fn, int cpu, slot = -1; /* - * enable_smccc_arch_workaround_1() passes NULL for the hyp_vecs - * start/end if we're a guest. Skip the hyp-vectors work. + * detect_harden_bp_fw() passes NULL for the hyp_vecs start/end if + * we're a guest. Skip the hyp-vectors work. */ if (!hyp_vecs_start) { __this_cpu_write(bp_hardening_data.fn, fn); diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 9323bcc40a58..cabebf1a7976 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -136,6 +136,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar0[] = { static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_SB_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_FRINTTS_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_PTR_AUTH), FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_GPI_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_PTR_AUTH), diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 84a822748c84..e304fe04b098 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -775,6 +775,7 @@ el0_sync_compat: b.ge el0_dbg b el0_inv el0_svc_compat: + gic_prio_kentry_setup tmp=x1 mov x0, sp bl el0_svc_compat_handler b ret_to_user diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index 171773257974..06e56b470315 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -121,10 +121,16 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) /* * Ensure updated trampoline is visible to instruction - * fetch before we patch in the branch. + * fetch before we patch in the branch. Although the + * architecture doesn't require an IPI in this case, + * Neoverse-N1 erratum #1542419 does require one + * if the TLB maintenance in module_enable_ro() is + * skipped due to rodata_enabled. It doesn't seem worth + * it to make it conditional given that this is + * certainly not a fast-path. */ - __flush_icache_range((unsigned long)&dst[0], - (unsigned long)&dst[1]); + flush_icache_range((unsigned long)&dst[0], + (unsigned long)&dst[1]); } addr = (unsigned long)dst; #else /* CONFIG_ARM64_MODULE_PLTS */ diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index a47462def04b..1fb2819fc048 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -332,22 +332,27 @@ void arch_release_task_struct(struct task_struct *tsk) fpsimd_release_task(tsk); } -/* - * src and dst may temporarily have aliased sve_state after task_struct - * is copied. We cannot fix this properly here, because src may have - * live SVE state and dst's thread_info may not exist yet, so tweaking - * either src's or dst's TIF_SVE is not safe. - * - * The unaliasing is done in copy_thread() instead. This works because - * dst is not schedulable or traceable until both of these functions - * have been called. - */ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { if (current->mm) fpsimd_preserve_current_state(); *dst = *src; + /* We rely on the above assignment to initialize dst's thread_flags: */ + BUILD_BUG_ON(!IS_ENABLED(CONFIG_THREAD_INFO_IN_TASK)); + + /* + * Detach src's sve_state (if any) from dst so that it does not + * get erroneously used or freed prematurely. dst's sve_state + * will be allocated on demand later on if dst uses SVE. + * For consistency, also clear TIF_SVE here: this could be done + * later in copy_process(), but to avoid tripping up future + * maintainers it is best not to leave TIF_SVE and sve_state in + * an inconsistent state, even temporarily. + */ + dst->thread.sve_state = NULL; + clear_tsk_thread_flag(dst, TIF_SVE); + return 0; } @@ -361,13 +366,6 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); /* - * Unalias p->thread.sve_state (if any) from the parent task - * and disable discard SVE state for p: - */ - clear_tsk_thread_flag(p, TIF_SVE); - p->thread.sve_state = NULL; - - /* * In case p was allocated the same task_struct pointer as some * other recently-exited task, make sure p is disassociated from * any cpu that may have run that now-exited task recently. diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ /dev/null diff --git a/arch/arm64/kernel/vdso32/Makefile b/arch/arm64/kernel/vdso32/Makefile index 1fba0776ed40..76b327f88fbb 100644 --- a/arch/arm64/kernel/vdso32/Makefile +++ b/arch/arm64/kernel/vdso32/Makefile @@ -8,15 +8,21 @@ ARCH_REL_TYPE_ABS := R_ARM_JUMP_SLOT|R_ARM_GLOB_DAT|R_ARM_ABS32 include $(srctree)/lib/vdso/Makefile -COMPATCC := $(CROSS_COMPILE_COMPAT)gcc +# Same as cc-*option, but using CC_COMPAT instead of CC +ifeq ($(CONFIG_CC_IS_CLANG), y) +CC_COMPAT ?= $(CC) +else +CC_COMPAT ?= $(CROSS_COMPILE_COMPAT)gcc +endif -# Same as cc-*option, but using COMPATCC instead of CC cc32-option = $(call try-run,\ - $(COMPATCC) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2)) + $(CC_COMPAT) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2)) cc32-disable-warning = $(call try-run,\ - $(COMPATCC) -W$(strip $(1)) -c -x c /dev/null -o "$$TMP",-Wno-$(strip $(1))) + $(CC_COMPAT) -W$(strip $(1)) -c -x c /dev/null -o "$$TMP",-Wno-$(strip $(1))) cc32-ldoption = $(call try-run,\ - $(COMPATCC) $(1) -nostdlib -x c /dev/null -o "$$TMP",$(1),$(2)) + $(CC_COMPAT) $(1) -nostdlib -x c /dev/null -o "$$TMP",$(1),$(2)) +cc32-as-instr = $(call try-run,\ + printf "%b\n" "$(1)" | $(CC_COMPAT) $(VDSO_AFLAGS) -c -x assembler -o "$$TMP" -,$(2),$(3)) # We cannot use the global flags to compile the vDSO files, the main reason # being that the 32-bit compiler may be older than the main (64-bit) compiler @@ -25,22 +31,21 @@ cc32-ldoption = $(call try-run,\ # arm64 one. # As a result we set our own flags here. -# From top-level Makefile -# NOSTDINC_FLAGS -VDSO_CPPFLAGS := -nostdinc -isystem $(shell $(COMPATCC) -print-file-name=include) +# KBUILD_CPPFLAGS and NOSTDINC_FLAGS from top-level Makefile +VDSO_CPPFLAGS := -D__KERNEL__ -nostdinc -isystem $(shell $(CC_COMPAT) -print-file-name=include) VDSO_CPPFLAGS += $(LINUXINCLUDE) -VDSO_CPPFLAGS += $(KBUILD_CPPFLAGS) # Common C and assembly flags # From top-level Makefile VDSO_CAFLAGS := $(VDSO_CPPFLAGS) +ifneq ($(shell $(CC_COMPAT) --version 2>&1 | head -n 1 | grep clang),) +VDSO_CAFLAGS += --target=$(notdir $(CROSS_COMPILE_COMPAT:%-=%)) +endif + VDSO_CAFLAGS += $(call cc32-option,-fno-PIE) ifdef CONFIG_DEBUG_INFO VDSO_CAFLAGS += -g endif -ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(COMPATCC)), y) -VDSO_CAFLAGS += -DCC_HAVE_ASM_GOTO -endif # From arm Makefile VDSO_CAFLAGS += $(call cc32-option,-fno-dwarf2-cfi-asm) @@ -55,6 +60,7 @@ endif VDSO_CAFLAGS += -fPIC -fno-builtin -fno-stack-protector VDSO_CAFLAGS += -DDISABLE_BRANCH_PROFILING + # Try to compile for ARMv8. If the compiler is too old and doesn't support it, # fall back to v7. There is no easy way to check for what architecture the code # is being compiled, so define a macro specifying that (see arch/arm/Makefile). @@ -91,6 +97,12 @@ VDSO_CFLAGS += -Wno-int-to-pointer-cast VDSO_AFLAGS := $(VDSO_CAFLAGS) VDSO_AFLAGS += -D__ASSEMBLY__ +# Check for binutils support for dmb ishld +dmbinstr := $(call cc32-as-instr,dmb ishld,-DCONFIG_AS_DMB_ISHLD=1) + +VDSO_CFLAGS += $(dmbinstr) +VDSO_AFLAGS += $(dmbinstr) + VDSO_LDFLAGS := $(VDSO_CPPFLAGS) # From arm vDSO Makefile VDSO_LDFLAGS += -Wl,-Bsymbolic -Wl,--no-undefined -Wl,-soname=linux-vdso.so.1 @@ -159,14 +171,14 @@ quiet_cmd_vdsold_and_vdso_check = LD32 $@ cmd_vdsold_and_vdso_check = $(cmd_vdsold); $(cmd_vdso_check) quiet_cmd_vdsold = LD32 $@ - cmd_vdsold = $(COMPATCC) -Wp,-MD,$(depfile) $(VDSO_LDFLAGS) \ + cmd_vdsold = $(CC_COMPAT) -Wp,-MD,$(depfile) $(VDSO_LDFLAGS) \ -Wl,-T $(filter %.lds,$^) $(filter %.o,$^) -o $@ quiet_cmd_vdsocc = CC32 $@ - cmd_vdsocc = $(COMPATCC) -Wp,-MD,$(depfile) $(VDSO_CFLAGS) -c -o $@ $< + cmd_vdsocc = $(CC_COMPAT) -Wp,-MD,$(depfile) $(VDSO_CFLAGS) -c -o $@ $< quiet_cmd_vdsocc_gettimeofday = CC32 $@ - cmd_vdsocc_gettimeofday = $(COMPATCC) -Wp,-MD,$(depfile) $(VDSO_CFLAGS) $(VDSO_CFLAGS_gettimeofday_o) -c -o $@ $< + cmd_vdsocc_gettimeofday = $(CC_COMPAT) -Wp,-MD,$(depfile) $(VDSO_CFLAGS) $(VDSO_CFLAGS_gettimeofday_o) -c -o $@ $< quiet_cmd_vdsoas = AS32 $@ - cmd_vdsoas = $(COMPATCC) -Wp,-MD,$(depfile) $(VDSO_AFLAGS) -c -o $@ $< + cmd_vdsoas = $(CC_COMPAT) -Wp,-MD,$(depfile) $(VDSO_AFLAGS) -c -o $@ $< quiet_cmd_vdsomunge = MUNGE $@ cmd_vdsomunge = $(obj)/$(munge) $< $@ diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c index bd978ad71936..3d3815020e36 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -229,20 +229,6 @@ static void __hyp_text __hyp_vgic_restore_state(struct kvm_vcpu *vcpu) } } -static bool __hyp_text __true_value(void) -{ - return true; -} - -static bool __hyp_text __false_value(void) -{ - return false; -} - -static hyp_alternate_select(__check_arm_834220, - __false_value, __true_value, - ARM64_WORKAROUND_834220); - static bool __hyp_text __translate_far_to_hpfar(u64 far, u64 *hpfar) { u64 par, tmp; @@ -298,7 +284,8 @@ static bool __hyp_text __populate_fault_info(struct kvm_vcpu *vcpu) * resolve the IPA using the AT instruction. */ if (!(esr & ESR_ELx_S1PTW) && - (__check_arm_834220()() || (esr & ESR_ELx_FSC_TYPE) == FSC_PERM)) { + (cpus_have_const_cap(ARM64_WORKAROUND_834220) || + (esr & ESR_ELx_FSC_TYPE) == FSC_PERM)) { if (!__translate_far_to_hpfar(far, &hpfar)) return false; } else { diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c index c466060b76d6..eb0efc5557f3 100644 --- a/arch/arm64/kvm/hyp/tlb.c +++ b/arch/arm64/kvm/hyp/tlb.c @@ -67,10 +67,14 @@ static void __hyp_text __tlb_switch_to_guest_nvhe(struct kvm *kvm, isb(); } -static hyp_alternate_select(__tlb_switch_to_guest, - __tlb_switch_to_guest_nvhe, - __tlb_switch_to_guest_vhe, - ARM64_HAS_VIRT_HOST_EXTN); +static void __hyp_text __tlb_switch_to_guest(struct kvm *kvm, + struct tlb_inv_context *cxt) +{ + if (has_vhe()) + __tlb_switch_to_guest_vhe(kvm, cxt); + else + __tlb_switch_to_guest_nvhe(kvm, cxt); +} static void __hyp_text __tlb_switch_to_host_vhe(struct kvm *kvm, struct tlb_inv_context *cxt) @@ -98,10 +102,14 @@ static void __hyp_text __tlb_switch_to_host_nvhe(struct kvm *kvm, write_sysreg(0, vttbr_el2); } -static hyp_alternate_select(__tlb_switch_to_host, - __tlb_switch_to_host_nvhe, - __tlb_switch_to_host_vhe, - ARM64_HAS_VIRT_HOST_EXTN); +static void __hyp_text __tlb_switch_to_host(struct kvm *kvm, + struct tlb_inv_context *cxt) +{ + if (has_vhe()) + __tlb_switch_to_host_vhe(kvm, cxt); + else + __tlb_switch_to_host_nvhe(kvm, cxt); +} void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa) { @@ -111,7 +119,7 @@ void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa) /* Switch to requested VMID */ kvm = kern_hyp_va(kvm); - __tlb_switch_to_guest()(kvm, &cxt); + __tlb_switch_to_guest(kvm, &cxt); /* * We could do so much better if we had the VA as well. @@ -154,7 +162,7 @@ void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa) if (!has_vhe() && icache_is_vpipt()) __flush_icache_all(); - __tlb_switch_to_host()(kvm, &cxt); + __tlb_switch_to_host(kvm, &cxt); } void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm) @@ -165,13 +173,13 @@ void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm) /* Switch to requested VMID */ kvm = kern_hyp_va(kvm); - __tlb_switch_to_guest()(kvm, &cxt); + __tlb_switch_to_guest(kvm, &cxt); __tlbi(vmalls12e1is); dsb(ish); isb(); - __tlb_switch_to_host()(kvm, &cxt); + __tlb_switch_to_host(kvm, &cxt); } void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu) @@ -180,13 +188,13 @@ void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu) struct tlb_inv_context cxt; /* Switch to requested VMID */ - __tlb_switch_to_guest()(kvm, &cxt); + __tlb_switch_to_guest(kvm, &cxt); __tlbi(vmalle1); dsb(nsh); isb(); - __tlb_switch_to_host()(kvm, &cxt); + __tlb_switch_to_host(kvm, &cxt); } void __hyp_text __kvm_flush_vm_context(void) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 115d7a0e4b08..855f2a7954e6 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -113,6 +113,15 @@ static inline bool is_ttbr1_addr(unsigned long addr) return arch_kasan_reset_tag(addr) >= PAGE_OFFSET; } +static inline unsigned long mm_to_pgd_phys(struct mm_struct *mm) +{ + /* Either init_pg_dir or swapper_pg_dir */ + if (mm == &init_mm) + return __pa_symbol(mm->pgd); + + return (unsigned long)virt_to_phys(mm->pgd); +} + /* * Dump out the page tables associated with 'addr' in the currently active mm. */ @@ -141,7 +150,7 @@ static void show_pte(unsigned long addr) pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n", mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K, - vabits_actual, (unsigned long)virt_to_phys(mm->pgd)); + vabits_actual, mm_to_pgd_phys(mm)); pgdp = pgd_offset(mm, addr); pgd = READ_ONCE(*pgdp); pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd)); @@ -266,7 +275,7 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr, * If we got a different type of fault from the AT instruction, * treat the translation fault as spurious. */ - dfsc = FIELD_PREP(SYS_PAR_EL1_FST, par); + dfsc = FIELD_GET(SYS_PAR_EL1_FST, par); return (dfsc & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT; } diff --git a/arch/arm64/xen/Makefile b/arch/arm64/xen/Makefile index a4fc65f3928d..b66215e8658e 100644 --- a/arch/arm64/xen/Makefile +++ b/arch/arm64/xen/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only xen-arm-y += $(addprefix ../../arm/xen/, enlighten.o grant-table.o p2m.o mm.o) obj-y := xen-arm.o hypercall.o -obj-$(CONFIG_XEN_EFI) += $(addprefix ../../arm/xen/, efi.o) diff --git a/arch/mips/boot/dts/qca/ar9331.dtsi b/arch/mips/boot/dts/qca/ar9331.dtsi index 63a9f33aa43e..5cfc9d347826 100644 --- a/arch/mips/boot/dts/qca/ar9331.dtsi +++ b/arch/mips/boot/dts/qca/ar9331.dtsi @@ -99,7 +99,7 @@ miscintc: interrupt-controller@18060010 { compatible = "qca,ar7240-misc-intc"; - reg = <0x18060010 0x4>; + reg = <0x18060010 0x8>; interrupt-parent = <&cpuintc>; interrupts = <6>; diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 16bef819fe98..914af125a7fa 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -571,7 +571,6 @@ CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m CONFIG_USB_ADUTUX=m -CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYPRESS_CY7C63=m diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index 8762e75f5d5f..2c7adea7638f 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -314,7 +314,6 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y CONFIG_USB_SERIAL_CYBERJACK=m CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m -CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYTHERM=m diff --git a/arch/mips/fw/arc/memory.c b/arch/mips/fw/arc/memory.c index af44b35d79a1..b4328b3b5288 100644 --- a/arch/mips/fw/arc/memory.c +++ b/arch/mips/fw/arc/memory.c @@ -160,7 +160,6 @@ void __init prom_meminit(void) void __init prom_free_prom_memory(void) { - unsigned long addr; int i; if (prom_flags & PROM_FLAG_DONT_FREE_TEMP) diff --git a/arch/mips/fw/sni/sniprom.c b/arch/mips/fw/sni/sniprom.c index 8772617b64ce..80112f2298b6 100644 --- a/arch/mips/fw/sni/sniprom.c +++ b/arch/mips/fw/sni/sniprom.c @@ -43,7 +43,7 @@ /* O32 stack has to be 8-byte aligned. */ static u64 o32_stk[4096]; -#define O32_STK &o32_stk[sizeof(o32_stk)] +#define O32_STK (&o32_stk[ARRAY_SIZE(o32_stk)]) #define __PROM_O32(fun, arg) fun arg __asm__(#fun); \ __asm__(#fun " = call_o32") diff --git a/arch/mips/include/asm/cmpxchg.h b/arch/mips/include/asm/cmpxchg.h index 79bf34efbc04..f6136871561d 100644 --- a/arch/mips/include/asm/cmpxchg.h +++ b/arch/mips/include/asm/cmpxchg.h @@ -77,8 +77,8 @@ extern unsigned long __xchg_called_with_bad_pointer(void) extern unsigned long __xchg_small(volatile void *ptr, unsigned long val, unsigned int size); -static inline unsigned long __xchg(volatile void *ptr, unsigned long x, - int size) +static __always_inline +unsigned long __xchg(volatile void *ptr, unsigned long x, int size) { switch (size) { case 1: @@ -153,8 +153,9 @@ static inline unsigned long __xchg(volatile void *ptr, unsigned long x, extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size); -static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, unsigned int size) +static __always_inline +unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, unsigned int size) { switch (size) { case 1: diff --git a/arch/mips/include/asm/octeon/cvmx-ipd.h b/arch/mips/include/asm/octeon/cvmx-ipd.h index cbdc14b77435..adab7b54c3b4 100644 --- a/arch/mips/include/asm/octeon/cvmx-ipd.h +++ b/arch/mips/include/asm/octeon/cvmx-ipd.h @@ -36,6 +36,7 @@ #include <asm/octeon/octeon-feature.h> #include <asm/octeon/cvmx-ipd-defs.h> +#include <asm/octeon/cvmx-pip-defs.h> enum cvmx_ipd_mode { CVMX_IPD_OPC_MODE_STT = 0LL, /* All blocks DRAM, not cached in L2 */ diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index 071053ece677..5d70babfc9ee 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -52,6 +52,7 @@ # endif #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_CLONE +#define __ARCH_WANT_SYS_CLONE3 /* whitelists for checksyscalls */ #define __IGNORE_fadvise64_64 diff --git a/arch/mips/include/uapi/asm/hwcap.h b/arch/mips/include/uapi/asm/hwcap.h index a2aba4b059e6..1ade1daa4921 100644 --- a/arch/mips/include/uapi/asm/hwcap.h +++ b/arch/mips/include/uapi/asm/hwcap.h @@ -6,5 +6,16 @@ #define HWCAP_MIPS_R6 (1 << 0) #define HWCAP_MIPS_MSA (1 << 1) #define HWCAP_MIPS_CRC32 (1 << 2) +#define HWCAP_MIPS_MIPS16 (1 << 3) +#define HWCAP_MIPS_MDMX (1 << 4) +#define HWCAP_MIPS_MIPS3D (1 << 5) +#define HWCAP_MIPS_SMARTMIPS (1 << 6) +#define HWCAP_MIPS_DSP (1 << 7) +#define HWCAP_MIPS_DSP2 (1 << 8) +#define HWCAP_MIPS_DSP3 (1 << 9) +#define HWCAP_MIPS_MIPS16E2 (1 << 10) +#define HWCAP_LOONGSON_MMI (1 << 11) +#define HWCAP_LOONGSON_EXT (1 << 12) +#define HWCAP_LOONGSON_EXT2 (1 << 13) #endif /* _UAPI_ASM_HWCAP_H */ diff --git a/arch/mips/kernel/cpu-bugs64.c b/arch/mips/kernel/cpu-bugs64.c index fa62cd1dff93..6a7afe7ef4d3 100644 --- a/arch/mips/kernel/cpu-bugs64.c +++ b/arch/mips/kernel/cpu-bugs64.c @@ -24,7 +24,8 @@ static char r4kwar[] __initdata = static char daddiwar[] __initdata = "Enable CPU_DADDI_WORKAROUNDS to rectify."; -static inline void align_mod(const int align, const int mod) +static __always_inline __init +void align_mod(const int align, const int mod) { asm volatile( ".set push\n\t" @@ -38,8 +39,9 @@ static inline void align_mod(const int align, const int mod) : "n"(align), "n"(mod)); } -static __always_inline void mult_sh_align_mod(long *v1, long *v2, long *w, - const int align, const int mod) +static __always_inline __init +void mult_sh_align_mod(long *v1, long *v2, long *w, + const int align, const int mod) { unsigned long flags; int m1, m2; @@ -113,7 +115,7 @@ static __always_inline void mult_sh_align_mod(long *v1, long *v2, long *w, *w = lw; } -static inline void check_mult_sh(void) +static __always_inline __init void check_mult_sh(void) { long v1[8], v2[8], w[8]; int bug, fix, i; @@ -176,7 +178,7 @@ asmlinkage void __init do_daddi_ov(struct pt_regs *regs) exception_exit(prev_state); } -static inline void check_daddi(void) +static __init void check_daddi(void) { extern asmlinkage void handle_daddi_ov(void); unsigned long flags; @@ -242,7 +244,7 @@ static inline void check_daddi(void) int daddiu_bug = IS_ENABLED(CONFIG_CPU_MIPSR6) ? 0 : -1; -static inline void check_daddiu(void) +static __init void check_daddiu(void) { long v, w, tmp; diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index c2eb392597bf..f521cbf934e7 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -2180,6 +2180,39 @@ void cpu_probe(void) elf_hwcap |= HWCAP_MIPS_MSA; } + if (cpu_has_mips16) + elf_hwcap |= HWCAP_MIPS_MIPS16; + + if (cpu_has_mdmx) + elf_hwcap |= HWCAP_MIPS_MDMX; + + if (cpu_has_mips3d) + elf_hwcap |= HWCAP_MIPS_MIPS3D; + + if (cpu_has_smartmips) + elf_hwcap |= HWCAP_MIPS_SMARTMIPS; + + if (cpu_has_dsp) + elf_hwcap |= HWCAP_MIPS_DSP; + + if (cpu_has_dsp2) + elf_hwcap |= HWCAP_MIPS_DSP2; + + if (cpu_has_dsp3) + elf_hwcap |= HWCAP_MIPS_DSP3; + + if (cpu_has_mips16e2) + elf_hwcap |= HWCAP_MIPS_MIPS16E2; + + if (cpu_has_loongson_mmi) + elf_hwcap |= HWCAP_LOONGSON_MMI; + + if (cpu_has_loongson_ext) + elf_hwcap |= HWCAP_LOONGSON_EXT; + + if (cpu_has_loongson_ext2) + elf_hwcap |= HWCAP_LOONGSON_EXT2; + if (cpu_has_vz) cpu_probe_vz(c); diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index b8249c233754..5eec13b8d222 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -108,6 +108,9 @@ void __init add_memory_region(phys_addr_t start, phys_addr_t size, long type) return; } + if (start < PHYS_OFFSET) + return; + memblock_add(start, size); /* Reserve any memory except the ordinary RAM ranges. */ switch (type) { @@ -321,7 +324,7 @@ static void __init bootmem_init(void) * Reserve any memory between the start of RAM and PHYS_OFFSET */ if (ramstart > PHYS_OFFSET) - memblock_reserve(PHYS_OFFSET, PFN_UP(ramstart) - PHYS_OFFSET); + memblock_reserve(PHYS_OFFSET, ramstart - PHYS_OFFSET); if (PFN_UP(ramstart) > ARCH_PFN_OFFSET) { pr_info("Wasting %lu bytes for tracking %lu unused pages\n", diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index b0e25e913bdb..3f16f3823031 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -80,6 +80,7 @@ SYSCALL_DEFINE6(mips_mmap2, unsigned long, addr, unsigned long, len, save_static_function(sys_fork); save_static_function(sys_clone); +save_static_function(sys_clone3); SYSCALL_DEFINE1(set_thread_area, unsigned long, addr) { diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl index c9c879ec9b6d..e7c5ab38e403 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl @@ -373,4 +373,4 @@ 432 n32 fsmount sys_fsmount 433 n32 fspick sys_fspick 434 n32 pidfd_open sys_pidfd_open -# 435 reserved for clone3 +435 n32 clone3 __sys_clone3 diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl index bbce9159caa1..13cd66581f3b 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl @@ -349,4 +349,4 @@ 432 n64 fsmount sys_fsmount 433 n64 fspick sys_fspick 434 n64 pidfd_open sys_pidfd_open -# 435 reserved for clone3 +435 n64 clone3 __sys_clone3 diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl index 9653591428ec..353539ea4140 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl @@ -422,4 +422,4 @@ 432 o32 fsmount sys_fsmount 433 o32 fspick sys_fspick 434 o32 pidfd_open sys_pidfd_open -# 435 reserved for clone3 +435 o32 clone3 __sys_clone3 diff --git a/arch/mips/loongson64/Platform b/arch/mips/loongson64/Platform index c1a4d4dc4665..9f79908f5063 100644 --- a/arch/mips/loongson64/Platform +++ b/arch/mips/loongson64/Platform @@ -66,6 +66,10 @@ else $(call cc-option,-march=mips64r2,-mips64r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64) endif +# Some -march= flags enable MMI instructions, and GCC complains about that +# support being enabled alongside -msoft-float. Thus explicitly disable MMI. +cflags-y += $(call cc-option,-mno-loongson-mmi) + # # Loongson Machines' Support # diff --git a/arch/mips/loongson64/common/mem.c b/arch/mips/loongson64/common/mem.c index 4abb92e0fc39..4254ac4ec616 100644 --- a/arch/mips/loongson64/common/mem.c +++ b/arch/mips/loongson64/common/mem.c @@ -3,6 +3,7 @@ */ #include <linux/fs.h> #include <linux/fcntl.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <asm/bootinfo.h> @@ -64,24 +65,22 @@ void __init prom_init_memory(void) node_id = loongson_memmap->map[i].node_id; mem_type = loongson_memmap->map[i].mem_type; - if (node_id == 0) { - switch (mem_type) { - case SYSTEM_RAM_LOW: - add_memory_region(loongson_memmap->map[i].mem_start, - (u64)loongson_memmap->map[i].mem_size << 20, - BOOT_MEM_RAM); - break; - case SYSTEM_RAM_HIGH: - add_memory_region(loongson_memmap->map[i].mem_start, - (u64)loongson_memmap->map[i].mem_size << 20, - BOOT_MEM_RAM); - break; - case SYSTEM_RAM_RESERVED: - add_memory_region(loongson_memmap->map[i].mem_start, - (u64)loongson_memmap->map[i].mem_size << 20, - BOOT_MEM_RESERVED); - break; - } + if (node_id != 0) + continue; + + switch (mem_type) { + case SYSTEM_RAM_LOW: + memblock_add(loongson_memmap->map[i].mem_start, + (u64)loongson_memmap->map[i].mem_size << 20); + break; + case SYSTEM_RAM_HIGH: + memblock_add(loongson_memmap->map[i].mem_start, + (u64)loongson_memmap->map[i].mem_size << 20); + break; + case SYSTEM_RAM_RESERVED: + memblock_reserve(loongson_memmap->map[i].mem_start, + (u64)loongson_memmap->map[i].mem_size << 20); + break; } } } diff --git a/arch/mips/loongson64/common/serial.c b/arch/mips/loongson64/common/serial.c index ffefc1cb2612..98c3a7feb10f 100644 --- a/arch/mips/loongson64/common/serial.c +++ b/arch/mips/loongson64/common/serial.c @@ -110,7 +110,7 @@ static int __init serial_init(void) } module_init(serial_init); -static void __init serial_exit(void) +static void __exit serial_exit(void) { platform_device_unregister(&uart8250_device); } diff --git a/arch/mips/loongson64/loongson-3/numa.c b/arch/mips/loongson64/loongson-3/numa.c index 414e97de5dc0..8f20d2cb3767 100644 --- a/arch/mips/loongson64/loongson-3/numa.c +++ b/arch/mips/loongson64/loongson-3/numa.c @@ -142,8 +142,6 @@ static void __init szmem(unsigned int node) (u32)node_id, mem_type, mem_start, mem_size); pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n", start_pfn, end_pfn, num_physpages); - add_memory_region((node_id << 44) + mem_start, - (u64)mem_size << 20, BOOT_MEM_RAM); memblock_add_node(PFN_PHYS(start_pfn), PFN_PHYS(end_pfn - start_pfn), node); break; @@ -156,16 +154,12 @@ static void __init szmem(unsigned int node) (u32)node_id, mem_type, mem_start, mem_size); pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n", start_pfn, end_pfn, num_physpages); - add_memory_region((node_id << 44) + mem_start, - (u64)mem_size << 20, BOOT_MEM_RAM); memblock_add_node(PFN_PHYS(start_pfn), PFN_PHYS(end_pfn - start_pfn), node); break; case SYSTEM_RAM_RESERVED: pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n", (u32)node_id, mem_type, mem_start, mem_size); - add_memory_region((node_id << 44) + mem_start, - (u64)mem_size << 20, BOOT_MEM_RESERVED); memblock_reserve(((node_id << 44) + mem_start), mem_size << 20); break; @@ -191,8 +185,6 @@ static void __init node_mem_init(unsigned int node) NODE_DATA(node)->node_start_pfn = start_pfn; NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; - free_bootmem_with_active_regions(node, end_pfn); - if (node == 0) { /* kernel end address */ unsigned long kernel_end_pfn = PFN_UP(__pa_symbol(&_end)); @@ -209,8 +201,6 @@ static void __init node_mem_init(unsigned int node) memblock_reserve((node_addrspace_offset | 0xfe000000), 32 << 20); } - - sparse_memory_present_with_active_regions(node); } static __init void prom_meminit(void) @@ -227,6 +217,7 @@ static __init void prom_meminit(void) cpumask_clear(&__node_data[(node)]->cpumask); } } + memblocks_present(); max_low_pfn = PHYS_PFN(memblock_end_of_DRAM()); for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) { diff --git a/arch/mips/pmcs-msp71xx/msp_prom.c b/arch/mips/pmcs-msp71xx/msp_prom.c index dfb527961a27..800a21b8b8b0 100644 --- a/arch/mips/pmcs-msp71xx/msp_prom.c +++ b/arch/mips/pmcs-msp71xx/msp_prom.c @@ -61,6 +61,7 @@ int init_debug = 1; /* memory blocks */ struct prom_pmemblock mdesc[PROM_MAX_PMEMBLOCKS]; +#define MAX_PROM_MEM 5 static phys_addr_t prom_mem_base[MAX_PROM_MEM] __initdata; static phys_addr_t prom_mem_size[MAX_PROM_MEM] __initdata; static unsigned int nr_prom_mem __initdata; @@ -358,7 +359,7 @@ void __init prom_meminit(void) p++; if (type == BOOT_MEM_ROM_DATA) { - if (nr_prom_mem >= 5) { + if (nr_prom_mem >= MAX_PROM_MEM) { pr_err("Too many ROM DATA regions"); continue; } @@ -377,7 +378,6 @@ void __init prom_free_prom_memory(void) char *ptr; int len = 0; int i; - unsigned long addr; /* * preserve environment variables and command line from pmon/bbload diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile index 69cfa0a5339e..996a934ece7d 100644 --- a/arch/mips/vdso/Makefile +++ b/arch/mips/vdso/Makefile @@ -15,6 +15,7 @@ ccflags-vdso := \ $(filter -mmicromips,$(KBUILD_CFLAGS)) \ $(filter -march=%,$(KBUILD_CFLAGS)) \ $(filter -m%-float,$(KBUILD_CFLAGS)) \ + $(filter -mno-loongson-%,$(KBUILD_CFLAGS)) \ -D__VDSO__ ifdef CONFIG_CC_IS_CLANG @@ -59,7 +60,7 @@ CFLAGS_REMOVE_vgettimeofday.o = -pg ifndef CONFIG_CPU_MIPSR6 ifeq ($(call ld-ifversion, -lt, 225000000, y),y) $(warning MIPS VDSO requires binutils >= 2.25) - obj-vdso-y := $(filter-out gettimeofday.o, $(obj-vdso-y)) + obj-vdso-y := $(filter-out vgettimeofday.o, $(obj-vdso-y)) ccflags-vdso += -DDISABLE_MIPS_VDSO endif endif diff --git a/arch/mips/vdso/gettimeofday.c b/arch/mips/vdso/gettimeofday.c deleted file mode 100644 index e8243c7fd5b5..000000000000 --- a/arch/mips/vdso/gettimeofday.c +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2015 Imagination Technologies - * Author: Alex Smith <alex.smith@imgtec.com> - */ - -#include "vdso.h" - -#include <linux/compiler.h> -#include <linux/time.h> - -#include <asm/clocksource.h> -#include <asm/io.h> -#include <asm/unistd.h> -#include <asm/vdso.h> - -#ifdef CONFIG_MIPS_CLOCK_VSYSCALL - -static __always_inline long gettimeofday_fallback(struct timeval *_tv, - struct timezone *_tz) -{ - register struct timezone *tz asm("a1") = _tz; - register struct timeval *tv asm("a0") = _tv; - register long ret asm("v0"); - register long nr asm("v0") = __NR_gettimeofday; - register long error asm("a3"); - - asm volatile( - " syscall\n" - : "=r" (ret), "=r" (error) - : "r" (tv), "r" (tz), "r" (nr) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - return error ? -ret : ret; -} - -#endif - -static __always_inline long clock_gettime_fallback(clockid_t _clkid, - struct timespec *_ts) -{ - register struct timespec *ts asm("a1") = _ts; - register clockid_t clkid asm("a0") = _clkid; - register long ret asm("v0"); - register long nr asm("v0") = __NR_clock_gettime; - register long error asm("a3"); - - asm volatile( - " syscall\n" - : "=r" (ret), "=r" (error) - : "r" (clkid), "r" (ts), "r" (nr) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - return error ? -ret : ret; -} - -static __always_inline int do_realtime_coarse(struct timespec *ts, - const union mips_vdso_data *data) -{ - u32 start_seq; - - do { - start_seq = vdso_data_read_begin(data); - - ts->tv_sec = data->xtime_sec; - ts->tv_nsec = data->xtime_nsec >> data->cs_shift; - } while (vdso_data_read_retry(data, start_seq)); - - return 0; -} - -static __always_inline int do_monotonic_coarse(struct timespec *ts, - const union mips_vdso_data *data) -{ - u32 start_seq; - u64 to_mono_sec; - u64 to_mono_nsec; - - do { - start_seq = vdso_data_read_begin(data); - - ts->tv_sec = data->xtime_sec; - ts->tv_nsec = data->xtime_nsec >> data->cs_shift; - - to_mono_sec = data->wall_to_mono_sec; - to_mono_nsec = data->wall_to_mono_nsec; - } while (vdso_data_read_retry(data, start_seq)); - - ts->tv_sec += to_mono_sec; - timespec_add_ns(ts, to_mono_nsec); - - return 0; -} - -#ifdef CONFIG_CSRC_R4K - -static __always_inline u64 read_r4k_count(void) -{ - unsigned int count; - - __asm__ __volatile__( - " .set push\n" - " .set mips32r2\n" - " rdhwr %0, $2\n" - " .set pop\n" - : "=r" (count)); - - return count; -} - -#endif - -#ifdef CONFIG_CLKSRC_MIPS_GIC - -static __always_inline u64 read_gic_count(const union mips_vdso_data *data) -{ - void __iomem *gic = get_gic(data); - u32 hi, hi2, lo; - - do { - hi = __raw_readl(gic + sizeof(lo)); - lo = __raw_readl(gic); - hi2 = __raw_readl(gic + sizeof(lo)); - } while (hi2 != hi); - - return (((u64)hi) << 32) + lo; -} - -#endif - -static __always_inline u64 get_ns(const union mips_vdso_data *data) -{ - u64 cycle_now, delta, nsec; - - switch (data->clock_mode) { -#ifdef CONFIG_CSRC_R4K - case VDSO_CLOCK_R4K: - cycle_now = read_r4k_count(); - break; -#endif -#ifdef CONFIG_CLKSRC_MIPS_GIC - case VDSO_CLOCK_GIC: - cycle_now = read_gic_count(data); - break; -#endif - default: - return 0; - } - - delta = (cycle_now - data->cs_cycle_last) & data->cs_mask; - - nsec = (delta * data->cs_mult) + data->xtime_nsec; - nsec >>= data->cs_shift; - - return nsec; -} - -static __always_inline int do_realtime(struct timespec *ts, - const union mips_vdso_data *data) -{ - u32 start_seq; - u64 ns; - - do { - start_seq = vdso_data_read_begin(data); - - if (data->clock_mode == VDSO_CLOCK_NONE) - return -ENOSYS; - - ts->tv_sec = data->xtime_sec; - ns = get_ns(data); - } while (vdso_data_read_retry(data, start_seq)); - - ts->tv_nsec = 0; - timespec_add_ns(ts, ns); - - return 0; -} - -static __always_inline int do_monotonic(struct timespec *ts, - const union mips_vdso_data *data) -{ - u32 start_seq; - u64 ns; - u64 to_mono_sec; - u64 to_mono_nsec; - - do { - start_seq = vdso_data_read_begin(data); - - if (data->clock_mode == VDSO_CLOCK_NONE) - return -ENOSYS; - - ts->tv_sec = data->xtime_sec; - ns = get_ns(data); - - to_mono_sec = data->wall_to_mono_sec; - to_mono_nsec = data->wall_to_mono_nsec; - } while (vdso_data_read_retry(data, start_seq)); - - ts->tv_sec += to_mono_sec; - ts->tv_nsec = 0; - timespec_add_ns(ts, ns + to_mono_nsec); - - return 0; -} - -#ifdef CONFIG_MIPS_CLOCK_VSYSCALL - -/* - * This is behind the ifdef so that we don't provide the symbol when there's no - * possibility of there being a usable clocksource, because there's nothing we - * can do without it. When libc fails the symbol lookup it should fall back on - * the standard syscall path. - */ -int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) -{ - const union mips_vdso_data *data = get_vdso_data(); - struct timespec ts; - int ret; - - ret = do_realtime(&ts, data); - if (ret) - return gettimeofday_fallback(tv, tz); - - if (tv) { - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / 1000; - } - - if (tz) { - tz->tz_minuteswest = data->tz_minuteswest; - tz->tz_dsttime = data->tz_dsttime; - } - - return 0; -} - -#endif /* CONFIG_MIPS_CLOCK_VSYSCALL */ - -int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts) -{ - const union mips_vdso_data *data = get_vdso_data(); - int ret = -1; - - switch (clkid) { - case CLOCK_REALTIME_COARSE: - ret = do_realtime_coarse(ts, data); - break; - case CLOCK_MONOTONIC_COARSE: - ret = do_monotonic_coarse(ts, data); - break; - case CLOCK_REALTIME: - ret = do_realtime(ts, data); - break; - case CLOCK_MONOTONIC: - ret = do_monotonic(ts, data); - break; - default: - break; - } - - if (ret) - ret = clock_gettime_fallback(clkid, ts); - - return ret; -} diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 6841bd52738b..dfbd7f22eef5 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -50,7 +50,7 @@ endif BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -nostdinc -BOOTARFLAGS := -cr$(KBUILD_ARFLAGS) +BOOTARFLAGS := -crD ifdef CONFIG_CC_IS_CLANG BOOTCFLAGS += $(CLANG_FLAGS) diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h b/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h index 4ce795d30377..ca8db193ae38 100644 --- a/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h +++ b/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h @@ -35,6 +35,10 @@ static inline void radix__flush_all_lpid(unsigned int lpid) { WARN_ON(1); } +static inline void radix__flush_all_lpid_guest(unsigned int lpid) +{ + WARN_ON(1); +} #endif extern void radix__flush_hugetlb_tlb_range(struct vm_area_struct *vma, diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index d7fcdfa7fee4..ec2547cc5ecb 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -36,8 +36,8 @@ #include "book3s.h" #include "trace.h" -#define VM_STAT(x) offsetof(struct kvm, stat.x), KVM_STAT_VM -#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU +#define VM_STAT(x, ...) offsetof(struct kvm, stat.x), KVM_STAT_VM, ## __VA_ARGS__ +#define VCPU_STAT(x, ...) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU, ## __VA_ARGS__ /* #define EXIT_DEBUG */ @@ -69,8 +69,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "pthru_all", VCPU_STAT(pthru_all) }, { "pthru_host", VCPU_STAT(pthru_host) }, { "pthru_bad_aff", VCPU_STAT(pthru_bad_aff) }, - { "largepages_2M", VM_STAT(num_2M_pages) }, - { "largepages_1G", VM_STAT(num_1G_pages) }, + { "largepages_2M", VM_STAT(num_2M_pages, .mode = 0444) }, + { "largepages_1G", VM_STAT(num_1G_pages, .mode = 0444) }, { NULL } }; diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 74a9cfe84aee..faebcbb8c4db 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -1921,6 +1921,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) mtspr SPRN_PCR, r6 18: /* Signal secondary CPUs to continue */ + li r0, 0 stb r0,VCORE_IN_GUEST(r5) 19: lis r8,0x7fff /* MAX_INT@h */ mtspr SPRN_HDEC,r8 diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 1d93e55a2de1..2dd452a047cd 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -761,6 +761,7 @@ static int spufs_init_fs_context(struct fs_context *fc) ctx->gid = current_gid(); ctx->mode = 0755; + fc->fs_private = ctx; fc->s_fs_info = sbi; fc->ops = &spufs_context_ops; return 0; diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index b53359258d99..f87a5c64e24d 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -1419,6 +1419,9 @@ void __init pseries_lpar_read_hblkrm_characteristics(void) unsigned char local_buffer[SPLPAR_TLB_BIC_MAXLENGTH]; int call_status, len, idx, bpsize; + if (!firmware_has_feature(FW_FEATURE_BLOCK_REMOVE)) + return; + spin_lock(&rtas_data_buf_lock); memset(rtas_data_buf, 0, RTAS_DATA_BUF_SIZE); call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h index 5a02b7d50940..9c992a88d858 100644 --- a/arch/riscv/include/asm/asm.h +++ b/arch/riscv/include/asm/asm.h @@ -22,6 +22,7 @@ #define REG_L __REG_SEL(ld, lw) #define REG_S __REG_SEL(sd, sw) +#define REG_SC __REG_SEL(sc.d, sc.w) #define SZREG __REG_SEL(8, 4) #define LGREG __REG_SEL(3, 2) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index da7aa88113c2..8ca479831142 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -98,7 +98,26 @@ _save_context: */ .macro RESTORE_ALL REG_L a0, PT_SSTATUS(sp) - REG_L a2, PT_SEPC(sp) + /* + * The current load reservation is effectively part of the processor's + * state, in the sense that load reservations cannot be shared between + * different hart contexts. We can't actually save and restore a load + * reservation, so instead here we clear any existing reservation -- + * it's always legal for implementations to clear load reservations at + * any point (as long as the forward progress guarantee is kept, but + * we'll ignore that here). + * + * Dangling load reservations can be the result of taking a trap in the + * middle of an LR/SC sequence, but can also be the result of a taken + * forward branch around an SC -- which is how we implement CAS. As a + * result we need to clear reservations between the last CAS and the + * jump back to the new context. While it is unlikely the store + * completes, implementations are allowed to expand reservations to be + * arbitrarily large. + */ + REG_L a2, PT_SEPC(sp) + REG_SC x0, a2, PT_SEPC(sp) + csrw CSR_SSTATUS, a0 csrw CSR_SEPC, a2 @@ -254,12 +273,11 @@ restore_all: resume_kernel: REG_L s0, TASK_TI_PREEMPT_COUNT(tp) bnez s0, restore_all -need_resched: REG_L s0, TASK_TI_FLAGS(tp) andi s0, s0, _TIF_NEED_RESCHED beqz s0, restore_all call preempt_schedule_irq - j need_resched + j restore_all #endif work_pending: diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index 424eb72d56b1..93742df9067f 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -124,24 +124,24 @@ static inline unsigned long get_break_insn_length(unsigned long pc) asmlinkage void do_trap_break(struct pt_regs *regs) { -#ifdef CONFIG_GENERIC_BUG if (!user_mode(regs)) { enum bug_trap_type type; type = report_bug(regs->sepc, regs); switch (type) { - case BUG_TRAP_TYPE_NONE: - break; +#ifdef CONFIG_GENERIC_BUG case BUG_TRAP_TYPE_WARN: regs->sepc += get_break_insn_length(regs->sepc); - break; + return; case BUG_TRAP_TYPE_BUG: +#endif /* CONFIG_GENERIC_BUG */ + default: die(regs, "Kernel BUG"); } + } else { + force_sig_fault(SIGTRAP, TRAP_BRKPT, + (void __user *)(regs->sepc)); } -#endif /* CONFIG_GENERIC_BUG */ - - force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)(regs->sepc)); } #ifdef CONFIG_GENERIC_BUG diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index f0ba71304b6e..83f7d12042fb 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -11,6 +11,7 @@ #include <linux/swap.h> #include <linux/sizes.h> #include <linux/of_fdt.h> +#include <linux/libfdt.h> #include <asm/fixmap.h> #include <asm/tlbflush.h> @@ -82,6 +83,8 @@ disable: } #endif /* CONFIG_BLK_DEV_INITRD */ +static phys_addr_t dtb_early_pa __initdata; + void __init setup_bootmem(void) { struct memblock_region *reg; @@ -117,7 +120,12 @@ void __init setup_bootmem(void) setup_initrd(); #endif /* CONFIG_BLK_DEV_INITRD */ - early_init_fdt_reserve_self(); + /* + * Avoid using early_init_fdt_reserve_self() since __pa() does + * not work for DTB pointers that are fixmap addresses + */ + memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va)); + early_init_fdt_scan_reserved_mem(); memblock_allow_resize(); memblock_dump_all(); @@ -393,6 +401,8 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa) /* Save pointer to DTB for early FDT parsing */ dtb_early_va = (void *)fix_to_virt(FIX_FDT) + (dtb_pa & ~PAGE_MASK); + /* Save physical address for memblock reservation */ + dtb_early_pa = dtb_pa; } static void __init setup_vm_final(void) diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 347f48702edb..38d64030aacf 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -44,6 +44,7 @@ CONFIG_NR_CPUS=512 CONFIG_NUMA=y CONFIG_HZ_100=y CONFIG_KEXEC_FILE=y +CONFIG_KEXEC_SIG=y CONFIG_EXPOLINE=y CONFIG_EXPOLINE_AUTO=y CONFIG_CHSC_SCH=y @@ -69,12 +70,13 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y -CONFIG_MODULE_SIG=y CONFIG_MODULE_SIG_SHA256=y +CONFIG_UNUSED_SYMBOLS=y CONFIG_BLK_DEV_INTEGRITY=y CONFIG_BLK_DEV_THROTTLING=y CONFIG_BLK_WBT=y CONFIG_BLK_CGROUP_IOLATENCY=y +CONFIG_BLK_CGROUP_IOCOST=y CONFIG_PARTITION_ADVANCED=y CONFIG_IBM_PARTITION=y CONFIG_BSD_DISKLABEL=y @@ -370,6 +372,7 @@ CONFIG_NETLINK_DIAG=m CONFIG_CGROUP_NET_PRIO=y CONFIG_BPF_JIT=y CONFIG_NET_PKTGEN=m +# CONFIG_NET_DROP_MONITOR is not set CONFIG_PCI=y CONFIG_PCI_DEBUG=y CONFIG_HOTPLUG_PCI=y @@ -424,6 +427,7 @@ CONFIG_DM_CRYPT=m CONFIG_DM_SNAPSHOT=m CONFIG_DM_THIN_PROVISIONING=m CONFIG_DM_WRITECACHE=m +CONFIG_DM_CLONE=m CONFIG_DM_MIRROR=m CONFIG_DM_LOG_USERSPACE=m CONFIG_DM_RAID=m @@ -435,6 +439,7 @@ CONFIG_DM_DELAY=m CONFIG_DM_UEVENT=y CONFIG_DM_FLAKEY=m CONFIG_DM_VERITY=m +CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y CONFIG_DM_SWITCH=m CONFIG_NETDEVICES=y CONFIG_BONDING=m @@ -489,6 +494,7 @@ CONFIG_MLX5_CORE_EN=y # CONFIG_NET_VENDOR_NVIDIA is not set # CONFIG_NET_VENDOR_OKI is not set # CONFIG_NET_VENDOR_PACKET_ENGINES is not set +# CONFIG_NET_VENDOR_PENSANDO is not set # CONFIG_NET_VENDOR_QLOGIC is not set # CONFIG_NET_VENDOR_QUALCOMM is not set # CONFIG_NET_VENDOR_RDC is not set @@ -538,15 +544,16 @@ CONFIG_WATCHDOG=y CONFIG_WATCHDOG_NOWAYOUT=y CONFIG_SOFT_WATCHDOG=m CONFIG_DIAG288_WATCHDOG=m -CONFIG_DRM=y -CONFIG_DRM_VIRTIO_GPU=y +CONFIG_FB=y CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set CONFIG_INFINIBAND=m CONFIG_INFINIBAND_USER_ACCESS=m CONFIG_MLX4_INFINIBAND=m CONFIG_MLX5_INFINIBAND=m +CONFIG_SYNC_FILE=y CONFIG_VFIO=m CONFIG_VFIO_PCI=m CONFIG_VFIO_MDEV=m @@ -580,6 +587,8 @@ CONFIG_NILFS2_FS=m CONFIG_FS_DAX=y CONFIG_EXPORTFS_BLOCK_OPS=y CONFIG_FS_ENCRYPTION=y +CONFIG_FS_VERITY=y +CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -589,6 +598,7 @@ CONFIG_QFMT_V2=m CONFIG_AUTOFS4_FS=m CONFIG_FUSE_FS=y CONFIG_CUSE=m +CONFIG_VIRTIO_FS=m CONFIG_OVERLAY_FS=m CONFIG_FSCACHE=m CONFIG_CACHEFILES=m @@ -648,12 +658,15 @@ CONFIG_FORTIFY_SOURCE=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y CONFIG_INTEGRITY_SIGNATURE=y CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y CONFIG_IMA=y CONFIG_IMA_DEFAULT_HASH_SHA256=y CONFIG_IMA_WRITE_POLICY=y CONFIG_IMA_APPRAISE=y +CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" CONFIG_CRYPTO_USER=m # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_PCRYPT=m @@ -664,10 +677,6 @@ CONFIG_CRYPTO_ECDH=m CONFIG_CRYPTO_ECRDSA=m CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_AEGIS128L=m -CONFIG_CRYPTO_AEGIS256=m -CONFIG_CRYPTO_MORUS640=m -CONFIG_CRYPTO_MORUS1280=m CONFIG_CRYPTO_CFB=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m @@ -739,7 +748,6 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO_DWARF4=y CONFIG_GDB_SCRIPTS=y CONFIG_FRAME_WARN=1024 -CONFIG_UNUSED_SYMBOLS=y CONFIG_HEADERS_INSTALL=y CONFIG_HEADERS_CHECK=y CONFIG_DEBUG_SECTION_MISMATCH=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index 8514b8b9500f..25f799849582 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -44,6 +44,7 @@ CONFIG_NUMA=y # CONFIG_NUMA_EMU is not set CONFIG_HZ_100=y CONFIG_KEXEC_FILE=y +CONFIG_KEXEC_SIG=y CONFIG_EXPOLINE=y CONFIG_EXPOLINE_AUTO=y CONFIG_CHSC_SCH=y @@ -66,11 +67,12 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y -CONFIG_MODULE_SIG=y CONFIG_MODULE_SIG_SHA256=y +CONFIG_UNUSED_SYMBOLS=y CONFIG_BLK_DEV_THROTTLING=y CONFIG_BLK_WBT=y CONFIG_BLK_CGROUP_IOLATENCY=y +CONFIG_BLK_CGROUP_IOCOST=y CONFIG_PARTITION_ADVANCED=y CONFIG_IBM_PARTITION=y CONFIG_BSD_DISKLABEL=y @@ -363,6 +365,7 @@ CONFIG_NETLINK_DIAG=m CONFIG_CGROUP_NET_PRIO=y CONFIG_BPF_JIT=y CONFIG_NET_PKTGEN=m +# CONFIG_NET_DROP_MONITOR is not set CONFIG_PCI=y CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_S390=y @@ -418,6 +421,7 @@ CONFIG_DM_CRYPT=m CONFIG_DM_SNAPSHOT=m CONFIG_DM_THIN_PROVISIONING=m CONFIG_DM_WRITECACHE=m +CONFIG_DM_CLONE=m CONFIG_DM_MIRROR=m CONFIG_DM_LOG_USERSPACE=m CONFIG_DM_RAID=m @@ -429,6 +433,7 @@ CONFIG_DM_DELAY=m CONFIG_DM_UEVENT=y CONFIG_DM_FLAKEY=m CONFIG_DM_VERITY=m +CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y CONFIG_DM_SWITCH=m CONFIG_DM_INTEGRITY=m CONFIG_NETDEVICES=y @@ -484,6 +489,7 @@ CONFIG_MLX5_CORE_EN=y # CONFIG_NET_VENDOR_NVIDIA is not set # CONFIG_NET_VENDOR_OKI is not set # CONFIG_NET_VENDOR_PACKET_ENGINES is not set +# CONFIG_NET_VENDOR_PENSANDO is not set # CONFIG_NET_VENDOR_QLOGIC is not set # CONFIG_NET_VENDOR_QUALCOMM is not set # CONFIG_NET_VENDOR_RDC is not set @@ -533,16 +539,16 @@ CONFIG_WATCHDOG_CORE=y CONFIG_WATCHDOG_NOWAYOUT=y CONFIG_SOFT_WATCHDOG=m CONFIG_DIAG288_WATCHDOG=m -CONFIG_DRM=y -CONFIG_DRM_VIRTIO_GPU=y -# CONFIG_BACKLIGHT_CLASS_DEVICE is not set +CONFIG_FB=y CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set CONFIG_INFINIBAND=m CONFIG_INFINIBAND_USER_ACCESS=m CONFIG_MLX4_INFINIBAND=m CONFIG_MLX5_INFINIBAND=m +CONFIG_SYNC_FILE=y CONFIG_VFIO=m CONFIG_VFIO_PCI=m CONFIG_VFIO_MDEV=m @@ -573,6 +579,8 @@ CONFIG_NILFS2_FS=m CONFIG_FS_DAX=y CONFIG_EXPORTFS_BLOCK_OPS=y CONFIG_FS_ENCRYPTION=y +CONFIG_FS_VERITY=y +CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA_NETLINK_INTERFACE=y @@ -581,6 +589,7 @@ CONFIG_QFMT_V2=m CONFIG_AUTOFS4_FS=m CONFIG_FUSE_FS=y CONFIG_CUSE=m +CONFIG_VIRTIO_FS=m CONFIG_OVERLAY_FS=m CONFIG_FSCACHE=m CONFIG_CACHEFILES=m @@ -639,12 +648,15 @@ CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y CONFIG_INTEGRITY_SIGNATURE=y CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y CONFIG_IMA=y CONFIG_IMA_DEFAULT_HASH_SHA256=y CONFIG_IMA_WRITE_POLICY=y CONFIG_IMA_APPRAISE=y +CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" CONFIG_CRYPTO_FIPS=y CONFIG_CRYPTO_USER=m # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set @@ -656,10 +668,6 @@ CONFIG_CRYPTO_ECDH=m CONFIG_CRYPTO_ECRDSA=m CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_AEGIS128=m -CONFIG_CRYPTO_AEGIS128L=m -CONFIG_CRYPTO_AEGIS256=m -CONFIG_CRYPTO_MORUS640=m -CONFIG_CRYPTO_MORUS1280=m CONFIG_CRYPTO_CFB=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_OFB=m @@ -727,7 +735,6 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO_DWARF4=y CONFIG_GDB_SCRIPTS=y CONFIG_FRAME_WARN=1024 -CONFIG_UNUSED_SYMBOLS=y CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_MEMORY_INIT=y diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig index be09a208b608..20c51e5d9353 100644 --- a/arch/s390/configs/zfcpdump_defconfig +++ b/arch/s390/configs/zfcpdump_defconfig @@ -61,7 +61,7 @@ CONFIG_RAW_DRIVER=y CONFIG_CONFIGFS_FS=y # CONFIG_MISC_FILESYSTEMS is not set # CONFIG_NETWORK_FILESYSTEMS is not set -# CONFIG_DIMLIB is not set +CONFIG_LSM="yama,loadpin,safesetid,integrity" CONFIG_PRINTK_TIME=y CONFIG_DEBUG_INFO=y CONFIG_DEBUG_FS=y diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h index d3f09526ee19..61467b9eecc7 100644 --- a/arch/s390/include/asm/atomic_ops.h +++ b/arch/s390/include/asm/atomic_ops.h @@ -41,7 +41,7 @@ __ATOMIC_OPS(__atomic64_xor, long, "laxg") #undef __ATOMIC_OP #define __ATOMIC_CONST_OP(op_name, op_type, op_string, op_barrier) \ -static inline void op_name(op_type val, op_type *ptr) \ +static __always_inline void op_name(op_type val, op_type *ptr) \ { \ asm volatile( \ op_string " %[ptr],%[val]\n" \ diff --git a/arch/s390/include/asm/bitops.h b/arch/s390/include/asm/bitops.h index b8833ac983fa..eb7eed43e780 100644 --- a/arch/s390/include/asm/bitops.h +++ b/arch/s390/include/asm/bitops.h @@ -56,7 +56,7 @@ __bitops_byte(unsigned long nr, volatile unsigned long *ptr) return ((unsigned char *)ptr) + ((nr ^ (BITS_PER_LONG - 8)) >> 3); } -static inline void arch_set_bit(unsigned long nr, volatile unsigned long *ptr) +static __always_inline void arch_set_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); unsigned long mask; @@ -77,7 +77,7 @@ static inline void arch_set_bit(unsigned long nr, volatile unsigned long *ptr) __atomic64_or(mask, (long *)addr); } -static inline void arch_clear_bit(unsigned long nr, volatile unsigned long *ptr) +static __always_inline void arch_clear_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); unsigned long mask; @@ -98,8 +98,8 @@ static inline void arch_clear_bit(unsigned long nr, volatile unsigned long *ptr) __atomic64_and(mask, (long *)addr); } -static inline void arch_change_bit(unsigned long nr, - volatile unsigned long *ptr) +static __always_inline void arch_change_bit(unsigned long nr, + volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); unsigned long mask; diff --git a/arch/s390/include/asm/cpacf.h b/arch/s390/include/asm/cpacf.h index a092f63aac6a..c0f3bfeddcbe 100644 --- a/arch/s390/include/asm/cpacf.h +++ b/arch/s390/include/asm/cpacf.h @@ -171,7 +171,7 @@ typedef struct { unsigned char bytes[16]; } cpacf_mask_t; * * Returns 1 if @func is available for @opcode, 0 otherwise */ -static inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask) +static __always_inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask) { register unsigned long r0 asm("0") = 0; /* query function */ register unsigned long r1 asm("1") = (unsigned long) mask; diff --git a/arch/s390/include/asm/cpu_mf.h b/arch/s390/include/asm/cpu_mf.h index ceeb552d3472..819803a97c2b 100644 --- a/arch/s390/include/asm/cpu_mf.h +++ b/arch/s390/include/asm/cpu_mf.h @@ -28,6 +28,8 @@ asm(".include \"asm/cpu_mf-insn.h\"\n"); CPU_MF_INT_SF_PRA|CPU_MF_INT_SF_SACA| \ CPU_MF_INT_SF_LSDA) +#define CPU_MF_SF_RIBM_NOTAV 0x1 /* Sampling unavailable */ + /* CPU measurement facility support */ static inline int cpum_cf_avail(void) { @@ -69,7 +71,8 @@ struct hws_qsi_info_block { /* Bit(s) */ unsigned long max_sampl_rate; /* 16-23: maximum sampling interval*/ unsigned long tear; /* 24-31: TEAR contents */ unsigned long dear; /* 32-39: DEAR contents */ - unsigned int rsvrd0; /* 40-43: reserved */ + unsigned int rsvrd0:24; /* 40-42: reserved */ + unsigned int ribm:8; /* 43: Reserved by IBM */ unsigned int cpu_speed; /* 44-47: CPU speed */ unsigned long long rsvrd1; /* 48-55: reserved */ unsigned long long rsvrd2; /* 56-63: reserved */ @@ -220,7 +223,8 @@ enum stcctm_ctr_set { MT_DIAG = 5, MT_DIAG_CLEARING = 9, /* clears loss-of-MT-ctr-data alert */ }; -static inline int stcctm(enum stcctm_ctr_set set, u64 range, u64 *dest) + +static __always_inline int stcctm(enum stcctm_ctr_set set, u64 range, u64 *dest) { int cc; diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h index bb59dd964590..de8f0bf5f238 100644 --- a/arch/s390/include/asm/hugetlb.h +++ b/arch/s390/include/asm/hugetlb.h @@ -12,8 +12,6 @@ #include <asm/page.h> #include <asm/pgtable.h> - -#define is_hugepage_only_range(mm, addr, len) 0 #define hugetlb_free_pgd_range free_pgd_range #define hugepages_supported() (MACHINE_HAS_EDAT1) @@ -23,6 +21,13 @@ pte_t huge_ptep_get(pte_t *ptep); pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep); +static inline bool is_hugepage_only_range(struct mm_struct *mm, + unsigned long addr, + unsigned long len) +{ + return false; +} + /* * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index e548ec1ec12c..39f747d63758 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h @@ -20,7 +20,7 @@ * We use a brcl 0,2 instruction for jump labels at compile time so it * can be easily distinguished from a hotpatch generated instruction. */ -static inline bool arch_static_branch(struct static_key *key, bool branch) +static __always_inline bool arch_static_branch(struct static_key *key, bool branch) { asm_volatile_goto("0: brcl 0,"__stringify(JUMP_LABEL_NOP_OFFSET)"\n" ".pushsection __jump_table,\"aw\"\n" @@ -34,7 +34,7 @@ label: return true; } -static inline bool arch_static_branch_jump(struct static_key *key, bool branch) +static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch) { asm_volatile_goto("0: brcl 15,%l[label]\n" ".pushsection __jump_table,\"aw\"\n" diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 36c578c0ff96..5ff98d76a66c 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -997,9 +997,9 @@ static inline pte_t pte_mkhuge(pte_t pte) #define IPTE_NODAT 0x400 #define IPTE_GUEST_ASCE 0x800 -static inline void __ptep_ipte(unsigned long address, pte_t *ptep, - unsigned long opt, unsigned long asce, - int local) +static __always_inline void __ptep_ipte(unsigned long address, pte_t *ptep, + unsigned long opt, unsigned long asce, + int local) { unsigned long pto = (unsigned long) ptep; @@ -1020,8 +1020,8 @@ static inline void __ptep_ipte(unsigned long address, pte_t *ptep, : [r1] "a" (pto), [m4] "i" (local) : "memory"); } -static inline void __ptep_ipte_range(unsigned long address, int nr, - pte_t *ptep, int local) +static __always_inline void __ptep_ipte_range(unsigned long address, int nr, + pte_t *ptep, int local) { unsigned long pto = (unsigned long) ptep; @@ -1269,7 +1269,8 @@ static inline pte_t *pte_offset(pmd_t *pmd, unsigned long address) #define pte_offset_kernel(pmd, address) pte_offset(pmd, address) #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) -#define pte_unmap(pte) do { } while (0) + +static inline void pte_unmap(pte_t *pte) { } static inline bool gup_fast_permitted(unsigned long start, unsigned long end) { @@ -1435,9 +1436,9 @@ static inline void __pmdp_csp(pmd_t *pmdp) #define IDTE_NODAT 0x1000 #define IDTE_GUEST_ASCE 0x2000 -static inline void __pmdp_idte(unsigned long addr, pmd_t *pmdp, - unsigned long opt, unsigned long asce, - int local) +static __always_inline void __pmdp_idte(unsigned long addr, pmd_t *pmdp, + unsigned long opt, unsigned long asce, + int local) { unsigned long sto; @@ -1461,9 +1462,9 @@ static inline void __pmdp_idte(unsigned long addr, pmd_t *pmdp, } } -static inline void __pudp_idte(unsigned long addr, pud_t *pudp, - unsigned long opt, unsigned long asce, - int local) +static __always_inline void __pudp_idte(unsigned long addr, pud_t *pudp, + unsigned long opt, unsigned long asce, + int local) { unsigned long r3o; diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index 78e8a888306d..e3f238e8c611 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -111,7 +111,7 @@ struct qib { /* private: */ u8 res[88]; /* public: */ - u8 parm[QDIO_MAX_BUFFERS_PER_Q]; + u8 parm[128]; } __attribute__ ((packed, aligned(256))); /** diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index bd2fd9a7821d..a470f1fa9f2a 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -83,7 +83,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n); __rc; \ }) -static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size) +static __always_inline int __put_user_fn(void *x, void __user *ptr, unsigned long size) { unsigned long spec = 0x010000UL; int rc; @@ -113,7 +113,7 @@ static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size) return rc; } -static inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size) +static __always_inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size) { unsigned long spec = 0x01UL; int rc; diff --git a/arch/s390/kernel/perf_cpum_cf_diag.c b/arch/s390/kernel/perf_cpum_cf_diag.c index 5f1fd1581330..2654e348801a 100644 --- a/arch/s390/kernel/perf_cpum_cf_diag.c +++ b/arch/s390/kernel/perf_cpum_cf_diag.c @@ -390,7 +390,7 @@ static size_t cf_diag_getctrset(struct cf_ctrset_entry *ctrdata, int ctrset, debug_sprintf_event(cf_diag_dbg, 6, "%s ctrset %d ctrset_size %zu cfvn %d csvn %d" - " need %zd rc:%d\n", + " need %zd rc %d\n", __func__, ctrset, ctrset_size, cpuhw->info.cfvn, cpuhw->info.csvn, need, rc); return need; @@ -567,7 +567,7 @@ static int cf_diag_add(struct perf_event *event, int flags) int err = 0; debug_sprintf_event(cf_diag_dbg, 5, - "%s event %p cpu %d flags %#x cpuhw:%p\n", + "%s event %p cpu %d flags %#x cpuhw %p\n", __func__, event, event->cpu, flags, cpuhw); if (cpuhw->flags & PMU_F_IN_USE) { diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 544a02e944c6..3d8b12a9a6ff 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -803,6 +803,12 @@ static int __hw_perf_event_init(struct perf_event *event) goto out; } + if (si.ribm & CPU_MF_SF_RIBM_NOTAV) { + pr_warn("CPU Measurement Facility sampling is temporarily not available\n"); + err = -EBUSY; + goto out; + } + /* Always enable basic sampling */ SAMPL_FLAGS(hwc) = PERF_CPUM_SF_BASIC_MODE; @@ -895,7 +901,7 @@ static int cpumsf_pmu_event_init(struct perf_event *event) /* Check online status of the CPU to which the event is pinned */ if (event->cpu >= 0 && !cpu_online(event->cpu)) - return -ENODEV; + return -ENODEV; /* Force reset of idle/hv excludes regardless of what the * user requested. diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f6db0f1bc867..d047e846e1b9 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -332,7 +332,7 @@ static inline int plo_test_bit(unsigned char nr) return cc == 0; } -static inline void __insn32_query(unsigned int opcode, u8 query[32]) +static __always_inline void __insn32_query(unsigned int opcode, u8 *query) { register unsigned long r0 asm("0") = 0; /* query function */ register unsigned long r1 asm("1") = (unsigned long) query; @@ -340,9 +340,9 @@ static inline void __insn32_query(unsigned int opcode, u8 query[32]) asm volatile( /* Parameter regs are ignored */ " .insn rrf,%[opc] << 16,2,4,6,0\n" - : "=m" (*query) + : : "d" (r0), "a" (r1), [opc] "i" (opcode) - : "cc"); + : "cc", "memory"); } #define INSN_SORTL 0xb938 diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index 9bdff4defef1..e585a62d6530 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c @@ -66,7 +66,7 @@ static inline int clp_get_ilp(unsigned long *ilp) /* * Call Logical Processor with c=0, the give constant lps and an lpcb request. */ -static inline int clp_req(void *data, unsigned int lps) +static __always_inline int clp_req(void *data, unsigned int lps) { struct { u8 _[CLP_BLK_SIZE]; } *req = data; u64 ignored; diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index e7d35f60d53f..64c3e70b0556 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -5,12 +5,14 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/jiffies.h> #include <asm/apicdef.h> #include <asm/nmi.h> #include "../perf_event.h" -static DEFINE_PER_CPU(unsigned int, perf_nmi_counter); +static DEFINE_PER_CPU(unsigned long, perf_nmi_tstamp); +static unsigned long perf_nmi_window; static __initconst const u64 amd_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] @@ -641,11 +643,12 @@ static void amd_pmu_disable_event(struct perf_event *event) * handler when multiple PMCs are active or PMC overflow while handling some * other source of an NMI. * - * Attempt to mitigate this by using the number of active PMCs to determine - * whether to return NMI_HANDLED if the perf NMI handler did not handle/reset - * any PMCs. The per-CPU perf_nmi_counter variable is set to a minimum of the - * number of active PMCs or 2. The value of 2 is used in case an NMI does not - * arrive at the LAPIC in time to be collapsed into an already pending NMI. + * Attempt to mitigate this by creating an NMI window in which un-handled NMIs + * received during this window will be claimed. This prevents extending the + * window past when it is possible that latent NMIs should be received. The + * per-CPU perf_nmi_tstamp will be set to the window end time whenever perf has + * handled a counter. When an un-handled NMI is received, it will be claimed + * only if arriving within that window. */ static int amd_pmu_handle_irq(struct pt_regs *regs) { @@ -663,21 +666,19 @@ static int amd_pmu_handle_irq(struct pt_regs *regs) handled = x86_pmu_handle_irq(regs); /* - * If a counter was handled, record the number of possible remaining - * NMIs that can occur. + * If a counter was handled, record a timestamp such that un-handled + * NMIs will be claimed if arriving within that window. */ if (handled) { - this_cpu_write(perf_nmi_counter, - min_t(unsigned int, 2, active)); + this_cpu_write(perf_nmi_tstamp, + jiffies + perf_nmi_window); return handled; } - if (!this_cpu_read(perf_nmi_counter)) + if (time_after(jiffies, this_cpu_read(perf_nmi_tstamp))) return NMI_DONE; - this_cpu_dec(perf_nmi_counter); - return NMI_HANDLED; } @@ -909,6 +910,9 @@ static int __init amd_core_pmu_init(void) if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) return 0; + /* Avoid calulating the value each time in the NMI handler */ + perf_nmi_window = msecs_to_jiffies(100); + switch (boot_cpu_data.x86) { case 0x15: pr_cont("Fam15h "); diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 27ee47a7be66..fcef678c3423 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -4983,6 +4983,8 @@ __init int intel_pmu_init(void) case INTEL_FAM6_SKYLAKE: case INTEL_FAM6_KABYLAKE_L: case INTEL_FAM6_KABYLAKE: + case INTEL_FAM6_COMETLAKE_L: + case INTEL_FAM6_COMETLAKE: x86_add_quirk(intel_pebs_isolation_quirk); x86_pmu.late_ack = true; memcpy(hw_cache_event_ids, skl_hw_cache_event_ids, sizeof(hw_cache_event_ids)); @@ -5031,6 +5033,8 @@ __init int intel_pmu_init(void) /* fall through */ case INTEL_FAM6_ICELAKE_L: case INTEL_FAM6_ICELAKE: + case INTEL_FAM6_TIGERLAKE_L: + case INTEL_FAM6_TIGERLAKE: x86_pmu.late_ack = true; memcpy(hw_cache_event_ids, skl_hw_cache_event_ids, sizeof(hw_cache_event_ids)); memcpy(hw_cache_extra_regs, skl_hw_cache_extra_regs, sizeof(hw_cache_extra_regs)); diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c index 9f2f39003d96..e1daf4151e11 100644 --- a/arch/x86/events/intel/cstate.c +++ b/arch/x86/events/intel/cstate.c @@ -45,46 +45,49 @@ * MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter * perf code: 0x01 * Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,GLM, - CNL + * CNL,KBL,CML * Scope: Core * MSR_CORE_C6_RESIDENCY: CORE C6 Residency Counter * perf code: 0x02 * Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW, - * SKL,KNL,GLM,CNL + * SKL,KNL,GLM,CNL,KBL,CML,ICL,TGL * Scope: Core * MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter * perf code: 0x03 - * Available model: SNB,IVB,HSW,BDW,SKL,CNL + * Available model: SNB,IVB,HSW,BDW,SKL,CNL,KBL,CML, + * ICL,TGL * Scope: Core * MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter. * perf code: 0x00 - * Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL + * Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL, + * KBL,CML,ICL,TGL * Scope: Package (physical package) * MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter. * perf code: 0x01 * Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,KNL, - * GLM,CNL + * GLM,CNL,KBL,CML,ICL,TGL * Scope: Package (physical package) * MSR_PKG_C6_RESIDENCY: Package C6 Residency Counter. * perf code: 0x02 * Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW - * SKL,KNL,GLM,CNL + * SKL,KNL,GLM,CNL,KBL,CML,ICL,TGL * Scope: Package (physical package) * MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter. * perf code: 0x03 - * Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,CNL + * Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,CNL, + * KBL,CML,ICL,TGL * Scope: Package (physical package) * MSR_PKG_C8_RESIDENCY: Package C8 Residency Counter. * perf code: 0x04 - * Available model: HSW ULT,KBL,CNL + * Available model: HSW ULT,KBL,CNL,CML,ICL,TGL * Scope: Package (physical package) * MSR_PKG_C9_RESIDENCY: Package C9 Residency Counter. * perf code: 0x05 - * Available model: HSW ULT,KBL,CNL + * Available model: HSW ULT,KBL,CNL,CML,ICL,TGL * Scope: Package (physical package) * MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter. * perf code: 0x06 - * Available model: HSW ULT,KBL,GLM,CNL + * Available model: HSW ULT,KBL,GLM,CNL,CML,ICL,TGL * Scope: Package (physical package) * */ @@ -544,6 +547,19 @@ static const struct cstate_model cnl_cstates __initconst = { BIT(PERF_CSTATE_PKG_C10_RES), }; +static const struct cstate_model icl_cstates __initconst = { + .core_events = BIT(PERF_CSTATE_CORE_C6_RES) | + BIT(PERF_CSTATE_CORE_C7_RES), + + .pkg_events = BIT(PERF_CSTATE_PKG_C2_RES) | + BIT(PERF_CSTATE_PKG_C3_RES) | + BIT(PERF_CSTATE_PKG_C6_RES) | + BIT(PERF_CSTATE_PKG_C7_RES) | + BIT(PERF_CSTATE_PKG_C8_RES) | + BIT(PERF_CSTATE_PKG_C9_RES) | + BIT(PERF_CSTATE_PKG_C10_RES), +}; + static const struct cstate_model slm_cstates __initconst = { .core_events = BIT(PERF_CSTATE_CORE_C1_RES) | BIT(PERF_CSTATE_CORE_C6_RES), @@ -614,6 +630,8 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = { X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_L, hswult_cstates), X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE, hswult_cstates), + X86_CSTATES_MODEL(INTEL_FAM6_COMETLAKE_L, hswult_cstates), + X86_CSTATES_MODEL(INTEL_FAM6_COMETLAKE, hswult_cstates), X86_CSTATES_MODEL(INTEL_FAM6_CANNONLAKE_L, cnl_cstates), @@ -625,8 +643,10 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = { X86_CSTATES_MODEL(INTEL_FAM6_ATOM_GOLDMONT_PLUS, glm_cstates), - X86_CSTATES_MODEL(INTEL_FAM6_ICELAKE_L, snb_cstates), - X86_CSTATES_MODEL(INTEL_FAM6_ICELAKE, snb_cstates), + X86_CSTATES_MODEL(INTEL_FAM6_ICELAKE_L, icl_cstates), + X86_CSTATES_MODEL(INTEL_FAM6_ICELAKE, icl_cstates), + X86_CSTATES_MODEL(INTEL_FAM6_TIGERLAKE_L, icl_cstates), + X86_CSTATES_MODEL(INTEL_FAM6_TIGERLAKE, icl_cstates), { }, }; MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match); diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c index b1afc77f0704..6f86650b3f77 100644 --- a/arch/x86/events/msr.c +++ b/arch/x86/events/msr.c @@ -89,7 +89,14 @@ static bool test_intel(int idx, void *data) case INTEL_FAM6_SKYLAKE_X: case INTEL_FAM6_KABYLAKE_L: case INTEL_FAM6_KABYLAKE: + case INTEL_FAM6_COMETLAKE_L: + case INTEL_FAM6_COMETLAKE: case INTEL_FAM6_ICELAKE_L: + case INTEL_FAM6_ICELAKE: + case INTEL_FAM6_ICELAKE_X: + case INTEL_FAM6_ICELAKE_D: + case INTEL_FAM6_TIGERLAKE_L: + case INTEL_FAM6_TIGERLAKE: if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF) return true; break; diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h index cff3f3f3bfe0..8348f7d69fd5 100644 --- a/arch/x86/include/asm/cpu_entry_area.h +++ b/arch/x86/include/asm/cpu_entry_area.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _ASM_X86_CPU_ENTRY_AREA_H #define _ASM_X86_CPU_ENTRY_AREA_H diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h index f04622500da3..c606c0b70738 100644 --- a/arch/x86/include/asm/intel-family.h +++ b/arch/x86/include/asm/intel-family.h @@ -83,6 +83,9 @@ #define INTEL_FAM6_TIGERLAKE_L 0x8C #define INTEL_FAM6_TIGERLAKE 0x8D +#define INTEL_FAM6_COMETLAKE 0xA5 +#define INTEL_FAM6_COMETLAKE_L 0xA6 + /* "Small Core" Processors (Atom) */ #define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */ diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 23edf56cf577..50eb430b0ad8 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -219,13 +219,6 @@ enum { PFERR_WRITE_MASK | \ PFERR_PRESENT_MASK) -/* - * The mask used to denote special SPTEs, which can be either MMIO SPTEs or - * Access Tracking SPTEs. We use bit 62 instead of bit 63 to avoid conflicting - * with the SVE bit in EPT PTEs. - */ -#define SPTE_SPECIAL_MASK (1ULL << 62) - /* apic attention bits */ #define KVM_APIC_CHECK_VAPIC 0 /* diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h index e28f8b723b5c..9d5252c9685c 100644 --- a/arch/x86/include/asm/mwait.h +++ b/arch/x86/include/asm/mwait.h @@ -21,7 +21,7 @@ #define MWAIT_ECX_INTERRUPT_BREAK 0x1 #define MWAITX_ECX_TIMER_ENABLE BIT(1) #define MWAITX_MAX_LOOPS ((u32)-1) -#define MWAITX_DISABLE_CSTATES 0xf +#define MWAITX_DISABLE_CSTATES 0xf0 static inline void __monitor(const void *eax, unsigned long ecx, unsigned long edx) diff --git a/arch/x86/include/asm/pti.h b/arch/x86/include/asm/pti.h index 5df09a0b80b8..07375b476c4f 100644 --- a/arch/x86/include/asm/pti.h +++ b/arch/x86/include/asm/pti.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _ASM_X86_PTI_H #define _ASM_X86_PTI_H #ifndef __ASSEMBLY__ diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 35c225ede0e4..61d93f062a36 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -734,5 +734,28 @@ do { \ if (unlikely(__gu_err)) goto err_label; \ } while (0) +/* + * We want the unsafe accessors to always be inlined and use + * the error labels - thus the macro games. + */ +#define unsafe_copy_loop(dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_put_user(*(type *)src,(type __user *)dst,label); \ + dst += sizeof(type); \ + src += sizeof(type); \ + len -= sizeof(type); \ + } + +#define unsafe_copy_to_user(_dst,_src,_len,label) \ +do { \ + char __user *__ucu_dst = (_dst); \ + const char *__ucu_src = (_src); \ + size_t __ucu_len = (_len); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +} while (0) + #endif /* _ASM_X86_UACCESS_H */ diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c index 9735139cfdf8..46d732696c1c 100644 --- a/arch/x86/kernel/cpu/vmware.c +++ b/arch/x86/kernel/cpu/vmware.c @@ -49,7 +49,7 @@ #define VMWARE_CMD_VCPU_RESERVED 31 #define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \ - __asm__("inl (%%dx)" : \ + __asm__("inl (%%dx), %%eax" : \ "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \ "a"(VMWARE_HYPERVISOR_MAGIC), \ "c"(VMWARE_CMD_##cmd), \ diff --git a/arch/x86/kernel/process.h b/arch/x86/kernel/process.h index 320ab978fb1f..1d0797b2338a 100644 --- a/arch/x86/kernel/process.h +++ b/arch/x86/kernel/process.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ // // Code shared between 32 and 64 bit diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 63316036f85a..9c5029cf6f3f 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -485,6 +485,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 0x80000008.ebx */ const u32 kvm_cpuid_8000_0008_ebx_x86_features = + F(CLZERO) | F(XSAVEERPTR) | F(WBNOINVD) | F(AMD_IBPB) | F(AMD_IBRS) | F(AMD_SSBD) | F(VIRT_SSBD) | F(AMD_SSB_NO) | F(AMD_STIBP) | F(AMD_STIBP_ALWAYS_ON); @@ -618,16 +619,20 @@ static inline int __do_cpuid_func(struct kvm_cpuid_entry2 *entry, u32 function, */ case 0x1f: case 0xb: { - int i, level_type; + int i; - /* read more entries until level_type is zero */ - for (i = 1; ; ++i) { + /* + * We filled in entry[0] for CPUID(EAX=<function>, + * ECX=00H) above. If its level type (ECX[15:8]) is + * zero, then the leaf is unimplemented, and we're + * done. Otherwise, continue to populate entries + * until the level type (ECX[15:8]) of the previously + * added entry is zero. + */ + for (i = 1; entry[i - 1].ecx & 0xff00; ++i) { if (*nent >= maxnent) goto out; - level_type = entry[i - 1].ecx & 0xff00; - if (!level_type) - break; do_host_cpuid(&entry[i], function, i); ++*nent; } @@ -969,53 +974,66 @@ struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu, EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry); /* - * If no match is found, check whether we exceed the vCPU's limit - * and return the content of the highest valid _standard_ leaf instead. - * This is to satisfy the CPUID specification. + * If the basic or extended CPUID leaf requested is higher than the + * maximum supported basic or extended leaf, respectively, then it is + * out of range. */ -static struct kvm_cpuid_entry2* check_cpuid_limit(struct kvm_vcpu *vcpu, - u32 function, u32 index) +static bool cpuid_function_in_range(struct kvm_vcpu *vcpu, u32 function) { - struct kvm_cpuid_entry2 *maxlevel; - - maxlevel = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0); - if (!maxlevel || maxlevel->eax >= function) - return NULL; - if (function & 0x80000000) { - maxlevel = kvm_find_cpuid_entry(vcpu, 0, 0); - if (!maxlevel) - return NULL; - } - return kvm_find_cpuid_entry(vcpu, maxlevel->eax, index); + struct kvm_cpuid_entry2 *max; + + max = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0); + return max && function <= max->eax; } bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, bool check_limit) { u32 function = *eax, index = *ecx; - struct kvm_cpuid_entry2 *best; - bool entry_found = true; - - best = kvm_find_cpuid_entry(vcpu, function, index); - - if (!best) { - entry_found = false; - if (!check_limit) - goto out; + struct kvm_cpuid_entry2 *entry; + struct kvm_cpuid_entry2 *max; + bool found; - best = check_cpuid_limit(vcpu, function, index); + entry = kvm_find_cpuid_entry(vcpu, function, index); + found = entry; + /* + * Intel CPUID semantics treats any query for an out-of-range + * leaf as if the highest basic leaf (i.e. CPUID.0H:EAX) were + * requested. AMD CPUID semantics returns all zeroes for any + * undefined leaf, whether or not the leaf is in range. + */ + if (!entry && check_limit && !guest_cpuid_is_amd(vcpu) && + !cpuid_function_in_range(vcpu, function)) { + max = kvm_find_cpuid_entry(vcpu, 0, 0); + if (max) { + function = max->eax; + entry = kvm_find_cpuid_entry(vcpu, function, index); + } } - -out: - if (best) { - *eax = best->eax; - *ebx = best->ebx; - *ecx = best->ecx; - *edx = best->edx; - } else + if (entry) { + *eax = entry->eax; + *ebx = entry->ebx; + *ecx = entry->ecx; + *edx = entry->edx; + } else { *eax = *ebx = *ecx = *edx = 0; - trace_kvm_cpuid(function, *eax, *ebx, *ecx, *edx, entry_found); - return entry_found; + /* + * When leaf 0BH or 1FH is defined, CL is pass-through + * and EDX is always the x2APIC ID, even for undefined + * subleaves. Index 1 will exist iff the leaf is + * implemented, so we pass through CL iff leaf 1 + * exists. EDX can be copied from any existing index. + */ + if (function == 0xb || function == 0x1f) { + entry = kvm_find_cpuid_entry(vcpu, function, 1); + if (entry) { + *ecx = index & 0xff; + *edx = entry->edx; + } + } + } + trace_kvm_cpuid(function, *eax, *ebx, *ecx, *edx, found); + return found; } EXPORT_SYMBOL_GPL(kvm_cpuid); diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 3a3a6854dcca..87b0fcc23ef8 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -66,9 +66,10 @@ #define X2APIC_BROADCAST 0xFFFFFFFFul static bool lapic_timer_advance_dynamic __read_mostly; -#define LAPIC_TIMER_ADVANCE_ADJUST_MIN 100 -#define LAPIC_TIMER_ADVANCE_ADJUST_MAX 5000 -#define LAPIC_TIMER_ADVANCE_ADJUST_INIT 1000 +#define LAPIC_TIMER_ADVANCE_ADJUST_MIN 100 /* clock cycles */ +#define LAPIC_TIMER_ADVANCE_ADJUST_MAX 10000 /* clock cycles */ +#define LAPIC_TIMER_ADVANCE_NS_INIT 1000 +#define LAPIC_TIMER_ADVANCE_NS_MAX 5000 /* step-by-step approximation to mitigate fluctuation */ #define LAPIC_TIMER_ADVANCE_ADJUST_STEP 8 @@ -1504,8 +1505,8 @@ static inline void adjust_lapic_timer_advance(struct kvm_vcpu *vcpu, timer_advance_ns += ns/LAPIC_TIMER_ADVANCE_ADJUST_STEP; } - if (unlikely(timer_advance_ns > LAPIC_TIMER_ADVANCE_ADJUST_MAX)) - timer_advance_ns = LAPIC_TIMER_ADVANCE_ADJUST_INIT; + if (unlikely(timer_advance_ns > LAPIC_TIMER_ADVANCE_NS_MAX)) + timer_advance_ns = LAPIC_TIMER_ADVANCE_NS_INIT; apic->lapic_timer.timer_advance_ns = timer_advance_ns; } @@ -2302,7 +2303,7 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns) HRTIMER_MODE_ABS_HARD); apic->lapic_timer.timer.function = apic_timer_fn; if (timer_advance_ns == -1) { - apic->lapic_timer.timer_advance_ns = LAPIC_TIMER_ADVANCE_ADJUST_INIT; + apic->lapic_timer.timer_advance_ns = LAPIC_TIMER_ADVANCE_NS_INIT; lapic_timer_advance_dynamic = true; } else { apic->lapic_timer.timer_advance_ns = timer_advance_ns; diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 5269aa057dfa..24c23c66b226 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -83,7 +83,17 @@ module_param(dbg, bool, 0644); #define PTE_PREFETCH_NUM 8 #define PT_FIRST_AVAIL_BITS_SHIFT 10 -#define PT64_SECOND_AVAIL_BITS_SHIFT 52 +#define PT64_SECOND_AVAIL_BITS_SHIFT 54 + +/* + * The mask used to denote special SPTEs, which can be either MMIO SPTEs or + * Access Tracking SPTEs. + */ +#define SPTE_SPECIAL_MASK (3ULL << 52) +#define SPTE_AD_ENABLED_MASK (0ULL << 52) +#define SPTE_AD_DISABLED_MASK (1ULL << 52) +#define SPTE_AD_WRPROT_ONLY_MASK (2ULL << 52) +#define SPTE_MMIO_MASK (3ULL << 52) #define PT64_LEVEL_BITS 9 @@ -219,12 +229,11 @@ static u64 __read_mostly shadow_present_mask; static u64 __read_mostly shadow_me_mask; /* - * SPTEs used by MMUs without A/D bits are marked with shadow_acc_track_value. - * Non-present SPTEs with shadow_acc_track_value set are in place for access - * tracking. + * SPTEs used by MMUs without A/D bits are marked with SPTE_AD_DISABLED_MASK; + * shadow_acc_track_mask is the set of bits to be cleared in non-accessed + * pages. */ static u64 __read_mostly shadow_acc_track_mask; -static const u64 shadow_acc_track_value = SPTE_SPECIAL_MASK; /* * The mask/shift to use for saving the original R/X bits when marking the PTE @@ -304,7 +313,7 @@ void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask, u64 mmio_value, u64 access_mask) { BUG_ON((u64)(unsigned)access_mask != access_mask); BUG_ON((mmio_mask & mmio_value) != mmio_value); - shadow_mmio_value = mmio_value | SPTE_SPECIAL_MASK; + shadow_mmio_value = mmio_value | SPTE_MMIO_MASK; shadow_mmio_mask = mmio_mask | SPTE_SPECIAL_MASK; shadow_mmio_access_mask = access_mask; } @@ -320,10 +329,27 @@ static inline bool sp_ad_disabled(struct kvm_mmu_page *sp) return sp->role.ad_disabled; } +static inline bool kvm_vcpu_ad_need_write_protect(struct kvm_vcpu *vcpu) +{ + /* + * When using the EPT page-modification log, the GPAs in the log + * would come from L2 rather than L1. Therefore, we need to rely + * on write protection to record dirty pages. This also bypasses + * PML, since writes now result in a vmexit. + */ + return vcpu->arch.mmu == &vcpu->arch.guest_mmu; +} + static inline bool spte_ad_enabled(u64 spte) { MMU_WARN_ON(is_mmio_spte(spte)); - return !(spte & shadow_acc_track_value); + return (spte & SPTE_SPECIAL_MASK) != SPTE_AD_DISABLED_MASK; +} + +static inline bool spte_ad_need_write_protect(u64 spte) +{ + MMU_WARN_ON(is_mmio_spte(spte)); + return (spte & SPTE_SPECIAL_MASK) != SPTE_AD_ENABLED_MASK; } static inline u64 spte_shadow_accessed_mask(u64 spte) @@ -461,7 +487,7 @@ void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask, { BUG_ON(!dirty_mask != !accessed_mask); BUG_ON(!accessed_mask && !acc_track_mask); - BUG_ON(acc_track_mask & shadow_acc_track_value); + BUG_ON(acc_track_mask & SPTE_SPECIAL_MASK); shadow_user_mask = user_mask; shadow_accessed_mask = accessed_mask; @@ -1589,16 +1615,16 @@ static bool spte_clear_dirty(u64 *sptep) rmap_printk("rmap_clear_dirty: spte %p %llx\n", sptep, *sptep); + MMU_WARN_ON(!spte_ad_enabled(spte)); spte &= ~shadow_dirty_mask; - return mmu_spte_update(sptep, spte); } -static bool wrprot_ad_disabled_spte(u64 *sptep) +static bool spte_wrprot_for_clear_dirty(u64 *sptep) { bool was_writable = test_and_clear_bit(PT_WRITABLE_SHIFT, (unsigned long *)sptep); - if (was_writable) + if (was_writable && !spte_ad_enabled(*sptep)) kvm_set_pfn_dirty(spte_to_pfn(*sptep)); return was_writable; @@ -1617,10 +1643,10 @@ static bool __rmap_clear_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head) bool flush = false; for_each_rmap_spte(rmap_head, &iter, sptep) - if (spte_ad_enabled(*sptep)) - flush |= spte_clear_dirty(sptep); + if (spte_ad_need_write_protect(*sptep)) + flush |= spte_wrprot_for_clear_dirty(sptep); else - flush |= wrprot_ad_disabled_spte(sptep); + flush |= spte_clear_dirty(sptep); return flush; } @@ -1631,6 +1657,11 @@ static bool spte_set_dirty(u64 *sptep) rmap_printk("rmap_set_dirty: spte %p %llx\n", sptep, *sptep); + /* + * Similar to the !kvm_x86_ops->slot_disable_log_dirty case, + * do not bother adding back write access to pages marked + * SPTE_AD_WRPROT_ONLY_MASK. + */ spte |= shadow_dirty_mask; return mmu_spte_update(sptep, spte); @@ -2622,7 +2653,7 @@ static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep, shadow_user_mask | shadow_x_mask | shadow_me_mask; if (sp_ad_disabled(sp)) - spte |= shadow_acc_track_value; + spte |= SPTE_AD_DISABLED_MASK; else spte |= shadow_accessed_mask; @@ -2968,7 +2999,9 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, sp = page_header(__pa(sptep)); if (sp_ad_disabled(sp)) - spte |= shadow_acc_track_value; + spte |= SPTE_AD_DISABLED_MASK; + else if (kvm_vcpu_ad_need_write_protect(vcpu)) + spte |= SPTE_AD_WRPROT_ONLY_MASK; /* * For the EPT case, shadow_present_mask is 0 if hardware diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 41abc62c9a8a..e76eb4f07f6c 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2610,7 +2610,7 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, /* VM-entry exception error code */ if (CC(has_error_code && - vmcs12->vm_entry_exception_error_code & GENMASK(31, 15))) + vmcs12->vm_entry_exception_error_code & GENMASK(31, 16))) return -EINVAL; /* VM-entry interruption-info field: reserved bits */ diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 4dea0e0e7e39..3e9c059099e9 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -262,6 +262,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) static void intel_pmu_refresh(struct kvm_vcpu *vcpu) { struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); + struct x86_pmu_capability x86_pmu; struct kvm_cpuid_entry2 *entry; union cpuid10_eax eax; union cpuid10_edx edx; @@ -283,8 +284,10 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) if (!pmu->version) return; + perf_get_x86_pmu_capability(&x86_pmu); + pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters, - INTEL_PMC_MAX_GENERIC); + x86_pmu.num_counters_gp); pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1; pmu->available_event_types = ~entry->ebx & ((1ull << eax.split.mask_length) - 1); @@ -294,7 +297,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) } else { pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed, - INTEL_PMC_MAX_FIXED); + x86_pmu.num_counters_fixed); pmu->counter_bitmask[KVM_PMC_FIXED] = ((u64)1 << edx.split.bit_width_fixed) - 1; } diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d4575ffb3cec..e7970a2e8eae 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -209,6 +209,11 @@ static int vmx_setup_l1d_flush(enum vmx_l1d_flush_state l1tf) struct page *page; unsigned int i; + if (!boot_cpu_has_bug(X86_BUG_L1TF)) { + l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_NOT_REQUIRED; + return 0; + } + if (!enable_ept) { l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_EPT_DISABLED; return 0; @@ -7995,12 +8000,10 @@ static int __init vmx_init(void) * contain 'auto' which will be turned into the default 'cond' * mitigation mode. */ - if (boot_cpu_has(X86_BUG_L1TF)) { - r = vmx_setup_l1d_flush(vmentry_l1d_flush_param); - if (r) { - vmx_exit(); - return r; - } + r = vmx_setup_l1d_flush(vmentry_l1d_flush_param); + if (r) { + vmx_exit(); + return r; } #ifdef CONFIG_KEXEC_CORE diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0ed07d8d2caa..661e2bf38526 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -92,8 +92,8 @@ u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA)); static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); #endif -#define VM_STAT(x) offsetof(struct kvm, stat.x), KVM_STAT_VM -#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU +#define VM_STAT(x, ...) offsetof(struct kvm, stat.x), KVM_STAT_VM, ## __VA_ARGS__ +#define VCPU_STAT(x, ...) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU, ## __VA_ARGS__ #define KVM_X2APIC_API_VALID_FLAGS (KVM_X2APIC_API_USE_32BIT_IDS | \ KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK) @@ -212,7 +212,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "mmu_cache_miss", VM_STAT(mmu_cache_miss) }, { "mmu_unsync", VM_STAT(mmu_unsync) }, { "remote_tlb_flush", VM_STAT(remote_tlb_flush) }, - { "largepages", VM_STAT(lpages) }, + { "largepages", VM_STAT(lpages, .mode = 0444) }, { "max_mmu_page_hash_collisions", VM_STAT(max_mmu_page_hash_collisions) }, { NULL } @@ -885,34 +885,42 @@ int kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr) } EXPORT_SYMBOL_GPL(kvm_set_xcr); -int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) +static int kvm_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) { - unsigned long old_cr4 = kvm_read_cr4(vcpu); - unsigned long pdptr_bits = X86_CR4_PGE | X86_CR4_PSE | X86_CR4_PAE | - X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE; - if (cr4 & CR4_RESERVED_BITS) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_XSAVE) && (cr4 & X86_CR4_OSXSAVE)) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_SMEP) && (cr4 & X86_CR4_SMEP)) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_SMAP) && (cr4 & X86_CR4_SMAP)) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_FSGSBASE) && (cr4 & X86_CR4_FSGSBASE)) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_PKU) && (cr4 & X86_CR4_PKE)) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_LA57) && (cr4 & X86_CR4_LA57)) - return 1; + return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_UMIP) && (cr4 & X86_CR4_UMIP)) + return -EINVAL; + + return 0; +} + +int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) +{ + unsigned long old_cr4 = kvm_read_cr4(vcpu); + unsigned long pdptr_bits = X86_CR4_PGE | X86_CR4_PSE | X86_CR4_PAE | + X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE; + + if (kvm_valid_cr4(vcpu, cr4)) return 1; if (is_long_mode(vcpu)) { @@ -1161,13 +1169,6 @@ static u32 msrs_to_save[] = { MSR_ARCH_PERFMON_PERFCTR0 + 12, MSR_ARCH_PERFMON_PERFCTR0 + 13, MSR_ARCH_PERFMON_PERFCTR0 + 14, MSR_ARCH_PERFMON_PERFCTR0 + 15, MSR_ARCH_PERFMON_PERFCTR0 + 16, MSR_ARCH_PERFMON_PERFCTR0 + 17, - MSR_ARCH_PERFMON_PERFCTR0 + 18, MSR_ARCH_PERFMON_PERFCTR0 + 19, - MSR_ARCH_PERFMON_PERFCTR0 + 20, MSR_ARCH_PERFMON_PERFCTR0 + 21, - MSR_ARCH_PERFMON_PERFCTR0 + 22, MSR_ARCH_PERFMON_PERFCTR0 + 23, - MSR_ARCH_PERFMON_PERFCTR0 + 24, MSR_ARCH_PERFMON_PERFCTR0 + 25, - MSR_ARCH_PERFMON_PERFCTR0 + 26, MSR_ARCH_PERFMON_PERFCTR0 + 27, - MSR_ARCH_PERFMON_PERFCTR0 + 28, MSR_ARCH_PERFMON_PERFCTR0 + 29, - MSR_ARCH_PERFMON_PERFCTR0 + 30, MSR_ARCH_PERFMON_PERFCTR0 + 31, MSR_ARCH_PERFMON_EVENTSEL0, MSR_ARCH_PERFMON_EVENTSEL1, MSR_ARCH_PERFMON_EVENTSEL0 + 2, MSR_ARCH_PERFMON_EVENTSEL0 + 3, MSR_ARCH_PERFMON_EVENTSEL0 + 4, MSR_ARCH_PERFMON_EVENTSEL0 + 5, @@ -1177,13 +1178,6 @@ static u32 msrs_to_save[] = { MSR_ARCH_PERFMON_EVENTSEL0 + 12, MSR_ARCH_PERFMON_EVENTSEL0 + 13, MSR_ARCH_PERFMON_EVENTSEL0 + 14, MSR_ARCH_PERFMON_EVENTSEL0 + 15, MSR_ARCH_PERFMON_EVENTSEL0 + 16, MSR_ARCH_PERFMON_EVENTSEL0 + 17, - MSR_ARCH_PERFMON_EVENTSEL0 + 18, MSR_ARCH_PERFMON_EVENTSEL0 + 19, - MSR_ARCH_PERFMON_EVENTSEL0 + 20, MSR_ARCH_PERFMON_EVENTSEL0 + 21, - MSR_ARCH_PERFMON_EVENTSEL0 + 22, MSR_ARCH_PERFMON_EVENTSEL0 + 23, - MSR_ARCH_PERFMON_EVENTSEL0 + 24, MSR_ARCH_PERFMON_EVENTSEL0 + 25, - MSR_ARCH_PERFMON_EVENTSEL0 + 26, MSR_ARCH_PERFMON_EVENTSEL0 + 27, - MSR_ARCH_PERFMON_EVENTSEL0 + 28, MSR_ARCH_PERFMON_EVENTSEL0 + 29, - MSR_ARCH_PERFMON_EVENTSEL0 + 30, MSR_ARCH_PERFMON_EVENTSEL0 + 31, }; static unsigned num_msrs_to_save; @@ -5097,13 +5091,14 @@ out: static void kvm_init_msr_list(void) { + struct x86_pmu_capability x86_pmu; u32 dummy[2]; unsigned i, j; BUILD_BUG_ON_MSG(INTEL_PMC_MAX_FIXED != 4, "Please update the fixed PMCs in msrs_to_save[]"); - BUILD_BUG_ON_MSG(INTEL_PMC_MAX_GENERIC != 32, - "Please update the generic perfctr/eventsel MSRs in msrs_to_save[]"); + + perf_get_x86_pmu_capability(&x86_pmu); for (i = j = 0; i < ARRAY_SIZE(msrs_to_save); i++) { if (rdmsr_safe(msrs_to_save[i], &dummy[0], &dummy[1]) < 0) @@ -5145,6 +5140,15 @@ static void kvm_init_msr_list(void) intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2) continue; break; + case MSR_ARCH_PERFMON_PERFCTR0 ... MSR_ARCH_PERFMON_PERFCTR0 + 17: + if (msrs_to_save[i] - MSR_ARCH_PERFMON_PERFCTR0 >= + min(INTEL_PMC_MAX_GENERIC, x86_pmu.num_counters_gp)) + continue; + break; + case MSR_ARCH_PERFMON_EVENTSEL0 ... MSR_ARCH_PERFMON_EVENTSEL0 + 17: + if (msrs_to_save[i] - MSR_ARCH_PERFMON_EVENTSEL0 >= + min(INTEL_PMC_MAX_GENERIC, x86_pmu.num_counters_gp)) + continue; } default: break; @@ -8714,10 +8718,6 @@ EXPORT_SYMBOL_GPL(kvm_task_switch); static int kvm_valid_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) { - if (!guest_cpuid_has(vcpu, X86_FEATURE_XSAVE) && - (sregs->cr4 & X86_CR4_OSXSAVE)) - return -EINVAL; - if ((sregs->efer & EFER_LME) && (sregs->cr0 & X86_CR0_PG)) { /* * When EFER.LME and CR0.PG are set, the processor is in @@ -8736,7 +8736,7 @@ static int kvm_valid_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) return -EINVAL; } - return 0; + return kvm_valid_cr4(vcpu, sregs->cr4); } static int __set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c index b7375dc6898f..c126571e5e2e 100644 --- a/arch/x86/lib/delay.c +++ b/arch/x86/lib/delay.c @@ -113,8 +113,8 @@ static void delay_mwaitx(unsigned long __loops) __monitorx(raw_cpu_ptr(&cpu_tss_rw), 0, 0); /* - * AMD, like Intel, supports the EAX hint and EAX=0xf - * means, do not enter any deep C-state and we use it + * AMD, like Intel's MWAIT version, supports the EAX hint and + * EAX=0xf0 means, do not enter any deep C-state and we use it * here in delay() to minimize wakeup latency. */ __mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index c202e1b07e29..425e025341db 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -917,9 +917,6 @@ static void __init kexec_enter_virtual_mode(void) if (efi_enabled(EFI_OLD_MEMMAP) && (__supported_pte_mask & _PAGE_NX)) runtime_code_page_mkexec(); - - /* clean DUMMY object */ - efi_delete_dummy_variable(); #endif } diff --git a/arch/x86/xen/efi.c b/arch/x86/xen/efi.c index 0d3365cb64de..a04551ee5568 100644 --- a/arch/x86/xen/efi.c +++ b/arch/x86/xen/efi.c @@ -57,19 +57,7 @@ static efi_system_table_t __init *xen_efi_probe(void) return NULL; /* Here we know that Xen runs on EFI platform. */ - - efi.get_time = xen_efi_get_time; - efi.set_time = xen_efi_set_time; - efi.get_wakeup_time = xen_efi_get_wakeup_time; - efi.set_wakeup_time = xen_efi_set_wakeup_time; - efi.get_variable = xen_efi_get_variable; - efi.get_next_variable = xen_efi_get_next_variable; - efi.set_variable = xen_efi_set_variable; - efi.query_variable_info = xen_efi_query_variable_info; - efi.update_capsule = xen_efi_update_capsule; - efi.query_capsule_caps = xen_efi_query_capsule_caps; - efi.get_next_high_mono_count = xen_efi_get_next_high_mono_count; - efi.reset_system = xen_efi_reset_system; + xen_efi_runtime_setup(); efi_systab_xen.tables = info->cfg.addr; efi_systab_xen.nr_tables = info->cfg.nent; diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 750f46ad018a..205b1176084f 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -269,19 +269,41 @@ void xen_reboot(int reason) BUG(); } +static int reboot_reason = SHUTDOWN_reboot; +static bool xen_legacy_crash; void xen_emergency_restart(void) { - xen_reboot(SHUTDOWN_reboot); + xen_reboot(reboot_reason); } static int xen_panic_event(struct notifier_block *this, unsigned long event, void *ptr) { - if (!kexec_crash_loaded()) - xen_reboot(SHUTDOWN_crash); + if (!kexec_crash_loaded()) { + if (xen_legacy_crash) + xen_reboot(SHUTDOWN_crash); + + reboot_reason = SHUTDOWN_crash; + + /* + * If panic_timeout==0 then we are supposed to wait forever. + * However, to preserve original dom0 behavior we have to drop + * into hypervisor. (domU behavior is controlled by its + * config file) + */ + if (panic_timeout == 0) + panic_timeout = -1; + } return NOTIFY_DONE; } +static int __init parse_xen_legacy_crash(char *arg) +{ + xen_legacy_crash = true; + return 0; +} +early_param("xen_legacy_crash", parse_xen_legacy_crash); + static struct notifier_block xen_panic_block = { .notifier_call = xen_panic_event, .priority = INT_MIN diff --git a/block/blk-mq.c b/block/blk-mq.c index 6e3b15f70cd7..ec791156e9cc 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1992,10 +1992,14 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) /* bypass scheduler for flush rq */ blk_insert_flush(rq); blk_mq_run_hw_queue(data.hctx, true); - } else if (plug && (q->nr_hw_queues == 1 || q->mq_ops->commit_rqs)) { + } else if (plug && (q->nr_hw_queues == 1 || q->mq_ops->commit_rqs || + !blk_queue_nonrot(q))) { /* * Use plugging if we have a ->commit_rqs() hook as well, as * we know the driver uses bd->last in a smart fashion. + * + * Use normal plugging if this disk is slow HDD, as sequential + * IO may benefit a lot from plug merging. */ unsigned int request_count = plug->rq_count; struct request *last = NULL; @@ -2012,6 +2016,8 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) } blk_add_rq_to_plug(plug, rq); + } else if (q->elevator) { + blk_mq_sched_insert_request(rq, false, true, true); } else if (plug && !blk_queue_nomerges(q)) { /* * We do limited plugging. If the bio can be merged, do that. @@ -2035,8 +2041,8 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) blk_mq_try_issue_directly(data.hctx, same_queue_rq, &cookie); } - } else if ((q->nr_hw_queues > 1 && is_sync) || (!q->elevator && - !data.hctx->dispatch_busy)) { + } else if ((q->nr_hw_queues > 1 && is_sync) || + !data.hctx->dispatch_busy) { blk_mq_try_issue_directly(data.hctx, rq, &cookie); } else { blk_mq_sched_insert_request(rq, false, true, true); diff --git a/block/blk-rq-qos.c b/block/blk-rq-qos.c index 61b635bc2a31..656460636ad3 100644 --- a/block/blk-rq-qos.c +++ b/block/blk-rq-qos.c @@ -160,24 +160,27 @@ bool rq_depth_calc_max_depth(struct rq_depth *rqd) return ret; } -void rq_depth_scale_up(struct rq_depth *rqd) +/* Returns true on success and false if scaling up wasn't possible */ +bool rq_depth_scale_up(struct rq_depth *rqd) { /* * Hit max in previous round, stop here */ if (rqd->scaled_max) - return; + return false; rqd->scale_step--; rqd->scaled_max = rq_depth_calc_max_depth(rqd); + return true; } /* * Scale rwb down. If 'hard_throttle' is set, do it quicker, since we - * had a latency violation. + * had a latency violation. Returns true on success and returns false if + * scaling down wasn't possible. */ -void rq_depth_scale_down(struct rq_depth *rqd, bool hard_throttle) +bool rq_depth_scale_down(struct rq_depth *rqd, bool hard_throttle) { /* * Stop scaling down when we've hit the limit. This also prevents @@ -185,7 +188,7 @@ void rq_depth_scale_down(struct rq_depth *rqd, bool hard_throttle) * keep up. */ if (rqd->max_depth == 1) - return; + return false; if (rqd->scale_step < 0 && hard_throttle) rqd->scale_step = 0; @@ -194,6 +197,7 @@ void rq_depth_scale_down(struct rq_depth *rqd, bool hard_throttle) rqd->scaled_max = false; rq_depth_calc_max_depth(rqd); + return true; } struct rq_qos_wait_data { diff --git a/block/blk-rq-qos.h b/block/blk-rq-qos.h index 08a09dbe0f4b..e8cb68f6958a 100644 --- a/block/blk-rq-qos.h +++ b/block/blk-rq-qos.h @@ -130,8 +130,8 @@ void rq_qos_wait(struct rq_wait *rqw, void *private_data, acquire_inflight_cb_t *acquire_inflight_cb, cleanup_cb_t *cleanup_cb); bool rq_wait_inc_below(struct rq_wait *rq_wait, unsigned int limit); -void rq_depth_scale_up(struct rq_depth *rqd); -void rq_depth_scale_down(struct rq_depth *rqd, bool hard_throttle); +bool rq_depth_scale_up(struct rq_depth *rqd); +bool rq_depth_scale_down(struct rq_depth *rqd, bool hard_throttle); bool rq_depth_calc_max_depth(struct rq_depth *rqd); void __rq_qos_cleanup(struct rq_qos *rqos, struct bio *bio); diff --git a/block/blk-wbt.c b/block/blk-wbt.c index 8af553a0ba00..8641ba9793c5 100644 --- a/block/blk-wbt.c +++ b/block/blk-wbt.c @@ -308,7 +308,8 @@ static void calc_wb_limits(struct rq_wb *rwb) static void scale_up(struct rq_wb *rwb) { - rq_depth_scale_up(&rwb->rq_depth); + if (!rq_depth_scale_up(&rwb->rq_depth)) + return; calc_wb_limits(rwb); rwb->unknown_cnt = 0; rwb_wake_all(rwb); @@ -317,7 +318,8 @@ static void scale_up(struct rq_wb *rwb) static void scale_down(struct rq_wb *rwb, bool hard_throttle) { - rq_depth_scale_down(&rwb->rq_depth, hard_throttle); + if (!rq_depth_scale_down(&rwb->rq_depth, hard_throttle)) + return; calc_wb_limits(rwb); rwb->unknown_cnt = 0; rwb_trace_step(rwb, "scale down"); diff --git a/block/sed-opal.c b/block/sed-opal.c index 4e95a9792162..b4c761973ac1 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -129,7 +129,7 @@ static const u8 opaluid[][OPAL_UID_LENGTH] = { { 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x84, 0x01 }, /* tables */ - [OPAL_TABLE_TABLE] + [OPAL_TABLE_TABLE] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01 }, [OPAL_LOCKINGRANGE_GLOBAL] = { 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x01 }, @@ -372,8 +372,8 @@ static void check_geometry(struct opal_dev *dev, const void *data) { const struct d0_geometry_features *geo = data; - dev->align = geo->alignment_granularity; - dev->lowest_lba = geo->lowest_aligned_lba; + dev->align = be64_to_cpu(geo->alignment_granularity); + dev->lowest_lba = be64_to_cpu(geo->lowest_aligned_lba); } static int execute_step(struct opal_dev *dev, diff --git a/drivers/android/binder.c b/drivers/android/binder.c index c0a491277aca..5b9ac2122e89 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -57,6 +57,7 @@ #include <linux/sched/signal.h> #include <linux/sched/mm.h> #include <linux/seq_file.h> +#include <linux/string.h> #include <linux/uaccess.h> #include <linux/pid_namespace.h> #include <linux/security.h> @@ -66,6 +67,7 @@ #include <linux/task_work.h> #include <uapi/linux/android/binder.h> +#include <uapi/linux/android/binderfs.h> #include <asm/cacheflush.h> @@ -2876,7 +2878,7 @@ static void binder_transaction(struct binder_proc *proc, e->target_handle = tr->target.handle; e->data_size = tr->data_size; e->offsets_size = tr->offsets_size; - e->context_name = proc->context->name; + strscpy(e->context_name, proc->context->name, BINDERFS_MAX_NAME); if (reply) { binder_inner_proc_lock(proc); diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 6d79a1b0d446..d42a8b2f636a 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -156,7 +156,7 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked( } /** - * binder_alloc_buffer_lookup() - get buffer given user ptr + * binder_alloc_prepare_to_free() - get buffer given user ptr * @alloc: binder_alloc for this proc * @user_ptr: User pointer to buffer data * diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index bd47f7f72075..ae991097d14d 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -130,7 +130,7 @@ struct binder_transaction_log_entry { int return_error_line; uint32_t return_error; uint32_t return_error_param; - const char *context_name; + char context_name[BINDERFS_MAX_NAME + 1]; }; struct binder_transaction_log { diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 76d0f9de767b..58e09ffe8b9c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4791,27 +4791,6 @@ void ata_scsi_hotplug(struct work_struct *work) return; } - /* - * XXX - UGLY HACK - * - * The block layer suspend/resume path is fundamentally broken due - * to freezable kthreads and workqueue and may deadlock if a block - * device gets removed while resume is in progress. I don't know - * what the solution is short of removing freezable kthreads and - * workqueues altogether. - * - * The following is an ugly hack to avoid kicking off device - * removal while freezer is active. This is a joke but does avoid - * this particular deadlock scenario. - * - * https://bugzilla.kernel.org/show_bug.cgi?id=62801 - * http://marc.info/?l=linux-kernel&m=138695698516487 - */ -#ifdef CONFIG_FREEZER - while (pm_freezing) - msleep(10); -#endif - DPRINTK("ENTER\n"); mutex_lock(&ap->scsi_scan_mutex); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index b6c6c7d97d5b..b230beb6ccb4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -241,12 +241,8 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, } EXPORT_SYMBOL_GPL(platform_get_resource_byname); -/** - * platform_get_irq_byname - get an IRQ for a device by name - * @dev: platform device - * @name: IRQ name - */ -int platform_get_irq_byname(struct platform_device *dev, const char *name) +static int __platform_get_irq_byname(struct platform_device *dev, + const char *name) { struct resource *r; @@ -262,12 +258,48 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name) if (r) return r->start; - dev_err(&dev->dev, "IRQ %s not found\n", name); return -ENXIO; } + +/** + * platform_get_irq_byname - get an IRQ for a device by name + * @dev: platform device + * @name: IRQ name + * + * Get an IRQ like platform_get_irq(), but then by name rather then by index. + * + * Return: IRQ number on success, negative error number on failure. + */ +int platform_get_irq_byname(struct platform_device *dev, const char *name) +{ + int ret; + + ret = __platform_get_irq_byname(dev, name); + if (ret < 0 && ret != -EPROBE_DEFER) + dev_err(&dev->dev, "IRQ %s not found\n", name); + + return ret; +} EXPORT_SYMBOL_GPL(platform_get_irq_byname); /** + * platform_get_irq_byname_optional - get an optional IRQ for a device by name + * @dev: platform device + * @name: IRQ name + * + * Get an optional IRQ by name like platform_get_irq_byname(). Except that it + * does not print an error message if an IRQ can not be obtained. + * + * Return: IRQ number on success, negative error number on failure. + */ +int platform_get_irq_byname_optional(struct platform_device *dev, + const char *name) +{ + return __platform_get_irq_byname(dev, name); +} +EXPORT_SYMBOL_GPL(platform_get_irq_byname_optional); + +/** * platform_add_devices - add a numbers of platform devices * @devs: array of platform devices to add * @num: number of platform devices in array diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 1410fa893653..f6f77eaa7217 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -994,6 +994,16 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync) blk_queue_write_cache(lo->lo_queue, true, false); + if (io_is_direct(lo->lo_backing_file) && inode->i_sb->s_bdev) { + /* In case of direct I/O, match underlying block size */ + unsigned short bsize = bdev_logical_block_size( + inode->i_sb->s_bdev); + + blk_queue_logical_block_size(lo->lo_queue, bsize); + blk_queue_physical_block_size(lo->lo_queue, bsize); + blk_queue_io_min(lo->lo_queue, bsize); + } + loop_update_rotational(lo); loop_update_dio(lo); set_capacity(lo->lo_disk, size); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index ac07e8c94c79..478aa86fc1f2 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -248,8 +248,8 @@ static void nbd_put(struct nbd_device *nbd) if (refcount_dec_and_mutex_lock(&nbd->refs, &nbd_index_mutex)) { idr_remove(&nbd_index_idr, nbd->index); - mutex_unlock(&nbd_index_mutex); nbd_dev_remove(nbd); + mutex_unlock(&nbd_index_mutex); } } diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index eabc116832a7..3d7fdea872f8 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -142,8 +142,7 @@ static blk_status_t null_zone_reset(struct nullb_cmd *cmd, sector_t sector) zone->wp = zone->start; break; default: - cmd->error = BLK_STS_NOTSUPP; - break; + return BLK_STS_NOTSUPP; } return BLK_STS_OK; } diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 6b331061d34b..97ab5ad171d4 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -150,6 +150,15 @@ config TEGRA_GMI Driver for the Tegra Generic Memory Interface bus which can be used to attach devices such as NOR, UART, FPGA and more. +config TI_PWMSS + bool + default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM || TI_EQEP) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + config TI_SYSC bool "TI sysc interconnect target module driver" depends on ARCH_OMAP2PLUS diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 16b43d3468c6..1320bcf9fa9d 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o +obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o obj-$(CONFIG_TI_SYSC) += ti-sysc.o obj-$(CONFIG_TS_NBUS) += ts-nbus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/bus/ti-pwmss.c index e9c26c94251b..e9c26c94251b 100644 --- a/drivers/pwm/pwm-tipwmss.c +++ b/drivers/bus/ti-pwmss.c diff --git a/drivers/char/random.c b/drivers/char/random.c index c2f7de9dc543..de434feb873a 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -2520,4 +2520,4 @@ void add_bootloader_randomness(const void *buf, unsigned int size) else add_device_randomness(buf, size); } -EXPORT_SYMBOL_GPL(add_bootloader_randomness);
\ No newline at end of file +EXPORT_SYMBOL_GPL(add_bootloader_randomness); diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c index b57fe09b428b..9dd6185a4b4e 100644 --- a/drivers/clk/ti/clk-7xx.c +++ b/drivers/clk/ti/clk-7xx.c @@ -683,7 +683,7 @@ static const struct omap_clkctrl_reg_data dra7_l4per2_clkctrl_regs[] __initconst { DRA7_L4PER2_MCASP2_CLKCTRL, dra7_mcasp2_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:0154:22" }, { DRA7_L4PER2_MCASP3_CLKCTRL, dra7_mcasp3_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:015c:22" }, { DRA7_L4PER2_MCASP5_CLKCTRL, dra7_mcasp5_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:016c:22" }, - { DRA7_L4PER2_MCASP8_CLKCTRL, dra7_mcasp8_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:0184:24" }, + { DRA7_L4PER2_MCASP8_CLKCTRL, dra7_mcasp8_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:0184:22" }, { DRA7_L4PER2_MCASP4_CLKCTRL, dra7_mcasp4_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:018c:22" }, { DRA7_L4PER2_UART7_CLKCTRL, dra7_uart7_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:01c4:24" }, { DRA7_L4PER2_UART8_CLKCTRL, dra7_uart8_bit_data, CLKF_SW_SUP, "l4per2-clkctrl:01d4:24" }, @@ -828,8 +828,8 @@ static struct ti_dt_clk dra7xx_clks[] = { DT_CLK(NULL, "mcasp6_aux_gfclk_mux", "l4per2-clkctrl:01f8:22"), DT_CLK(NULL, "mcasp7_ahclkx_mux", "l4per2-clkctrl:01fc:24"), DT_CLK(NULL, "mcasp7_aux_gfclk_mux", "l4per2-clkctrl:01fc:22"), - DT_CLK(NULL, "mcasp8_ahclkx_mux", "l4per2-clkctrl:0184:22"), - DT_CLK(NULL, "mcasp8_aux_gfclk_mux", "l4per2-clkctrl:0184:24"), + DT_CLK(NULL, "mcasp8_ahclkx_mux", "l4per2-clkctrl:0184:24"), + DT_CLK(NULL, "mcasp8_aux_gfclk_mux", "l4per2-clkctrl:0184:22"), DT_CLK(NULL, "mmc1_clk32k", "l3init-clkctrl:0008:8"), DT_CLK(NULL, "mmc1_fclk_div", "l3init-clkctrl:0008:25"), DT_CLK(NULL, "mmc1_fclk_mux", "l3init-clkctrl:0008:24"), diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c index d8c2bd4391d0..11ff701ff4bb 100644 --- a/drivers/clocksource/timer-of.c +++ b/drivers/clocksource/timer-of.c @@ -25,7 +25,9 @@ static __init void timer_of_irq_exit(struct of_timer_irq *of_irq) struct clock_event_device *clkevt = &to->clkevt; - of_irq->percpu ? free_percpu_irq(of_irq->irq, clkevt) : + if (of_irq->percpu) + free_percpu_irq(of_irq->irq, clkevt); + else free_irq(of_irq->irq, clkevt); } diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 2967d0a9ff91..c80fa76bb531 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -49,6 +49,17 @@ config STM32_LPTIMER_CNT To compile this driver as a module, choose M here: the module will be called stm32-lptimer-cnt. +config TI_EQEP + tristate "TI eQEP counter driver" + depends on (SOC_AM33XX || COMPILE_TEST) + select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Quadrature + Encoder Pulse (eQEP) counter driver. + + To compile this driver as a module, choose M here: the module will be + called ti-eqep. + config FTM_QUADDEC tristate "Flex Timer Module Quadrature decoder driver" depends on HAS_IOMEM && OF diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index 40d35522937d..55142d1f4c43 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_COUNTER) += counter.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o +obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index bbc930a5962c..28b63645c411 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -347,7 +347,7 @@ static const struct iio_chan_spec stm32_lptim_cnt_channels = { }; /** - * stm32_lptim_cnt_function - enumerates stm32 LPTimer counter & encoder modes + * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature) */ diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 644ba18a72ad..b61135b63ee8 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -28,7 +28,7 @@ struct stm32_timer_cnt { }; /** - * stm32_count_function - enumerates stm32 timer counter encoder modes + * enum stm32_count_function - enumerates stm32 timer counter encoder modes * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1 * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level @@ -219,8 +219,8 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter, if (enable) { regmap_read(priv->regmap, TIM_CR1, &cr1); - if (!(cr1 & TIM_CR1_CEN)) - clk_enable(priv->clk); + if (!(cr1 & TIM_CR1_CEN)) + clk_enable(priv->clk); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c new file mode 100644 index 000000000000..4b3ef2449c06 --- /dev/null +++ b/drivers/counter/ti-eqep.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 David Lechner <david@lechnology.com> + * + * Counter driver for Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) + */ + +#include <linux/bitops.h> +#include <linux/counter.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +/* 32-bit registers */ +#define QPOSCNT 0x0 +#define QPOSINIT 0x4 +#define QPOSMAX 0x8 +#define QPOSCMP 0xc +#define QPOSILAT 0x10 +#define QPOSSLAT 0x14 +#define QPOSLAT 0x18 +#define QUTMR 0x1c +#define QUPRD 0x20 + +/* 16-bit registers */ +#define QWDTMR 0x0 /* 0x24 */ +#define QWDPRD 0x2 /* 0x26 */ +#define QDECCTL 0x4 /* 0x28 */ +#define QEPCTL 0x6 /* 0x2a */ +#define QCAPCTL 0x8 /* 0x2c */ +#define QPOSCTL 0xa /* 0x2e */ +#define QEINT 0xc /* 0x30 */ +#define QFLG 0xe /* 0x32 */ +#define QCLR 0x10 /* 0x34 */ +#define QFRC 0x12 /* 0x36 */ +#define QEPSTS 0x14 /* 0x38 */ +#define QCTMR 0x16 /* 0x3a */ +#define QCPRD 0x18 /* 0x3c */ +#define QCTMRLAT 0x1a /* 0x3e */ +#define QCPRDLAT 0x1c /* 0x40 */ + +#define QDECCTL_QSRC_SHIFT 14 +#define QDECCTL_QSRC GENMASK(15, 14) +#define QDECCTL_SOEN BIT(13) +#define QDECCTL_SPSEL BIT(12) +#define QDECCTL_XCR BIT(11) +#define QDECCTL_SWAP BIT(10) +#define QDECCTL_IGATE BIT(9) +#define QDECCTL_QAP BIT(8) +#define QDECCTL_QBP BIT(7) +#define QDECCTL_QIP BIT(6) +#define QDECCTL_QSP BIT(5) + +#define QEPCTL_FREE_SOFT GENMASK(15, 14) +#define QEPCTL_PCRM GENMASK(13, 12) +#define QEPCTL_SEI GENMASK(11, 10) +#define QEPCTL_IEI GENMASK(9, 8) +#define QEPCTL_SWI BIT(7) +#define QEPCTL_SEL BIT(6) +#define QEPCTL_IEL GENMASK(5, 4) +#define QEPCTL_PHEN BIT(3) +#define QEPCTL_QCLM BIT(2) +#define QEPCTL_UTE BIT(1) +#define QEPCTL_WDE BIT(0) + +/* EQEP Inputs */ +enum { + TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */ + TI_EQEP_SIGNAL_QEPB, /* QEPB/XDIR */ +}; + +/* Position Counter Input Modes */ +enum { + TI_EQEP_COUNT_FUNC_QUAD_COUNT, + TI_EQEP_COUNT_FUNC_DIR_COUNT, + TI_EQEP_COUNT_FUNC_UP_COUNT, + TI_EQEP_COUNT_FUNC_DOWN_COUNT, +}; + +enum { + TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES, + TI_EQEP_SYNAPSE_ACTION_RISING_EDGE, + TI_EQEP_SYNAPSE_ACTION_NONE, +}; + +struct ti_eqep_cnt { + struct counter_device counter; + struct regmap *regmap32; + struct regmap *regmap16; +}; + +static int ti_eqep_count_read(struct counter_device *counter, + struct counter_count *count, + struct counter_count_read_value *val) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 cnt; + + regmap_read(priv->regmap32, QPOSCNT, &cnt); + counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt); + + return 0; +} + +static int ti_eqep_count_write(struct counter_device *counter, + struct counter_count *count, + struct counter_count_write_value *val) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 cnt, max; + int err; + + err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val); + if (err) + return err; + + regmap_read(priv->regmap32, QPOSMAX, &max); + if (cnt > max) + return -EINVAL; + + return regmap_write(priv->regmap32, QPOSCNT, cnt); +} + +static int ti_eqep_function_get(struct counter_device *counter, + struct counter_count *count, size_t *function) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qdecctl; + + regmap_read(priv->regmap16, QDECCTL, &qdecctl); + *function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT; + + return 0; +} + +static int ti_eqep_function_set(struct counter_device *counter, + struct counter_count *count, size_t function) +{ + struct ti_eqep_cnt *priv = counter->priv; + + return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC, + function << QDECCTL_QSRC_SHIFT); +} + +static int ti_eqep_action_get(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, size_t *action) +{ + struct ti_eqep_cnt *priv = counter->priv; + size_t function; + u32 qdecctl; + int err; + + err = ti_eqep_function_get(counter, count, &function); + if (err) + return err; + + switch (function) { + case TI_EQEP_COUNT_FUNC_QUAD_COUNT: + /* In quadrature mode, the rising and falling edge of both + * QEPA and QEPB trigger QCLK. + */ + *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; + break; + case TI_EQEP_COUNT_FUNC_DIR_COUNT: + /* In direction-count mode only rising edge of QEPA is counted + * and QEPB gives direction. + */ + switch (synapse->signal->id) { + case TI_EQEP_SIGNAL_QEPA: + *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; + break; + default: + *action = TI_EQEP_SYNAPSE_ACTION_NONE; + break; + } + break; + case TI_EQEP_COUNT_FUNC_UP_COUNT: + case TI_EQEP_COUNT_FUNC_DOWN_COUNT: + /* In up/down-count modes only QEPA is counted and QEPB is not + * used. + */ + switch (synapse->signal->id) { + case TI_EQEP_SIGNAL_QEPA: + err = regmap_read(priv->regmap16, QDECCTL, &qdecctl); + if (err) + return err; + + if (qdecctl & QDECCTL_XCR) + *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; + else + *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; + break; + default: + *action = TI_EQEP_SYNAPSE_ACTION_NONE; + break; + } + break; + } + + return 0; +} + +static const struct counter_ops ti_eqep_counter_ops = { + .count_read = ti_eqep_count_read, + .count_write = ti_eqep_count_write, + .function_get = ti_eqep_function_get, + .function_set = ti_eqep_function_set, + .action_get = ti_eqep_action_get, +}; + +static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, char *buf) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qposmax; + + regmap_read(priv->regmap32, QPOSMAX, &qposmax); + + return sprintf(buf, "%u\n", qposmax); +} + +static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, const char *buf, + size_t len) +{ + struct ti_eqep_cnt *priv = counter->priv; + int err; + u32 res; + + err = kstrtouint(buf, 0, &res); + if (err < 0) + return err; + + regmap_write(priv->regmap32, QPOSMAX, res); + + return len; +} + +static ssize_t ti_eqep_position_floor_read(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, char *buf) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qposinit; + + regmap_read(priv->regmap32, QPOSINIT, &qposinit); + + return sprintf(buf, "%u\n", qposinit); +} + +static ssize_t ti_eqep_position_floor_write(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, const char *buf, + size_t len) +{ + struct ti_eqep_cnt *priv = counter->priv; + int err; + u32 res; + + err = kstrtouint(buf, 0, &res); + if (err < 0) + return err; + + regmap_write(priv->regmap32, QPOSINIT, res); + + return len; +} + +static ssize_t ti_eqep_position_enable_read(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, char *buf) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qepctl; + + regmap_read(priv->regmap16, QEPCTL, &qepctl); + + return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN)); +} + +static ssize_t ti_eqep_position_enable_write(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, const char *buf, + size_t len) +{ + struct ti_eqep_cnt *priv = counter->priv; + int err; + bool res; + + err = kstrtobool(buf, &res); + if (err < 0) + return err; + + regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0); + + return len; +} + +static struct counter_count_ext ti_eqep_position_ext[] = { + { + .name = "ceiling", + .read = ti_eqep_position_ceiling_read, + .write = ti_eqep_position_ceiling_write, + }, + { + .name = "floor", + .read = ti_eqep_position_floor_read, + .write = ti_eqep_position_floor_write, + }, + { + .name = "enable", + .read = ti_eqep_position_enable_read, + .write = ti_eqep_position_enable_write, + }, +}; + +static struct counter_signal ti_eqep_signals[] = { + [TI_EQEP_SIGNAL_QEPA] = { + .id = TI_EQEP_SIGNAL_QEPA, + .name = "QEPA" + }, + [TI_EQEP_SIGNAL_QEPB] = { + .id = TI_EQEP_SIGNAL_QEPB, + .name = "QEPB" + }, +}; + +static const enum counter_count_function ti_eqep_position_functions[] = { + [TI_EQEP_COUNT_FUNC_QUAD_COUNT] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4, + [TI_EQEP_COUNT_FUNC_DIR_COUNT] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION, + [TI_EQEP_COUNT_FUNC_UP_COUNT] = COUNTER_COUNT_FUNCTION_INCREASE, + [TI_EQEP_COUNT_FUNC_DOWN_COUNT] = COUNTER_COUNT_FUNCTION_DECREASE, +}; + +static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = { + [TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES, + [TI_EQEP_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, + [TI_EQEP_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, +}; + +static struct counter_synapse ti_eqep_position_synapses[] = { + { + .actions_list = ti_eqep_position_synapse_actions, + .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA], + }, + { + .actions_list = ti_eqep_position_synapse_actions, + .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB], + }, +}; + +static struct counter_count ti_eqep_counts[] = { + { + .id = 0, + .name = "QPOSCNT", + .functions_list = ti_eqep_position_functions, + .num_functions = ARRAY_SIZE(ti_eqep_position_functions), + .synapses = ti_eqep_position_synapses, + .num_synapses = ARRAY_SIZE(ti_eqep_position_synapses), + .ext = ti_eqep_position_ext, + .num_ext = ARRAY_SIZE(ti_eqep_position_ext), + }, +}; + +static const struct regmap_config ti_eqep_regmap32_config = { + .name = "32-bit", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x24, +}; + +static const struct regmap_config ti_eqep_regmap16_config = { + .name = "16-bit", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x1e, +}; + +static int ti_eqep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_eqep_cnt *priv; + void __iomem *base; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap32 = devm_regmap_init_mmio(dev, base, + &ti_eqep_regmap32_config); + if (IS_ERR(priv->regmap32)) + return PTR_ERR(priv->regmap32); + + priv->regmap16 = devm_regmap_init_mmio(dev, base + 0x24, + &ti_eqep_regmap16_config); + if (IS_ERR(priv->regmap16)) + return PTR_ERR(priv->regmap16); + + priv->counter.name = dev_name(dev); + priv->counter.parent = dev; + priv->counter.ops = &ti_eqep_counter_ops; + priv->counter.counts = ti_eqep_counts; + priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts); + priv->counter.signals = ti_eqep_signals; + priv->counter.num_signals = ARRAY_SIZE(ti_eqep_signals); + priv->counter.priv = priv; + + platform_set_drvdata(pdev, priv); + + /* + * Need to make sure power is turned on. On AM33xx, this comes from the + * parent PWMSS bus driver. On AM17xx, this comes from the PSC power + * domain. + */ + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + err = counter_register(&priv->counter); + if (err < 0) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return err; + } + + return 0; +} + +static int ti_eqep_remove(struct platform_device *pdev) +{ + struct ti_eqep_cnt *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + counter_unregister(&priv->counter); + pm_runtime_put_sync(dev), + pm_runtime_disable(dev); + + return 0; +} + +static const struct of_device_id ti_eqep_of_match[] = { + { .compatible = "ti,am3352-eqep", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ti_eqep_of_match); + +static struct platform_driver ti_eqep_driver = { + .probe = ti_eqep_probe, + .remove = ti_eqep_remove, + .driver = { + .name = "ti-eqep-cnt", + .of_match_table = ti_eqep_of_match, + }, +}; +module_platform_driver(ti_eqep_driver); + +MODULE_AUTHOR("David Lechner <david@lechnology.com>"); +MODULE_DESCRIPTION("TI eQEP counter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index addf0749dd8b..b1af0de2e100 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -381,7 +381,7 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, pcie->device_id.vendor_id, pcie->device_id.device_id); p = pcie->device_id.class_code; - printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); + printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); } if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8d3e778e988b..69f00f7453a3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -267,6 +267,9 @@ static __init int efivar_ssdt_load(void) void *data; int ret; + if (!efivar_ssdt[0]) + return 0; + ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries); list_for_each_entry_safe(entry, aux, &entries, list) { diff --git a/drivers/firmware/efi/rci2-table.c b/drivers/firmware/efi/rci2-table.c index 3e290f96620a..76b0c354a027 100644 --- a/drivers/firmware/efi/rci2-table.c +++ b/drivers/firmware/efi/rci2-table.c @@ -76,7 +76,7 @@ static u16 checksum(void) return chksum; } -int __init efi_rci2_sysfs_init(void) +static int __init efi_rci2_sysfs_init(void) { struct kobject *tables_kobj; int ret = -ENOMEM; diff --git a/drivers/firmware/efi/tpm.c b/drivers/firmware/efi/tpm.c index 1d3f5ca3eaaf..ebd7977653a8 100644 --- a/drivers/firmware/efi/tpm.c +++ b/drivers/firmware/efi/tpm.c @@ -40,7 +40,7 @@ int __init efi_tpm_eventlog_init(void) { struct linux_efi_tpm_eventlog *log_tbl; struct efi_tcg2_final_events_table *final_tbl; - unsigned int tbl_size; + int tbl_size; int ret = 0; if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) { @@ -75,16 +75,28 @@ int __init efi_tpm_eventlog_init(void) goto out; } - tbl_size = tpm2_calc_event_log_size((void *)efi.tpm_final_log - + sizeof(final_tbl->version) - + sizeof(final_tbl->nr_events), - final_tbl->nr_events, - log_tbl->log); + tbl_size = 0; + if (final_tbl->nr_events != 0) { + void *events = (void *)efi.tpm_final_log + + sizeof(final_tbl->version) + + sizeof(final_tbl->nr_events); + + tbl_size = tpm2_calc_event_log_size(events, + final_tbl->nr_events, + log_tbl->log); + } + + if (tbl_size < 0) { + pr_err(FW_BUG "Failed to parse event in TPM Final Events Log\n"); + goto out_calc; + } + memblock_reserve((unsigned long)final_tbl, tbl_size + sizeof(*final_tbl)); - early_memunmap(final_tbl, sizeof(*final_tbl)); efi_tpm_final_log_size = tbl_size; +out_calc: + early_memunmap(final_tbl, sizeof(*final_tbl)); out: early_memunmap(log_tbl, sizeof(*log_tbl)); return ret; diff --git a/drivers/firmware/google/vpd_decode.c b/drivers/firmware/google/vpd_decode.c index dda525c0f968..5c6f2a74f104 100644 --- a/drivers/firmware/google/vpd_decode.c +++ b/drivers/firmware/google/vpd_decode.c @@ -52,7 +52,7 @@ static int vpd_decode_entry(const u32 max_len, const u8 *input_buf, if (max_len - consumed < *entry_len) return VPD_FAIL; - consumed += decoded_len; + consumed += *entry_len; *_consumed = consumed; return VPD_OK; } diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index fe7a73f52329..bb287f35cf40 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -530,11 +530,12 @@ static void sprd_eic_handle_one_type(struct gpio_chip *chip) } for_each_set_bit(n, ®, SPRD_EIC_PER_BANK_NR) { - girq = irq_find_mapping(chip->irq.domain, - bank * SPRD_EIC_PER_BANK_NR + n); + u32 offset = bank * SPRD_EIC_PER_BANK_NR + n; + + girq = irq_find_mapping(chip->irq.domain, offset); generic_handle_irq(girq); - sprd_eic_toggle_trigger(chip, girq, n); + sprd_eic_toggle_trigger(chip, girq, offset); } } } diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 47d05e357e61..faf86ea9c51a 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -192,13 +192,13 @@ static int max77620_gpio_set_debounce(struct max77620_gpio *mgpio, case 0: val = MAX77620_CNFG_GPIO_DBNC_None; break; - case 1 ... 8: + case 1000 ... 8000: val = MAX77620_CNFG_GPIO_DBNC_8ms; break; - case 9 ... 16: + case 9000 ... 16000: val = MAX77620_CNFG_GPIO_DBNC_16ms; break; - case 17 ... 32: + case 17000 ... 32000: val = MAX77620_CNFG_GPIO_DBNC_32ms; break; default: diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 1eea2c6c2e1d..80ea49f570f4 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -317,7 +317,7 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, transitory = flags & OF_GPIO_TRANSITORY; ret = gpiod_request(desc, label); - if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) + if (ret == -EBUSY && (dflags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) return desc; if (ret) return ERR_PTR(ret); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bdbc1649eafa..5833e4f380d6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3070,8 +3070,10 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ - if (value) - return gpiod_direction_input(desc); + if (value) { + ret = gpiod_direction_input(desc); + goto set_output_flag; + } } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), @@ -3079,8 +3081,10 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ - if (!value) - return gpiod_direction_input(desc); + if (!value) { + ret = gpiod_direction_input(desc); + goto set_output_flag; + } } else { gpio_set_config(gc, gpio_chip_hwgpio(desc), PIN_CONFIG_DRIVE_PUSH_PULL); @@ -3088,6 +3092,17 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) set_output_value: return gpiod_direction_output_raw_commit(desc, value); + +set_output_flag: + /* + * When emulating open-source or open-drain functionalities by not + * actively driving the line (setting mode to input) we still need to + * set the IS_OUT flag or otherwise we won't be able to set the line + * value anymore. + */ + if (ret == 0) + set_bit(FLAG_IS_OUT, &desc->flags); + return ret; } EXPORT_SYMBOL_GPL(gpiod_direction_output); @@ -3448,8 +3463,6 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) if (value) { ret = chip->direction_input(chip, offset); - if (!ret) - clear_bit(FLAG_IS_OUT, &desc->flags); } else { ret = chip->direction_output(chip, offset, 0); if (!ret) @@ -3479,8 +3492,6 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value set_bit(FLAG_IS_OUT, &desc->flags); } else { ret = chip->direction_input(chip, offset); - if (!ret) - clear_bit(FLAG_IS_OUT, &desc->flags); } trace_gpio_direction(desc_to_gpio(desc), !value, ret); if (ret < 0) diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index 42e2c1f57152..00962a659009 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -54,7 +54,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \ amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \ amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \ amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \ - amdgpu_vm_sdma.o amdgpu_pmu.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o + amdgpu_vm_sdma.o amdgpu_discovery.o amdgpu_ras_eeprom.o smu_v11_0_i2c.o amdgpu-$(CONFIG_PERF_EVENTS) += amdgpu_pmu.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index eba42c752bca..82155ac3288a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -189,7 +189,7 @@ static int acp_hw_init(void *handle) u32 val = 0; u32 count = 0; struct device *dev; - struct i2s_platform_data *i2s_pdata; + struct i2s_platform_data *i2s_pdata = NULL; struct amdgpu_device *adev = (struct amdgpu_device *)handle; @@ -231,20 +231,21 @@ static int acp_hw_init(void *handle) adev->acp.acp_cell = kcalloc(ACP_DEVS, sizeof(struct mfd_cell), GFP_KERNEL); - if (adev->acp.acp_cell == NULL) - return -ENOMEM; + if (adev->acp.acp_cell == NULL) { + r = -ENOMEM; + goto failure; + } adev->acp.acp_res = kcalloc(5, sizeof(struct resource), GFP_KERNEL); if (adev->acp.acp_res == NULL) { - kfree(adev->acp.acp_cell); - return -ENOMEM; + r = -ENOMEM; + goto failure; } i2s_pdata = kcalloc(3, sizeof(struct i2s_platform_data), GFP_KERNEL); if (i2s_pdata == NULL) { - kfree(adev->acp.acp_res); - kfree(adev->acp.acp_cell); - return -ENOMEM; + r = -ENOMEM; + goto failure; } switch (adev->asic_type) { @@ -341,14 +342,14 @@ static int acp_hw_init(void *handle) r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell, ACP_DEVS); if (r) - return r; + goto failure; for (i = 0; i < ACP_DEVS ; i++) { dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i); r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev); if (r) { dev_err(dev, "Failed to add dev to genpd\n"); - return r; + goto failure; } } @@ -367,7 +368,8 @@ static int acp_hw_init(void *handle) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + r = -ETIMEDOUT; + goto failure; } udelay(100); } @@ -384,7 +386,8 @@ static int acp_hw_init(void *handle) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + r = -ETIMEDOUT; + goto failure; } udelay(100); } @@ -393,6 +396,13 @@ static int acp_hw_init(void *handle) val &= ~ACP_SOFT_RESET__SoftResetAud_MASK; cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val); return 0; + +failure: + kfree(i2s_pdata); + kfree(adev->acp.acp_res); + kfree(adev->acp.acp_cell); + kfree(adev->acp.acp_genpd); + return r; } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index 7bcf86c61999..61e38e43ad1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -270,7 +270,7 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, r = amdgpu_bo_create_list_entry_array(&args->in, &info); if (r) - goto error_free; + return r; switch (args->in.operation) { case AMDGPU_BO_LIST_OP_CREATE: @@ -283,8 +283,7 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL); mutex_unlock(&fpriv->bo_list_lock); if (r < 0) { - amdgpu_bo_list_put(list); - return r; + goto error_put_list; } handle = r; @@ -306,9 +305,8 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, mutex_unlock(&fpriv->bo_list_lock); if (IS_ERR(old)) { - amdgpu_bo_list_put(list); r = PTR_ERR(old); - goto error_free; + goto error_put_list; } amdgpu_bo_list_put(old); @@ -325,8 +323,10 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, return 0; +error_put_list: + amdgpu_bo_list_put(list); + error_free: - if (info) - kvfree(info); + kvfree(info); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 264677ab248a..6f8aaf655a9f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -81,9 +81,10 @@ * - 3.32.0 - Add syncobj timeline support to AMDGPU_CS. * - 3.33.0 - Fixes for GDS ENOMEM failures in AMDGPU_CS. * - 3.34.0 - Non-DC can flip correctly between buffers with different pitches + * - 3.35.0 - Add drm_amdgpu_info_device::tcc_disabled_mask */ #define KMS_DRIVER_MAJOR 3 -#define KMS_DRIVER_MINOR 34 +#define KMS_DRIVER_MINOR 35 #define KMS_DRIVER_PATCHLEVEL 0 #define AMDGPU_MAX_TIMEOUT_PARAM_LENTH 256 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index 554a59b3c4a6..6ee4021910e2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -165,6 +165,7 @@ struct amdgpu_gfx_config { uint32_t num_sc_per_sh; uint32_t num_packer_per_sc; uint32_t pa_sc_tile_steering_override; + uint64_t tcc_disabled_mask; }; struct amdgpu_cu_info { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index f6147528be64..f2c097983f48 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -787,6 +787,8 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file dev_info.pa_sc_tile_steering_override = adev->gfx.config.pa_sc_tile_steering_override; + dev_info.tcc_disabled_mask = adev->gfx.config.tcc_disabled_mask; + return copy_to_user(out, &dev_info, min((size_t)size, sizeof(dev_info))) ? -EFAULT : 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index e2fb141ff2e5..5251352f5922 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -603,14 +603,12 @@ void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev, struct ttm_bo_global *glob = adev->mman.bdev.glob; struct amdgpu_vm_bo_base *bo_base; -#if 0 if (vm->bulk_moveable) { spin_lock(&glob->lru_lock); ttm_bo_bulk_move_lru_tail(&vm->lru_bulk_move); spin_unlock(&glob->lru_lock); return; } -#endif memset(&vm->lru_bulk_move, 0, sizeof(vm->lru_bulk_move)); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 638c821611ab..957811b73672 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -1691,6 +1691,17 @@ static void gfx_v10_0_tcp_harvest(struct amdgpu_device *adev) } } +static void gfx_v10_0_get_tcc_info(struct amdgpu_device *adev) +{ + /* TCCs are global (not instanced). */ + uint32_t tcc_disable = RREG32_SOC15(GC, 0, mmCGTS_TCC_DISABLE) | + RREG32_SOC15(GC, 0, mmCGTS_USER_TCC_DISABLE); + + adev->gfx.config.tcc_disabled_mask = + REG_GET_FIELD(tcc_disable, CGTS_TCC_DISABLE, TCC_DISABLE) | + (REG_GET_FIELD(tcc_disable, CGTS_TCC_DISABLE, HI_TCC_DISABLE) << 16); +} + static void gfx_v10_0_constants_init(struct amdgpu_device *adev) { u32 tmp; @@ -1702,6 +1713,7 @@ static void gfx_v10_0_constants_init(struct amdgpu_device *adev) gfx_v10_0_setup_rb(adev); gfx_v10_0_get_cu_info(adev, &adev->gfx.cu_info); + gfx_v10_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = gfx_v10_0_init_pa_sc_tile_steering_override(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c index 85393a99a848..de9b995b65b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.c +++ b/drivers/gpu/drm/amd/amdgpu/nv.c @@ -317,10 +317,12 @@ static int nv_asic_reset(struct amdgpu_device *adev) struct smu_context *smu = &adev->smu; if (nv_asic_reset_method(adev) == AMD_RESET_METHOD_BACO) { - amdgpu_inc_vram_lost(adev); + if (!adev->in_suspend) + amdgpu_inc_vram_lost(adev); ret = smu_baco_reset(smu); } else { - amdgpu_inc_vram_lost(adev); + if (!adev->in_suspend) + amdgpu_inc_vram_lost(adev); ret = nv_asic_mode1_reset(adev); } diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index f70658a536a9..f8ab80c8801b 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -558,12 +558,14 @@ static int soc15_asic_reset(struct amdgpu_device *adev) { switch (soc15_asic_reset_method(adev)) { case AMD_RESET_METHOD_BACO: - amdgpu_inc_vram_lost(adev); + if (!adev->in_suspend) + amdgpu_inc_vram_lost(adev); return soc15_asic_baco_reset(adev); case AMD_RESET_METHOD_MODE2: return soc15_mode2_reset(adev); default: - amdgpu_inc_vram_lost(adev); + if (!adev->in_suspend) + amdgpu_inc_vram_lost(adev); return soc15_asic_mode1_reset(adev); } } @@ -771,8 +773,6 @@ int soc15_set_ip_blocks(struct amdgpu_device *adev) #if defined(CONFIG_DRM_AMD_DC) else if (amdgpu_device_has_dc_support(adev)) amdgpu_device_ip_block_add(adev, &dm_ip_block); -#else -# warning "Enable CONFIG_DRM_AMD_DC for display support on SOC15." #endif amdgpu_device_ip_block_add(adev, &vcn_v2_0_ip_block); break; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 8cab6da512a0..a52f0b13a2c8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2385,8 +2385,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) if (adev->asic_type != CHIP_CARRIZO && adev->asic_type != CHIP_STONEY) dm->dc->debug.disable_stutter = amdgpu_pp_feature_mask & PP_STUTTER_MODE ? false : true; - if (adev->asic_type == CHIP_RENOIR) - dm->dc->debug.disable_stutter = true; return 0; fail: @@ -6019,7 +6017,9 @@ static void amdgpu_dm_enable_crtc_interrupts(struct drm_device *dev, struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i; +#ifdef CONFIG_DEBUG_FS enum amdgpu_dm_pipe_crc_source source; +#endif for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c index 1787b9bf800a..76d54885374a 100644 --- a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c @@ -668,6 +668,7 @@ struct clock_source *dce100_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c index 318e9c2e2ca8..89620adc81d8 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c @@ -714,6 +714,7 @@ struct clock_source *dce110_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c index 83e1878161c9..21a657e79306 100644 --- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c @@ -687,6 +687,7 @@ struct clock_source *dce112_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c index 8b85e5274bba..7c52f7f9196c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c @@ -500,6 +500,7 @@ static struct clock_source *dce120_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c index 4625df9f9fd2..643ccb0ade00 100644 --- a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c @@ -701,6 +701,7 @@ struct clock_source *dce80_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c index 59305e411a66..1599bb971111 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c @@ -786,6 +786,7 @@ struct clock_source *dcn10_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c index b4e3ce22ed52..5a2763daff4d 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c @@ -1077,6 +1077,7 @@ struct clock_source *dcn20_clock_source_create( return &clk_src->base; } + kfree(clk_src); BREAK_TO_DEBUGGER(); return NULL; } diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/Makefile b/drivers/gpu/drm/amd/display/dc/dcn21/Makefile index 8cd9de8b1a7a..ef673bffc241 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dcn21/Makefile @@ -3,7 +3,17 @@ DCN21 = dcn21_hubp.o dcn21_hubbub.o dcn21_resource.o -CFLAGS_$(AMDDALPATH)/dc/dcn21/dcn21_resource.o := -mhard-float -msse -mpreferred-stack-boundary=4 +ifneq ($(call cc-option, -mpreferred-stack-boundary=4),) + cc_stack_align := -mpreferred-stack-boundary=4 +else ifneq ($(call cc-option, -mstack-alignment=16),) + cc_stack_align := -mstack-alignment=16 +endif + +CFLAGS_$(AMDDALPATH)/dc/dcn21/dcn21_resource.o := -mhard-float -msse $(cc_stack_align) + +ifdef CONFIG_CC_IS_CLANG +CFLAGS_$(AMDDALPATH)/dc/dcn21/dcn21_resource.o += -msse2 +endif AMD_DAL_DCN21 = $(addprefix $(AMDDALPATH)/dc/dcn21/,$(DCN21)) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c index 456cd0e3289c..3b6ed60dcd35 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c @@ -39,9 +39,6 @@ * ways. Unless there is something clearly wrong with it the code should * remain as-is as it provides us with a guarantee from HW that it is correct. */ - -typedef unsigned int uint; - typedef struct { double DPPCLK; double DISPCLK; @@ -4774,7 +4771,7 @@ void dml21_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l mode_lib->vba.MaximumReadBandwidthWithoutPrefetch = 0.0; mode_lib->vba.MaximumReadBandwidthWithPrefetch = 0.0; for (k = 0; k <= mode_lib->vba.NumberOfActivePlanes - 1; k++) { - uint m; + unsigned int m; locals->cursor_bw[k] = 0; locals->cursor_bw_pre[k] = 0; @@ -5285,7 +5282,7 @@ static void CalculateWatermarksAndDRAMSpeedChangeSupport( double SecondMinActiveDRAMClockChangeMarginOneDisplayInVBLank; double FullDETBufferingTimeYStutterCriticalPlane = 0; double TimeToFinishSwathTransferStutterCriticalPlane = 0; - uint k, j; + unsigned int k, j; mode_lib->vba.TotalActiveDPP = 0; mode_lib->vba.TotalDCCActiveDPP = 0; @@ -5507,7 +5504,7 @@ static void CalculateDCFCLKDeepSleep( double DPPCLK[], double *DCFCLKDeepSleep) { - uint k; + unsigned int k; double DisplayPipeLineDeliveryTimeLuma; double DisplayPipeLineDeliveryTimeChroma; //double DCFCLKDeepSleepPerPlane[DC__NUM_DPP__MAX]; @@ -5727,7 +5724,7 @@ static void CalculatePixelDeliveryTimes( double DisplayPipeRequestDeliveryTimeChromaPrefetch[]) { double req_per_swath_ub; - uint k; + unsigned int k; for (k = 0; k < NumberOfActivePlanes; ++k) { if (VRatio[k] <= 1) { @@ -5869,7 +5866,7 @@ static void CalculateMetaAndPTETimes( unsigned int dpte_groups_per_row_chroma_ub; unsigned int num_group_per_lower_vm_stage; unsigned int num_req_per_lower_vm_stage; - uint k; + unsigned int k; for (k = 0; k < NumberOfActivePlanes; ++k) { if (GPUVMEnable == true) { diff --git a/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c b/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c index 33960fb38a5d..4acf139ea014 100644 --- a/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c @@ -843,6 +843,8 @@ static int smu_sw_init(void *handle) smu->smu_baco.state = SMU_BACO_STATE_EXIT; smu->smu_baco.platform_support = false; + mutex_init(&smu->sensor_lock); + smu->watermarks_bitmap = 0; smu->power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT; smu->default_power_profile_mode = PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT; diff --git a/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c b/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c index f1f072012fac..d493a3f8c07a 100644 --- a/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c @@ -1018,6 +1018,7 @@ static int arcturus_read_sensor(struct smu_context *smu, if (!data || !size) return -EINVAL; + mutex_lock(&smu->sensor_lock); switch (sensor) { case AMDGPU_PP_SENSOR_MAX_FAN_RPM: *(uint32_t *)data = pptable->FanMaximumRpm; @@ -1044,6 +1045,7 @@ static int arcturus_read_sensor(struct smu_context *smu, default: ret = smu_smc_read_sensor(smu, sensor, data, size); } + mutex_unlock(&smu->sensor_lock); return ret; } diff --git a/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h index 6109815a0401..23171a4d9a31 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h @@ -344,6 +344,7 @@ struct smu_context const struct smu_funcs *funcs; const struct pptable_funcs *ppt_funcs; struct mutex mutex; + struct mutex sensor_lock; uint64_t pool_size; struct smu_table_context smu_table; diff --git a/drivers/gpu/drm/amd/powerplay/navi10_ppt.c b/drivers/gpu/drm/amd/powerplay/navi10_ppt.c index 12c0e469bf35..0b461404af6b 100644 --- a/drivers/gpu/drm/amd/powerplay/navi10_ppt.c +++ b/drivers/gpu/drm/amd/powerplay/navi10_ppt.c @@ -547,7 +547,7 @@ static int navi10_get_metrics_table(struct smu_context *smu, struct smu_table_context *smu_table= &smu->smu_table; int ret = 0; - if (!smu_table->metrics_time || time_after(jiffies, smu_table->metrics_time + HZ / 1000)) { + if (!smu_table->metrics_time || time_after(jiffies, smu_table->metrics_time + msecs_to_jiffies(100))) { ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, (void *)smu_table->metrics_table, false); if (ret) { @@ -1386,6 +1386,7 @@ static int navi10_read_sensor(struct smu_context *smu, if(!data || !size) return -EINVAL; + mutex_lock(&smu->sensor_lock); switch (sensor) { case AMDGPU_PP_SENSOR_MAX_FAN_RPM: *(uint32_t *)data = pptable->FanMaximumRpm; @@ -1409,6 +1410,7 @@ static int navi10_read_sensor(struct smu_context *smu, default: ret = smu_smc_read_sensor(smu, sensor, data, size); } + mutex_unlock(&smu->sensor_lock); return ret; } diff --git a/drivers/gpu/drm/amd/powerplay/vega20_ppt.c b/drivers/gpu/drm/amd/powerplay/vega20_ppt.c index 64386ee3f878..bbd8ebd58434 100644 --- a/drivers/gpu/drm/amd/powerplay/vega20_ppt.c +++ b/drivers/gpu/drm/amd/powerplay/vega20_ppt.c @@ -3023,6 +3023,7 @@ static int vega20_read_sensor(struct smu_context *smu, if(!data || !size) return -EINVAL; + mutex_lock(&smu->sensor_lock); switch (sensor) { case AMDGPU_PP_SENSOR_MAX_FAN_RPM: *(uint32_t *)data = pptable->FanMaximumRpm; @@ -3048,6 +3049,7 @@ static int vega20_read_sensor(struct smu_context *smu, default: ret = smu_smc_read_sensor(smu, sensor, data, size); } + mutex_unlock(&smu->sensor_lock); return ret; } diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c index 2851cac94d86..b72840c06ab7 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c @@ -43,9 +43,8 @@ komeda_wb_encoder_atomic_check(struct drm_encoder *encoder, struct komeda_data_flow_cfg dflow; int err; - if (!writeback_job || !writeback_job->fb) { + if (!writeback_job) return 0; - } if (!crtc_st->active) { DRM_DEBUG_ATOMIC("Cannot write the composition result out on a inactive CRTC.\n"); @@ -166,8 +165,10 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms, &komeda_wb_encoder_helper_funcs, formats, n_formats); komeda_put_fourcc_list(formats); - if (err) + if (err) { + kfree(kwb_conn); return err; + } drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs); diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c index 22c0847986df..875a3a9eabfa 100644 --- a/drivers/gpu/drm/arm/malidp_mw.c +++ b/drivers/gpu/drm/arm/malidp_mw.c @@ -131,7 +131,7 @@ malidp_mw_encoder_atomic_check(struct drm_encoder *encoder, struct drm_framebuffer *fb; int i, n_planes; - if (!conn_state->writeback_job || !conn_state->writeback_job->fb) + if (!conn_state->writeback_job) return 0; fb = conn_state->writeback_job->fb; @@ -248,7 +248,7 @@ void malidp_mw_atomic_commit(struct drm_device *drm, mw_state = to_mw_state(conn_state); - if (conn_state->writeback_job && conn_state->writeback_job->fb) { + if (conn_state->writeback_job) { struct drm_framebuffer *fb = conn_state->writeback_job->fb; DRM_DEV_DEBUG_DRIVER(drm->dev, diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index cebc8e620820..8a8d605021f0 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -728,6 +728,8 @@ static int tc_set_video_mode(struct tc_data *tc, int lower_margin = mode->vsync_start - mode->vdisplay; int vsync_len = mode->vsync_end - mode->vsync_start; u32 dp0_syncval; + u32 bits_per_pixel = 24; + u32 in_bw, out_bw; /* * Recommended maximum number of symbols transferred in a transfer unit: @@ -735,7 +737,10 @@ static int tc_set_video_mode(struct tc_data *tc, * (output active video bandwidth in bytes)) * Must be less than tu_size. */ - max_tu_symbol = TU_SIZE_RECOMMENDED - 1; + + in_bw = mode->clock * bits_per_pixel / 8; + out_bw = tc->link.base.num_lanes * tc->link.base.rate; + max_tu_symbol = DIV_ROUND_UP(in_bw * TU_SIZE_RECOMMENDED, out_bw); dev_dbg(tc->dev, "set mode %dx%d\n", mode->hdisplay, mode->vdisplay); diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 419381abbdd1..14aeaf736321 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -430,10 +430,15 @@ static int drm_atomic_connector_check(struct drm_connector *connector, return -EINVAL; } - if (writeback_job->out_fence && !writeback_job->fb) { - DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] requesting out-fence without framebuffer\n", - connector->base.id, connector->name); - return -EINVAL; + if (!writeback_job->fb) { + if (writeback_job->out_fence) { + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] requesting out-fence without framebuffer\n", + connector->base.id, connector->name); + return -EINVAL; + } + + drm_writeback_cleanup_job(writeback_job); + state->writeback_job = NULL; } return 0; diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c index ff138b6ec48b..43d9e3bb3a94 100644 --- a/drivers/gpu/drm/drm_writeback.c +++ b/drivers/gpu/drm/drm_writeback.c @@ -324,6 +324,9 @@ void drm_writeback_cleanup_job(struct drm_writeback_job *job) if (job->fb) drm_framebuffer_put(job->fb); + if (job->out_fence) + dma_fence_put(job->out_fence); + kfree(job); } EXPORT_SYMBOL(drm_writeback_cleanup_job); @@ -366,25 +369,29 @@ drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector, { unsigned long flags; struct drm_writeback_job *job; + struct dma_fence *out_fence; spin_lock_irqsave(&wb_connector->job_lock, flags); job = list_first_entry_or_null(&wb_connector->job_queue, struct drm_writeback_job, list_entry); - if (job) { + if (job) list_del(&job->list_entry); - if (job->out_fence) { - if (status) - dma_fence_set_error(job->out_fence, status); - dma_fence_signal(job->out_fence); - dma_fence_put(job->out_fence); - } - } + spin_unlock_irqrestore(&wb_connector->job_lock, flags); if (WARN_ON(!job)) return; + out_fence = job->out_fence; + if (out_fence) { + if (status) + dma_fence_set_error(out_fence, status); + dma_fence_signal(out_fence); + dma_fence_put(out_fence); + job->out_fence = NULL; + } + INIT_WORK(&job->cleanup_work, cleanup_work); queue_work(system_long_wq, &job->cleanup_work); } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index b51d1ceb8739..aa54bb22796d 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -3280,7 +3280,20 @@ static int skl_max_plane_width(const struct drm_framebuffer *fb, switch (fb->modifier) { case DRM_FORMAT_MOD_LINEAR: case I915_FORMAT_MOD_X_TILED: - return 4096; + /* + * Validated limit is 4k, but has 5k should + * work apart from the following features: + * - Ytile (already limited to 4k) + * - FP16 (already limited to 4k) + * - render compression (already limited to 4k) + * - KVMR sprite and cursor (don't care) + * - horizontal panning (TODO verify this) + * - pipe and plane scaling (TODO verify this) + */ + if (cpp == 8) + return 4096; + else + return 5120; case I915_FORMAT_MOD_Y_TILED_CCS: case I915_FORMAT_MOD_Yf_TILED_CCS: /* FIXME AUX plane? */ @@ -7261,7 +7274,7 @@ retry: pipe_config->fdi_lanes = lane; intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, - link_bw, &pipe_config->fdi_m_n, false); + link_bw, &pipe_config->fdi_m_n, false, false); ret = ironlake_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); if (ret == -EDEADLK) @@ -7508,11 +7521,15 @@ void intel_link_compute_m_n(u16 bits_per_pixel, int nlanes, int pixel_clock, int link_clock, struct intel_link_m_n *m_n, - bool constant_n) + bool constant_n, bool fec_enable) { - m_n->tu = 64; + u32 data_clock = bits_per_pixel * pixel_clock; - compute_m_n(bits_per_pixel * pixel_clock, + if (fec_enable) + data_clock = intel_dp_mode_to_fec_clock(data_clock); + + m_n->tu = 64; + compute_m_n(data_clock, link_clock * nlanes * 8, &m_n->gmch_m, &m_n->gmch_n, constant_n); diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index e57e6969051d..01fa87ad3270 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -414,7 +414,7 @@ enum phy_fia { void intel_link_compute_m_n(u16 bpp, int nlanes, int pixel_clock, int link_clock, struct intel_link_m_n *m_n, - bool constant_n); + bool constant_n, bool fec_enable); bool is_ccs_modifier(u64 modifier); void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv); u32 intel_plane_fb_max_stride(struct drm_i915_private *dev_priv, diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 921ad0a2f7ba..57e9f0ba331b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -78,8 +78,8 @@ #define DP_DSC_MAX_ENC_THROUGHPUT_0 340000 #define DP_DSC_MAX_ENC_THROUGHPUT_1 400000 -/* DP DSC FEC Overhead factor = (100 - 2.4)/100 */ -#define DP_DSC_FEC_OVERHEAD_FACTOR 976 +/* DP DSC FEC Overhead factor = 1/(0.972261) */ +#define DP_DSC_FEC_OVERHEAD_FACTOR 972261 /* Compliance test status bits */ #define INTEL_DP_RESOLUTION_SHIFT_MASK 0 @@ -494,6 +494,97 @@ int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, return 0; } +u32 intel_dp_mode_to_fec_clock(u32 mode_clock) +{ + return div_u64(mul_u32_u32(mode_clock, 1000000U), + DP_DSC_FEC_OVERHEAD_FACTOR); +} + +static u16 intel_dp_dsc_get_output_bpp(u32 link_clock, u32 lane_count, + u32 mode_clock, u32 mode_hdisplay) +{ + u32 bits_per_pixel, max_bpp_small_joiner_ram; + int i; + + /* + * Available Link Bandwidth(Kbits/sec) = (NumberOfLanes)* + * (LinkSymbolClock)* 8 * (TimeSlotsPerMTP) + * for SST -> TimeSlotsPerMTP is 1, + * for MST -> TimeSlotsPerMTP has to be calculated + */ + bits_per_pixel = (link_clock * lane_count * 8) / + intel_dp_mode_to_fec_clock(mode_clock); + DRM_DEBUG_KMS("Max link bpp: %u\n", bits_per_pixel); + + /* Small Joiner Check: output bpp <= joiner RAM (bits) / Horiz. width */ + max_bpp_small_joiner_ram = DP_DSC_MAX_SMALL_JOINER_RAM_BUFFER / mode_hdisplay; + DRM_DEBUG_KMS("Max small joiner bpp: %u\n", max_bpp_small_joiner_ram); + + /* + * Greatest allowed DSC BPP = MIN (output BPP from available Link BW + * check, output bpp from small joiner RAM check) + */ + bits_per_pixel = min(bits_per_pixel, max_bpp_small_joiner_ram); + + /* Error out if the max bpp is less than smallest allowed valid bpp */ + if (bits_per_pixel < valid_dsc_bpp[0]) { + DRM_DEBUG_KMS("Unsupported BPP %u, min %u\n", + bits_per_pixel, valid_dsc_bpp[0]); + return 0; + } + + /* Find the nearest match in the array of known BPPs from VESA */ + for (i = 0; i < ARRAY_SIZE(valid_dsc_bpp) - 1; i++) { + if (bits_per_pixel < valid_dsc_bpp[i + 1]) + break; + } + bits_per_pixel = valid_dsc_bpp[i]; + + /* + * Compressed BPP in U6.4 format so multiply by 16, for Gen 11, + * fractional part is 0 + */ + return bits_per_pixel << 4; +} + +static u8 intel_dp_dsc_get_slice_count(struct intel_dp *intel_dp, + int mode_clock, int mode_hdisplay) +{ + u8 min_slice_count, i; + int max_slice_width; + + if (mode_clock <= DP_DSC_PEAK_PIXEL_RATE) + min_slice_count = DIV_ROUND_UP(mode_clock, + DP_DSC_MAX_ENC_THROUGHPUT_0); + else + min_slice_count = DIV_ROUND_UP(mode_clock, + DP_DSC_MAX_ENC_THROUGHPUT_1); + + max_slice_width = drm_dp_dsc_sink_max_slice_width(intel_dp->dsc_dpcd); + if (max_slice_width < DP_DSC_MIN_SLICE_WIDTH_VALUE) { + DRM_DEBUG_KMS("Unsupported slice width %d by DP DSC Sink device\n", + max_slice_width); + return 0; + } + /* Also take into account max slice width */ + min_slice_count = min_t(u8, min_slice_count, + DIV_ROUND_UP(mode_hdisplay, + max_slice_width)); + + /* Find the closest match to the valid slice count values */ + for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) { + if (valid_dsc_slicecount[i] > + drm_dp_dsc_sink_max_slice_count(intel_dp->dsc_dpcd, + false)) + break; + if (min_slice_count <= valid_dsc_slicecount[i]) + return valid_dsc_slicecount[i]; + } + + DRM_DEBUG_KMS("Unsupported Slice Count %d\n", min_slice_count); + return 0; +} + static enum drm_mode_status intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -2226,7 +2317,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, adjusted_mode->crtc_clock, pipe_config->port_clock, &pipe_config->dp_m_n, - constant_n); + constant_n, pipe_config->fec_enable); if (intel_connector->panel.downclock_mode != NULL && dev_priv->drrs.type == SEAMLESS_DRRS_SUPPORT) { @@ -2236,7 +2327,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_connector->panel.downclock_mode->clock, pipe_config->port_clock, &pipe_config->dp_m2_n2, - constant_n); + constant_n, pipe_config->fec_enable); } if (!HAS_DDI(dev_priv)) @@ -4323,91 +4414,6 @@ intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector) DP_DPRX_ESI_LEN; } -u16 intel_dp_dsc_get_output_bpp(int link_clock, u8 lane_count, - int mode_clock, int mode_hdisplay) -{ - u16 bits_per_pixel, max_bpp_small_joiner_ram; - int i; - - /* - * Available Link Bandwidth(Kbits/sec) = (NumberOfLanes)* - * (LinkSymbolClock)* 8 * ((100-FECOverhead)/100)*(TimeSlotsPerMTP) - * FECOverhead = 2.4%, for SST -> TimeSlotsPerMTP is 1, - * for MST -> TimeSlotsPerMTP has to be calculated - */ - bits_per_pixel = (link_clock * lane_count * 8 * - DP_DSC_FEC_OVERHEAD_FACTOR) / - mode_clock; - - /* Small Joiner Check: output bpp <= joiner RAM (bits) / Horiz. width */ - max_bpp_small_joiner_ram = DP_DSC_MAX_SMALL_JOINER_RAM_BUFFER / - mode_hdisplay; - - /* - * Greatest allowed DSC BPP = MIN (output BPP from avaialble Link BW - * check, output bpp from small joiner RAM check) - */ - bits_per_pixel = min(bits_per_pixel, max_bpp_small_joiner_ram); - - /* Error out if the max bpp is less than smallest allowed valid bpp */ - if (bits_per_pixel < valid_dsc_bpp[0]) { - DRM_DEBUG_KMS("Unsupported BPP %d\n", bits_per_pixel); - return 0; - } - - /* Find the nearest match in the array of known BPPs from VESA */ - for (i = 0; i < ARRAY_SIZE(valid_dsc_bpp) - 1; i++) { - if (bits_per_pixel < valid_dsc_bpp[i + 1]) - break; - } - bits_per_pixel = valid_dsc_bpp[i]; - - /* - * Compressed BPP in U6.4 format so multiply by 16, for Gen 11, - * fractional part is 0 - */ - return bits_per_pixel << 4; -} - -u8 intel_dp_dsc_get_slice_count(struct intel_dp *intel_dp, - int mode_clock, - int mode_hdisplay) -{ - u8 min_slice_count, i; - int max_slice_width; - - if (mode_clock <= DP_DSC_PEAK_PIXEL_RATE) - min_slice_count = DIV_ROUND_UP(mode_clock, - DP_DSC_MAX_ENC_THROUGHPUT_0); - else - min_slice_count = DIV_ROUND_UP(mode_clock, - DP_DSC_MAX_ENC_THROUGHPUT_1); - - max_slice_width = drm_dp_dsc_sink_max_slice_width(intel_dp->dsc_dpcd); - if (max_slice_width < DP_DSC_MIN_SLICE_WIDTH_VALUE) { - DRM_DEBUG_KMS("Unsupported slice width %d by DP DSC Sink device\n", - max_slice_width); - return 0; - } - /* Also take into account max slice width */ - min_slice_count = min_t(u8, min_slice_count, - DIV_ROUND_UP(mode_hdisplay, - max_slice_width)); - - /* Find the closest match to the valid slice count values */ - for (i = 0; i < ARRAY_SIZE(valid_dsc_slicecount); i++) { - if (valid_dsc_slicecount[i] > - drm_dp_dsc_sink_max_slice_count(intel_dp->dsc_dpcd, - false)) - break; - if (min_slice_count <= valid_dsc_slicecount[i]) - return valid_dsc_slicecount[i]; - } - - DRM_DEBUG_KMS("Unsupported Slice Count %d\n", min_slice_count); - return 0; -} - static void intel_pixel_encoding_setup_vsc(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 657bbb1f5ed0..00981fb9414b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -102,10 +102,6 @@ bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp); bool intel_dp_source_supports_hbr3(struct intel_dp *intel_dp); bool intel_dp_get_link_status(struct intel_dp *intel_dp, u8 *link_status); -u16 intel_dp_dsc_get_output_bpp(int link_clock, u8 lane_count, - int mode_clock, int mode_hdisplay); -u8 intel_dp_dsc_get_slice_count(struct intel_dp *intel_dp, int mode_clock, - int mode_hdisplay); bool intel_dp_read_dpcd(struct intel_dp *intel_dp); bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp); @@ -118,4 +114,6 @@ static inline unsigned int intel_dp_unused_lane_mask(int lane_count) return ~((1 << lane_count) - 1) & 0xf; } +u32 intel_dp_mode_to_fec_clock(u32 mode_clock); + #endif /* __INTEL_DP_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 6df240a01b8c..600873c796d0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -81,7 +81,7 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder, adjusted_mode->crtc_clock, crtc_state->port_clock, &crtc_state->dp_m_n, - constant_n); + constant_n, crtc_state->fec_enable); crtc_state->dp_m_n.tu = slots; return 0; @@ -615,7 +615,7 @@ intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum intel_encoder->type = INTEL_OUTPUT_DP_MST; intel_encoder->power_domain = intel_dig_port->base.power_domain; intel_encoder->port = intel_dig_port->base.port; - intel_encoder->crtc_mask = BIT(pipe); + intel_encoder->crtc_mask = 0x7; intel_encoder->cloneable = 0; intel_encoder->compute_config = intel_dp_mst_compute_config; diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c index dea63be1964f..cae25e493128 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.c +++ b/drivers/gpu/drm/i915/display/intel_sprite.c @@ -1528,6 +1528,7 @@ g4x_sprite_check_scaling(struct intel_crtc_state *crtc_state, int src_x, src_w, src_h, crtc_w, crtc_h; const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode; + unsigned int stride = plane_state->color_plane[0].stride; unsigned int cpp = fb->format->cpp[0]; unsigned int width_bytes; int min_width, min_height; @@ -1569,9 +1570,9 @@ g4x_sprite_check_scaling(struct intel_crtc_state *crtc_state, return -EINVAL; } - if (width_bytes > 4096 || fb->pitches[0] > 4096) { + if (stride > 4096) { DRM_DEBUG_KMS("Stride (%u) exceeds hardware max with scaling (%u)\n", - fb->pitches[0], 4096); + stride, 4096); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c index 261c9bd83f51..91051e178021 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c @@ -245,11 +245,9 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf) wakeref = intel_runtime_pm_get(rpm); - srcu = intel_gt_reset_trylock(ggtt->vm.gt); - if (srcu < 0) { - ret = srcu; + ret = intel_gt_reset_trylock(ggtt->vm.gt, &srcu); + if (ret) goto err_rpm; - } ret = i915_mutex_lock_interruptible(dev); if (ret) @@ -318,7 +316,11 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf) intel_wakeref_auto(&i915->ggtt.userfault_wakeref, msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND)); - i915_vma_set_ggtt_write(vma); + if (write) { + GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj)); + i915_vma_set_ggtt_write(vma); + obj->mm.dirty = true; + } err_fence: i915_vma_unpin_fence(vma); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c index 92e53c25424c..ad2a63dbcac2 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c @@ -241,9 +241,6 @@ void i915_gem_resume(struct drm_i915_private *i915) mutex_lock(&i915->drm.struct_mutex); intel_uncore_forcewake_get(&i915->uncore, FORCEWAKE_ALL); - i915_gem_restore_gtt_mappings(i915); - i915_gem_restore_fences(i915); - if (i915_gem_init_hw(i915)) goto err_wedged; diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h index d3c6993f4f46..22aab8593abf 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine.h +++ b/drivers/gpu/drm/i915/gt/intel_engine.h @@ -136,6 +136,20 @@ execlists_active(const struct intel_engine_execlists *execlists) return READ_ONCE(*execlists->active); } +static inline void +execlists_active_lock_bh(struct intel_engine_execlists *execlists) +{ + local_bh_disable(); /* prevent local softirq and lock recursion */ + tasklet_lock(&execlists->tasklet); +} + +static inline void +execlists_active_unlock_bh(struct intel_engine_execlists *execlists) +{ + tasklet_unlock(&execlists->tasklet); + local_bh_enable(); /* restore softirq, and kick ksoftirqd! */ +} + struct i915_request * execlists_unwind_incomplete_requests(struct intel_engine_execlists *execlists); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index 82630db0394b..4ce8626b140e 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -1197,9 +1197,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine, struct drm_printer *m) { struct drm_i915_private *dev_priv = engine->i915; - const struct intel_engine_execlists * const execlists = - &engine->execlists; - unsigned long flags; + struct intel_engine_execlists * const execlists = &engine->execlists; u64 addr; if (engine->id == RENDER_CLASS && IS_GEN_RANGE(dev_priv, 4, 7)) @@ -1281,7 +1279,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine, idx, hws[idx * 2], hws[idx * 2 + 1]); } - spin_lock_irqsave(&engine->active.lock, flags); + execlists_active_lock_bh(execlists); for (port = execlists->active; (rq = *port); port++) { char hdr[80]; int len; @@ -1309,7 +1307,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine, hwsp_seqno(rq)); print_request(m, rq, hdr); } - spin_unlock_irqrestore(&engine->active.lock, flags); + execlists_active_unlock_bh(execlists); } else if (INTEL_GEN(dev_priv) > 6) { drm_printf(m, "\tPP_DIR_BASE: 0x%08x\n", ENGINE_READ(engine, RING_PP_DIR_BASE)); @@ -1440,8 +1438,8 @@ int intel_enable_engine_stats(struct intel_engine_cs *engine) if (!intel_engine_supports_stats(engine)) return -ENODEV; - spin_lock_irqsave(&engine->active.lock, flags); - write_seqlock(&engine->stats.lock); + execlists_active_lock_bh(execlists); + write_seqlock_irqsave(&engine->stats.lock, flags); if (unlikely(engine->stats.enabled == ~0)) { err = -EBUSY; @@ -1469,8 +1467,8 @@ int intel_enable_engine_stats(struct intel_engine_cs *engine) } unlock: - write_sequnlock(&engine->stats.lock); - spin_unlock_irqrestore(&engine->active.lock, flags); + write_sequnlock_irqrestore(&engine->stats.lock, flags); + execlists_active_unlock_bh(execlists); return err; } diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index d42584439f51..bdfcc7bdadbf 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -631,7 +631,6 @@ execlists_schedule_out(struct i915_request *rq) struct intel_engine_cs *cur, *old; trace_i915_request_out(rq); - GEM_BUG_ON(intel_context_inflight(ce) != rq->engine); old = READ_ONCE(ce->inflight); do @@ -797,6 +796,17 @@ static bool can_merge_rq(const struct i915_request *prev, GEM_BUG_ON(prev == next); GEM_BUG_ON(!assert_priority_queue(prev, next)); + /* + * We do not submit known completed requests. Therefore if the next + * request is already completed, we can pretend to merge it in + * with the previous context (and we will skip updating the ELSP + * and tracking). Thus hopefully keeping the ELSP full with active + * contexts, despite the best efforts of preempt-to-busy to confuse + * us. + */ + if (i915_request_completed(next)) + return true; + if (!can_merge_ctx(prev->hw_context, next->hw_context)) return false; @@ -893,7 +903,7 @@ static void virtual_xfer_breadcrumbs(struct virtual_engine *ve, static struct i915_request * last_active(const struct intel_engine_execlists *execlists) { - struct i915_request * const *last = execlists->active; + struct i915_request * const *last = READ_ONCE(execlists->active); while (*last && i915_request_completed(*last)) last++; @@ -1172,21 +1182,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) continue; } - if (i915_request_completed(rq)) { - ve->request = NULL; - ve->base.execlists.queue_priority_hint = INT_MIN; - rb_erase_cached(rb, &execlists->virtual); - RB_CLEAR_NODE(rb); - - rq->engine = engine; - __i915_request_submit(rq); - - spin_unlock(&ve->base.active.lock); - - rb = rb_first_cached(&execlists->virtual); - continue; - } - if (last && !can_merge_rq(last, rq)) { spin_unlock(&ve->base.active.lock); return; /* leave this for another */ @@ -1237,11 +1232,23 @@ static void execlists_dequeue(struct intel_engine_cs *engine) GEM_BUG_ON(ve->siblings[0] != engine); } - __i915_request_submit(rq); - if (!i915_request_completed(rq)) { + if (__i915_request_submit(rq)) { submit = true; last = rq; } + + /* + * Hmm, we have a bunch of virtual engine requests, + * but the first one was already completed (thanks + * preempt-to-busy!). Keep looking at the veng queue + * until we have no more relevant requests (i.e. + * the normal submit queue has higher priority). + */ + if (!submit) { + spin_unlock(&ve->base.active.lock); + rb = rb_first_cached(&execlists->virtual); + continue; + } } spin_unlock(&ve->base.active.lock); @@ -1254,8 +1261,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) int i; priolist_for_each_request_consume(rq, rn, p, i) { - if (i915_request_completed(rq)) - goto skip; + bool merge = true; /* * Can we combine this request with the current port? @@ -1296,14 +1302,23 @@ static void execlists_dequeue(struct intel_engine_cs *engine) ctx_single_port_submission(rq->hw_context)) goto done; - *port = execlists_schedule_in(last, port - execlists->pending); - port++; + merge = false; } - last = rq; - submit = true; -skip: - __i915_request_submit(rq); + if (__i915_request_submit(rq)) { + if (!merge) { + *port = execlists_schedule_in(last, port - execlists->pending); + port++; + last = NULL; + } + + GEM_BUG_ON(last && + !can_merge_ctx(last->hw_context, + rq->hw_context)); + + submit = true; + last = rq; + } } rb_erase_cached(&p->node, &execlists->queue); @@ -1593,8 +1608,11 @@ static void process_csb(struct intel_engine_cs *engine) static void __execlists_submission_tasklet(struct intel_engine_cs *const engine) { lockdep_assert_held(&engine->active.lock); - if (!engine->execlists.pending[0]) + if (!engine->execlists.pending[0]) { + rcu_read_lock(); /* protect peeking at execlists->active */ execlists_dequeue(engine); + rcu_read_unlock(); + } } /* @@ -2399,10 +2417,14 @@ static void reset_csb_pointers(struct intel_engine_cs *engine) static struct i915_request *active_request(struct i915_request *rq) { - const struct list_head * const list = &rq->timeline->requests; const struct intel_context * const ce = rq->hw_context; struct i915_request *active = NULL; + struct list_head *list; + + if (!i915_request_is_active(rq)) /* unwound, but incomplete! */ + return rq; + list = &rq->timeline->requests; list_for_each_entry_from_reverse(rq, list, link) { if (i915_request_completed(rq)) break; @@ -2565,7 +2587,6 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine) int i; priolist_for_each_request_consume(rq, rn, p, i) { - list_del_init(&rq->sched.link); __i915_request_submit(rq); dma_fence_set_error(&rq->fence, -EIO); i915_request_mark_complete(rq); @@ -3631,18 +3652,22 @@ static void virtual_bond_execute(struct i915_request *rq, struct dma_fence *signal) { struct virtual_engine *ve = to_virtual_engine(rq->engine); + intel_engine_mask_t allowed, exec; struct ve_bond *bond; + allowed = ~to_request(signal)->engine->mask; + bond = virtual_find_bond(ve, to_request(signal)->engine); - if (bond) { - intel_engine_mask_t old, new, cmp; + if (bond) + allowed &= bond->sibling_mask; - cmp = READ_ONCE(rq->execution_mask); - do { - old = cmp; - new = cmp & bond->sibling_mask; - } while ((cmp = cmpxchg(&rq->execution_mask, old, new)) != old); - } + /* Restrict the bonded request to run on only the available engines */ + exec = READ_ONCE(rq->execution_mask); + while (!try_cmpxchg(&rq->execution_mask, &exec, exec & allowed)) + ; + + /* Prevent the master from being re-run on the bonded engines */ + to_request(signal)->execution_mask &= ~allowed; } struct intel_context * diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index b9d84d52e986..8cea42379dd7 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -42,11 +42,10 @@ static void engine_skip_context(struct i915_request *rq) struct intel_engine_cs *engine = rq->engine; struct i915_gem_context *hung_ctx = rq->gem_context; - lockdep_assert_held(&engine->active.lock); - if (!i915_request_is_active(rq)) return; + lockdep_assert_held(&engine->active.lock); list_for_each_entry_continue(rq, &engine->active.requests, sched.link) if (rq->gem_context == hung_ctx) i915_request_skip(rq, -EIO); @@ -123,7 +122,6 @@ void __i915_request_reset(struct i915_request *rq, bool guilty) rq->fence.seqno, yesno(guilty)); - lockdep_assert_held(&rq->engine->active.lock); GEM_BUG_ON(i915_request_completed(rq)); if (guilty) { @@ -1214,10 +1212,8 @@ out: intel_runtime_pm_put(>->i915->runtime_pm, wakeref); } -int intel_gt_reset_trylock(struct intel_gt *gt) +int intel_gt_reset_trylock(struct intel_gt *gt, int *srcu) { - int srcu; - might_lock(>->reset.backoff_srcu); might_sleep(); @@ -1232,10 +1228,10 @@ int intel_gt_reset_trylock(struct intel_gt *gt) rcu_read_lock(); } - srcu = srcu_read_lock(>->reset.backoff_srcu); + *srcu = srcu_read_lock(>->reset.backoff_srcu); rcu_read_unlock(); - return srcu; + return 0; } void intel_gt_reset_unlock(struct intel_gt *gt, int tag) diff --git a/drivers/gpu/drm/i915/gt/intel_reset.h b/drivers/gpu/drm/i915/gt/intel_reset.h index 37a987b17108..52c00199e069 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.h +++ b/drivers/gpu/drm/i915/gt/intel_reset.h @@ -38,7 +38,7 @@ int intel_engine_reset(struct intel_engine_cs *engine, void __i915_request_reset(struct i915_request *rq, bool guilty); -int __must_check intel_gt_reset_trylock(struct intel_gt *gt); +int __must_check intel_gt_reset_trylock(struct intel_gt *gt, int *srcu); void intel_gt_reset_unlock(struct intel_gt *gt, int tag); void intel_gt_set_wedged(struct intel_gt *gt); diff --git a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c index 601c16239fdf..bacaa7bb8c9a 100644 --- a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c @@ -1573,7 +1573,7 @@ static inline int mi_set_context(struct i915_request *rq, u32 flags) struct intel_engine_cs *engine = rq->engine; enum intel_engine_id id; const int num_engines = - IS_HSW_GT1(i915) ? RUNTIME_INFO(i915)->num_engines - 1 : 0; + IS_HASWELL(i915) ? RUNTIME_INFO(i915)->num_engines - 1 : 0; bool force_restore = false; int len; u32 *cs; diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c index 45481eb1fa3c..5f6ec2fd29a0 100644 --- a/drivers/gpu/drm/i915/gt/intel_workarounds.c +++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c @@ -1063,6 +1063,9 @@ static void gen9_whitelist_build(struct i915_wa_list *w) /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl,glk,cfl */ whitelist_reg(w, GEN8_HDC_CHICKEN1); + + /* WaSendPushConstantsFromMMIO:skl,bxt */ + whitelist_reg(w, COMMON_SLICE_CHICKEN2); } static void skl_whitelist_build(struct intel_engine_cs *engine) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 020696726f9e..bb6f86c7067a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1924,6 +1924,11 @@ static int i915_drm_resume(struct drm_device *dev) if (ret) DRM_ERROR("failed to re-enable GGTT\n"); + mutex_lock(&dev_priv->drm.struct_mutex); + i915_gem_restore_gtt_mappings(dev_priv); + i915_gem_restore_fences(dev_priv); + mutex_unlock(&dev_priv->drm.struct_mutex); + intel_csr_ucode_resume(dev_priv); i915_restore_state(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_gem.h b/drivers/gpu/drm/i915/i915_gem.h index 167a7b56ed5b..6795f1daa3d5 100644 --- a/drivers/gpu/drm/i915/i915_gem.h +++ b/drivers/gpu/drm/i915/i915_gem.h @@ -77,6 +77,12 @@ struct drm_i915_private; #define I915_GEM_IDLE_TIMEOUT (HZ / 5) +static inline void tasklet_lock(struct tasklet_struct *t) +{ + while (!tasklet_trylock(t)) + cpu_relax(); +} + static inline void __tasklet_disable_sync_once(struct tasklet_struct *t) { if (!atomic_fetch_inc(&t->count)) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index a53777dd371c..1c5506822dc7 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -194,6 +194,27 @@ static void free_capture_list(struct i915_request *request) } } +static void remove_from_engine(struct i915_request *rq) +{ + struct intel_engine_cs *engine, *locked; + + /* + * Virtual engines complicate acquiring the engine timeline lock, + * as their rq->engine pointer is not stable until under that + * engine lock. The simple ploy we use is to take the lock then + * check that the rq still belongs to the newly locked engine. + */ + locked = READ_ONCE(rq->engine); + spin_lock(&locked->active.lock); + while (unlikely(locked != (engine = READ_ONCE(rq->engine)))) { + spin_unlock(&locked->active.lock); + spin_lock(&engine->active.lock); + locked = engine; + } + list_del(&rq->sched.link); + spin_unlock(&locked->active.lock); +} + static bool i915_request_retire(struct i915_request *rq) { struct i915_active_request *active, *next; @@ -259,9 +280,7 @@ static bool i915_request_retire(struct i915_request *rq) * request that we have removed from the HW and put back on a run * queue. */ - spin_lock(&rq->engine->active.lock); - list_del(&rq->sched.link); - spin_unlock(&rq->engine->active.lock); + remove_from_engine(rq); spin_lock(&rq->lock); i915_request_mark_complete(rq); @@ -358,9 +377,10 @@ __i915_request_await_execution(struct i915_request *rq, return 0; } -void __i915_request_submit(struct i915_request *request) +bool __i915_request_submit(struct i915_request *request) { struct intel_engine_cs *engine = request->engine; + bool result = false; GEM_TRACE("%s fence %llx:%lld, current %d\n", engine->name, @@ -370,6 +390,25 @@ void __i915_request_submit(struct i915_request *request) GEM_BUG_ON(!irqs_disabled()); lockdep_assert_held(&engine->active.lock); + /* + * With the advent of preempt-to-busy, we frequently encounter + * requests that we have unsubmitted from HW, but left running + * until the next ack and so have completed in the meantime. On + * resubmission of that completed request, we can skip + * updating the payload, and execlists can even skip submitting + * the request. + * + * We must remove the request from the caller's priority queue, + * and the caller must only call us when the request is in their + * priority queue, under the active.lock. This ensures that the + * request has *not* yet been retired and we can safely move + * the request into the engine->active.list where it will be + * dropped upon retiring. (Otherwise if resubmit a *retired* + * request, this would be a horrible use-after-free.) + */ + if (i915_request_completed(request)) + goto xfer; + if (i915_gem_context_is_banned(request->gem_context)) i915_request_skip(request, -EIO); @@ -393,13 +432,18 @@ void __i915_request_submit(struct i915_request *request) i915_sw_fence_signaled(&request->semaphore)) engine->saturated |= request->sched.semaphores; - /* We may be recursing from the signal callback of another i915 fence */ - spin_lock_nested(&request->lock, SINGLE_DEPTH_NESTING); + engine->emit_fini_breadcrumb(request, + request->ring->vaddr + request->postfix); - list_move_tail(&request->sched.link, &engine->active.requests); + trace_i915_request_execute(request); + engine->serial++; + result = true; + +xfer: /* We may be recursing from the signal callback of another i915 fence */ + spin_lock_nested(&request->lock, SINGLE_DEPTH_NESTING); - GEM_BUG_ON(test_bit(I915_FENCE_FLAG_ACTIVE, &request->fence.flags)); - set_bit(I915_FENCE_FLAG_ACTIVE, &request->fence.flags); + if (!test_and_set_bit(I915_FENCE_FLAG_ACTIVE, &request->fence.flags)) + list_move_tail(&request->sched.link, &engine->active.requests); if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &request->fence.flags) && !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &request->fence.flags) && @@ -410,12 +454,7 @@ void __i915_request_submit(struct i915_request *request) spin_unlock(&request->lock); - engine->emit_fini_breadcrumb(request, - request->ring->vaddr + request->postfix); - - engine->serial++; - - trace_i915_request_execute(request); + return result; } void i915_request_submit(struct i915_request *request) diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h index 8ac6e1226a56..e4dd013761e8 100644 --- a/drivers/gpu/drm/i915/i915_request.h +++ b/drivers/gpu/drm/i915/i915_request.h @@ -292,7 +292,7 @@ int i915_request_await_execution(struct i915_request *rq, void i915_request_add(struct i915_request *rq); -void __i915_request_submit(struct i915_request *request); +bool __i915_request_submit(struct i915_request *request); void i915_request_submit(struct i915_request *request); void i915_request_skip(struct i915_request *request, int error); diff --git a/drivers/gpu/drm/i915/intel_pch.c b/drivers/gpu/drm/i915/intel_pch.c index fa864d8f2b73..15f8bff141f9 100644 --- a/drivers/gpu/drm/i915/intel_pch.c +++ b/drivers/gpu/drm/i915/intel_pch.c @@ -69,6 +69,7 @@ intel_pch_type(const struct drm_i915_private *dev_priv, unsigned short id) WARN_ON(!IS_CANNONLAKE(dev_priv) && !IS_COFFEELAKE(dev_priv)); return PCH_CNP; case INTEL_PCH_CMP_DEVICE_ID_TYPE: + case INTEL_PCH_CMP2_DEVICE_ID_TYPE: DRM_DEBUG_KMS("Found Comet Lake PCH (CMP)\n"); WARN_ON(!IS_COFFEELAKE(dev_priv)); /* CometPoint is CNP Compatible */ diff --git a/drivers/gpu/drm/i915/intel_pch.h b/drivers/gpu/drm/i915/intel_pch.h index e6a2d65f19c6..c29c81ec7971 100644 --- a/drivers/gpu/drm/i915/intel_pch.h +++ b/drivers/gpu/drm/i915/intel_pch.h @@ -41,6 +41,7 @@ enum intel_pch { #define INTEL_PCH_CNP_DEVICE_ID_TYPE 0xA300 #define INTEL_PCH_CNP_LP_DEVICE_ID_TYPE 0x9D80 #define INTEL_PCH_CMP_DEVICE_ID_TYPE 0x0280 +#define INTEL_PCH_CMP2_DEVICE_ID_TYPE 0x0680 #define INTEL_PCH_ICP_DEVICE_ID_TYPE 0x3480 #define INTEL_PCH_MCC_DEVICE_ID_TYPE 0x4B00 #define INTEL_PCH_MCC2_DEVICE_ID_TYPE 0x3880 diff --git a/drivers/gpu/drm/i915/selftests/i915_gem.c b/drivers/gpu/drm/i915/selftests/i915_gem.c index bb6dd54a6ff3..37593831b539 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem.c @@ -118,6 +118,12 @@ static void pm_resume(struct drm_i915_private *i915) with_intel_runtime_pm(&i915->runtime_pm, wakeref) { intel_gt_sanitize(&i915->gt, false); i915_gem_sanitize(i915); + + mutex_lock(&i915->drm.struct_mutex); + i915_gem_restore_gtt_mappings(i915); + i915_gem_restore_fences(i915); + mutex_unlock(&i915->drm.struct_mutex); + i915_gem_resume(i915); } } diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index e226324adb69..4bdd63b57100 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1083,7 +1083,7 @@ static const struct dss_features omap34xx_dss_feats = { static const struct dss_features omap3630_dss_feats = { .model = DSS_MODEL_OMAP3, - .fck_div_max = 32, + .fck_div_max = 31, .fck_freq_max = 173000000, .dss_fck_multiplier = 1, .parent_clk_name = "dpll4_ck", diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c index fc82a525b071..ee4379729a5b 100644 --- a/drivers/gpu/drm/panel/panel-lg-lb035q02.c +++ b/drivers/gpu/drm/panel/panel-lg-lb035q02.c @@ -220,9 +220,17 @@ static const struct of_device_id lb035q02_of_match[] = { MODULE_DEVICE_TABLE(of, lb035q02_of_match); +static const struct spi_device_id lb035q02_ids[] = { + { "lb035q02", 0 }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(spi, lb035q02_ids); + static struct spi_driver lb035q02_driver = { .probe = lb035q02_probe, .remove = lb035q02_remove, + .id_table = lb035q02_ids, .driver = { .name = "panel-lg-lb035q02", .of_match_table = lb035q02_of_match, @@ -231,7 +239,6 @@ static struct spi_driver lb035q02_driver = { module_spi_driver(lb035q02_driver); -MODULE_ALIAS("spi:lgphilips,lb035q02"); MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c index 299b217c83e1..20f17e46e65d 100644 --- a/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c +++ b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c @@ -230,9 +230,17 @@ static const struct of_device_id nl8048_of_match[] = { MODULE_DEVICE_TABLE(of, nl8048_of_match); +static const struct spi_device_id nl8048_ids[] = { + { "nl8048hl11", 0 }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(spi, nl8048_ids); + static struct spi_driver nl8048_driver = { .probe = nl8048_probe, .remove = nl8048_remove, + .id_table = nl8048_ids, .driver = { .name = "panel-nec-nl8048hl11", .pm = &nl8048_pm_ops, @@ -242,7 +250,6 @@ static struct spi_driver nl8048_driver = { module_spi_driver(nl8048_driver); -MODULE_ALIAS("spi:nec,nl8048hl11"); MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); MODULE_DESCRIPTION("NEC-NL8048HL11 Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-sony-acx565akm.c b/drivers/gpu/drm/panel/panel-sony-acx565akm.c index 305259b58767..3d5b9c4f68d9 100644 --- a/drivers/gpu/drm/panel/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/panel/panel-sony-acx565akm.c @@ -684,9 +684,17 @@ static const struct of_device_id acx565akm_of_match[] = { MODULE_DEVICE_TABLE(of, acx565akm_of_match); +static const struct spi_device_id acx565akm_ids[] = { + { "acx565akm", 0 }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(spi, acx565akm_ids); + static struct spi_driver acx565akm_driver = { .probe = acx565akm_probe, .remove = acx565akm_remove, + .id_table = acx565akm_ids, .driver = { .name = "panel-sony-acx565akm", .of_match_table = acx565akm_of_match, @@ -695,7 +703,6 @@ static struct spi_driver acx565akm_driver = { module_spi_driver(acx565akm_driver); -MODULE_ALIAS("spi:sony,acx565akm"); MODULE_AUTHOR("Nokia Corporation"); MODULE_DESCRIPTION("Sony ACX565AKM LCD Panel Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c index d7b2e34626ef..f2baff827f50 100644 --- a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c @@ -375,8 +375,7 @@ static const struct of_device_id td028ttec1_of_match[] = { MODULE_DEVICE_TABLE(of, td028ttec1_of_match); static const struct spi_device_id td028ttec1_ids[] = { - { "tpo,td028ttec1", 0}, - { "toppoly,td028ttec1", 0 }, + { "td028ttec1", 0 }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c index 84370562910f..ba163c779084 100644 --- a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c +++ b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c @@ -491,9 +491,17 @@ static const struct of_device_id td043mtea1_of_match[] = { MODULE_DEVICE_TABLE(of, td043mtea1_of_match); +static const struct spi_device_id td043mtea1_ids[] = { + { "td043mtea1", 0 }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(spi, td043mtea1_ids); + static struct spi_driver td043mtea1_driver = { .probe = td043mtea1_probe, .remove = td043mtea1_remove, + .id_table = td043mtea1_ids, .driver = { .name = "panel-tpo-td043mtea1", .pm = &td043mtea1_pm_ops, @@ -503,7 +511,6 @@ static struct spi_driver td043mtea1_driver = { module_spi_driver(td043mtea1_driver); -MODULE_ALIAS("spi:tpo,td043mtea1"); MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c index ae07290bba6a..04efa78d70b6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c @@ -147,7 +147,7 @@ static int rcar_du_wb_enc_atomic_check(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_framebuffer *fb; - if (!conn_state->writeback_job || !conn_state->writeback_job->fb) + if (!conn_state->writeback_job) return 0; fb = conn_state->writeback_job->fb; @@ -221,7 +221,7 @@ void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc, unsigned int i; state = rcrtc->writeback.base.state; - if (!state || !state->writeback_job || !state->writeback_job->fb) + if (!state || !state->writeback_job) return; fb = state->writeback_job->fb; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 525dc1c0f1c1..530edb3b51cc 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -7,6 +7,7 @@ #include <linux/gpio.h> #include <linux/mod_devicetable.h> #include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <drm/drm_atomic_helper.h> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 1ce4d7142b6e..bf720206727f 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -231,7 +231,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, int i; conn_state = drm_atomic_get_new_connector_state(state, conn); - if (!conn_state->writeback_job || !conn_state->writeback_job->fb) + if (!conn_state->writeback_job) return 0; crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); @@ -271,8 +271,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, u32 ctrl; int i; - if (WARN_ON(!conn_state->writeback_job || - !conn_state->writeback_job->fb)) + if (WARN_ON(!conn_state->writeback_job)) return; mode = &conn_state->crtc->state->adjusted_mode; diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c index ba1828acd8c9..4be49c1aef51 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.c +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -718,17 +718,9 @@ static int xen_drv_probe(struct xenbus_device *xb_dev, struct device *dev = &xb_dev->dev; int ret; - /* - * The device is not spawn from a device tree, so arch_setup_dma_ops - * is not called, thus leaving the device with dummy DMA ops. - * This makes the device return error on PRIME buffer import, which - * is not correct: to fix this call of_dma_configure() with a NULL - * node to set default DMA ops. - */ - dev->coherent_dma_mask = DMA_BIT_MASK(32); - ret = of_dma_configure(dev, NULL, true); + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret < 0) { - DRM_ERROR("Cannot setup DMA ops, ret %d", ret); + DRM_ERROR("Cannot setup DMA mask, ret %d", ret); return ret; } diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index cc5b09b87ab0..79a28fc91521 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -314,60 +314,24 @@ static void mousevsc_on_receive(struct hv_device *device, static void mousevsc_on_channel_callback(void *context) { - const int packet_size = 0x100; - int ret; struct hv_device *device = context; - u32 bytes_recvd; - u64 req_id; struct vmpacket_descriptor *desc; - unsigned char *buffer; - int bufferlen = packet_size; - - buffer = kmalloc(bufferlen, GFP_ATOMIC); - if (!buffer) - return; - - do { - ret = vmbus_recvpacket_raw(device->channel, buffer, - bufferlen, &bytes_recvd, &req_id); - - switch (ret) { - case 0: - if (bytes_recvd <= 0) { - kfree(buffer); - return; - } - desc = (struct vmpacket_descriptor *)buffer; - - switch (desc->type) { - case VM_PKT_COMP: - break; - - case VM_PKT_DATA_INBAND: - mousevsc_on_receive(device, desc); - break; - - default: - pr_err("unhandled packet type %d, tid %llx len %d\n", - desc->type, req_id, bytes_recvd); - break; - } + foreach_vmbus_pkt(desc, device->channel) { + switch (desc->type) { + case VM_PKT_COMP: break; - case -ENOBUFS: - kfree(buffer); - /* Handle large packet */ - bufferlen = bytes_recvd; - buffer = kmalloc(bytes_recvd, GFP_ATOMIC); - - if (!buffer) - return; + case VM_PKT_DATA_INBAND: + mousevsc_on_receive(device, desc); + break; + default: + pr_err("Unhandled packet type %d, tid %llx len %d\n", + desc->type, desc->trans_id, desc->len8 * 8); break; } - } while (1); - + } } static int mousevsc_connect_to_vsp(struct hv_device *device) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 391f0b225c9a..53a60c81e220 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -912,6 +912,7 @@ static void vmbus_shutdown(struct device *child_device) drv->shutdown(dev); } +#ifdef CONFIG_PM_SLEEP /* * vmbus_suspend - Suspend a vmbus device */ @@ -949,6 +950,7 @@ static int vmbus_resume(struct device *child_device) return drv->resume(dev); } +#endif /* CONFIG_PM_SLEEP */ /* * vmbus_device_release - Final callback release of the vmbus child device @@ -1070,6 +1072,7 @@ msg_handled: vmbus_signal_eom(msg, message_type); } +#ifdef CONFIG_PM_SLEEP /* * Fake RESCIND_CHANNEL messages to clean up hv_sock channels by force for * hibernation, because hv_sock connections can not persist across hibernation. @@ -1105,6 +1108,7 @@ static void vmbus_force_channel_rescinded(struct vmbus_channel *channel) vmbus_connection.work_queue, &ctx->work); } +#endif /* CONFIG_PM_SLEEP */ /* * Direct callback for channels using other deferred processing @@ -2125,6 +2129,7 @@ acpi_walk_err: return ret_val; } +#ifdef CONFIG_PM_SLEEP static int vmbus_bus_suspend(struct device *dev) { struct vmbus_channel *channel, *sc; @@ -2247,6 +2252,7 @@ static int vmbus_bus_resume(struct device *dev) return 0; } +#endif /* CONFIG_PM_SLEEP */ static const struct acpi_device_id vmbus_acpi_device_ids[] = { {"VMBUS", 0}, diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 95b447cfa24c..b26419dbe840 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -99,6 +99,8 @@ struct nct7904_data { u8 enable_dts; u8 has_dts; u8 temp_mode; /* 0: TR mode, 1: TD mode */ + u8 fan_alarm[2]; + u8 vsen_alarm[3]; }; /* Access functions */ @@ -214,7 +216,15 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel, SMI_STS5_REG + (channel >> 3)); if (ret < 0) return ret; - *val = (ret >> (channel & 0x07)) & 1; + if (!data->fan_alarm[channel >> 3]) + data->fan_alarm[channel >> 3] = ret & 0xff; + else + /* If there is new alarm showing up */ + data->fan_alarm[channel >> 3] |= (ret & 0xff); + *val = (data->fan_alarm[channel >> 3] >> (channel & 0x07)) & 1; + /* Needs to clean the alarm if alarm existing */ + if (*val) + data->fan_alarm[channel >> 3] ^= 1 << (channel & 0x07); return 0; default: return -EOPNOTSUPP; @@ -298,7 +308,15 @@ static int nct7904_read_in(struct device *dev, u32 attr, int channel, SMI_STS1_REG + (index >> 3)); if (ret < 0) return ret; - *val = (ret >> (index & 0x07)) & 1; + if (!data->vsen_alarm[index >> 3]) + data->vsen_alarm[index >> 3] = ret & 0xff; + else + /* If there is new alarm showing up */ + data->vsen_alarm[index >> 3] |= (ret & 0xff); + *val = (data->vsen_alarm[index >> 3] >> (index & 0x07)) & 1; + /* Needs to clean the alarm if alarm existing */ + if (*val) + data->vsen_alarm[index >> 3] ^= 1 << (index & 0x07); return 0; default: return -EOPNOTSUPP; @@ -915,12 +933,15 @@ static int nct7904_probe(struct i2c_client *client, data->temp_mode = 0; for (i = 0; i < 4; i++) { - val = (ret & (0x03 << i)) >> (i * 2); + val = (ret >> (i * 2)) & 0x03; bit = (1 << i); - if (val == 0) + if (val == 0) { data->tcpu_mask &= ~bit; - else if (val == 0x1 || val == 0x2) - data->temp_mode |= bit; + } else { + if (val == 0x1 || val == 0x2) + data->temp_mode |= bit; + data->vsen_mask &= ~(0x06 << (i * 2)); + } } /* PECI */ diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 055227cb3d43..67b8817995c0 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -474,12 +474,17 @@ static int adxl372_configure_fifo(struct adxl372_state *st) if (ret < 0) return ret; - fifo_samples = st->watermark & 0xFF; + /* + * watermark stores the number of sets; we need to write the FIFO + * registers with the number of samples + */ + fifo_samples = (st->watermark * st->fifo_set_size); fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) | ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) | - ADXL372_FIFO_CTL_SAMPLES_MODE(st->watermark); + ADXL372_FIFO_CTL_SAMPLES_MODE(fifo_samples); - ret = regmap_write(st->regmap, ADXL372_FIFO_SAMPLES, fifo_samples); + ret = regmap_write(st->regmap, + ADXL372_FIFO_SAMPLES, fifo_samples & 0xFF); if (ret < 0) return ret; @@ -548,8 +553,7 @@ static irqreturn_t adxl372_trigger_handler(int irq, void *p) goto err; /* Each sample is 2 bytes */ - for (i = 0; i < fifo_entries * sizeof(u16); - i += st->fifo_set_size * sizeof(u16)) + for (i = 0; i < fifo_entries; i += st->fifo_set_size) iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); } err: @@ -571,6 +575,14 @@ static int adxl372_setup(struct adxl372_state *st) return -ENODEV; } + /* + * Perform a software reset to make sure the device is in a consistent + * state after start up. + */ + ret = regmap_write(st->regmap, ADXL372_RESET, ADXL372_RESET_CODE); + if (ret < 0) + return ret; + ret = adxl372_set_op_mode(st, ADXL372_STANDBY); if (ret < 0) return ret; diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index cf6c0e3a83d3..121b4e89f038 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -117,7 +117,7 @@ #define BMC150_ACCEL_SLEEP_1_SEC 0x0F #define BMC150_ACCEL_REG_TEMP 0x08 -#define BMC150_ACCEL_TEMP_CENTER_VAL 24 +#define BMC150_ACCEL_TEMP_CENTER_VAL 23 #define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2)) #define BMC150_AUTO_SUSPEND_DELAY_MS 2000 diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 0b21dd405dd5..9554890a3200 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -442,6 +442,17 @@ config INGENIC_ADC This driver can also be built as a module. If so, the module will be called ingenic_adc. +config INTEL_MRFLD_ADC + tristate "Intel Merrifield Basin Cove ADC driver" + depends on INTEL_SOC_PMIC_MRFLD + help + Say yes here to have support for Basin Cove power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called intel_mrfld_adc. + config IMX7D_ADC tristate "Freescale IMX7D ADC driver" depends on ARCH_MXC || COMPILE_TEST @@ -518,8 +529,8 @@ config MAX1027 select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for Maxim SPI ADC models - max1027, max1029 and max1031. + Say yes here to build support for Maxim SPI {10,12}-bit ADC models: + max1027, max1029, max1031, max1227, max1229 and max1231. To compile this driver as a module, choose M here: the module will be called max1027. diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index fc1b6ebb0cde..5ecc481c2967 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o +obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index ac0ffff6c5ae..5c2b3446fa4a 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -54,38 +54,20 @@ struct ad7949_adc_chip { u8 resolution; u16 cfg; unsigned int current_channel; - u32 buffer ____cacheline_aligned; + u16 buffer ____cacheline_aligned; }; -static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc) -{ - if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK)) - return true; - - return false; -} - -static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc) -{ - int ret = ad7949_adc->resolution; - - if (ad7949_spi_cfg_is_read_back(ad7949_adc)) - ret += AD7949_CFG_REG_SIZE_BITS; - - return ret; -} - static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, u16 mask) { int ret; - int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int bits_per_word = ad7949_adc->resolution; int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; struct spi_message msg; struct spi_transfer tx[] = { { .tx_buf = &ad7949_adc->buffer, - .len = 4, + .len = 2, .bits_per_word = bits_per_word, }, }; @@ -107,13 +89,13 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, unsigned int channel) { int ret; - int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int bits_per_word = ad7949_adc->resolution; int mask = GENMASK(ad7949_adc->resolution, 0); struct spi_message msg; struct spi_transfer tx[] = { { .rx_buf = &ad7949_adc->buffer, - .len = 4, + .len = 2, .bits_per_word = bits_per_word, }, }; @@ -138,10 +120,7 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, ad7949_adc->current_channel = channel; - if (ad7949_spi_cfg_is_read_back(ad7949_adc)) - *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask; - else - *val = ad7949_adc->buffer & mask; + *val = ad7949_adc->buffer & mask; return 0; } diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 5a3ca5904ded..f658012baad8 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -810,10 +810,10 @@ static int ad799x_probe(struct i2c_client *client, ret = ad799x_write_config(st, st->chip_config->default_config); if (ret < 0) - goto error_disable_reg; + goto error_disable_vref; ret = ad799x_read_config(st); if (ret < 0) - goto error_disable_reg; + goto error_disable_vref; st->config = ret; ret = iio_triggered_buffer_setup(indio_dev, NULL, diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 2640b75fb774..8ba90486c787 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -205,7 +205,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, } EXPORT_SYMBOL_GPL(ad_sd_reset); -static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, +int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { int ret; @@ -242,6 +242,7 @@ out: return ret; } +EXPORT_SYMBOL_GPL(ad_sd_calibrate); /** * ad_sd_calibrate_all() - Performs channel calibration diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index adc9cf7a075d..8ea2aed6d6f5 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -7,6 +7,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include <linux/dmi.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -25,6 +26,11 @@ #define AXP288_ADC_EN_MASK 0xF0 #define AXP288_ADC_TS_ENABLE 0x01 +#define AXP288_ADC_TS_BIAS_MASK GENMASK(5, 4) +#define AXP288_ADC_TS_BIAS_20UA (0 << 4) +#define AXP288_ADC_TS_BIAS_40UA (1 << 4) +#define AXP288_ADC_TS_BIAS_60UA (2 << 4) +#define AXP288_ADC_TS_BIAS_80UA (3 << 4) #define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0) #define AXP288_ADC_TS_CURRENT_OFF (0 << 0) #define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0) @@ -177,10 +183,36 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev, return ret; } +/* + * We rely on the machine's firmware to correctly setup the TS pin bias current + * at boot. This lists systems with broken fw where we need to set it ourselves. + */ +static const struct dmi_system_id axp288_adc_ts_bias_override[] = { + { + /* Lenovo Ideapad 100S (11 inch) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 100S-11IBY"), + }, + .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, + }, + {} +}; + static int axp288_adc_initialize(struct axp288_adc_info *info) { + const struct dmi_system_id *bias_override; int ret, adc_enable_val; + bias_override = dmi_first_match(axp288_adc_ts_bias_override); + if (bias_override) { + ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL, + AXP288_ADC_TS_BIAS_MASK, + (uintptr_t)bias_override->driver_data); + if (ret) + return ret; + } + /* * Determine if the TS pin is enabled and set the TS current-source * accordingly. diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 88c7fe15003b..62e6c8badd22 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -100,14 +100,14 @@ struct hx711_data { static int hx711_cycle(struct hx711_data *hx711_data) { - int val; + unsigned long flags; /* * if preempted for more then 60us while PD_SCK is high: * hx711 is going in reset * ==> measuring is false */ - preempt_disable(); + local_irq_save(flags); gpiod_set_value(hx711_data->gpiod_pd_sck, 1); /* @@ -117,7 +117,6 @@ static int hx711_cycle(struct hx711_data *hx711_data) */ ndelay(hx711_data->data_ready_delay_ns); - val = gpiod_get_value(hx711_data->gpiod_dout); /* * here we are not waiting for 0.2 us as suggested by the datasheet, * because the oscilloscope showed in a test scenario @@ -125,7 +124,7 @@ static int hx711_cycle(struct hx711_data *hx711_data) * and 0.56 us for PD_SCK low on TI Sitara with 800 MHz */ gpiod_set_value(hx711_data->gpiod_pd_sck, 0); - preempt_enable(); + local_irq_restore(flags); /* * make it a square wave for addressing cases with capacitance on @@ -133,7 +132,8 @@ static int hx711_cycle(struct hx711_data *hx711_data) */ ndelay(hx711_data->data_ready_delay_ns); - return val; + /* sample as late as possible */ + return gpiod_get_value(hx711_data->gpiod_dout); } static int hx711_read(struct hx711_data *hx711_data) diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index e234970b7150..7a53c2f8d438 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -25,9 +25,13 @@ #define JZ_ADC_REG_ADSDAT 0x20 #define JZ_ADC_REG_ADCLK 0x28 +#define JZ_ADC_REG_ENABLE_PD BIT(7) +#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1)) #define JZ_ADC_REG_CFG_BAT_MD BIT(4) #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 -#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16 +#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16 +#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8 +#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16 #define JZ_ADC_AUX_VREF 3300 #define JZ_ADC_AUX_VREF_BITS 12 @@ -37,6 +41,8 @@ #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 +#define JZ4770_ADC_BATTERY_VREF 6600 +#define JZ4770_ADC_BATTERY_VREF_BITS 12 struct ingenic_adc; @@ -47,6 +53,8 @@ struct ingenic_adc_soc_data { size_t battery_raw_avail_size; const int *battery_scale_avail; size_t battery_scale_avail_size; + unsigned int battery_vref_mode: 1; + unsigned int has_aux2: 1; int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); }; @@ -54,6 +62,7 @@ struct ingenic_adc { void __iomem *base; struct clk *clk; struct mutex lock; + struct mutex aux_lock; const struct ingenic_adc_soc_data *soc_data; bool low_vref_mode; }; @@ -120,6 +129,8 @@ static int ingenic_adc_write_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->channel) { case INGENIC_ADC_BATTERY: + if (!adc->soc_data->battery_vref_mode) + return -EINVAL; if (val > JZ_ADC_BATTERY_LOW_VREF) { ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_BAT_MD, @@ -158,6 +169,14 @@ static const int jz4740_adc_battery_scale_avail[] = { JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, }; +static const int jz4770_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1, +}; + +static const int jz4770_adc_battery_scale_avail[] = { + JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, +}; + static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) { struct clk *parent_clk; @@ -187,7 +206,45 @@ static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) /* We also need a divider that produces a 10us clock. */ div_10us = DIV_ROUND_UP(rate, 100000); - writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) | + writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, + adc->base + JZ_ADC_REG_ADCLK); + + return 0; +} + +static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) +{ + struct clk *parent_clk; + unsigned long parent_rate, rate; + unsigned int div_main, div_ms, div_10us; + + parent_clk = clk_get_parent(adc->clk); + if (!parent_clk) { + dev_err(dev, "ADC clock has no parent\n"); + return -ENODEV; + } + parent_rate = clk_get_rate(parent_clk); + + /* + * The JZ4770 ADC works at 20 kHz to 200 kHz. + * We pick the highest rate possible. + */ + div_main = DIV_ROUND_UP(parent_rate, 200000); + div_main = clamp(div_main, 1u, 256u); + rate = parent_rate / div_main; + if (rate < 20000 || rate > 200000) { + dev_err(dev, "No valid divider for ADC main clock\n"); + return -EINVAL; + } + + /* We also need a divider that produces a 10us clock. */ + div_10us = DIV_ROUND_UP(rate, 10000); + /* And another, which produces a 1ms clock. */ + div_ms = DIV_ROUND_UP(rate, 1000); + + writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | + ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) | (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, adc->base + JZ_ADC_REG_ADCLK); @@ -201,6 +258,8 @@ static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), .battery_scale_avail = jz4725b_adc_battery_scale_avail, .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), + .battery_vref_mode = true, + .has_aux2 = false, .init_clk_div = jz4725b_adc_init_clk_div, }; @@ -211,9 +270,23 @@ static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), .battery_scale_avail = jz4740_adc_battery_scale_avail, .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), + .battery_vref_mode = true, + .has_aux2 = false, .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ }; +static const struct ingenic_adc_soc_data jz4770_adc_soc_data = { + .battery_high_vref = JZ4770_ADC_BATTERY_VREF, + .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, + .battery_raw_avail = jz4770_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), + .battery_scale_avail = jz4770_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail), + .battery_vref_mode = false, + .has_aux2 = true, + .init_clk_div = jz4770_adc_init_clk_div, +}; + static int ingenic_adc_read_avail(struct iio_dev *iio_dev, struct iio_chan_spec const *chan, const int **vals, @@ -239,6 +312,42 @@ static int ingenic_adc_read_avail(struct iio_dev *iio_dev, }; } +static int ingenic_adc_read_chan_info_raw(struct ingenic_adc *adc, + struct iio_chan_spec const *chan, + int *val) +{ + int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); + + /* We cannot sample AUX/AUX2 in parallel. */ + mutex_lock(&adc->aux_lock); + if (adc->soc_data->has_aux2 && engine == 0) { + bit = BIT(chan->channel == INGENIC_ADC_AUX2); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit); + } + + clk_enable(adc->clk); + ret = ingenic_adc_capture(adc, engine); + if (ret) + goto out; + + switch (chan->channel) { + case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: + *val = readw(adc->base + JZ_ADC_REG_ADSDAT); + break; + case INGENIC_ADC_BATTERY: + *val = readw(adc->base + JZ_ADC_REG_ADBDAT); + break; + } + + ret = IIO_VAL_INT; +out: + clk_disable(adc->clk); + mutex_unlock(&adc->aux_lock); + + return ret; +} + static int ingenic_adc_read_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *chan, int *val, @@ -246,32 +355,14 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev, long m) { struct ingenic_adc *adc = iio_priv(iio_dev); - int ret; switch (m) { case IIO_CHAN_INFO_RAW: - clk_enable(adc->clk); - ret = ingenic_adc_capture(adc, chan->channel); - if (ret) { - clk_disable(adc->clk); - return ret; - } - - switch (chan->channel) { - case INGENIC_ADC_AUX: - *val = readw(adc->base + JZ_ADC_REG_ADSDAT); - break; - case INGENIC_ADC_BATTERY: - *val = readw(adc->base + JZ_ADC_REG_ADBDAT); - break; - } - - clk_disable(adc->clk); - - return IIO_VAL_INT; + return ingenic_adc_read_chan_info_raw(adc, chan, val); case IIO_CHAN_INFO_SCALE: switch (chan->channel) { case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: *val = JZ_ADC_AUX_VREF; *val2 = JZ_ADC_AUX_VREF_BITS; break; @@ -322,6 +413,14 @@ static const struct iio_chan_spec ingenic_channels[] = { .indexed = 1, .channel = INGENIC_ADC_BATTERY, }, + { /* Must always be last in the array. */ + .extend_name = "aux2", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX2, + }, }; static int ingenic_adc_probe(struct platform_device *pdev) @@ -343,6 +442,7 @@ static int ingenic_adc_probe(struct platform_device *pdev) adc = iio_priv(iio_dev); mutex_init(&adc->lock); + mutex_init(&adc->aux_lock); adc->soc_data = soc_data; mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -374,6 +474,7 @@ static int ingenic_adc_probe(struct platform_device *pdev) /* Put hardware in a known passive state. */ writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + usleep_range(2000, 3000); /* Must wait at least 2ms. */ clk_disable(adc->clk); ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); @@ -387,6 +488,9 @@ static int ingenic_adc_probe(struct platform_device *pdev) iio_dev->modes = INDIO_DIRECT_MODE; iio_dev->channels = ingenic_channels; iio_dev->num_channels = ARRAY_SIZE(ingenic_channels); + /* Remove AUX2 from the list of supported channels. */ + if (!adc->soc_data->has_aux2) + iio_dev->num_channels -= 1; iio_dev->info = &ingenic_adc_info; ret = devm_iio_device_register(dev, iio_dev); @@ -400,6 +504,7 @@ static int ingenic_adc_probe(struct platform_device *pdev) static const struct of_device_id ingenic_adc_of_match[] = { { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, + { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, { }, }; MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c new file mode 100644 index 000000000000..67d096f8180d --- /dev/null +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for Basin Cove PMIC + * + * Copyright (C) 2012 Intel Corporation + * Author: Bin Yang <bin.yang@intel.com> + * + * Rewritten for upstream by: + * Vincent Pelletier <plr.vincent@gmail.com> + * Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/mfd/intel_soc_pmic_mrfld.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <linux/iio/driver.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> + +#include <asm/unaligned.h> + +#define BCOVE_GPADCREQ 0xDC +#define BCOVE_GPADCREQ_BUSY BIT(0) +#define BCOVE_GPADCREQ_IRQEN BIT(1) + +#define BCOVE_ADCIRQ_ALL ( \ + BCOVE_ADCIRQ_BATTEMP | \ + BCOVE_ADCIRQ_SYSTEMP | \ + BCOVE_ADCIRQ_BATTID | \ + BCOVE_ADCIRQ_VIBATT | \ + BCOVE_ADCIRQ_CCTICK) + +#define BCOVE_ADC_TIMEOUT msecs_to_jiffies(1000) + +static const u8 mrfld_adc_requests[] = { + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_BATTID, + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, +}; + +struct mrfld_adc { + struct regmap *regmap; + struct completion completion; + /* Lock to protect the IPC transfers */ + struct mutex lock; +}; + +static irqreturn_t mrfld_adc_thread_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct mrfld_adc *adc = iio_priv(indio_dev); + + complete(&adc->completion); + return IRQ_HANDLED; +} + +static int mrfld_adc_single_conv(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *result) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->regmap; + unsigned int req; + long timeout; + u8 buf[2]; + int ret; + + reinit_completion(&adc->completion); + + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0); + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0); + + ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req, + !(req & BCOVE_GPADCREQ_BUSY), + 2000, 1000000); + if (ret) + goto done; + + req = mrfld_adc_requests[chan->channel]; + ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req); + if (ret) + goto done; + + timeout = wait_for_completion_interruptible_timeout(&adc->completion, + BCOVE_ADC_TIMEOUT); + if (timeout < 0) { + ret = timeout; + goto done; + } + if (timeout == 0) { + ret = -ETIMEDOUT; + goto done; + } + + ret = regmap_bulk_read(regmap, chan->address, buf, 2); + if (ret) + goto done; + + *result = get_unaligned_be16(buf); + ret = IIO_VAL_INT; + +done: + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff); + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff); + + return ret; +} + +static int mrfld_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = mrfld_adc_single_conv(indio_dev, chan, val); + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info mrfld_adc_iio_info = { + .read_raw = &mrfld_adc_read_raw, +}; + +#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address) \ + { \ + .indexed = 1, \ + .type = _type, \ + .channel = _channel, \ + .address = _address, \ + .datasheet_name = _datasheet_name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } + +static const struct iio_chan_spec mrfld_adc_channels[] = { + BCOVE_ADC_CHANNEL(IIO_VOLTAGE, 0, "CH0", 0xE9), + BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB), + BCOVE_ADC_CHANNEL(IIO_CURRENT, 2, "CH2", 0xED), + BCOVE_ADC_CHANNEL(IIO_TEMP, 3, "CH3", 0xCC), + BCOVE_ADC_CHANNEL(IIO_TEMP, 4, "CH4", 0xC8), + BCOVE_ADC_CHANNEL(IIO_TEMP, 5, "CH5", 0xCA), + BCOVE_ADC_CHANNEL(IIO_TEMP, 6, "CH6", 0xC2), + BCOVE_ADC_CHANNEL(IIO_TEMP, 7, "CH7", 0xC4), + BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6), +}; + +static struct iio_map iio_maps[] = { + IIO_MAP("CH0", "bcove-battery", "VBATRSLT"), + IIO_MAP("CH1", "bcove-battery", "BATTID"), + IIO_MAP("CH2", "bcove-battery", "IBATRSLT"), + IIO_MAP("CH3", "bcove-temp", "PMICTEMP"), + IIO_MAP("CH4", "bcove-temp", "BATTEMP0"), + IIO_MAP("CH5", "bcove-temp", "BATTEMP1"), + IIO_MAP("CH6", "bcove-temp", "SYSTEMP0"), + IIO_MAP("CH7", "bcove-temp", "SYSTEMP1"), + IIO_MAP("CH8", "bcove-temp", "SYSTEMP2"), + {} +}; + +static int mrfld_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct mrfld_adc *adc; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*indio_dev)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + + mutex_init(&adc->lock); + init_completion(&adc->completion); + adc->regmap = pmic->regmap; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr, + IRQF_ONESHOT | IRQF_SHARED, pdev->name, + indio_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->dev.parent = dev; + indio_dev->name = pdev->name; + + indio_dev->channels = mrfld_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels); + indio_dev->info = &mrfld_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_map_array_register(indio_dev, iio_maps); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + goto err_array_unregister; + + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + return ret; +} + +static int mrfld_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct platform_device_id mrfld_adc_id_table[] = { + { .name = "mrfld_bcove_adc" }, + {} +}; +MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table); + +static struct platform_driver mrfld_adc_driver = { + .driver = { + .name = "mrfld_bcove_adc", + }, + .probe = mrfld_adc_probe, + .remove = mrfld_adc_remove, + .id_table = mrfld_adc_id_table, +}; +module_platform_driver(mrfld_adc_driver); + +MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>"); +MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>"); +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 214883458582..e171db20c04a 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -63,12 +63,18 @@ enum max1027_id { max1027, max1029, max1031, + max1227, + max1229, + max1231, }; static const struct spi_device_id max1027_id[] = { {"max1027", max1027}, {"max1029", max1029}, {"max1031", max1031}, + {"max1227", max1227}, + {"max1229", max1229}, + {"max1231", max1231}, {} }; MODULE_DEVICE_TABLE(spi, max1027_id); @@ -78,12 +84,15 @@ static const struct of_device_id max1027_adc_dt_ids[] = { { .compatible = "maxim,max1027" }, { .compatible = "maxim,max1029" }, { .compatible = "maxim,max1031" }, + { .compatible = "maxim,max1227" }, + { .compatible = "maxim,max1229" }, + { .compatible = "maxim,max1231" }, {}, }; MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); #endif -#define MAX1027_V_CHAN(index) \ +#define MAX1027_V_CHAN(index, depth) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -93,7 +102,7 @@ MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); .scan_index = index + 1, \ .scan_type = { \ .sign = 'u', \ - .realbits = 10, \ + .realbits = depth, \ .storagebits = 16, \ .shift = 2, \ .endianness = IIO_BE, \ @@ -115,52 +124,54 @@ MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); }, \ } +#define MAX1X27_CHANNELS(depth) \ + MAX1027_T_CHAN, \ + MAX1027_V_CHAN(0, depth), \ + MAX1027_V_CHAN(1, depth), \ + MAX1027_V_CHAN(2, depth), \ + MAX1027_V_CHAN(3, depth), \ + MAX1027_V_CHAN(4, depth), \ + MAX1027_V_CHAN(5, depth), \ + MAX1027_V_CHAN(6, depth), \ + MAX1027_V_CHAN(7, depth) + +#define MAX1X29_CHANNELS(depth) \ + MAX1X27_CHANNELS(depth), \ + MAX1027_V_CHAN(8, depth), \ + MAX1027_V_CHAN(9, depth), \ + MAX1027_V_CHAN(10, depth), \ + MAX1027_V_CHAN(11, depth) + +#define MAX1X31_CHANNELS(depth) \ + MAX1X27_CHANNELS(depth), \ + MAX1X29_CHANNELS(depth), \ + MAX1027_V_CHAN(12, depth), \ + MAX1027_V_CHAN(13, depth), \ + MAX1027_V_CHAN(14, depth), \ + MAX1027_V_CHAN(15, depth) + static const struct iio_chan_spec max1027_channels[] = { - MAX1027_T_CHAN, - MAX1027_V_CHAN(0), - MAX1027_V_CHAN(1), - MAX1027_V_CHAN(2), - MAX1027_V_CHAN(3), - MAX1027_V_CHAN(4), - MAX1027_V_CHAN(5), - MAX1027_V_CHAN(6), - MAX1027_V_CHAN(7) + MAX1X27_CHANNELS(10), }; static const struct iio_chan_spec max1029_channels[] = { - MAX1027_T_CHAN, - MAX1027_V_CHAN(0), - MAX1027_V_CHAN(1), - MAX1027_V_CHAN(2), - MAX1027_V_CHAN(3), - MAX1027_V_CHAN(4), - MAX1027_V_CHAN(5), - MAX1027_V_CHAN(6), - MAX1027_V_CHAN(7), - MAX1027_V_CHAN(8), - MAX1027_V_CHAN(9), - MAX1027_V_CHAN(10), - MAX1027_V_CHAN(11) + MAX1X29_CHANNELS(10), }; static const struct iio_chan_spec max1031_channels[] = { - MAX1027_T_CHAN, - MAX1027_V_CHAN(0), - MAX1027_V_CHAN(1), - MAX1027_V_CHAN(2), - MAX1027_V_CHAN(3), - MAX1027_V_CHAN(4), - MAX1027_V_CHAN(5), - MAX1027_V_CHAN(6), - MAX1027_V_CHAN(7), - MAX1027_V_CHAN(8), - MAX1027_V_CHAN(9), - MAX1027_V_CHAN(10), - MAX1027_V_CHAN(11), - MAX1027_V_CHAN(12), - MAX1027_V_CHAN(13), - MAX1027_V_CHAN(14), - MAX1027_V_CHAN(15) + MAX1X31_CHANNELS(10), +}; + +static const struct iio_chan_spec max1227_channels[] = { + MAX1X27_CHANNELS(12), +}; + +static const struct iio_chan_spec max1229_channels[] = { + MAX1X29_CHANNELS(12), +}; + +static const struct iio_chan_spec max1231_channels[] = { + MAX1X31_CHANNELS(12), }; static const unsigned long max1027_available_scan_masks[] = { @@ -200,6 +211,21 @@ static const struct max1027_chip_info max1027_chip_info_tbl[] = { .num_channels = ARRAY_SIZE(max1031_channels), .available_scan_masks = max1031_available_scan_masks, }, + [max1227] = { + .channels = max1227_channels, + .num_channels = ARRAY_SIZE(max1227_channels), + .available_scan_masks = max1027_available_scan_masks, + }, + [max1229] = { + .channels = max1229_channels, + .num_channels = ARRAY_SIZE(max1229_channels), + .available_scan_masks = max1029_available_scan_masks, + }, + [max1231] = { + .channels = max1231_channels, + .num_channels = ARRAY_SIZE(max1231_channels), + .available_scan_masks = max1031_available_scan_masks, + }, }; struct max1027_state { @@ -284,7 +310,7 @@ static int max1027_read_raw(struct iio_dev *indio_dev, break; case IIO_VOLTAGE: *val = 2500; - *val2 = 10; + *val2 = chan->scan_type.realbits; ret = IIO_VAL_FRACTIONAL_LOG2; break; default: @@ -309,8 +335,11 @@ static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, struct max1027_state *st = iio_priv(indio_dev); u8 *val = (u8 *)st->buffer; - if (readval != NULL) - return -EINVAL; + if (readval) { + int ret = spi_read(st->spi, val, 2); + *readval = be16_to_cpu(st->buffer[0]); + return ret; + } *val = (u8)writeval; return spi_write(st->spi, val, 1); @@ -427,34 +456,47 @@ static int max1027_probe(struct spi_device *spi) return -ENOMEM; } - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - &iio_pollfunc_store_time, - &max1027_trigger_handler, NULL); - if (ret < 0) { - dev_err(&indio_dev->dev, "Failed to setup buffer\n"); - return ret; - } + if (spi->irq) { + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &max1027_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to setup buffer\n"); + return ret; + } - st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", - indio_dev->name); - if (st->trig == NULL) { - ret = -ENOMEM; - dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n"); - return ret; - } + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", + indio_dev->name); + if (st->trig == NULL) { + ret = -ENOMEM; + dev_err(&indio_dev->dev, + "Failed to allocate iio trigger\n"); + return ret; + } - st->trig->ops = &max1027_trigger_ops; - st->trig->dev.parent = &spi->dev; - iio_trigger_set_drvdata(st->trig, indio_dev); - iio_trigger_register(st->trig); + st->trig->ops = &max1027_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + iio_trigger_register(st->trig); + + ret = devm_request_threaded_irq(&spi->dev, spi->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_FALLING, + spi->dev.driver->name, + st->trig); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); + return ret; + } + } - ret = devm_request_threaded_irq(&spi->dev, spi->irq, - iio_trigger_generic_data_rdy_poll, - NULL, - IRQF_TRIGGER_FALLING, - spi->dev.driver->name, st->trig); + /* Internal reset */ + st->reg = MAX1027_RST_REG; + ret = spi_write(st->spi, &st->reg, 1); if (ret < 0) { - dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); + dev_err(&indio_dev->dev, "Failed to reset the ADC\n"); return ret; } @@ -480,5 +522,5 @@ static struct spi_driver max1027_driver = { module_spi_driver(max1027_driver); MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>"); -MODULE_DESCRIPTION("MAX1027/MAX1029/MAX1031 ADC"); +MODULE_DESCRIPTION("MAX1X27/MAX1X29/MAX1X31 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 7b28d045d271..7b27306330a3 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1219,6 +1219,11 @@ static int meson_sar_adc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, + priv->param->regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!irq) return -EINVAL; @@ -1228,11 +1233,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev) if (ret) return ret; - priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, - priv->param->regmap_config); - if (IS_ERR(priv->regmap)) - return PTR_ERR(priv->regmap); - priv->clkin = devm_clk_get(&pdev->dev, "clkin"); if (IS_ERR(priv->clkin)) { dev_err(&pdev->dev, "failed to get clkin\n"); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index a6c046575ec3..66b387f9b36d 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -477,13 +477,6 @@ static void sc27xx_adc_disable(void *_data) SC27XX_MODULE_ADC_EN, 0); } -static void sc27xx_adc_free_hwlock(void *_data) -{ - struct hwspinlock *hwlock = _data; - - hwspin_lock_free(hwlock); -} - static int sc27xx_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -520,19 +513,12 @@ static int sc27xx_adc_probe(struct platform_device *pdev) return ret; } - sc27xx_data->hwlock = hwspin_lock_request_specific(ret); + sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); if (!sc27xx_data->hwlock) { dev_err(dev, "failed to request hwspinlock\n"); return -ENXIO; } - ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock, - sc27xx_data->hwlock); - if (ret) { - dev_err(dev, "failed to add hwspinlock action\n"); - return ret; - } - sc27xx_data->dev = dev; ret = sc27xx_adc_enable(sc27xx_data); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 9b85fefc0a96..93a096a91f8c 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -24,33 +24,6 @@ #include "stm32-adc-core.h" -/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */ -#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) -#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04) - -/* STM32F4_ADC_CSR - bit fields */ -#define STM32F4_EOC3 BIT(17) -#define STM32F4_EOC2 BIT(9) -#define STM32F4_EOC1 BIT(1) - -/* STM32F4_ADC_CCR - bit fields */ -#define STM32F4_ADC_ADCPRE_SHIFT 16 -#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16) - -/* STM32H7 - common registers for all ADC instances */ -#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) -#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) - -/* STM32H7_ADC_CSR - bit fields */ -#define STM32H7_EOC_SLV BIT(18) -#define STM32H7_EOC_MST BIT(2) - -/* STM32H7_ADC_CCR - bit fields */ -#define STM32H7_PRESC_SHIFT 18 -#define STM32H7_PRESC_MASK GENMASK(21, 18) -#define STM32H7_CKMODE_SHIFT 16 -#define STM32H7_CKMODE_MASK GENMASK(17, 16) - #define STM32_ADC_CORE_SLEEP_DELAY_MS 2000 /* SYSCFG registers */ @@ -71,6 +44,8 @@ * @eoc1: adc1 end of conversion flag in @csr * @eoc2: adc2 end of conversion flag in @csr * @eoc3: adc3 end of conversion flag in @csr + * @ier: interrupt enable register offset for each adc + * @eocie_msk: end of conversion interrupt enable mask in @ier */ struct stm32_adc_common_regs { u32 csr; @@ -78,6 +53,8 @@ struct stm32_adc_common_regs { u32 eoc1_msk; u32 eoc2_msk; u32 eoc3_msk; + u32 ier; + u32 eocie_msk; }; struct stm32_adc_priv; @@ -303,6 +280,8 @@ static const struct stm32_adc_common_regs stm32f4_adc_common_regs = { .eoc1_msk = STM32F4_EOC1, .eoc2_msk = STM32F4_EOC2, .eoc3_msk = STM32F4_EOC3, + .ier = STM32F4_ADC_CR1, + .eocie_msk = STM32F4_EOCIE, }; /* STM32H7 common registers definitions */ @@ -311,8 +290,24 @@ static const struct stm32_adc_common_regs stm32h7_adc_common_regs = { .ccr = STM32H7_ADC_CCR, .eoc1_msk = STM32H7_EOC_MST, .eoc2_msk = STM32H7_EOC_SLV, + .ier = STM32H7_ADC_IER, + .eocie_msk = STM32H7_EOCIE, +}; + +static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = { + 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2, }; +static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv, + unsigned int adc) +{ + u32 ier, offset = stm32_adc_offset[adc]; + + ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier); + + return ier & priv->cfg->regs->eocie_msk; +} + /* ADC common interrupt for all instances */ static void stm32_adc_irq_handler(struct irq_desc *desc) { @@ -323,13 +318,28 @@ static void stm32_adc_irq_handler(struct irq_desc *desc) chained_irq_enter(chip, desc); status = readl_relaxed(priv->common.base + priv->cfg->regs->csr); - if (status & priv->cfg->regs->eoc1_msk) + /* + * End of conversion may be handled by using IRQ or DMA. There may be a + * race here when two conversions complete at the same time on several + * ADCs. EOC may be read 'set' for several ADCs, with: + * - an ADC configured to use DMA (EOC triggers the DMA request, and + * is then automatically cleared by DR read in hardware) + * - an ADC configured to use IRQs (EOCIE bit is set. The handler must + * be called in this case) + * So both EOC status bit in CSR and EOCIE control bit must be checked + * before invoking the interrupt handler (e.g. call ISR only for + * IRQ-enabled ADCs). + */ + if (status & priv->cfg->regs->eoc1_msk && + stm32_adc_eoc_enabled(priv, 0)) generic_handle_irq(irq_find_mapping(priv->domain, 0)); - if (status & priv->cfg->regs->eoc2_msk) + if (status & priv->cfg->regs->eoc2_msk && + stm32_adc_eoc_enabled(priv, 1)) generic_handle_irq(irq_find_mapping(priv->domain, 1)); - if (status & priv->cfg->regs->eoc3_msk) + if (status & priv->cfg->regs->eoc3_msk && + stm32_adc_eoc_enabled(priv, 2)) generic_handle_irq(irq_find_mapping(priv->domain, 2)); chained_irq_exit(chip, desc); diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 8af507b3f32d..2579d514c2a3 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -25,8 +25,145 @@ * -------------------------------------------------------- */ #define STM32_ADC_MAX_ADCS 3 +#define STM32_ADC_OFFSET 0x100 #define STM32_ADCX_COMN_OFFSET 0x300 +/* STM32F4 - Registers for each ADC instance */ +#define STM32F4_ADC_SR 0x00 +#define STM32F4_ADC_CR1 0x04 +#define STM32F4_ADC_CR2 0x08 +#define STM32F4_ADC_SMPR1 0x0C +#define STM32F4_ADC_SMPR2 0x10 +#define STM32F4_ADC_HTR 0x24 +#define STM32F4_ADC_LTR 0x28 +#define STM32F4_ADC_SQR1 0x2C +#define STM32F4_ADC_SQR2 0x30 +#define STM32F4_ADC_SQR3 0x34 +#define STM32F4_ADC_JSQR 0x38 +#define STM32F4_ADC_JDR1 0x3C +#define STM32F4_ADC_JDR2 0x40 +#define STM32F4_ADC_JDR3 0x44 +#define STM32F4_ADC_JDR4 0x48 +#define STM32F4_ADC_DR 0x4C + +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */ +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04) + +/* STM32F4_ADC_SR - bit fields */ +#define STM32F4_STRT BIT(4) +#define STM32F4_EOC BIT(1) + +/* STM32F4_ADC_CR1 - bit fields */ +#define STM32F4_RES_SHIFT 24 +#define STM32F4_RES_MASK GENMASK(25, 24) +#define STM32F4_SCAN BIT(8) +#define STM32F4_EOCIE BIT(5) + +/* STM32F4_ADC_CR2 - bit fields */ +#define STM32F4_SWSTART BIT(30) +#define STM32F4_EXTEN_SHIFT 28 +#define STM32F4_EXTEN_MASK GENMASK(29, 28) +#define STM32F4_EXTSEL_SHIFT 24 +#define STM32F4_EXTSEL_MASK GENMASK(27, 24) +#define STM32F4_EOCS BIT(10) +#define STM32F4_DDS BIT(9) +#define STM32F4_DMA BIT(8) +#define STM32F4_ADON BIT(0) + +/* STM32F4_ADC_CSR - bit fields */ +#define STM32F4_EOC3 BIT(17) +#define STM32F4_EOC2 BIT(9) +#define STM32F4_EOC1 BIT(1) + +/* STM32F4_ADC_CCR - bit fields */ +#define STM32F4_ADC_ADCPRE_SHIFT 16 +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16) + +/* STM32H7 - Registers for each ADC instance */ +#define STM32H7_ADC_ISR 0x00 +#define STM32H7_ADC_IER 0x04 +#define STM32H7_ADC_CR 0x08 +#define STM32H7_ADC_CFGR 0x0C +#define STM32H7_ADC_SMPR1 0x14 +#define STM32H7_ADC_SMPR2 0x18 +#define STM32H7_ADC_PCSEL 0x1C +#define STM32H7_ADC_SQR1 0x30 +#define STM32H7_ADC_SQR2 0x34 +#define STM32H7_ADC_SQR3 0x38 +#define STM32H7_ADC_SQR4 0x3C +#define STM32H7_ADC_DR 0x40 +#define STM32H7_ADC_DIFSEL 0xC0 +#define STM32H7_ADC_CALFACT 0xC4 +#define STM32H7_ADC_CALFACT2 0xC8 + +/* STM32H7 - common registers for all ADC instances */ +#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) +#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) + +/* STM32H7_ADC_ISR - bit fields */ +#define STM32MP1_VREGREADY BIT(12) +#define STM32H7_EOC BIT(2) +#define STM32H7_ADRDY BIT(0) + +/* STM32H7_ADC_IER - bit fields */ +#define STM32H7_EOCIE STM32H7_EOC + +/* STM32H7_ADC_CR - bit fields */ +#define STM32H7_ADCAL BIT(31) +#define STM32H7_ADCALDIF BIT(30) +#define STM32H7_DEEPPWD BIT(29) +#define STM32H7_ADVREGEN BIT(28) +#define STM32H7_LINCALRDYW6 BIT(27) +#define STM32H7_LINCALRDYW5 BIT(26) +#define STM32H7_LINCALRDYW4 BIT(25) +#define STM32H7_LINCALRDYW3 BIT(24) +#define STM32H7_LINCALRDYW2 BIT(23) +#define STM32H7_LINCALRDYW1 BIT(22) +#define STM32H7_ADCALLIN BIT(16) +#define STM32H7_BOOST BIT(8) +#define STM32H7_ADSTP BIT(4) +#define STM32H7_ADSTART BIT(2) +#define STM32H7_ADDIS BIT(1) +#define STM32H7_ADEN BIT(0) + +/* STM32H7_ADC_CFGR bit fields */ +#define STM32H7_EXTEN_SHIFT 10 +#define STM32H7_EXTEN_MASK GENMASK(11, 10) +#define STM32H7_EXTSEL_SHIFT 5 +#define STM32H7_EXTSEL_MASK GENMASK(9, 5) +#define STM32H7_RES_SHIFT 2 +#define STM32H7_RES_MASK GENMASK(4, 2) +#define STM32H7_DMNGT_SHIFT 0 +#define STM32H7_DMNGT_MASK GENMASK(1, 0) + +enum stm32h7_adc_dmngt { + STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */ + STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */ + STM32H7_DMNGT_DFSDM, /* DFSDM mode */ + STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */ +}; + +/* STM32H7_ADC_CALFACT - bit fields */ +#define STM32H7_CALFACT_D_SHIFT 16 +#define STM32H7_CALFACT_D_MASK GENMASK(26, 16) +#define STM32H7_CALFACT_S_SHIFT 0 +#define STM32H7_CALFACT_S_MASK GENMASK(10, 0) + +/* STM32H7_ADC_CALFACT2 - bit fields */ +#define STM32H7_LINCALFACT_SHIFT 0 +#define STM32H7_LINCALFACT_MASK GENMASK(29, 0) + +/* STM32H7_ADC_CSR - bit fields */ +#define STM32H7_EOC_SLV BIT(18) +#define STM32H7_EOC_MST BIT(2) + +/* STM32H7_ADC_CCR - bit fields */ +#define STM32H7_PRESC_SHIFT 18 +#define STM32H7_PRESC_MASK GENMASK(21, 18) +#define STM32H7_CKMODE_SHIFT 16 +#define STM32H7_CKMODE_MASK GENMASK(17, 16) + /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 6a7dd08b1e0b..663f8a5012d6 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -28,115 +28,6 @@ #include "stm32-adc-core.h" -/* STM32F4 - Registers for each ADC instance */ -#define STM32F4_ADC_SR 0x00 -#define STM32F4_ADC_CR1 0x04 -#define STM32F4_ADC_CR2 0x08 -#define STM32F4_ADC_SMPR1 0x0C -#define STM32F4_ADC_SMPR2 0x10 -#define STM32F4_ADC_HTR 0x24 -#define STM32F4_ADC_LTR 0x28 -#define STM32F4_ADC_SQR1 0x2C -#define STM32F4_ADC_SQR2 0x30 -#define STM32F4_ADC_SQR3 0x34 -#define STM32F4_ADC_JSQR 0x38 -#define STM32F4_ADC_JDR1 0x3C -#define STM32F4_ADC_JDR2 0x40 -#define STM32F4_ADC_JDR3 0x44 -#define STM32F4_ADC_JDR4 0x48 -#define STM32F4_ADC_DR 0x4C - -/* STM32F4_ADC_SR - bit fields */ -#define STM32F4_STRT BIT(4) -#define STM32F4_EOC BIT(1) - -/* STM32F4_ADC_CR1 - bit fields */ -#define STM32F4_RES_SHIFT 24 -#define STM32F4_RES_MASK GENMASK(25, 24) -#define STM32F4_SCAN BIT(8) -#define STM32F4_EOCIE BIT(5) - -/* STM32F4_ADC_CR2 - bit fields */ -#define STM32F4_SWSTART BIT(30) -#define STM32F4_EXTEN_SHIFT 28 -#define STM32F4_EXTEN_MASK GENMASK(29, 28) -#define STM32F4_EXTSEL_SHIFT 24 -#define STM32F4_EXTSEL_MASK GENMASK(27, 24) -#define STM32F4_EOCS BIT(10) -#define STM32F4_DDS BIT(9) -#define STM32F4_DMA BIT(8) -#define STM32F4_ADON BIT(0) - -/* STM32H7 - Registers for each ADC instance */ -#define STM32H7_ADC_ISR 0x00 -#define STM32H7_ADC_IER 0x04 -#define STM32H7_ADC_CR 0x08 -#define STM32H7_ADC_CFGR 0x0C -#define STM32H7_ADC_SMPR1 0x14 -#define STM32H7_ADC_SMPR2 0x18 -#define STM32H7_ADC_PCSEL 0x1C -#define STM32H7_ADC_SQR1 0x30 -#define STM32H7_ADC_SQR2 0x34 -#define STM32H7_ADC_SQR3 0x38 -#define STM32H7_ADC_SQR4 0x3C -#define STM32H7_ADC_DR 0x40 -#define STM32H7_ADC_DIFSEL 0xC0 -#define STM32H7_ADC_CALFACT 0xC4 -#define STM32H7_ADC_CALFACT2 0xC8 - -/* STM32H7_ADC_ISR - bit fields */ -#define STM32MP1_VREGREADY BIT(12) -#define STM32H7_EOC BIT(2) -#define STM32H7_ADRDY BIT(0) - -/* STM32H7_ADC_IER - bit fields */ -#define STM32H7_EOCIE STM32H7_EOC - -/* STM32H7_ADC_CR - bit fields */ -#define STM32H7_ADCAL BIT(31) -#define STM32H7_ADCALDIF BIT(30) -#define STM32H7_DEEPPWD BIT(29) -#define STM32H7_ADVREGEN BIT(28) -#define STM32H7_LINCALRDYW6 BIT(27) -#define STM32H7_LINCALRDYW5 BIT(26) -#define STM32H7_LINCALRDYW4 BIT(25) -#define STM32H7_LINCALRDYW3 BIT(24) -#define STM32H7_LINCALRDYW2 BIT(23) -#define STM32H7_LINCALRDYW1 BIT(22) -#define STM32H7_ADCALLIN BIT(16) -#define STM32H7_BOOST BIT(8) -#define STM32H7_ADSTP BIT(4) -#define STM32H7_ADSTART BIT(2) -#define STM32H7_ADDIS BIT(1) -#define STM32H7_ADEN BIT(0) - -/* STM32H7_ADC_CFGR bit fields */ -#define STM32H7_EXTEN_SHIFT 10 -#define STM32H7_EXTEN_MASK GENMASK(11, 10) -#define STM32H7_EXTSEL_SHIFT 5 -#define STM32H7_EXTSEL_MASK GENMASK(9, 5) -#define STM32H7_RES_SHIFT 2 -#define STM32H7_RES_MASK GENMASK(4, 2) -#define STM32H7_DMNGT_SHIFT 0 -#define STM32H7_DMNGT_MASK GENMASK(1, 0) - -enum stm32h7_adc_dmngt { - STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */ - STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */ - STM32H7_DMNGT_DFSDM, /* DFSDM mode */ - STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */ -}; - -/* STM32H7_ADC_CALFACT - bit fields */ -#define STM32H7_CALFACT_D_SHIFT 16 -#define STM32H7_CALFACT_D_MASK GENMASK(26, 16) -#define STM32H7_CALFACT_S_SHIFT 0 -#define STM32H7_CALFACT_S_MASK GENMASK(10, 0) - -/* STM32H7_ADC_CALFACT2 - bit fields */ -#define STM32H7_LINCALFACT_SHIFT 0 -#define STM32H7_LINCALFACT_MASK GENMASK(29, 0) - /* Number of linear calibration shadow registers / LINCALRDYW control bits */ #define STM32H7_LINCALFACT_NUM 6 diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 3a20cb5d9bff..6c175eb1c7a7 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -323,16 +323,16 @@ static int atlas_buffer_predisable(struct iio_dev *indio_dev) struct atlas_data *data = iio_priv(indio_dev); int ret; - ret = iio_triggered_buffer_predisable(indio_dev); + ret = atlas_set_interrupt(data, false); if (ret) return ret; - ret = atlas_set_interrupt(data, false); + pm_runtime_mark_last_busy(&data->client->dev); + ret = pm_runtime_put_autosuspend(&data->client->dev); if (ret) return ret; - pm_runtime_mark_last_busy(&data->client->dev); - return pm_runtime_put_autosuspend(&data->client->dev); + return iio_triggered_buffer_predisable(indio_dev); } static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 8de9f40226e6..14bbac6bee98 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -41,6 +41,7 @@ struct ad7303_state { struct regulator *vdd_reg; struct regulator *vref_reg; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -79,7 +80,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if (pwr_down) st->config |= AD7303_CFG_POWER_DOWN(chan->channel); @@ -90,7 +91,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, * mode, so just write one of the DAC channels again */ ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return len; } @@ -116,7 +117,9 @@ static int ad7303_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); *val = st->dac_cache[chan->channel]; + mutex_unlock(&st->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: vref_uv = ad7303_get_vref(st, chan); @@ -144,11 +147,11 @@ static int ad7303_write_raw(struct iio_dev *indio_dev, if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = ad7303_write(st, chan->address, val); if (ret == 0) st->dac_cache[chan->channel] = val; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); break; default: ret = -EINVAL; @@ -211,6 +214,8 @@ static int ad7303_probe(struct spi_device *spi) st->spi = spi; + mutex_init(&st->lock); + st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd"); if (IS_ERR(st->vdd_reg)) return PTR_ERR(st->vdd_reg); diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index d0fb3124de07..9e6b4cd0a5cc 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> @@ -50,6 +51,41 @@ static const struct regmap_config stm32_dac_regmap_cfg = { .max_register = 0x3fc, }; +static int stm32_dac_core_hw_start(struct device *dev) +{ + struct stm32_dac_common *common = dev_get_drvdata(dev); + struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + int ret; + + ret = regulator_enable(priv->vref); + if (ret < 0) { + dev_err(dev, "vref enable failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->pclk); + if (ret < 0) { + dev_err(dev, "pclk enable failed: %d\n", ret); + goto err_regulator_disable; + } + + return 0; + +err_regulator_disable: + regulator_disable(priv->vref); + + return ret; +} + +static void stm32_dac_core_hw_stop(struct device *dev) +{ + struct stm32_dac_common *common = dev_get_drvdata(dev); + struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + + clk_disable_unprepare(priv->pclk); + regulator_disable(priv->vref); +} + static int stm32_dac_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -66,6 +102,8 @@ static int stm32_dac_probe(struct platform_device *pdev) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + platform_set_drvdata(pdev, &priv->common); + cfg = (const struct stm32_dac_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; @@ -74,11 +112,19 @@ static int stm32_dac_probe(struct platform_device *pdev) if (IS_ERR(mmio)) return PTR_ERR(mmio); - regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg); + regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio, + &stm32_dac_regmap_cfg); if (IS_ERR(regmap)) return PTR_ERR(regmap); priv->common.regmap = regmap; + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + dev_err(dev, "pclk get failed\n"); + return ret; + } + priv->vref = devm_regulator_get(dev, "vref"); if (IS_ERR(priv->vref)) { ret = PTR_ERR(priv->vref); @@ -86,33 +132,22 @@ static int stm32_dac_probe(struct platform_device *pdev) return ret; } - ret = regulator_enable(priv->vref); - if (ret < 0) { - dev_err(dev, "vref enable failed\n"); - return ret; - } + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = stm32_dac_core_hw_start(dev); + if (ret) + goto err_pm_stop; ret = regulator_get_voltage(priv->vref); if (ret < 0) { dev_err(dev, "vref get voltage failed, %d\n", ret); - goto err_vref; + goto err_hw_stop; } priv->common.vref_mv = ret / 1000; dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv); - priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - dev_err(dev, "pclk get failed\n"); - goto err_vref; - } - - ret = clk_prepare_enable(priv->pclk); - if (ret < 0) { - dev_err(dev, "pclk enable failed\n"); - goto err_vref; - } - priv->rst = devm_reset_control_get_exclusive(dev, NULL); if (!IS_ERR(priv->rst)) { reset_control_assert(priv->rst); @@ -128,39 +163,79 @@ static int stm32_dac_probe(struct platform_device *pdev) priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0); if (ret) - goto err_pclk; + goto err_hw_stop; } - platform_set_drvdata(pdev, &priv->common); ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev); if (ret < 0) { dev_err(dev, "failed to populate DT children\n"); - goto err_pclk; + goto err_hw_stop; } + pm_runtime_put(dev); + return 0; -err_pclk: - clk_disable_unprepare(priv->pclk); -err_vref: - regulator_disable(priv->vref); +err_hw_stop: + stm32_dac_core_hw_stop(dev); +err_pm_stop: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); return ret; } static int stm32_dac_remove(struct platform_device *pdev) { - struct stm32_dac_common *common = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + of_platform_depopulate(&pdev->dev); + stm32_dac_core_hw_stop(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + return 0; +} + +static int __maybe_unused stm32_dac_core_resume(struct device *dev) +{ + struct stm32_dac_common *common = dev_get_drvdata(dev); struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + int ret; - of_platform_depopulate(&pdev->dev); - clk_disable_unprepare(priv->pclk); - regulator_disable(priv->vref); + if (priv->common.hfsel) { + /* restore hfsel (maybe lost under low power state) */ + ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR, + STM32H7_DAC_CR_HFSEL, + STM32H7_DAC_CR_HFSEL); + if (ret) + return ret; + } + + return pm_runtime_force_resume(dev); +} + +static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev) +{ + stm32_dac_core_hw_stop(dev); return 0; } +static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev) +{ + return stm32_dac_core_hw_start(dev); +} + +static const struct dev_pm_ops stm32_dac_core_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume) + SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend, + stm32_dac_core_runtime_resume, + NULL) +}; + static const struct stm32_dac_cfg stm32h7_dac_cfg = { .has_hfsel = true, }; @@ -182,6 +257,7 @@ static struct platform_driver stm32_dac_driver = { .driver = { .name = "stm32-dac-core", .of_match_table = stm32_dac_of_match, + .pm = &stm32_dac_core_pm_ops, }, }; module_platform_driver(stm32_dac_driver); diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index cce26a3a6627..f22c1d9129b2 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include "stm32-dac-core.h" @@ -20,6 +21,8 @@ #define STM32_DAC_CHANNEL_2 2 #define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1) +#define STM32_DAC_AUTO_SUSPEND_DELAY_MS 2000 + /** * struct stm32_dac - private data of DAC driver * @common: reference to DAC common data @@ -49,15 +52,34 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, bool enable) { struct stm32_dac *dac = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2; u32 en = enable ? msk : 0; int ret; + /* already enabled / disabled ? */ + mutex_lock(&indio_dev->mlock); + ret = stm32_dac_is_enabled(indio_dev, ch); + if (ret < 0 || enable == !!ret) { + mutex_unlock(&indio_dev->mlock); + return ret < 0 ? ret : 0; + } + + if (enable) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + mutex_unlock(&indio_dev->mlock); + return ret; + } + } + ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en); + mutex_unlock(&indio_dev->mlock); if (ret < 0) { dev_err(&indio_dev->dev, "%s failed\n", en ? "Enable" : "Disable"); - return ret; + goto err_put_pm; } /* @@ -68,7 +90,20 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, if (en && dac->common->hfsel) udelay(1); + if (!enable) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + return 0; + +err_put_pm: + if (enable) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + + return ret; } static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val) @@ -272,6 +307,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev) static int stm32_dac_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct iio_dev *indio_dev; struct stm32_dac *dac; int ret; @@ -296,9 +332,61 @@ static int stm32_dac_probe(struct platform_device *pdev) if (ret < 0) return ret; - return devm_iio_device_register(&pdev->dev, indio_dev); + /* Get stm32-dac-core PM online */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, STM32_DAC_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_pm_put; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; + +err_pm_put: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + return ret; } +static int stm32_dac_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + return 0; +} + +static int __maybe_unused stm32_dac_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + int channel = indio_dev->channels[0].channel; + int ret; + + /* Ensure DAC is disabled before suspend */ + ret = stm32_dac_is_enabled(indio_dev, channel); + if (ret) + return ret < 0 ? ret : -EBUSY; + + return pm_runtime_force_suspend(dev); +} + +static const struct dev_pm_ops stm32_dac_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id stm32_dac_of_match[] = { { .compatible = "st,stm32-dac", }, {}, @@ -307,9 +395,11 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); static struct platform_driver stm32_dac_driver = { .probe = stm32_dac_probe, + .remove = stm32_dac_remove, .driver = { .name = "stm32-dac", .of_match_table = stm32_dac_of_match, + .pm = &stm32_dac_pm_ops, }, }; module_platform_driver(stm32_dac_driver); diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index 236220d6de02..1b84b8e112fe 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -38,10 +38,12 @@ struct adis16080_chip_info { * @us: actual spi_device to write data * @info: chip specific parameters * @buf: transmit or receive buffer + * @lock lock to protect buffer during reads **/ struct adis16080_state { struct spi_device *us; const struct adis16080_chip_info *info; + struct mutex lock; __be16 buf ____cacheline_aligned; }; @@ -82,9 +84,9 @@ static int adis16080_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = adis16080_read_sample(indio_dev, chan->address, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret ? ret : IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (chan->type) { @@ -196,6 +198,8 @@ static int adis16080_probe(struct spi_device *spi) /* this is only used for removal purposes */ spi_set_drvdata(spi, indio_dev); + mutex_init(&st->lock); + /* Allocate the comms buffers */ st->us = spi; st->info = &adis16080_chip_info[id->driver_data]; diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c index de3f66f89496..79e63c8a2ea8 100644 --- a/drivers/iio/gyro/adis16130.c +++ b/drivers/iio/gyro/adis16130.c @@ -76,9 +76,7 @@ static int adis16130_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: /* Take the iio_dev status lock */ - mutex_lock(&indio_dev->mlock); ret = adis16130_spi_read(indio_dev, chan->address, &temp); - mutex_unlock(&indio_dev->mlock); if (ret) return ret; *val = temp; diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index 998fb8d66fe3..981ae2291505 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -154,7 +154,7 @@ static int itg3200_write_raw(struct iio_dev *indio_dev, t); mutex_unlock(&indio_dev->mlock); - return ret; + return ret; default: return -EINVAL; diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 9ac8356d9a95..4998a89d083d 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -35,8 +35,11 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, return -ENOMEM; adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL); - if (!adis->buffer) + if (!adis->buffer) { + kfree(adis->xfer); + adis->xfer = NULL; return -ENOMEM; + } tx = adis->buffer + burst_length; tx[0] = ADIS_READ_REG(adis->burst->reg_cmd); @@ -78,8 +81,11 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, return -ENOMEM; adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL); - if (!adis->buffer) + if (!adis->buffer) { + kfree(adis->xfer); + adis->xfer = NULL; return -ENOMEM; + } rx = adis->buffer; tx = rx + scan_count; diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile index 70ffe0d13d8c..c103441a906b 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu/inv_mpu6050/Makefile @@ -4,10 +4,11 @@ # obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o -inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o +inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \ + inv_mpu_aux.o inv_mpu_magn.o obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o -inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o +inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.o obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o -inv-mpu6050-spi-objs := inv_mpu_spi.o +inv-mpu6050-spi-y := inv_mpu_spi.o diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c new file mode 100644 index 000000000000..7327e5723f96 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/delay.h> + +#include "inv_mpu_aux.h" +#include "inv_mpu_iio.h" + +/* + * i2c master auxiliary bus transfer function. + * Requires the i2c operations to be correctly setup before. + */ +static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st) +{ + /* use 50hz frequency for xfer */ + const unsigned int freq = 50; + const unsigned int period_ms = 1000 / freq; + uint8_t d; + unsigned int user_ctrl; + int ret; + + /* set sample rate */ + d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq); + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* start i2c master */ + user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + goto error_restore_rate; + + /* wait for xfer: 1 period + half-period margin */ + msleep(period_ms + period_ms / 2); + + /* stop i2c master */ + user_ctrl = st->chip_config.user_ctrl; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + goto error_stop_i2c; + + /* restore sample rate */ + d = st->chip_config.divider; + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + goto error_restore_rate; + + return 0; + +error_stop_i2c: + regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); +error_restore_rate: + regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider); + return ret; +} + +/** + * inv_mpu_aux_init() - init i2c auxiliary bus + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_init(const struct inv_mpu6050_state *st) +{ + unsigned int val; + int ret; + + /* configure i2c master */ + val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ | + INV_MPU6050_BIT_WAIT_FOR_ES; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val); + if (ret) + return ret; + + /* configure i2c master delay */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0); + if (ret) + return ret; + + val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN | + INV_MPU6050_BIT_I2C_SLV1_DLY_EN | + INV_MPU6050_BIT_I2C_SLV2_DLY_EN | + INV_MPU6050_BIT_I2C_SLV3_DLY_EN | + INV_MPU6050_BIT_DELAY_ES_SHADOW; + return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val); +} + +/** + * inv_mpu_aux_read() - read register function for i2c auxiliary bus + * @st: driver internal state. + * @addr: chip i2c Address + * @reg: chip register address + * @val: buffer for storing read bytes + * @size: number of bytes to read + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t *val, size_t size) +{ + unsigned int status; + int ret; + + if (size > 0x0F) + return -EINVAL; + + /* setup i2c SLV0 control: i2c addr, register, enable + size */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), + INV_MPU6050_BIT_I2C_SLV_RNW | addr); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | size); + if (ret) + return ret; + + /* do i2c xfer */ + ret = inv_mpu_i2c_master_xfer(st); + if (ret) + goto error_disable_i2c; + + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + + /* read data in registers */ + return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, + val, size); + +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + return ret; +} + +/** + * inv_mpu_aux_write() - write register function for i2c auxiliary bus + * @st: driver internal state. + * @addr: chip i2c Address + * @reg: chip register address + * @val: 1 byte value to write + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t val) +{ + unsigned int status; + int ret; + + /* setup i2c SLV0 control: i2c addr, register, value, enable + size */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), addr); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | 1); + if (ret) + return ret; + + /* do i2c xfer */ + ret = inv_mpu_i2c_master_xfer(st); + if (ret) + goto error_disable_i2c; + + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + + return 0; + +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + return ret; +} diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h new file mode 100644 index 000000000000..b66997545762 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#ifndef INV_MPU_AUX_H_ +#define INV_MPU_AUX_H_ + +#include "inv_mpu_iio.h" + +int inv_mpu_aux_init(const struct inv_mpu6050_state *st); + +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t *val, size_t size); + +int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t val); + +#endif /* INV_MPU_AUX_H_ */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index b17f060b52fc..354030e9bed5 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include "inv_mpu_iio.h" +#include "inv_mpu_magn.h" /* * this is the gyro scale translated from dynamic range plus/minus @@ -103,6 +104,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = { .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE), .gyro_fifo_enable = false, .accl_fifo_enable = false, + .magn_fifo_enable = false, .accl_fs = INV_MPU6050_FS_02G, .user_ctrl = 0, }; @@ -332,6 +334,11 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) */ st->chip_period = NSEC_PER_MSEC; + /* magn chip init, noop if not present in the chip */ + result = inv_mpu_magn_probe(st); + if (result) + goto error_power_off; + return inv_mpu6050_set_power_itg(st, false); error_power_off: @@ -411,6 +418,9 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, ret = inv_mpu6050_sensor_show(st, st->reg->temperature, IIO_MOD_X, val); break; + case IIO_MAGN: + ret = inv_mpu_magn_read(st, chan->channel2, val); + break; default: ret = -EINVAL; break; @@ -469,6 +479,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, *val2 = INV_MPU6050_TEMP_SCALE; return IIO_VAL_INT_PLUS_MICRO; + case IIO_MAGN: + return inv_mpu_magn_get_scale(st, chan, val, val2); default: return -EINVAL; } @@ -710,6 +722,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, if (result) goto fifo_rate_fail_power_off; + /* update rate for magn, noop if not present in chip */ + result = inv_mpu_magn_set_rate(st, fifo_rate); + if (result) + goto fifo_rate_fail_power_off; + fifo_rate_fail_power_off: result |= inv_mpu6050_set_power_itg(st, false); fifo_rate_fail_unlock: @@ -795,8 +812,14 @@ inv_get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct inv_mpu6050_state *data = iio_priv(indio_dev); + const struct iio_mount_matrix *matrix; - return &data->orientation; + if (chan->type == IIO_MAGN) + matrix = &data->magn_orient; + else + matrix = &data->orientation; + + return matrix; } static const struct iio_chan_spec_ext_info inv_ext_info[] = { @@ -864,6 +887,98 @@ static const unsigned long inv_mpu_scan_masks[] = { 0, }; +#define INV_MPU9X50_MAGN_CHAN(_chan2, _bits, _index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _bits, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = inv_ext_info, \ + } + +static const struct iio_chan_spec inv_mpu9250_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP), + /* + * Note that temperature should only be via polled reading only, + * not the final scan elements output. + */ + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_OFFSET) + | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = -1, + }, + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), + + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), + + /* Magnetometer resolution is 16 bits */ + INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 16, INV_MPU9X50_SCAN_MAGN_X), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 16, INV_MPU9X50_SCAN_MAGN_Y), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 16, INV_MPU9X50_SCAN_MAGN_Z), +}; + +static const unsigned long inv_mpu9x50_scan_masks[] = { + /* 3-axis accel */ + BIT(INV_MPU6050_SCAN_ACCL_X) + | BIT(INV_MPU6050_SCAN_ACCL_Y) + | BIT(INV_MPU6050_SCAN_ACCL_Z), + /* 3-axis gyro */ + BIT(INV_MPU6050_SCAN_GYRO_X) + | BIT(INV_MPU6050_SCAN_GYRO_Y) + | BIT(INV_MPU6050_SCAN_GYRO_Z), + /* 3-axis magn */ + BIT(INV_MPU9X50_SCAN_MAGN_X) + | BIT(INV_MPU9X50_SCAN_MAGN_Y) + | BIT(INV_MPU9X50_SCAN_MAGN_Z), + /* 6-axis accel + gyro */ + BIT(INV_MPU6050_SCAN_ACCL_X) + | BIT(INV_MPU6050_SCAN_ACCL_Y) + | BIT(INV_MPU6050_SCAN_ACCL_Z) + | BIT(INV_MPU6050_SCAN_GYRO_X) + | BIT(INV_MPU6050_SCAN_GYRO_Y) + | BIT(INV_MPU6050_SCAN_GYRO_Z), + /* 6-axis accel + magn */ + BIT(INV_MPU6050_SCAN_ACCL_X) + | BIT(INV_MPU6050_SCAN_ACCL_Y) + | BIT(INV_MPU6050_SCAN_ACCL_Z) + | BIT(INV_MPU9X50_SCAN_MAGN_X) + | BIT(INV_MPU9X50_SCAN_MAGN_Y) + | BIT(INV_MPU9X50_SCAN_MAGN_Z), + /* 6-axis gyro + magn */ + BIT(INV_MPU6050_SCAN_GYRO_X) + | BIT(INV_MPU6050_SCAN_GYRO_Y) + | BIT(INV_MPU6050_SCAN_GYRO_Z) + | BIT(INV_MPU9X50_SCAN_MAGN_X) + | BIT(INV_MPU9X50_SCAN_MAGN_Y) + | BIT(INV_MPU9X50_SCAN_MAGN_Z), + /* 9-axis accel + gyro + magn */ + BIT(INV_MPU6050_SCAN_ACCL_X) + | BIT(INV_MPU6050_SCAN_ACCL_Y) + | BIT(INV_MPU6050_SCAN_ACCL_Z) + | BIT(INV_MPU6050_SCAN_GYRO_X) + | BIT(INV_MPU6050_SCAN_GYRO_Y) + | BIT(INV_MPU6050_SCAN_GYRO_Z) + | BIT(INV_MPU9X50_SCAN_MAGN_X) + | BIT(INV_MPU9X50_SCAN_MAGN_Y) + | BIT(INV_MPU9X50_SCAN_MAGN_Z), + 0, +}; + static const struct iio_chan_spec inv_icm20602_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP), { @@ -1145,6 +1260,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return result; } + /* fill magnetometer orientation */ + result = inv_mpu_magn_set_orient(st); + if (result) + return result; + /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st); if (result) @@ -1156,9 +1276,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return result; } - if (inv_mpu_bus_setup) - inv_mpu_bus_setup(indio_dev); - dev_set_drvdata(dev, indio_dev); indio_dev->dev.parent = dev; /* name will be NULL when enumerated via ACPI */ @@ -1167,14 +1284,37 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, else indio_dev->name = dev_name(dev); - if (chip_type == INV_ICM20602) { + /* requires parent device set in indio_dev */ + if (inv_mpu_bus_setup) + inv_mpu_bus_setup(indio_dev); + + switch (chip_type) { + case INV_MPU9250: + case INV_MPU9255: + /* + * Use magnetometer inside the chip only if there is no i2c + * auxiliary device in use. + */ + if (!st->magn_disabled) { + indio_dev->channels = inv_mpu9250_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels); + indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; + } else { + indio_dev->channels = inv_mpu_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; + } + break; + case INV_ICM20602: indio_dev->channels = inv_icm20602_channels; indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels); indio_dev->available_scan_masks = inv_icm20602_scan_masks; - } else { + break; + default: indio_dev->channels = inv_mpu_channels; indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); indio_dev->available_scan_masks = inv_mpu_scan_masks; + break; } indio_dev->info = &mpu_info; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 4b8b5a87398c..389cc8505e0e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -68,6 +68,56 @@ static const char *inv_mpu_match_acpi_device(struct device *dev, return dev_name(dev); } +static bool inv_mpu_i2c_aux_bus(struct device *dev) +{ + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); + + switch (st->chip_type) { + case INV_ICM20608: + case INV_ICM20602: + /* no i2c auxiliary bus on the chip */ + return false; + case INV_MPU9250: + case INV_MPU9255: + if (st->magn_disabled) + return true; + else + return false; + default: + return true; + } +} + +/* + * MPU9xxx magnetometer support requires to disable i2c auxiliary bus support. + * To ensure backward compatibility with existing setups, do not disable + * i2c auxiliary bus if it used. + * Check for i2c-gate node in devicetree and set magnetometer disabled. + * Only MPU6500 is supported by ACPI, no need to check. + */ +static int inv_mpu_magn_disable(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct device_node *mux_node; + + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + mux_node = of_get_child_by_name(dev->of_node, "i2c-gate"); + if (mux_node != NULL) { + st->magn_disabled = true; + dev_warn(dev, "disable internal use of magnetometer\n"); + } + of_node_put(mux_node); + break; + default: + break; + } + + return 0; +} + /** * inv_mpu_probe() - probe function. * @client: i2c client. @@ -112,17 +162,12 @@ static int inv_mpu_probe(struct i2c_client *client, } result = inv_mpu_core_probe(regmap, client->irq, name, - NULL, chip_type); + inv_mpu_magn_disable, chip_type); if (result < 0) return result; st = iio_priv(dev_get_drvdata(&client->dev)); - switch (st->chip_type) { - case INV_ICM20608: - case INV_ICM20602: - /* no i2c auxiliary bus on the chip */ - break; - default: + if (inv_mpu_i2c_aux_bus(&client->dev)) { /* declare i2c auxiliary bus */ st->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, @@ -137,7 +182,6 @@ static int inv_mpu_probe(struct i2c_client *client, result = inv_mpu_acpi_create_mux_client(client); if (result) goto out_del_mux; - break; } return 0; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index db1c6904388b..52fcf45050a5 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2012 Invensense, Inc. */ + +#ifndef INV_MPU_IIO_H_ +#define INV_MPU_IIO_H_ + #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/mutex.h> @@ -82,6 +86,7 @@ enum inv_devices { * @accl_fs: accel full scale range. * @accl_fifo_enable: enable accel data output * @gyro_fifo_enable: enable gyro data output + * @magn_fifo_enable: enable magn data output * @divider: chip sample rate divider (sample rate divider - 1) */ struct inv_mpu6050_chip_config { @@ -90,6 +95,7 @@ struct inv_mpu6050_chip_config { unsigned int accl_fs:2; unsigned int accl_fifo_enable:1; unsigned int gyro_fifo_enable:1; + unsigned int magn_fifo_enable:1; u8 divider; u8 user_ctrl; }; @@ -125,6 +131,9 @@ struct inv_mpu6050_hw { * @it_timestamp: timestamp from previous interrupt. * @data_timestamp: timestamp for next data sample. * @vddio_supply voltage regulator for the chip. + * @magn_disabled: magnetometer disabled for backward compatibility reason. + * @magn_raw_to_gauss: coefficient to convert mag raw value to Gauss. + * @magn_orient: magnetometer sensor chip orientation if available. */ struct inv_mpu6050_state { struct mutex lock; @@ -146,6 +155,9 @@ struct inv_mpu6050_state { s64 it_timestamp; s64 data_timestamp; struct regulator *vddio_supply; + bool magn_disabled; + s32 magn_raw_to_gauss[3]; + struct iio_mount_matrix magn_orient; }; /*register and associated bit definition*/ @@ -158,9 +170,41 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_ACCEL_CONFIG 0x1C #define INV_MPU6050_REG_FIFO_EN 0x23 +#define INV_MPU6050_BIT_SLAVE_0 0x01 +#define INV_MPU6050_BIT_SLAVE_1 0x02 +#define INV_MPU6050_BIT_SLAVE_2 0x04 #define INV_MPU6050_BIT_ACCEL_OUT 0x08 #define INV_MPU6050_BITS_GYRO_OUT 0x70 +#define INV_MPU6050_REG_I2C_MST_CTRL 0x24 +#define INV_MPU6050_BITS_I2C_MST_CLK_400KHZ 0x0D +#define INV_MPU6050_BIT_I2C_MST_P_NSR 0x10 +#define INV_MPU6050_BIT_SLV3_FIFO_EN 0x20 +#define INV_MPU6050_BIT_WAIT_FOR_ES 0x40 +#define INV_MPU6050_BIT_MULT_MST_EN 0x80 + +/* control I2C slaves from 0 to 3 */ +#define INV_MPU6050_REG_I2C_SLV_ADDR(_x) (0x25 + 3 * (_x)) +#define INV_MPU6050_BIT_I2C_SLV_RNW 0x80 + +#define INV_MPU6050_REG_I2C_SLV_REG(_x) (0x26 + 3 * (_x)) + +#define INV_MPU6050_REG_I2C_SLV_CTRL(_x) (0x27 + 3 * (_x)) +#define INV_MPU6050_BIT_SLV_GRP 0x10 +#define INV_MPU6050_BIT_SLV_REG_DIS 0x20 +#define INV_MPU6050_BIT_SLV_BYTE_SW 0x40 +#define INV_MPU6050_BIT_SLV_EN 0x80 + +/* I2C master delay register */ +#define INV_MPU6050_REG_I2C_SLV4_CTRL 0x34 +#define INV_MPU6050_BITS_I2C_MST_DLY(_x) ((_x) & 0x1F) + +#define INV_MPU6050_REG_I2C_MST_STATUS 0x36 +#define INV_MPU6050_BIT_I2C_SLV0_NACK 0x01 +#define INV_MPU6050_BIT_I2C_SLV1_NACK 0x02 +#define INV_MPU6050_BIT_I2C_SLV2_NACK 0x04 +#define INV_MPU6050_BIT_I2C_SLV3_NACK 0x08 + #define INV_MPU6050_REG_INT_ENABLE 0x38 #define INV_MPU6050_BIT_DATA_RDY_EN 0x01 #define INV_MPU6050_BIT_DMP_INT_EN 0x02 @@ -173,6 +217,18 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10 #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 +#define INV_MPU6050_REG_EXT_SENS_DATA 0x49 + +/* I2C slaves data output from 0 to 3 */ +#define INV_MPU6050_REG_I2C_SLV_DO(_x) (0x63 + (_x)) + +#define INV_MPU6050_REG_I2C_MST_DELAY_CTRL 0x67 +#define INV_MPU6050_BIT_I2C_SLV0_DLY_EN 0x01 +#define INV_MPU6050_BIT_I2C_SLV1_DLY_EN 0x02 +#define INV_MPU6050_BIT_I2C_SLV2_DLY_EN 0x04 +#define INV_MPU6050_BIT_I2C_SLV3_DLY_EN 0x08 +#define INV_MPU6050_BIT_DELAY_ES_SHADOW 0x80 + #define INV_MPU6050_REG_USER_CTRL 0x6A #define INV_MPU6050_BIT_FIFO_RST 0x04 #define INV_MPU6050_BIT_DMP_RST 0x08 @@ -200,6 +256,9 @@ struct inv_mpu6050_state { #define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6 #define INV_MPU6050_FIFO_COUNT_BYTE 2 +/* MPU9X50 9-axis magnetometer */ +#define INV_MPU9X50_BYTES_MAGN 7 + /* ICM20602 FIFO samples include temperature readings */ #define INV_ICM20602_BYTES_PER_TEMP_SENSOR 2 @@ -227,8 +286,8 @@ struct inv_mpu6050_state { #define INV_ICM20602_TEMP_OFFSET 8170 #define INV_ICM20602_TEMP_SCALE 3060 -/* 6 + 6 round up and plus 8 */ -#define INV_MPU6050_OUTPUT_DATA_SIZE 24 +/* 6 + 6 + 7 (for MPU9x50) = 19 round up to 24 and plus 8 */ +#define INV_MPU6050_OUTPUT_DATA_SIZE 32 #define INV_MPU6050_REG_INT_PIN_CFG 0x37 #define INV_MPU6050_ACTIVE_HIGH 0x00 @@ -277,6 +336,11 @@ enum inv_mpu6050_scan { INV_MPU6050_SCAN_GYRO_Y, INV_MPU6050_SCAN_GYRO_Z, INV_MPU6050_SCAN_TIMESTAMP, + + INV_MPU9X50_SCAN_MAGN_X = INV_MPU6050_SCAN_GYRO_Z + 1, + INV_MPU9X50_SCAN_MAGN_Y, + INV_MPU9X50_SCAN_MAGN_Z, + INV_MPU9X50_SCAN_TIMESTAMP, }; /* scan element definition for ICM20602, which includes temperature */ @@ -342,3 +406,5 @@ void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type); extern const struct dev_pm_ops inv_mpu_pmops; + +#endif diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c new file mode 100644 index 000000000000..02735af152c8 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/string.h> + +#include "inv_mpu_aux.h" +#include "inv_mpu_iio.h" +#include "inv_mpu_magn.h" + +/* + * MPU9250 magnetometer is an AKM AK8963 chip on I2C aux bus + */ +#define INV_MPU_MAGN_I2C_ADDR 0x0C + +#define INV_MPU_MAGN_REG_WIA 0x00 +#define INV_MPU_MAGN_BITS_WIA 0x48 + +#define INV_MPU_MAGN_REG_ST1 0x02 +#define INV_MPU_MAGN_BIT_DRDY 0x01 +#define INV_MPU_MAGN_BIT_DOR 0x02 + +#define INV_MPU_MAGN_REG_DATA 0x03 + +#define INV_MPU_MAGN_REG_ST2 0x09 +#define INV_MPU_MAGN_BIT_HOFL 0x08 +#define INV_MPU_MAGN_BIT_BITM 0x10 + +#define INV_MPU_MAGN_REG_CNTL1 0x0A +#define INV_MPU_MAGN_BITS_MODE_PWDN 0x00 +#define INV_MPU_MAGN_BITS_MODE_SINGLE 0x01 +#define INV_MPU_MAGN_BITS_MODE_FUSE 0x0F +#define INV_MPU_MAGN_BIT_OUTPUT_BIT 0x10 + +#define INV_MPU_MAGN_REG_CNTL2 0x0B +#define INV_MPU_MAGN_BIT_SRST 0x01 + +#define INV_MPU_MAGN_REG_ASAX 0x10 +#define INV_MPU_MAGN_REG_ASAY 0x11 +#define INV_MPU_MAGN_REG_ASAZ 0x12 + +/* Magnetometer maximum frequency */ +#define INV_MPU_MAGN_FREQ_HZ_MAX 50 + +static bool inv_magn_supported(const struct inv_mpu6050_state *st) +{ + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + return true; + default: + return false; + } +} + +/* init magnetometer chip */ +static int inv_magn_init(struct inv_mpu6050_state *st) +{ + uint8_t val; + uint8_t asa[3]; + int ret; + + /* check whoami */ + ret = inv_mpu_aux_read(st, INV_MPU_MAGN_I2C_ADDR, INV_MPU_MAGN_REG_WIA, + &val, sizeof(val)); + if (ret) + return ret; + if (val != INV_MPU_MAGN_BITS_WIA) + return -ENODEV; + + /* reset chip */ + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU_MAGN_REG_CNTL2, + INV_MPU_MAGN_BIT_SRST); + if (ret) + return ret; + + /* read fuse ROM data */ + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU_MAGN_REG_CNTL1, + INV_MPU_MAGN_BITS_MODE_FUSE); + if (ret) + return ret; + + ret = inv_mpu_aux_read(st, INV_MPU_MAGN_I2C_ADDR, INV_MPU_MAGN_REG_ASAX, + asa, sizeof(asa)); + if (ret) + return ret; + + /* switch back to power-down */ + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU_MAGN_REG_CNTL1, + INV_MPU_MAGN_BITS_MODE_PWDN); + if (ret) + return ret; + + /* + * Sensitivity adjustement and scale to Gauss + * + * Hadj = H * (((ASA - 128) * 0.5 / 128) + 1) + * Factor simplification: + * Hadj = H * ((ASA + 128) / 256) + * + * Sensor sentivity + * 0.15 uT in 16 bits mode + * 1 uT = 0.01 G and value is in micron (1e6) + * sensitvity = 0.15 uT * 0.01 * 1e6 + * + * raw_to_gauss = Hadj * 1500 + */ + st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * 1500) / 256; + st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * 1500) / 256; + st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * 1500) / 256; + + return 0; +} + +/** + * inv_mpu_magn_probe() - probe and setup magnetometer chip + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise + * + * It is probing the chip and setting up all needed i2c transfers. + * Noop if there is no magnetometer in the chip. + */ +int inv_mpu_magn_probe(struct inv_mpu6050_state *st) +{ + int ret; + + /* quit if chip is not supported */ + if (!inv_magn_supported(st)) + return 0; + + /* configure i2c master aux port */ + ret = inv_mpu_aux_init(st); + if (ret) + return ret; + + /* check and init mag chip */ + ret = inv_magn_init(st); + if (ret) + return ret; + + /* + * configure mpu i2c master accesses + * i2c SLV0: read sensor data, 7 bytes data(6)-ST2 + * Byte swap data to store them in big-endian in impair address groups + */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), + INV_MPU6050_BIT_I2C_SLV_RNW | INV_MPU_MAGN_I2C_ADDR); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), + INV_MPU_MAGN_REG_DATA); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | + INV_MPU6050_BIT_SLV_BYTE_SW | + INV_MPU6050_BIT_SLV_GRP | + INV_MPU9X50_BYTES_MAGN); + if (ret) + return ret; + + /* i2c SLV1: launch single measurement */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(1), + INV_MPU_MAGN_I2C_ADDR); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(1), + INV_MPU_MAGN_REG_CNTL1); + if (ret) + return ret; + + /* add 16 bits mode */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1), + INV_MPU_MAGN_BITS_MODE_SINGLE | + INV_MPU_MAGN_BIT_OUTPUT_BIT); + if (ret) + return ret; + + return regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(1), + INV_MPU6050_BIT_SLV_EN | 1); +} + +/** + * inv_mpu_magn_set_rate() - set magnetometer sampling rate + * @st: driver internal state + * @fifo_rate: mpu set fifo rate + * + * Returns 0 on success, a negative error code otherwise + * + * Limit sampling frequency to the maximum value supported by the + * magnetometer chip. Resulting in duplicated data for higher frequencies. + * Noop if there is no magnetometer in the chip. + */ +int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate) +{ + uint8_t d; + + /* quit if chip is not supported */ + if (!inv_magn_supported(st)) + return 0; + + /* + * update i2c master delay to limit mag sampling to max frequency + * compute fifo_rate divider d: rate = fifo_rate / (d + 1) + */ + if (fifo_rate > INV_MPU_MAGN_FREQ_HZ_MAX) + d = fifo_rate / INV_MPU_MAGN_FREQ_HZ_MAX - 1; + else + d = 0; + + return regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, d); +} + +/** + * inv_mpu_magn_set_orient() - fill magnetometer mounting matrix + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise + * + * Fill magnetometer mounting matrix using the provided chip matrix. + */ +int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) +{ + const char *orient; + char *str; + int i; + + /* fill magnetometer orientation */ + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + /* x <- y */ + st->magn_orient.rotation[0] = st->orientation.rotation[3]; + st->magn_orient.rotation[1] = st->orientation.rotation[4]; + st->magn_orient.rotation[2] = st->orientation.rotation[5]; + /* y <- x */ + st->magn_orient.rotation[3] = st->orientation.rotation[0]; + st->magn_orient.rotation[4] = st->orientation.rotation[1]; + st->magn_orient.rotation[5] = st->orientation.rotation[2]; + /* z <- -z */ + for (i = 0; i < 3; ++i) { + orient = st->orientation.rotation[6 + i]; + /* use length + 2 for adding minus sign if needed */ + str = devm_kzalloc(regmap_get_device(st->map), + strlen(orient) + 2, GFP_KERNEL); + if (str == NULL) + return -ENOMEM; + if (strcmp(orient, "0") == 0) { + strcpy(str, orient); + } else if (orient[0] == '-') { + strcpy(str, &orient[1]); + } else { + str[0] = '-'; + strcpy(&str[1], orient); + } + st->magn_orient.rotation[6 + i] = str; + } + break; + default: + st->magn_orient = st->orientation; + break; + } + + return 0; +} + +/** + * inv_mpu_magn_read() - read magnetometer data + * @st: driver internal state + * @axis: IIO modifier axis value + * @val: store corresponding axis value + * + * Returns 0 on success, a negative error code otherwise + */ +int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) +{ + unsigned int user_ctrl, status; + __be16 data[3]; + uint8_t addr; + uint8_t d; + unsigned int period_ms; + int ret; + + /* quit if chip is not supported */ + if (!inv_magn_supported(st)) + return -ENODEV; + + /* Mag data: X - Y - Z */ + switch (axis) { + case IIO_MOD_X: + addr = 0; + break; + case IIO_MOD_Y: + addr = 1; + break; + case IIO_MOD_Z: + addr = 2; + break; + default: + return -EINVAL; + } + + /* set sample rate to max mag freq */ + d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU_MAGN_FREQ_HZ_MAX); + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* start i2c master, wait for xfer, stop */ + user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + return ret; + + /* need to wait 2 periods + half-period margin */ + period_ms = 1000 / INV_MPU_MAGN_FREQ_HZ_MAX; + msleep(period_ms * 2 + period_ms / 2); + user_ctrl = st->chip_config.user_ctrl; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + return ret; + + /* restore sample rate */ + d = st->chip_config.divider; + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* check i2c status and read raw data */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK || + status & INV_MPU6050_BIT_I2C_SLV1_NACK) + return -EIO; + + ret = regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, + data, sizeof(data)); + if (ret) + return ret; + + *val = (int16_t)be16_to_cpu(data[addr]); + + return IIO_VAL_INT; +} diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h new file mode 100644 index 000000000000..b41bd0578478 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#ifndef INV_MPU_MAGN_H_ +#define INV_MPU_MAGN_H_ + +#include <linux/kernel.h> + +#include "inv_mpu_iio.h" + +int inv_mpu_magn_probe(struct inv_mpu6050_state *st); + +/** + * inv_mpu_magn_get_scale() - get magnetometer scale value + * @st: driver internal state + * + * Returns IIO data format. + */ +static inline int inv_mpu_magn_get_scale(const struct inv_mpu6050_state *st, + const struct iio_chan_spec *chan, + int *val, int *val2) +{ + *val = 0; + *val2 = st->magn_raw_to_gauss[chan->address]; + return IIO_VAL_INT_PLUS_MICRO; +} + +int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate); + +int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st); + +int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val); + +#endif /* INV_MPU_MAGN_H_ */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 5f9a5de0bab4..bbf68b474556 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -124,7 +124,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev) /* enable interrupt */ if (st->chip_config.accl_fifo_enable || - st->chip_config.gyro_fifo_enable) { + st->chip_config.gyro_fifo_enable || + st->chip_config.magn_fifo_enable) { result = regmap_write(st->map, st->reg->int_enable, INV_MPU6050_BIT_DATA_RDY_EN); if (result) @@ -141,6 +142,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev) d |= INV_MPU6050_BITS_GYRO_OUT; if (st->chip_config.accl_fifo_enable) d |= INV_MPU6050_BIT_ACCEL_OUT; + if (st->chip_config.magn_fifo_enable) + d |= INV_MPU6050_BIT_SLAVE_0; result = regmap_write(st->map, st->reg->fifo_en, d); if (result) goto reset_fifo_fail; @@ -190,7 +193,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) } if (!(st->chip_config.accl_fifo_enable | - st->chip_config.gyro_fifo_enable)) + st->chip_config.gyro_fifo_enable | + st->chip_config.magn_fifo_enable)) goto end_session; bytes_per_datum = 0; if (st->chip_config.accl_fifo_enable) @@ -202,6 +206,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) if (st->chip_type == INV_ICM20602) bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR; + if (st->chip_config.magn_fifo_enable) + bytes_per_datum += INV_MPU9X50_BYTES_MAGN; + /* * read fifo_count register to know how many bytes are inside the FIFO * right now diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index dd55e70b6f77..d7d951927a44 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -5,7 +5,7 @@ #include "inv_mpu_iio.h" -static void inv_scan_query(struct iio_dev *indio_dev) +static void inv_scan_query_mpu6050(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); @@ -26,6 +26,60 @@ static void inv_scan_query(struct iio_dev *indio_dev) indio_dev->active_scan_mask); } +static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + inv_scan_query_mpu6050(indio_dev); + + /* no magnetometer if i2c auxiliary bus is used */ + if (st->magn_disabled) + return; + + st->chip_config.magn_fifo_enable = + test_bit(INV_MPU9X50_SCAN_MAGN_X, + indio_dev->active_scan_mask) || + test_bit(INV_MPU9X50_SCAN_MAGN_Y, + indio_dev->active_scan_mask) || + test_bit(INV_MPU9X50_SCAN_MAGN_Z, + indio_dev->active_scan_mask); +} + +static void inv_scan_query(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + return inv_scan_query_mpu9x50(indio_dev); + default: + return inv_scan_query_mpu6050(indio_dev); + } +} + +static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st) +{ + unsigned int gyro_skip = 0; + unsigned int magn_skip = 0; + unsigned int skip_samples; + + /* gyro first sample is out of specs, skip it */ + if (st->chip_config.gyro_fifo_enable) + gyro_skip = 1; + + /* mag first sample is always not ready, skip it */ + if (st->chip_config.magn_fifo_enable) + magn_skip = 1; + + /* compute first samples to skip */ + skip_samples = gyro_skip; + if (magn_skip > skip_samples) + skip_samples = magn_skip; + + return skip_samples; +} + /** * inv_mpu6050_set_enable() - enable chip functions. * @indio_dev: Device driver instance. @@ -34,6 +88,7 @@ static void inv_scan_query(struct iio_dev *indio_dev) static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + uint8_t d; int result; if (enable) { @@ -41,14 +96,11 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) return result; inv_scan_query(indio_dev); - st->skip_samples = 0; if (st->chip_config.gyro_fifo_enable) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) goto error_power_off; - /* gyro first sample is out of specs, skip it */ - st->skip_samples = 1; } if (st->chip_config.accl_fifo_enable) { result = inv_mpu6050_switch_engine(st, true, @@ -56,22 +108,32 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) goto error_gyro_off; } + if (st->chip_config.magn_fifo_enable) { + d = st->chip_config.user_ctrl | + INV_MPU6050_BIT_I2C_MST_EN; + result = regmap_write(st->map, st->reg->user_ctrl, d); + if (result) + goto error_accl_off; + st->chip_config.user_ctrl = d; + } + st->skip_samples = inv_compute_skip_samples(st); result = inv_reset_fifo(indio_dev); if (result) - goto error_accl_off; + goto error_magn_off; } else { result = regmap_write(st->map, st->reg->fifo_en, 0); if (result) - goto error_accl_off; + goto error_magn_off; result = regmap_write(st->map, st->reg->int_enable, 0); if (result) - goto error_accl_off; + goto error_magn_off; - result = regmap_write(st->map, st->reg->user_ctrl, - st->chip_config.user_ctrl); + d = st->chip_config.user_ctrl & ~INV_MPU6050_BIT_I2C_MST_EN; + result = regmap_write(st->map, st->reg->user_ctrl, d); if (result) - goto error_accl_off; + goto error_magn_off; + st->chip_config.user_ctrl = d; result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_BIT_PWR_ACCL_STBY); @@ -90,6 +152,10 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) return 0; +error_magn_off: + /* always restore user_ctrl to disable fifo properly */ + st->chip_config.user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN; + regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); error_accl_off: if (st->chip_config.accl_fifo_enable) inv_mpu6050_switch_engine(st, false, diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 77aa0e77212d..b425a356d99e 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -12,7 +12,7 @@ config IIO_ST_LSM6DSX Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr, lsm6ds3tr-c, - ism330dhcx and the accelerometer/gyroscope of lsm9ds1. + ism330dhcx, lsm6ds0 and the accelerometer/gyroscope of lsm9ds1. To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 80e42c7dbcbe..f660359ccb4d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -12,6 +12,7 @@ #define ST_LSM6DSX_H #include <linux/device.h> +#include <linux/iio/iio.h> #define ST_LSM6DS3_DEV_NAME "lsm6ds3" #define ST_LSM6DS3H_DEV_NAME "lsm6ds3h" @@ -25,6 +26,7 @@ #define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c" #define ST_ISM330DHCX_DEV_NAME "ism330dhcx" #define ST_LSM9DS1_DEV_NAME "lsm9ds1-imu" +#define ST_LSM6DS0_DEV_NAME "lsm6ds0" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -39,6 +41,7 @@ enum st_lsm6dsx_hw_id { ST_LSM6DS3TRC_ID, ST_ISM330DHCX_ID, ST_LSM9DS1_ID, + ST_LSM6DS0_ID, ST_LSM6DSX_MAX_ID, }; @@ -54,6 +57,26 @@ enum st_lsm6dsx_hw_id { * ST_LSM6DSX_TAGGED_SAMPLE_SIZE) #define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) +#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .event_spec = &st_lsm6dsx_event, \ + .num_event_specs = 1, \ +} + #define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ { \ .type = chan_type, \ @@ -99,7 +122,9 @@ struct st_lsm6dsx_fs { #define ST_LSM6DSX_FS_LIST_SIZE 4 struct st_lsm6dsx_fs_table_entry { struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; + int fs_len; }; /** @@ -130,12 +155,14 @@ struct st_lsm6dsx_fifo_ops { * @hr_timer: Hw timer resolution register info (addr + mask). * @fifo_en: Hw timer FIFO enable register info (addr + mask). * @decimator: Hw timer FIFO decimator register info (addr + mask). + * @freq_fine: Difference in % of ODR with respect to the typical. */ struct st_lsm6dsx_hw_ts_settings { struct st_lsm6dsx_reg timer_en; struct st_lsm6dsx_reg hr_timer; struct st_lsm6dsx_reg fifo_en; struct st_lsm6dsx_reg decimator; + u8 freq_fine; }; /** @@ -162,6 +189,16 @@ struct st_lsm6dsx_shub_settings { u8 batch_en; }; +struct st_lsm6dsx_event_settings { + struct st_lsm6dsx_reg enable_reg; + struct st_lsm6dsx_reg wakeup_reg; + u8 wakeup_src_reg; + u8 wakeup_src_status_mask; + u8 wakeup_src_z_mask; + u8 wakeup_src_y_mask; + u8 wakeup_src_x_mask; +}; + enum st_lsm6dsx_ext_sensor_id { ST_LSM6DSX_ID_MAGN, }; @@ -205,12 +242,14 @@ struct st_lsm6dsx_ext_dev_settings { /** * struct st_lsm6dsx_settings - ST IMU sensor settings * @wai: Sensor WhoAmI default value. - * @int1_addr: Control Register address for INT1 - * @int2_addr: Control Register address for INT2 - * @reset_addr: register address for reset/reboot + * @reset: register address for reset. + * @boot: register address for boot. + * @bdu: register address for Block Data Update. * @max_fifo_size: Sensor max fifo length in FIFO words. * @id: List of hw id/device name supported by the driver configuration. * @channels: IIO channels supported by the device. + * @irq_config: interrupts related registers. + * @drdy_mask: register info for data-ready mask (addr + mask). * @odr_table: Hw sensors odr table (Hz + val). * @fs_table: Hw sensors gain table (gain + val). * @decimator: List of decimator register info (addr + mask). @@ -221,9 +260,9 @@ struct st_lsm6dsx_ext_dev_settings { */ struct st_lsm6dsx_settings { u8 wai; - u8 int1_addr; - u8 int2_addr; - u8 reset_addr; + struct st_lsm6dsx_reg reset; + struct st_lsm6dsx_reg boot; + struct st_lsm6dsx_reg bdu; u16 max_fifo_size; struct { enum st_lsm6dsx_hw_id hw_id; @@ -233,6 +272,17 @@ struct st_lsm6dsx_settings { const struct iio_chan_spec *chan; int len; } channels[2]; + struct { + struct st_lsm6dsx_reg irq1; + struct st_lsm6dsx_reg irq2; + struct st_lsm6dsx_reg irq1_func; + struct st_lsm6dsx_reg irq2_func; + struct st_lsm6dsx_reg lir; + struct st_lsm6dsx_reg clear_on_read; + struct st_lsm6dsx_reg hla; + struct st_lsm6dsx_reg od; + } irq_config; + struct st_lsm6dsx_reg drdy_mask; struct st_lsm6dsx_odr_table_entry odr_table[2]; struct st_lsm6dsx_fs_table_entry fs_table[2]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; @@ -240,6 +290,7 @@ struct st_lsm6dsx_settings { struct st_lsm6dsx_fifo_ops fifo_ops; struct st_lsm6dsx_hw_ts_settings ts_settings; struct st_lsm6dsx_shub_settings shub_settings; + struct st_lsm6dsx_event_settings event_settings; }; enum st_lsm6dsx_sensor_id { @@ -299,9 +350,13 @@ struct st_lsm6dsx_sensor { * @fifo_mode: FIFO operating mode supported by the device. * @suspend_mask: Suspended sensor bitmask. * @enable_mask: Enabled sensor bitmask. + * @ts_gain: Hw timestamp rate after internal calibration. * @ts_sip: Total number of timestamp samples in a given pattern. * @sip: Total number of samples (acc/gyro/ts) in a given pattern. * @buff: Device read buffer. + * @irq_routing: pointer to interrupt routing configuration. + * @event_threshold: wakeup event threshold. + * @enable_event: enabled event bitmask. * @iio_devs: Pointers to acc/gyro iio_dev instances. * @settings: Pointer to the specific sensor settings in use. */ @@ -317,9 +372,14 @@ struct st_lsm6dsx_hw { enum st_lsm6dsx_fifo_mode fifo_mode; u8 suspend_mask; u8 enable_mask; + s64 ts_gain; u8 ts_sip; u8 sip; + const struct st_lsm6dsx_reg *irq_routing; + u8 event_threshold; + u8 enable_event; + u8 *buff; struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX]; @@ -327,6 +387,13 @@ struct st_lsm6dsx_hw { const struct st_lsm6dsx_settings *settings; }; +static const struct iio_event_spec st_lsm6dsx_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) +}; + static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; extern const struct dev_pm_ops st_lsm6dsx_pm_ops; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index b0f3da1976e4..ffeb2596b97b 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -30,8 +30,6 @@ * Denis Ciocca <denis.ciocca@st.com> */ #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> @@ -42,10 +40,6 @@ #include "st_lsm6dsx.h" -#define ST_LSM6DSX_REG_HLACTIVE_ADDR 0x12 -#define ST_LSM6DSX_REG_HLACTIVE_MASK BIT(5) -#define ST_LSM6DSX_REG_PP_OD_ADDR 0x12 -#define ST_LSM6DSX_REG_PP_OD_MASK BIT(4) #define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a #define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0) #define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) @@ -56,7 +50,6 @@ #define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 -#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */ #define ST_LSM6DSX_TS_RESET_VAL 0xaa struct st_lsm6dsx_decimator_entry { @@ -429,7 +422,7 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) */ if (!reset_ts && ts >= 0xff0000) reset_ts = true; - ts *= ST_LSM6DSX_TS_SENSITIVITY; + ts *= hw->ts_gain; offset += ST_LSM6DSX_SAMPLE_SIZE; } @@ -456,13 +449,19 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) return read_len; } +#define ST_LSM6DSX_INVALID_SAMPLE 0x7ffd static int st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, u8 *data, s64 ts) { + s16 val = le16_to_cpu(*(__le16 *)data); struct st_lsm6dsx_sensor *sensor; struct iio_dev *iio_dev; + /* invalid sample during bootstrap phase */ + if (val >= ST_LSM6DSX_INVALID_SAMPLE) + return -EINVAL; + /* * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG @@ -572,7 +571,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) */ if (!reset_ts && ts >= 0xffff0000) reset_ts = true; - ts *= ST_LSM6DSX_TS_SENSITIVITY; + ts *= hw->ts_gain; } else { st_lsm6dsx_push_tagged_data(hw, tag, iio_buff, ts); @@ -592,6 +591,9 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) { int err; + if (!hw->settings->fifo_ops.read_fifo) + return -ENOTSUPP; + mutex_lock(&hw->fifo_lock); hw->settings->fifo_ops.read_fifo(hw); @@ -654,25 +656,6 @@ out: return err; } -static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private) -{ - struct st_lsm6dsx_hw *hw = private; - - return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE; -} - -static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) -{ - struct st_lsm6dsx_hw *hw = private; - int count; - - mutex_lock(&hw->fifo_lock); - count = hw->settings->fifo_ops.read_fifo(hw); - mutex_unlock(&hw->fifo_lock); - - return count ? IRQ_HANDLED : IRQ_NONE; -} - static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); @@ -702,59 +685,8 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) { - struct device_node *np = hw->dev->of_node; - struct st_sensors_platform_data *pdata; struct iio_buffer *buffer; - unsigned long irq_type; - bool irq_active_low; - int i, err; - - irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); - - switch (irq_type) { - case IRQF_TRIGGER_HIGH: - case IRQF_TRIGGER_RISING: - irq_active_low = false; - break; - case IRQF_TRIGGER_LOW: - case IRQF_TRIGGER_FALLING: - irq_active_low = true; - break; - default: - dev_info(hw->dev, "mode %lx unsupported\n", irq_type); - return -EINVAL; - } - - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_HLACTIVE_ADDR, - ST_LSM6DSX_REG_HLACTIVE_MASK, - FIELD_PREP(ST_LSM6DSX_REG_HLACTIVE_MASK, - irq_active_low)); - if (err < 0) - return err; - - pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; - if ((np && of_property_read_bool(np, "drive-open-drain")) || - (pdata && pdata->open_drain)) { - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_PP_OD_ADDR, - ST_LSM6DSX_REG_PP_OD_MASK, - FIELD_PREP(ST_LSM6DSX_REG_PP_OD_MASK, - 1)); - if (err < 0) - return err; - - irq_type |= IRQF_SHARED; - } - - err = devm_request_threaded_irq(hw->dev, hw->irq, - st_lsm6dsx_handler_irq, - st_lsm6dsx_handler_thread, - irq_type | IRQF_ONESHOT, - "lsm6dsx", hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); - return err; - } + int i; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { if (!hw->iio_devs[i]) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 2d3495560136..f9c83aa2c9b7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -32,7 +32,7 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 3KB * - * - LSM9DS1: + * - LSM9DS1/LSM6DS0: * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952 @@ -48,8 +48,11 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/pm.h> #include <linux/regmap.h> #include <linux/bitfield.h> @@ -58,17 +61,14 @@ #include "st_lsm6dsx.h" -#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3) #define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f -#define ST_LSM6DSX_REG_RESET_MASK BIT(0) -#define ST_LSM6DSX_REG_BOOT_MASK BIT(7) -#define ST_LSM6DSX_REG_BDU_ADDR 0x12 -#define ST_LSM6DSX_REG_BDU_MASK BIT(6) + +#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */ static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { - ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0), - ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1), - ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2), + ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2), IIO_CHAN_SOFT_TIMESTAMP(3), }; @@ -89,14 +89,26 @@ static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = { static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { { .wai = 0x68, - .int1_addr = 0x0c, - .int2_addr = 0x0d, - .reset_addr = 0x22, + .reset = { + .addr = 0x22, + .mask = BIT(0), + }, + .boot = { + .addr = 0x22, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x22, + .mask = BIT(6), + }, .max_fifo_size = 32, .id = { { .hw_id = ST_LSM9DS1_ID, .name = ST_LSM9DS1_DEV_NAME, + }, { + .hw_id = ST_LSM6DS0_ID, + .name = ST_LSM6DS0_DEV_NAME, }, }, .channels = { @@ -145,23 +157,53 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(732), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { .addr = 0x10, .mask = GENMASK(4, 3), }, - .fs_avl[0] = { IIO_DEGREE_TO_RAD(245), 0x0 }, - .fs_avl[1] = { IIO_DEGREE_TO_RAD(500), 0x1 }, - .fs_avl[2] = { IIO_DEGREE_TO_RAD(2000), 0x3 }, + + .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 }, + .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, + .fs_avl[2] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 3, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0c, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .hla = { + .addr = 0x22, + .mask = BIT(5), + }, + .od = { + .addr = 0x22, + .mask = BIT(4), }, }, }, { .wai = 0x69, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 1365, .id = { { @@ -215,6 +257,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { @@ -225,6 +268,37 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 }, .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), }, }, .decimator = { @@ -268,12 +342,32 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, + .event_settings = { + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, }, { .wai = 0x69, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 682, .id = { { @@ -327,6 +421,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { @@ -337,6 +432,37 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 }, .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), }, }, .decimator = { @@ -380,12 +506,32 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, + .event_settings = { + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, }, { .wai = 0x6a, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 682, .id = { { @@ -448,6 +594,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { @@ -458,6 +605,37 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 }, .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), }, }, .decimator = { @@ -501,12 +679,36 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, }, { .wai = 0x6c, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 512, .id = { { @@ -527,6 +729,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), }, }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, .odr_table = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -563,6 +769,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { @@ -573,6 +780,41 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 }, .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), }, }, .batch = { @@ -607,6 +849,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x0a, .mask = GENMASK(7, 6), }, + .freq_fine = 0x63, }, .shub_settings = { .page_mux = { @@ -633,13 +876,37 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .slv0_addr = 0x15, .dw_slv0_addr = 0x21, .batch_en = BIT(3), - } + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5b, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, }, { .wai = 0x6b, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 512, .id = { { @@ -657,6 +924,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), }, }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, .odr_table = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -693,6 +964,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { @@ -703,6 +975,41 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 }, .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), }, }, .batch = { @@ -737,13 +1044,38 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x0a, .mask = GENMASK(7, 6), }, + .freq_fine = 0x63, + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), }, }, { .wai = 0x6b, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 512, .id = { { @@ -764,6 +1096,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), }, }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, .odr_table = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -800,6 +1136,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 }, .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 }, .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 }, + .fs_len = 4, }, [ST_LSM6DSX_ID_GYRO] = { .reg = { @@ -810,6 +1147,41 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 }, .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), }, }, .batch = { @@ -844,6 +1216,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x0a, .mask = GENMASK(7, 6), }, + .freq_fine = 0x63, }, .shub_settings = { .page_mux = { @@ -870,6 +1243,21 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .slv0_addr = 0x15, .dw_slv0_addr = 0x21, .batch_en = BIT(3), + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), } }, }; @@ -933,11 +1321,12 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, int i, err; fs_table = &sensor->hw->settings->fs_table[sensor->id]; - for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) + for (i = 0; i < fs_table->fs_len; i++) { if (fs_table->fs_avl[i].gain == gain) break; + } - if (i == ST_LSM6DSX_FS_LIST_SIZE) + if (i == fs_table->fs_len) return -EINVAL; data = ST_LSM6DSX_SHIFT_VAL(fs_table->fs_avl[i].val, @@ -1076,7 +1465,8 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, if (err < 0) return err; - st_lsm6dsx_sensor_set_enable(sensor, false); + if (!hw->enable_event) + st_lsm6dsx_sensor_set_enable(sensor, false); *val = (s16)le16_to_cpu(data); @@ -1149,6 +1539,144 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, return err; } +static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, int state) +{ + const struct st_lsm6dsx_reg *reg; + unsigned int data; + int err; + + if (!hw->settings->irq_config.irq1_func.addr) + return -ENOTSUPP; + + reg = &hw->settings->event_settings.enable_reg; + if (reg->addr) { + data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, + reg->mask, data); + if (err < 0) + return err; + } + + /* Enable wakeup interrupt */ + data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask); + return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr, + hw->irq_routing->mask, data); +} + +static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + *val2 = 0; + *val = hw->event_threshold; + + return IIO_VAL_INT; +} + +static int +st_lsm6dsx_write_event(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *reg; + unsigned int data; + int err; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (val < 0 || val > 31) + return -EINVAL; + + reg = &hw->settings->event_settings.wakeup_reg; + data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, + reg->mask, data); + if (err < 0) + return -EINVAL; + + hw->event_threshold = val; + + return 0; +} + +static int +st_lsm6dsx_read_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + return !!(hw->enable_event & BIT(chan->channel2)); +} + +static int +st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + u8 enable_event; + int err = 0; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (state) { + enable_event = hw->enable_event | BIT(chan->channel2); + + /* do not enable events if they are already enabled */ + if (hw->enable_event) + goto out; + } else { + enable_event = hw->enable_event & ~BIT(chan->channel2); + + /* only turn off sensor if no events is enabled */ + if (enable_event) + goto out; + } + + /* stop here if no changes have been made */ + if (hw->enable_event == enable_event) + return 0; + + err = st_lsm6dsx_event_setup(hw, state); + if (err < 0) + return err; + + mutex_lock(&hw->conf_lock); + err = st_lsm6dsx_sensor_set_enable(sensor, state); + mutex_unlock(&hw->conf_lock); + if (err < 0) + return err; + +out: + hw->enable_event = enable_event; + + return 0; +} + int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); @@ -1196,18 +1724,13 @@ static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev, { struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); const struct st_lsm6dsx_fs_table_entry *fs_table; - enum st_lsm6dsx_sensor_id id = sensor->id; struct st_lsm6dsx_hw *hw = sensor->hw; int i, len = 0; - fs_table = &hw->settings->fs_table[id]; - for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) { - if (!fs_table->fs_avl[i].gain) - break; - + fs_table = &hw->settings->fs_table[sensor->id]; + for (i = 0; i < fs_table->fs_len; i++) len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", fs_table->fs_avl[i].gain); - } buf[len - 1] = '\n'; return len; @@ -1233,6 +1756,10 @@ static const struct iio_info st_lsm6dsx_acc_info = { .attrs = &st_lsm6dsx_acc_attribute_group, .read_raw = st_lsm6dsx_read_raw, .write_raw = st_lsm6dsx_write_raw, + .read_event_value = st_lsm6dsx_read_event, + .write_event_value = st_lsm6dsx_write_event, + .read_event_config = st_lsm6dsx_read_event_config, + .write_event_config = st_lsm6dsx_write_event_config, .hwfifo_set_watermark = st_lsm6dsx_set_watermark, }; @@ -1263,7 +1790,9 @@ static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin); } -static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) +static int +st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, + const struct st_lsm6dsx_reg **drdy_reg) { int err = 0, drdy_pin; @@ -1277,10 +1806,12 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) switch (drdy_pin) { case 1: - *drdy_reg = hw->settings->int1_addr; + hw->irq_routing = &hw->settings->irq_config.irq1_func; + *drdy_reg = &hw->settings->irq_config.irq1; break; case 2: - *drdy_reg = hw->settings->int2_addr; + hw->irq_routing = &hw->settings->irq_config.irq2_func; + *drdy_reg = &hw->settings->irq_config.irq2; break; default: dev_err(hw->dev, "unsupported data ready pin\n"); @@ -1371,51 +1902,95 @@ static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw) if (err < 0) return err; } + + /* calibrate timestamp sensitivity */ + hw->ts_gain = ST_LSM6DSX_TS_SENSITIVITY; + if (ts_settings->freq_fine) { + err = regmap_read(hw->regmap, ts_settings->freq_fine, &val); + if (err < 0) + return err; + + /* + * linearize the AN5192 formula: + * 1 / (1 + x) ~= 1 - x (Taylor’s Series) + * ttrim[s] = 1 / (40000 * (1 + 0.0015 * val)) + * ttrim[ns] ~= 25000 - 37.5 * val + * ttrim[ns] ~= 25000 - (37500 * val) / 1000 + */ + hw->ts_gain -= ((s8)val * 37500) / 1000; + } + return 0; } static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) { - u8 drdy_int_reg; + const struct st_lsm6dsx_reg *reg; int err; /* device sw reset */ - err = regmap_update_bits(hw->regmap, hw->settings->reset_addr, - ST_LSM6DSX_REG_RESET_MASK, - FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1)); + reg = &hw->settings->reset; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; msleep(50); /* reload trimming parameter */ - err = regmap_update_bits(hw->regmap, hw->settings->reset_addr, - ST_LSM6DSX_REG_BOOT_MASK, - FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1)); + reg = &hw->settings->boot; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; msleep(50); /* enable Block Data Update */ - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR, - ST_LSM6DSX_REG_BDU_MASK, - FIELD_PREP(ST_LSM6DSX_REG_BDU_MASK, 1)); + reg = &hw->settings->bdu; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; /* enable FIFO watermak interrupt */ - err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg); + err = st_lsm6dsx_get_drdy_reg(hw, ®); if (err < 0) return err; - err = regmap_update_bits(hw->regmap, drdy_int_reg, - ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, - FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, - 1)); + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; + /* enable Latched interrupts for device events */ + if (hw->settings->irq_config.lir.addr) { + reg = &hw->settings->irq_config.lir; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + /* enable clear on read for latched interrupts */ + if (hw->settings->irq_config.clear_on_read.addr) { + reg = &hw->settings->irq_config.clear_on_read; + err = regmap_update_bits(hw->regmap, + reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + } + } + + /* enable drdy-mas if available */ + if (hw->settings->drdy_mask.addr) { + reg = &hw->settings->drdy_mask; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + } + err = st_lsm6dsx_init_shub(hw); if (err < 0) return err; @@ -1466,10 +2041,138 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, return iio_dev; } +static bool +st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_event_settings *event_settings; + int err, data; + s64 timestamp; + + if (!hw->enable_event) + return false; + + event_settings = &hw->settings->event_settings; + err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg, + &data, sizeof(data)); + if (err < 0) + return false; + + timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + if ((data & hw->settings->event_settings.wakeup_src_z_mask) && + (hw->enable_event & BIT(IIO_MOD_Z))) + iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + + if ((data & hw->settings->event_settings.wakeup_src_y_mask) && + (hw->enable_event & BIT(IIO_MOD_Y))) + iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + + if ((data & hw->settings->event_settings.wakeup_src_x_mask) && + (hw->enable_event & BIT(IIO_MOD_X))) + iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + + return data & event_settings->wakeup_src_status_mask; +} + +static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) +{ + struct st_lsm6dsx_hw *hw = private; + bool event; + int count; + + event = st_lsm6dsx_report_motion_event(hw); + + if (!hw->settings->fifo_ops.read_fifo) + return event ? IRQ_HANDLED : IRQ_NONE; + + mutex_lock(&hw->fifo_lock); + count = hw->settings->fifo_ops.read_fifo(hw); + mutex_unlock(&hw->fifo_lock); + + return count || event ? IRQ_HANDLED : IRQ_NONE; +} + +static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) +{ + struct device_node *np = hw->dev->of_node; + struct st_sensors_platform_data *pdata; + const struct st_lsm6dsx_reg *reg; + unsigned long irq_type; + bool irq_active_low; + int err; + + irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); + + switch (irq_type) { + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + irq_active_low = false; + break; + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_FALLING: + irq_active_low = true; + break; + default: + dev_info(hw->dev, "mode %lx unsupported\n", irq_type); + return -EINVAL; + } + + reg = &hw->settings->irq_config.hla; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(irq_active_low, + reg->mask)); + if (err < 0) + return err; + + pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; + if ((np && of_property_read_bool(np, "drive-open-drain")) || + (pdata && pdata->open_drain)) { + reg = &hw->settings->irq_config.od; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + irq_type |= IRQF_SHARED; + } + + err = devm_request_threaded_irq(hw->dev, hw->irq, + NULL, + st_lsm6dsx_handler_thread, + irq_type | IRQF_ONESHOT, + "lsm6dsx", hw); + if (err) { + dev_err(hw->dev, "failed to request trigger irq %d\n", + hw->irq); + return err; + } + + return 0; +} + int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { + struct st_sensors_platform_data *pdata = dev->platform_data; const struct st_lsm6dsx_shub_settings *hub_settings; + struct device_node *np = dev->of_node; struct st_lsm6dsx_hw *hw; const char *name = NULL; int i, err; @@ -1514,6 +2217,10 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, } if (hw->irq > 0) { + err = st_lsm6dsx_irq_setup(hw); + if (err < 0) + return err; + err = st_lsm6dsx_fifo_setup(hw); if (err < 0) return err; @@ -1528,6 +2235,10 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } + if ((np && of_property_read_bool(np, "wakeup-source")) || + (pdata && pdata->wakeup_source)) + device_init_wakeup(dev, true); + return 0; } EXPORT_SYMBOL(st_lsm6dsx_probe); @@ -1546,6 +2257,13 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev) if (!(hw->enable_mask & BIT(sensor->id))) continue; + if (device_may_wakeup(dev) && + sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) { + /* Enable wake from IRQ */ + enable_irq_wake(hw->irq); + continue; + } + if (sensor->id == ST_LSM6DSX_ID_EXT0 || sensor->id == ST_LSM6DSX_ID_EXT1 || sensor->id == ST_LSM6DSX_ID_EXT2) @@ -1575,6 +2293,10 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev) continue; sensor = iio_priv(hw->iio_devs[i]); + if (device_may_wakeup(dev) && + sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) + disable_irq_wake(hw->irq); + if (!(hw->suspend_mask & BIT(sensor->id))) continue; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index f52511059545..e57744affbd0 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -87,6 +87,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,lsm9ds1-imu", .data = (void *)ST_LSM9DS1_ID, }, + { + .compatible = "st,lsm6ds0", + .data = (void *)ST_LSM6DS0_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -104,6 +108,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, + { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index 66fbcd94642d..ea472cf6db7b 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -61,6 +61,7 @@ static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { .gain = 1500, .val = 0x0, }, /* 1500 uG/LSB */ + .fs_len = 1, }, .temp_comp = { .addr = 0x60, @@ -92,9 +93,11 @@ static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) { struct st_lsm6dsx_sensor *sensor; + u16 odr; sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - msleep((2000U / sensor->odr) + 1); + odr = (hw->enable_mask & BIT(ST_LSM6DSX_ID_ACC)) ? sensor->odr : 13; + msleep((2000U / odr) + 1); } /** @@ -555,13 +558,9 @@ static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, int i, len = 0; settings = sensor->ext_info.settings; - for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) { - u16 val = settings->fs_table.fs_avl[i].gain; - - if (val > 0) - len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", - val); - } + for (i = 0; i < settings->fs_table.fs_len; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + settings->fs_table.fs_avl[i].gain); buf[len - 1] = '\n'; return len; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 344b28dddebb..933d4f9f6a4a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -87,6 +87,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,lsm9ds1-imu", .data = (void *)ST_LSM9DS1_ID, }, + { + .compatible = "st,lsm6ds0", + .data = (void *)ST_LSM6DS0_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -104,6 +108,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, + { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 524a686077ca..f72c2dc5f703 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1238,6 +1238,16 @@ static ssize_t iio_show_dev_name(struct device *dev, static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); +static ssize_t iio_show_dev_label(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", indio_dev->label); +} + +static DEVICE_ATTR(label, S_IRUGO, iio_show_dev_label, NULL); + static ssize_t iio_show_timestamp_clock(struct device *dev, struct device_attribute *attr, char *buf) @@ -1354,6 +1364,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) if (indio_dev->name) attrcount++; + if (indio_dev->label) + attrcount++; if (clk) attrcount++; @@ -1376,6 +1388,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr; if (indio_dev->name) indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr; + if (indio_dev->label) + indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_label.attr; if (clk) indio_dev->chan_attr_group.attrs[attrn++] = clk; @@ -1647,6 +1661,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; + indio_dev->label = of_get_property(indio_dev->dev.of_node, "label", + NULL); + ret = iio_check_unique_scan_index(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 08d7e1ef2186..d793c1541705 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -32,6 +32,17 @@ config ADJD_S311 This driver can also be built as a module. If so, the module will be called adjd_s311. +config ADUX1020 + tristate "ADUX1020 photometric sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Analog Devices + ADUX1020 photometric sensor. + + To compile this driver as a module, choose M here: the + module will be called adux1020. + config AL3320A tristate "AL3320A ambient light sensor" depends on I2C @@ -314,6 +325,7 @@ config MAX44009 config NOA1305 tristate "ON Semiconductor NOA1305 ambient light sensor" depends on I2C + select REGMAP_I2C help Say Y here if you want to build support for the ON Semiconductor NOA1305 ambient light sensor. diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 00d1f9b98f39..5d650ce46a40 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -6,6 +6,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ACPI_ALS) += acpi-als.o obj-$(CONFIG_ADJD_S311) += adjd_s311.o +obj-$(CONFIG_ADUX1020) += adux1020.o obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9960) += apds9960.o diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c new file mode 100644 index 000000000000..b07797ac10d7 --- /dev/null +++ b/drivers/iio/light/adux1020.c @@ -0,0 +1,849 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * adux1020.c - Support for Analog Devices ADUX1020 photometric sensor + * + * Copyright (C) 2019 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + * + * TODO: Triggered buffer support + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#define ADUX1020_REGMAP_NAME "adux1020_regmap" +#define ADUX1020_DRV_NAME "adux1020" + +/* System registers */ +#define ADUX1020_REG_CHIP_ID 0x08 +#define ADUX1020_REG_SLAVE_ADDRESS 0x09 + +#define ADUX1020_REG_SW_RESET 0x0f +#define ADUX1020_REG_INT_ENABLE 0x1c +#define ADUX1020_REG_INT_POLARITY 0x1d +#define ADUX1020_REG_PROX_TH_ON1 0x2a +#define ADUX1020_REG_PROX_TH_OFF1 0x2b +#define ADUX1020_REG_PROX_TYPE 0x2f +#define ADUX1020_REG_TEST_MODES_3 0x32 +#define ADUX1020_REG_FORCE_MODE 0x33 +#define ADUX1020_REG_FREQUENCY 0x40 +#define ADUX1020_REG_LED_CURRENT 0x41 +#define ADUX1020_REG_OP_MODE 0x45 +#define ADUX1020_REG_INT_MASK 0x48 +#define ADUX1020_REG_INT_STATUS 0x49 +#define ADUX1020_REG_DATA_BUFFER 0x60 + +/* Chip ID bits */ +#define ADUX1020_CHIP_ID_MASK GENMASK(11, 0) +#define ADUX1020_CHIP_ID 0x03fc + +#define ADUX1020_SW_RESET BIT(1) +#define ADUX1020_FIFO_FLUSH BIT(15) +#define ADUX1020_OP_MODE_MASK GENMASK(3, 0) +#define ADUX1020_DATA_OUT_MODE_MASK GENMASK(7, 4) +#define ADUX1020_DATA_OUT_PROX_I FIELD_PREP(ADUX1020_DATA_OUT_MODE_MASK, 1) + +#define ADUX1020_MODE_INT_MASK GENMASK(7, 0) +#define ADUX1020_INT_ENABLE 0x2094 +#define ADUX1020_INT_DISABLE 0x2090 +#define ADUX1020_PROX_INT_ENABLE 0x00f0 +#define ADUX1020_PROX_ON1_INT BIT(0) +#define ADUX1020_PROX_OFF1_INT BIT(1) +#define ADUX1020_FIFO_INT_ENABLE 0x7f +#define ADUX1020_MODE_INT_DISABLE 0xff +#define ADUX1020_MODE_INT_STATUS_MASK GENMASK(7, 0) +#define ADUX1020_FIFO_STATUS_MASK GENMASK(15, 8) +#define ADUX1020_INT_CLEAR 0xff +#define ADUX1020_PROX_TYPE BIT(15) + +#define ADUX1020_INT_PROX_ON1 BIT(0) +#define ADUX1020_INT_PROX_OFF1 BIT(1) + +#define ADUX1020_FORCE_CLOCK_ON 0x0f4f +#define ADUX1020_FORCE_CLOCK_RESET 0x0040 +#define ADUX1020_ACTIVE_4_STATE 0x0008 + +#define ADUX1020_PROX_FREQ_MASK GENMASK(7, 4) +#define ADUX1020_PROX_FREQ(x) FIELD_PREP(ADUX1020_PROX_FREQ_MASK, x) + +#define ADUX1020_LED_CURRENT_MASK GENMASK(3, 0) +#define ADUX1020_LED_PIREF_EN BIT(12) + +/* Operating modes */ +enum adux1020_op_modes { + ADUX1020_MODE_STANDBY, + ADUX1020_MODE_PROX_I, + ADUX1020_MODE_PROX_XY, + ADUX1020_MODE_GEST, + ADUX1020_MODE_SAMPLE, + ADUX1020_MODE_FORCE = 0x0e, + ADUX1020_MODE_IDLE = 0x0f, +}; + +struct adux1020_data { + struct i2c_client *client; + struct iio_dev *indio_dev; + struct mutex lock; + struct regmap *regmap; +}; + +struct adux1020_mode_data { + u8 bytes; + u8 buf_len; + u16 int_en; +}; + +static const struct adux1020_mode_data adux1020_modes[] = { + [ADUX1020_MODE_PROX_I] = { + .bytes = 2, + .buf_len = 1, + .int_en = ADUX1020_PROX_INT_ENABLE, + }, +}; + +static const struct regmap_config adux1020_regmap_config = { + .name = ADUX1020_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x6F, + .cache_type = REGCACHE_NONE, +}; + +static const struct reg_sequence adux1020_def_conf[] = { + { 0x000c, 0x000f }, + { 0x0010, 0x1010 }, + { 0x0011, 0x004c }, + { 0x0012, 0x5f0c }, + { 0x0013, 0xada5 }, + { 0x0014, 0x0080 }, + { 0x0015, 0x0000 }, + { 0x0016, 0x0600 }, + { 0x0017, 0x0000 }, + { 0x0018, 0x2693 }, + { 0x0019, 0x0004 }, + { 0x001a, 0x4280 }, + { 0x001b, 0x0060 }, + { 0x001c, 0x2094 }, + { 0x001d, 0x0020 }, + { 0x001e, 0x0001 }, + { 0x001f, 0x0100 }, + { 0x0020, 0x0320 }, + { 0x0021, 0x0A13 }, + { 0x0022, 0x0320 }, + { 0x0023, 0x0113 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x2412 }, + { 0x0026, 0x2412 }, + { 0x0027, 0x0022 }, + { 0x0028, 0x0000 }, + { 0x0029, 0x0300 }, + { 0x002a, 0x0700 }, + { 0x002b, 0x0600 }, + { 0x002c, 0x6000 }, + { 0x002d, 0x4000 }, + { 0x002e, 0x0000 }, + { 0x002f, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0031, 0x0000 }, + { 0x0032, 0x0040 }, + { 0x0033, 0x0008 }, + { 0x0034, 0xE400 }, + { 0x0038, 0x8080 }, + { 0x0039, 0x8080 }, + { 0x003a, 0x2000 }, + { 0x003b, 0x1f00 }, + { 0x003c, 0x2000 }, + { 0x003d, 0x2000 }, + { 0x003e, 0x0000 }, + { 0x0040, 0x8069 }, + { 0x0041, 0x1f2f }, + { 0x0042, 0x4000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0008 }, + { 0x0046, 0x0000 }, + { 0x0048, 0x00ef }, + { 0x0049, 0x0000 }, + { 0x0045, 0x0000 }, +}; + +static const int adux1020_rates[][2] = { + { 0, 100000 }, + { 0, 200000 }, + { 0, 500000 }, + { 1, 0 }, + { 2, 0 }, + { 5, 0 }, + { 10, 0 }, + { 20, 0 }, + { 50, 0 }, + { 100, 0 }, + { 190, 0 }, + { 450, 0 }, + { 820, 0 }, + { 1400, 0 }, +}; + +static const int adux1020_led_currents[][2] = { + { 0, 25000 }, + { 0, 40000 }, + { 0, 55000 }, + { 0, 70000 }, + { 0, 85000 }, + { 0, 100000 }, + { 0, 115000 }, + { 0, 130000 }, + { 0, 145000 }, + { 0, 160000 }, + { 0, 175000 }, + { 0, 190000 }, + { 0, 205000 }, + { 0, 220000 }, + { 0, 235000 }, + { 0, 250000 }, +}; + +static int adux1020_flush_fifo(struct adux1020_data *data) +{ + int ret; + + /* Force Idle mode */ + ret = regmap_write(data->regmap, ADUX1020_REG_FORCE_MODE, + ADUX1020_ACTIVE_4_STATE); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_OP_MODE_MASK, ADUX1020_MODE_FORCE); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_OP_MODE_MASK, ADUX1020_MODE_IDLE); + if (ret < 0) + return ret; + + /* Flush FIFO */ + ret = regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_ON); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, ADUX1020_REG_INT_STATUS, + ADUX1020_FIFO_FLUSH); + if (ret < 0) + return ret; + + return regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_RESET); +} + +static int adux1020_read_fifo(struct adux1020_data *data, u16 *buf, u8 buf_len) +{ + unsigned int regval; + int i, ret; + + /* Enable 32MHz clock */ + ret = regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_ON); + if (ret < 0) + return ret; + + for (i = 0; i < buf_len; i++) { + ret = regmap_read(data->regmap, ADUX1020_REG_DATA_BUFFER, + ®val); + if (ret < 0) + return ret; + + buf[i] = regval; + } + + /* Set 32MHz clock to be controlled by internal state machine */ + return regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_RESET); +} + +static int adux1020_set_mode(struct adux1020_data *data, + enum adux1020_op_modes mode) +{ + int ret; + + /* Switch to standby mode before changing the mode */ + ret = regmap_write(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_MODE_STANDBY); + if (ret < 0) + return ret; + + /* Set data out and switch to the desired mode */ + switch (mode) { + case ADUX1020_MODE_PROX_I: + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_DATA_OUT_MODE_MASK, + ADUX1020_DATA_OUT_PROX_I); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_OP_MODE_MASK, + ADUX1020_MODE_PROX_I); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int adux1020_measure(struct adux1020_data *data, + enum adux1020_op_modes mode, + u16 *val) +{ + unsigned int status; + int ret, tries = 50; + + /* Disable INT pin as polling is going to be used */ + ret = regmap_write(data->regmap, ADUX1020_REG_INT_ENABLE, + ADUX1020_INT_DISABLE); + if (ret < 0) + return ret; + + /* Enable mode interrupt */ + ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + ADUX1020_MODE_INT_MASK, + adux1020_modes[mode].int_en); + if (ret < 0) + return ret; + + while (tries--) { + ret = regmap_read(data->regmap, ADUX1020_REG_INT_STATUS, + &status); + if (ret < 0) + return ret; + + status &= ADUX1020_FIFO_STATUS_MASK; + if (status >= adux1020_modes[mode].bytes) + break; + msleep(20); + } + + if (tries < 0) + return -EIO; + + ret = adux1020_read_fifo(data, val, adux1020_modes[mode].buf_len); + if (ret < 0) + return ret; + + /* Clear mode interrupt */ + ret = regmap_write(data->regmap, ADUX1020_REG_INT_STATUS, + (~adux1020_modes[mode].int_en)); + if (ret < 0) + return ret; + + /* Disable mode interrupts */ + return regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + ADUX1020_MODE_INT_MASK, + ADUX1020_MODE_INT_DISABLE); +} + +static int adux1020_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adux1020_data *data = iio_priv(indio_dev); + u16 buf[3]; + int ret = -EINVAL; + unsigned int regval; + + mutex_lock(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_PROXIMITY: + ret = adux1020_set_mode(data, ADUX1020_MODE_PROX_I); + if (ret < 0) + goto fail; + + ret = adux1020_measure(data, ADUX1020_MODE_PROX_I, buf); + if (ret < 0) + goto fail; + + *val = buf[0]; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_CURRENT: + ret = regmap_read(data->regmap, + ADUX1020_REG_LED_CURRENT, ®val); + if (ret < 0) + goto fail; + + regval = regval & ADUX1020_LED_CURRENT_MASK; + + *val = adux1020_led_currents[regval][0]; + *val2 = adux1020_led_currents[regval][1]; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_PROXIMITY: + ret = regmap_read(data->regmap, ADUX1020_REG_FREQUENCY, + ®val); + if (ret < 0) + goto fail; + + regval = FIELD_GET(ADUX1020_PROX_FREQ_MASK, regval); + + *val = adux1020_rates[regval][0]; + *val2 = adux1020_rates[regval][1]; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + break; + } + break; + default: + break; + } + +fail: + mutex_unlock(&data->lock); + + return ret; +}; + +static inline int adux1020_find_index(const int array[][2], int count, int val, + int val2) +{ + int i; + + for (i = 0; i < count; i++) + if (val == array[i][0] && val2 == array[i][1]) + return i; + + return -EINVAL; +} + +static int adux1020_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adux1020_data *data = iio_priv(indio_dev); + int i, ret = -EINVAL; + + mutex_lock(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type == IIO_PROXIMITY) { + i = adux1020_find_index(adux1020_rates, + ARRAY_SIZE(adux1020_rates), + val, val2); + if (i < 0) { + ret = i; + goto fail; + } + + ret = regmap_update_bits(data->regmap, + ADUX1020_REG_FREQUENCY, + ADUX1020_PROX_FREQ_MASK, + ADUX1020_PROX_FREQ(i)); + } + break; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type == IIO_CURRENT) { + i = adux1020_find_index(adux1020_led_currents, + ARRAY_SIZE(adux1020_led_currents), + val, val2); + if (i < 0) { + ret = i; + goto fail; + } + + ret = regmap_update_bits(data->regmap, + ADUX1020_REG_LED_CURRENT, + ADUX1020_LED_CURRENT_MASK, i); + } + break; + default: + break; + } + +fail: + mutex_unlock(&data->lock); + + return ret; +} + +static int adux1020_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct adux1020_data *data = iio_priv(indio_dev); + int ret, mask; + + mutex_lock(&data->lock); + + ret = regmap_write(data->regmap, ADUX1020_REG_INT_ENABLE, + ADUX1020_INT_ENABLE); + if (ret < 0) + goto fail; + + ret = regmap_write(data->regmap, ADUX1020_REG_INT_POLARITY, 0); + if (ret < 0) + goto fail; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + mask = ADUX1020_PROX_ON1_INT; + else + mask = ADUX1020_PROX_OFF1_INT; + + if (state) + state = 0; + else + state = mask; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + mask, state); + if (ret < 0) + goto fail; + + /* + * Trigger proximity interrupt when the intensity is above + * or below threshold + */ + ret = regmap_update_bits(data->regmap, ADUX1020_REG_PROX_TYPE, + ADUX1020_PROX_TYPE, + ADUX1020_PROX_TYPE); + if (ret < 0) + goto fail; + + /* Set proximity mode */ + ret = adux1020_set_mode(data, ADUX1020_MODE_PROX_I); + break; + default: + ret = -EINVAL; + break; + } + +fail: + mutex_unlock(&data->lock); + + return ret; +} + +static int adux1020_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct adux1020_data *data = iio_priv(indio_dev); + int ret, mask; + unsigned int regval; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + mask = ADUX1020_PROX_ON1_INT; + else + mask = ADUX1020_PROX_OFF1_INT; + break; + default: + return -EINVAL; + } + + ret = regmap_read(data->regmap, ADUX1020_REG_INT_MASK, ®val); + if (ret < 0) + return ret; + + return !(regval & mask); +} + +static int adux1020_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct adux1020_data *data = iio_priv(indio_dev); + u8 reg; + int ret; + unsigned int regval; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + reg = ADUX1020_REG_PROX_TH_ON1; + else + reg = ADUX1020_REG_PROX_TH_OFF1; + break; + default: + return -EINVAL; + } + + ret = regmap_read(data->regmap, reg, ®val); + if (ret < 0) + return ret; + + *val = regval; + + return IIO_VAL_INT; +} + +static int adux1020_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct adux1020_data *data = iio_priv(indio_dev); + u8 reg; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + reg = ADUX1020_REG_PROX_TH_ON1; + else + reg = ADUX1020_REG_PROX_TH_OFF1; + break; + default: + return -EINVAL; + } + + /* Full scale threshold value is 0-65535 */ + if (val < 0 || val > 65535) + return -EINVAL; + + return regmap_write(data->regmap, reg, val); +} + +static const struct iio_event_spec adux1020_proximity_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec adux1020_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .event_spec = adux1020_proximity_event, + .num_event_specs = ARRAY_SIZE(adux1020_proximity_event), + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .extend_name = "led", + .output = 1, + }, +}; + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "0.1 0.2 0.5 1 2 5 10 20 50 100 190 450 820 1400"); + +static struct attribute *adux1020_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group adux1020_attribute_group = { + .attrs = adux1020_attributes, +}; + +static const struct iio_info adux1020_info = { + .attrs = &adux1020_attribute_group, + .read_raw = adux1020_read_raw, + .write_raw = adux1020_write_raw, + .read_event_config = adux1020_read_event_config, + .write_event_config = adux1020_write_event_config, + .read_event_value = adux1020_read_thresh, + .write_event_value = adux1020_write_thresh, +}; + +static irqreturn_t adux1020_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct adux1020_data *data = iio_priv(indio_dev); + int ret, status; + + ret = regmap_read(data->regmap, ADUX1020_REG_INT_STATUS, &status); + if (ret < 0) + return IRQ_HANDLED; + + status &= ADUX1020_MODE_INT_STATUS_MASK; + + if (status & ADUX1020_INT_PROX_ON1) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + if (status & ADUX1020_INT_PROX_OFF1) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + regmap_update_bits(data->regmap, ADUX1020_REG_INT_STATUS, + ADUX1020_MODE_INT_MASK, ADUX1020_INT_CLEAR); + + return IRQ_HANDLED; +} + +static int adux1020_chip_init(struct adux1020_data *data) +{ + struct i2c_client *client = data->client; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ADUX1020_REG_CHIP_ID, &val); + if (ret < 0) + return ret; + + if ((val & ADUX1020_CHIP_ID_MASK) != ADUX1020_CHIP_ID) { + dev_err(&client->dev, "invalid chip id 0x%04x\n", val); + return -ENODEV; + } + + dev_dbg(&client->dev, "Detected ADUX1020 with chip id: 0x%04x\n", val); + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_SW_RESET, + ADUX1020_SW_RESET, ADUX1020_SW_RESET); + if (ret < 0) + return ret; + + /* Load default configuration */ + ret = regmap_multi_reg_write(data->regmap, adux1020_def_conf, + ARRAY_SIZE(adux1020_def_conf)); + if (ret < 0) + return ret; + + ret = adux1020_flush_fifo(data); + if (ret < 0) + return ret; + + /* Use LED_IREF for proximity mode */ + ret = regmap_update_bits(data->regmap, ADUX1020_REG_LED_CURRENT, + ADUX1020_LED_PIREF_EN, 0); + if (ret < 0) + return ret; + + /* Mask all interrupts */ + return regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + ADUX1020_MODE_INT_MASK, ADUX1020_MODE_INT_DISABLE); +} + +static int adux1020_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adux1020_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &adux1020_info; + indio_dev->name = ADUX1020_DRV_NAME; + indio_dev->channels = adux1020_channels; + indio_dev->num_channels = ARRAY_SIZE(adux1020_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + data = iio_priv(indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &adux1020_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "regmap initialization failed.\n"); + return PTR_ERR(data->regmap); + } + + data->client = client; + data->indio_dev = indio_dev; + mutex_init(&data->lock); + + ret = adux1020_chip_init(data); + if (ret) + return ret; + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, adux1020_interrupt_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + ADUX1020_DRV_NAME, indio_dev); + if (ret) { + dev_err(&client->dev, "irq request error %d\n", -ret); + return ret; + } + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id adux1020_id[] = { + { "adux1020", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adux1020_id); + +static const struct of_device_id adux1020_of_match[] = { + { .compatible = "adi,adux1020" }, + { } +}; +MODULE_DEVICE_TABLE(of, adux1020_of_match); + +static struct i2c_driver adux1020_driver = { + .driver = { + .name = ADUX1020_DRV_NAME, + .of_match_table = adux1020_of_match, + }, + .probe = adux1020_probe, + .id_table = adux1020_id, +}; +module_i2c_driver(adux1020_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_DESCRIPTION("ADUX1020 photometric sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index 28347df78cff..adb5ab9e3439 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -59,9 +59,9 @@ struct bh1750_chip_info { u16 int_time_low_mask; u16 int_time_high_mask; -} +}; -static const bh1750_chip_info_tbl[] = { +static const struct bh1750_chip_info bh1750_chip_info_tbl[] = { [BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 }, [BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 }, [BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 }, diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index 1019d625adb1..90e38fcc974b 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -532,7 +532,7 @@ static int cm36651_write_prox_event_config(struct iio_dev *indio_dev, int state) { struct cm36651_data *cm36651 = iio_priv(indio_dev); - int cmd, ret = -EINVAL; + int cmd, ret; mutex_lock(&cm36651->lock); diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index e666879007d2..92004a2563ea 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -686,6 +686,7 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) struct iio_dev *iio = _iio; struct opt3001 *opt = iio_priv(iio); int ret; + bool wake_result_ready_queue = false; if (!opt->ok_to_ignore_lock) mutex_lock(&opt->lock); @@ -720,13 +721,16 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) } opt->result = ret; opt->result_ready = true; - wake_up(&opt->result_ready_queue); + wake_result_ready_queue = true; } out: if (!opt->ok_to_ignore_lock) mutex_unlock(&opt->lock); + if (wake_result_ready_queue) + wake_up(&opt->result_ready_queue); + return IRQ_HANDLED; } diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 7c0291c5fe76..b542e5619ead 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -240,32 +240,42 @@ static const struct iio_info tcs3414_info = { .attrs = &tcs3414_attribute_group, }; -static int tcs3414_buffer_preenable(struct iio_dev *indio_dev) +static int tcs3414_buffer_postenable(struct iio_dev *indio_dev) { struct tcs3414_data *data = iio_priv(indio_dev); + int ret; + + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret) + return ret; data->control |= TCS3414_CONTROL_ADC_EN; - return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, + ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, data->control); + if (ret) + iio_triggered_buffer_predisable(indio_dev); + + return ret; } static int tcs3414_buffer_predisable(struct iio_dev *indio_dev) { struct tcs3414_data *data = iio_priv(indio_dev); - int ret; - - ret = iio_triggered_buffer_predisable(indio_dev); - if (ret < 0) - return ret; + int ret, ret2; data->control &= ~TCS3414_CONTROL_ADC_EN; - return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, + ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, data->control); + + ret2 = iio_triggered_buffer_predisable(indio_dev); + if (!ret) + ret = ret2; + + return ret; } static const struct iio_buffer_setup_ops tcs3414_buffer_setup_ops = { - .preenable = tcs3414_buffer_preenable, - .postenable = &iio_triggered_buffer_postenable, + .postenable = tcs3414_buffer_postenable, .predisable = tcs3414_buffer_predisable, }; diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 51421ac32517..16dacea9eadf 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -398,19 +398,23 @@ static int vcnl4000_probe(struct i2c_client *client, static const struct of_device_id vcnl_4000_of_match[] = { { .compatible = "vishay,vcnl4000", - .data = "VCNL4000", + .data = (void *)VCNL4000, }, { .compatible = "vishay,vcnl4010", - .data = "VCNL4010", + .data = (void *)VCNL4010, }, { - .compatible = "vishay,vcnl4010", - .data = "VCNL4020", + .compatible = "vishay,vcnl4020", + .data = (void *)VCNL4010, + }, + { + .compatible = "vishay,vcnl4040", + .data = (void *)VCNL4040, }, { .compatible = "vishay,vcnl4200", - .data = "VCNL4200", + .data = (void *)VCNL4200, }, {}, }; diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 8d0f15f27dc5..c3b5c1f6614d 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1130,7 +1130,6 @@ int bmp280_common_probe(struct device *dev, if (ret) goto out_runtime_pm_disable; - return 0; out_runtime_pm_disable: diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 612f79c53cfc..287d288e40c2 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -675,11 +675,15 @@ out: return IRQ_HANDLED; } -static int sx9500_buffer_preenable(struct iio_dev *indio_dev) +static int sx9500_buffer_postenable(struct iio_dev *indio_dev) { struct sx9500_data *data = iio_priv(indio_dev); int ret = 0, i; + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret) + return ret; + mutex_lock(&data->mutex); for (i = 0; i < SX9500_NUM_CHANNELS; i++) @@ -696,6 +700,9 @@ static int sx9500_buffer_preenable(struct iio_dev *indio_dev) mutex_unlock(&data->mutex); + if (ret) + iio_triggered_buffer_predisable(indio_dev); + return ret; } @@ -704,8 +711,6 @@ static int sx9500_buffer_predisable(struct iio_dev *indio_dev) struct sx9500_data *data = iio_priv(indio_dev); int ret = 0, i; - iio_triggered_buffer_predisable(indio_dev); - mutex_lock(&data->mutex); for (i = 0; i < SX9500_NUM_CHANNELS; i++) @@ -722,12 +727,13 @@ static int sx9500_buffer_predisable(struct iio_dev *indio_dev) mutex_unlock(&data->mutex); + iio_triggered_buffer_predisable(indio_dev); + return ret; } static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = { - .preenable = sx9500_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, + .postenable = sx9500_buffer_postenable, .predisable = sx9500_buffer_predisable, }; diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index f184ba5601d9..73ed550e3fc9 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -284,6 +284,8 @@ static int max31856_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); indio_dev->info = &max31856_info; + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = id->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = max31856_channels; diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index da10e6ccb43c..5920c0085d35 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -4399,6 +4399,7 @@ error2: error1: port_modify.set_port_cap_mask = 0; port_modify.clr_port_cap_mask = IB_PORT_CM_SUP; + kfree(port); while (--i) { if (!rdma_cap_ib_cm(ib_device, i)) continue; @@ -4407,6 +4408,7 @@ error1: ib_modify_port(ib_device, port->port_num, 0, &port_modify); ib_unregister_mad_agent(port->mad_agent); cm_remove_port_fs(port); + kfree(port); } free: kfree(cm_dev); @@ -4460,6 +4462,7 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data) spin_unlock_irq(&cm.state_lock); ib_unregister_mad_agent(cur_mad_agent); cm_remove_port_fs(port); + kfree(port); } kfree(cm_dev); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 0e3cf3461999..d78f67623f24 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2396,9 +2396,10 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, conn_id->cm_id.iw = NULL; cma_exch(conn_id, RDMA_CM_DESTROYING); mutex_unlock(&conn_id->handler_mutex); + mutex_unlock(&listen_id->handler_mutex); cma_deref_id(conn_id); rdma_destroy_id(&conn_id->id); - goto out; + return ret; } mutex_unlock(&conn_id->handler_mutex); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 99c4a55545cf..2dd2cfe9b561 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1987,8 +1987,6 @@ static int iw_query_port(struct ib_device *device, if (!netdev) return -ENODEV; - dev_put(netdev); - port_attr->max_mtu = IB_MTU_4096; port_attr->active_mtu = ib_mtu_int_to_enum(netdev->mtu); @@ -1996,19 +1994,22 @@ static int iw_query_port(struct ib_device *device, port_attr->state = IB_PORT_DOWN; port_attr->phys_state = IB_PORT_PHYS_STATE_DISABLED; } else { - inetdev = in_dev_get(netdev); + rcu_read_lock(); + inetdev = __in_dev_get_rcu(netdev); if (inetdev && inetdev->ifa_list) { port_attr->state = IB_PORT_ACTIVE; port_attr->phys_state = IB_PORT_PHYS_STATE_LINK_UP; - in_dev_put(inetdev); } else { port_attr->state = IB_PORT_INIT; port_attr->phys_state = IB_PORT_PHYS_STATE_PORT_CONFIGURATION_TRAINING; } + + rcu_read_unlock(); } + dev_put(netdev); err = device->ops.query_port(device, port_num, port_attr); if (err) return err; diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 7a7474000100..65b36548bc17 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1230,7 +1230,7 @@ static int res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh, msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { ret = -ENOMEM; - goto err; + goto err_get; } nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, @@ -1787,10 +1787,6 @@ static int nldev_stat_del_doit(struct sk_buff *skb, struct nlmsghdr *nlh, cntn = nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]); qpn = nla_get_u32(tb[RDMA_NLDEV_ATTR_RES_LQPN]); - ret = rdma_counter_unbind_qpn(device, port, qpn, cntn); - if (ret) - goto err_unbind; - if (fill_nldev_handle(msg, device) || nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port) || nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn) || @@ -1799,13 +1795,15 @@ static int nldev_stat_del_doit(struct sk_buff *skb, struct nlmsghdr *nlh, goto err_fill; } + ret = rdma_counter_unbind_qpn(device, port, qpn, cntn); + if (ret) + goto err_fill; + nlmsg_end(msg, nlh); ib_device_put(device); return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid); err_fill: - rdma_counter_bind_qpn(device, port, qpn, cntn); -err_unbind: nlmsg_free(msg); err: ib_device_put(device); diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index 1ab423b19f77..6eb6d2717ca5 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -426,7 +426,7 @@ int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev) int ret; rdma_for_each_port (dev, i) { - is_ib = rdma_protocol_ib(dev, i++); + is_ib = rdma_protocol_ib(dev, i); if (is_ib) break; } diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index f67a30fda1ed..163ff7ba92b7 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -451,8 +451,10 @@ void ib_umem_odp_release(struct ib_umem_odp *umem_odp) * that the hardware will not attempt to access the MR any more. */ if (!umem_odp->is_implicit_odp) { + mutex_lock(&umem_odp->umem_mutex); ib_umem_odp_unmap_dma_pages(umem_odp, ib_umem_start(umem_odp), ib_umem_end(umem_odp)); + mutex_unlock(&umem_odp->umem_mutex); kvfree(umem_odp->dma_list); kvfree(umem_odp->page_list); } @@ -719,6 +721,8 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem_odp *umem_odp, u64 virt, u64 addr; struct ib_device *dev = umem_odp->umem.ibdev; + lockdep_assert_held(&umem_odp->umem_mutex); + virt = max_t(u64, virt, ib_umem_start(umem_odp)); bound = min_t(u64, bound, ib_umem_end(umem_odp)); /* Note that during the run of this function, the @@ -726,7 +730,6 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem_odp *umem_odp, u64 virt, * faults from completion. We might be racing with other * invalidations, so we must make sure we free each page only * once. */ - mutex_lock(&umem_odp->umem_mutex); for (addr = virt; addr < bound; addr += BIT(umem_odp->page_shift)) { idx = (addr - ib_umem_start(umem_odp)) >> umem_odp->page_shift; if (umem_odp->page_list[idx]) { @@ -757,7 +760,6 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem_odp *umem_odp, u64 virt, umem_odp->npages--; } } - mutex_unlock(&umem_odp->umem_mutex); } EXPORT_SYMBOL(ib_umem_odp_unmap_dma_pages); diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index a8b9548bd1a2..599340c1f0b8 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -242,10 +242,13 @@ static void set_ep_sin6_addrs(struct c4iw_ep *ep, } } -static int dump_qp(struct c4iw_qp *qp, struct c4iw_debugfs_data *qpd) +static int dump_qp(unsigned long id, struct c4iw_qp *qp, + struct c4iw_debugfs_data *qpd) { int space; int cc; + if (id != qp->wq.sq.qid) + return 0; space = qpd->bufsize - qpd->pos - 1; if (space == 0) @@ -350,7 +353,7 @@ static int qp_open(struct inode *inode, struct file *file) xa_lock_irq(&qpd->devp->qps); xa_for_each(&qpd->devp->qps, index, qp) - dump_qp(qp, qpd); + dump_qp(index, qp, qpd); xa_unlock_irq(&qpd->devp->qps); qpd->buf[qpd->pos++] = 0; diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index aa772ee0706f..35c284af574d 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -275,13 +275,17 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, struct sk_buff *skb, struct c4iw_wr_wait *wr_waitp) { int err; - struct fw_ri_tpte tpt; + struct fw_ri_tpte *tpt; u32 stag_idx; static atomic_t key; if (c4iw_fatal_error(rdev)) return -EIO; + tpt = kmalloc(sizeof(*tpt), GFP_KERNEL); + if (!tpt) + return -ENOMEM; + stag_state = stag_state > 0; stag_idx = (*stag) >> 8; @@ -291,6 +295,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, mutex_lock(&rdev->stats.lock); rdev->stats.stag.fail++; mutex_unlock(&rdev->stats.lock); + kfree(tpt); return -ENOMEM; } mutex_lock(&rdev->stats.lock); @@ -305,28 +310,28 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, /* write TPT entry */ if (reset_tpt_entry) - memset(&tpt, 0, sizeof(tpt)); + memset(tpt, 0, sizeof(*tpt)); else { - tpt.valid_to_pdid = cpu_to_be32(FW_RI_TPTE_VALID_F | + tpt->valid_to_pdid = cpu_to_be32(FW_RI_TPTE_VALID_F | FW_RI_TPTE_STAGKEY_V((*stag & FW_RI_TPTE_STAGKEY_M)) | FW_RI_TPTE_STAGSTATE_V(stag_state) | FW_RI_TPTE_STAGTYPE_V(type) | FW_RI_TPTE_PDID_V(pdid)); - tpt.locread_to_qpid = cpu_to_be32(FW_RI_TPTE_PERM_V(perm) | + tpt->locread_to_qpid = cpu_to_be32(FW_RI_TPTE_PERM_V(perm) | (bind_enabled ? FW_RI_TPTE_MWBINDEN_F : 0) | FW_RI_TPTE_ADDRTYPE_V((zbva ? FW_RI_ZERO_BASED_TO : FW_RI_VA_BASED_TO))| FW_RI_TPTE_PS_V(page_size)); - tpt.nosnoop_pbladdr = !pbl_size ? 0 : cpu_to_be32( + tpt->nosnoop_pbladdr = !pbl_size ? 0 : cpu_to_be32( FW_RI_TPTE_PBLADDR_V(PBL_OFF(rdev, pbl_addr)>>3)); - tpt.len_lo = cpu_to_be32((u32)(len & 0xffffffffUL)); - tpt.va_hi = cpu_to_be32((u32)(to >> 32)); - tpt.va_lo_fbo = cpu_to_be32((u32)(to & 0xffffffffUL)); - tpt.dca_mwbcnt_pstag = cpu_to_be32(0); - tpt.len_hi = cpu_to_be32((u32)(len >> 32)); + tpt->len_lo = cpu_to_be32((u32)(len & 0xffffffffUL)); + tpt->va_hi = cpu_to_be32((u32)(to >> 32)); + tpt->va_lo_fbo = cpu_to_be32((u32)(to & 0xffffffffUL)); + tpt->dca_mwbcnt_pstag = cpu_to_be32(0); + tpt->len_hi = cpu_to_be32((u32)(len >> 32)); } err = write_adapter_mem(rdev, stag_idx + (rdev->lldi.vr->stag.start >> 5), - sizeof(tpt), &tpt, skb, wr_waitp); + sizeof(*tpt), tpt, skb, wr_waitp); if (reset_tpt_entry) { c4iw_put_resource(&rdev->resource.tpt_table, stag_idx); @@ -334,6 +339,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, rdev->stats.stag.cur -= 32; mutex_unlock(&rdev->stats.lock); } + kfree(tpt); return err; } diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index eb9368be28c1..bbcac539777a 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -2737,15 +2737,11 @@ int c4iw_create_srq(struct ib_srq *ib_srq, struct ib_srq_init_attr *attrs, if (CHELSIO_CHIP_VERSION(rhp->rdev.lldi.adapter_type) > CHELSIO_T6) srq->flags = T4_SRQ_LIMIT_SUPPORT; - ret = xa_insert_irq(&rhp->qps, srq->wq.qid, srq, GFP_KERNEL); - if (ret) - goto err_free_queue; - if (udata) { srq_key_mm = kmalloc(sizeof(*srq_key_mm), GFP_KERNEL); if (!srq_key_mm) { ret = -ENOMEM; - goto err_remove_handle; + goto err_free_queue; } srq_db_key_mm = kmalloc(sizeof(*srq_db_key_mm), GFP_KERNEL); if (!srq_db_key_mm) { @@ -2789,8 +2785,6 @@ err_free_srq_db_key_mm: kfree(srq_db_key_mm); err_free_srq_key_mm: kfree(srq_key_mm); -err_remove_handle: - xa_erase_irq(&rhp->qps, srq->wq.qid); err_free_queue: free_srq_queue(srq, ucontext ? &ucontext->uctx : &rhp->rdev.uctx, srq->wr_waitp); @@ -2813,8 +2807,6 @@ void c4iw_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata) rhp = srq->rhp; pr_debug("%s id %d\n", __func__, srq->wq.qid); - - xa_erase_irq(&rhp->qps, srq->wq.qid); ucontext = rdma_udata_to_drv_context(udata, struct c4iw_ucontext, ibucontext); free_srq_queue(srq, ucontext ? &ucontext->uctx : &rhp->rdev.uctx, diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index 2395fd4233a7..2ed7bfd5feea 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -1526,8 +1526,11 @@ int sdma_init(struct hfi1_devdata *dd, u8 port) } ret = rhashtable_init(tmp_sdma_rht, &sdma_rht_params); - if (ret < 0) + if (ret < 0) { + kfree(tmp_sdma_rht); goto bail; + } + dd->sdma_rht = tmp_sdma_rht; dd_dev_info(dd, "SDMA num_sdma: %u\n", dd->num_sdma); diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 8056930bbe2c..cd9ee1664a69 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -2773,6 +2773,10 @@ int i40iw_register_rdma_device(struct i40iw_device *iwdev) return -ENOMEM; iwibdev = iwdev->iwibdev; rdma_set_device_sysfs_group(&iwibdev->ibdev, &i40iw_attr_group); + ret = ib_device_set_netdev(&iwibdev->ibdev, iwdev->netdev, 1); + if (ret) + goto error; + ret = ib_register_device(&iwibdev->ibdev, "i40iw%d"); if (ret) goto error; diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 59022b744144..d609f4659afb 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1298,29 +1298,6 @@ static int devx_handle_mkey_create(struct mlx5_ib_dev *dev, return 0; } -static void devx_free_indirect_mkey(struct rcu_head *rcu) -{ - kfree(container_of(rcu, struct devx_obj, devx_mr.rcu)); -} - -/* This function to delete from the radix tree needs to be called before - * destroying the underlying mkey. Otherwise a race might occur in case that - * other thread will get the same mkey before this one will be deleted, - * in that case it will fail via inserting to the tree its own data. - * - * Note: - * An error in the destroy is not expected unless there is some other indirect - * mkey which points to this one. In a kernel cleanup flow it will be just - * destroyed in the iterative destruction call. In a user flow, in case - * the application didn't close in the expected order it's its own problem, - * the mkey won't be part of the tree, in both cases the kernel is safe. - */ -static void devx_cleanup_mkey(struct devx_obj *obj) -{ - xa_erase(&obj->ib_dev->mdev->priv.mkey_table, - mlx5_base_mkey(obj->devx_mr.mmkey.key)); -} - static void devx_cleanup_subscription(struct mlx5_ib_dev *dev, struct devx_event_subscription *sub) { @@ -1362,8 +1339,16 @@ static int devx_obj_cleanup(struct ib_uobject *uobject, int ret; dev = mlx5_udata_to_mdev(&attrs->driver_udata); - if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) - devx_cleanup_mkey(obj); + if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) { + /* + * The pagefault_single_data_segment() does commands against + * the mmkey, we must wait for that to stop before freeing the + * mkey, as another allocation could get the same mkey #. + */ + xa_erase(&obj->ib_dev->mdev->priv.mkey_table, + mlx5_base_mkey(obj->devx_mr.mmkey.key)); + synchronize_srcu(&dev->mr_srcu); + } if (obj->flags & DEVX_OBJ_FLAGS_DCT) ret = mlx5_core_destroy_dct(obj->ib_dev->mdev, &obj->core_dct); @@ -1382,12 +1367,6 @@ static int devx_obj_cleanup(struct ib_uobject *uobject, devx_cleanup_subscription(dev, sub_entry); mutex_unlock(&devx_event_table->event_xa_lock); - if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) { - call_srcu(&dev->mr_srcu, &obj->devx_mr.rcu, - devx_free_indirect_mkey); - return ret; - } - kfree(obj); return ret; } @@ -1491,26 +1470,21 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( &obj_id); WARN_ON(obj->dinlen > MLX5_MAX_DESTROY_INBOX_SIZE_DW * sizeof(u32)); - if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) { - err = devx_handle_mkey_indirect(obj, dev, cmd_in, cmd_out); - if (err) - goto obj_destroy; - } - err = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT, cmd_out, cmd_out_len); if (err) - goto err_copy; + goto obj_destroy; if (opcode == MLX5_CMD_OP_CREATE_GENERAL_OBJECT) obj_type = MLX5_GET(general_obj_in_cmd_hdr, cmd_in, obj_type); - obj->obj_id = get_enc_obj_id(opcode | obj_type << 16, obj_id); + if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) { + err = devx_handle_mkey_indirect(obj, dev, cmd_in, cmd_out); + if (err) + goto obj_destroy; + } return 0; -err_copy: - if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) - devx_cleanup_mkey(obj); obj_destroy: if (obj->flags & DEVX_OBJ_FLAGS_DCT) mlx5_core_destroy_dct(obj->ib_dev->mdev, &obj->core_dct); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 2ceaef3ea3fb..1a98ee2e01c4 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -606,7 +606,7 @@ struct mlx5_ib_mr { struct mlx5_ib_dev *dev; u32 out[MLX5_ST_SZ_DW(create_mkey_out)]; struct mlx5_core_sig_ctx *sig; - int live; + unsigned int live; void *descs_alloc; int access_flags; /* Needed for rereg MR */ @@ -639,7 +639,6 @@ struct mlx5_ib_mw { struct mlx5_ib_devx_mr { struct mlx5_core_mkey mmkey; int ndescs; - struct rcu_head rcu; }; struct mlx5_ib_umr_context { diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 1eff031ef048..630599311586 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -84,32 +84,6 @@ static bool use_umr_mtt_update(struct mlx5_ib_mr *mr, u64 start, u64 length) length + (start & (MLX5_ADAPTER_PAGE_SIZE - 1)); } -static void update_odp_mr(struct mlx5_ib_mr *mr) -{ - if (is_odp_mr(mr)) { - /* - * This barrier prevents the compiler from moving the - * setting of umem->odp_data->private to point to our - * MR, before reg_umr finished, to ensure that the MR - * initialization have finished before starting to - * handle invalidations. - */ - smp_wmb(); - to_ib_umem_odp(mr->umem)->private = mr; - /* - * Make sure we will see the new - * umem->odp_data->private value in the invalidation - * routines, before we can get page faults on the - * MR. Page faults can happen once we put the MR in - * the tree, below this line. Without the barrier, - * there can be a fault handling and an invalidation - * before umem->odp_data->private == mr is visible to - * the invalidation handler. - */ - smp_wmb(); - } -} - static void reg_mr_callback(int status, struct mlx5_async_work *context) { struct mlx5_ib_mr *mr = @@ -1346,8 +1320,6 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mr->umem = umem; set_mr_fields(dev, mr, npages, length, access_flags); - update_odp_mr(mr); - if (use_umr) { int update_xlt_flags = MLX5_IB_UPD_XLT_ENABLE; @@ -1363,10 +1335,12 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, } } - if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { - mr->live = 1; + if (is_odp_mr(mr)) { + to_ib_umem_odp(mr->umem)->private = mr; atomic_set(&mr->num_pending_prefetch, 0); } + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) + smp_store_release(&mr->live, 1); return &mr->ibmr; error: @@ -1441,6 +1415,9 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (!mr->umem) return -EINVAL; + if (is_odp_mr(mr)) + return -EOPNOTSUPP; + if (flags & IB_MR_REREG_TRANS) { addr = virt_addr; len = length; @@ -1486,8 +1463,6 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, } mr->allocated_from_cache = 0; - if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) - mr->live = 1; } else { /* * Send a UMR WQE @@ -1516,7 +1491,6 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, set_mr_fields(dev, mr, npages, len, access_flags); - update_odp_mr(mr); return 0; err: @@ -1607,15 +1581,16 @@ static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) /* Prevent new page faults and * prefetch requests from succeeding */ - mr->live = 0; + WRITE_ONCE(mr->live, 0); + + /* Wait for all running page-fault handlers to finish. */ + synchronize_srcu(&dev->mr_srcu); /* dequeue pending prefetch requests for the mr */ if (atomic_read(&mr->num_pending_prefetch)) flush_workqueue(system_unbound_wq); WARN_ON(atomic_read(&mr->num_pending_prefetch)); - /* Wait for all running page-fault handlers to finish. */ - synchronize_srcu(&dev->mr_srcu); /* Destroy all page mappings */ if (!umem_odp->is_implicit_odp) mlx5_ib_invalidate_range(umem_odp, @@ -1987,14 +1962,25 @@ free: int mlx5_ib_dealloc_mw(struct ib_mw *mw) { + struct mlx5_ib_dev *dev = to_mdev(mw->device); struct mlx5_ib_mw *mmw = to_mmw(mw); int err; - err = mlx5_core_destroy_mkey((to_mdev(mw->device))->mdev, - &mmw->mmkey); - if (!err) - kfree(mmw); - return err; + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { + xa_erase(&dev->mdev->priv.mkey_table, + mlx5_base_mkey(mmw->mmkey.key)); + /* + * pagefault_single_data_segment() may be accessing mmw under + * SRCU if the user bound an ODP MR to this MW. + */ + synchronize_srcu(&dev->mr_srcu); + } + + err = mlx5_core_destroy_mkey(dev->mdev, &mmw->mmkey); + if (err) + return err; + kfree(mmw); + return 0; } int mlx5_ib_check_mr_status(struct ib_mr *ibmr, u32 check_mask, diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 2e9b43061797..3f9478d19376 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -178,6 +178,29 @@ void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset, return; } + /* + * The locking here is pretty subtle. Ideally the implicit children + * list would be protected by the umem_mutex, however that is not + * possible. Instead this uses a weaker update-then-lock pattern: + * + * srcu_read_lock() + * <change children list> + * mutex_lock(umem_mutex) + * mlx5_ib_update_xlt() + * mutex_unlock(umem_mutex) + * destroy lkey + * + * ie any change the children list must be followed by the locked + * update_xlt before destroying. + * + * The umem_mutex provides the acquire/release semantic needed to make + * the children list visible to a racing thread. While SRCU is not + * technically required, using it gives consistent use of the SRCU + * locking around the children list. + */ + lockdep_assert_held(&to_ib_umem_odp(mr->umem)->umem_mutex); + lockdep_assert_held(&mr->dev->mr_srcu); + odp = odp_lookup(offset * MLX5_IMR_MTT_SIZE, nentries * MLX5_IMR_MTT_SIZE, mr); @@ -202,15 +225,22 @@ static void mr_leaf_free_action(struct work_struct *work) struct ib_umem_odp *odp = container_of(work, struct ib_umem_odp, work); int idx = ib_umem_start(odp) >> MLX5_IMR_MTT_SHIFT; struct mlx5_ib_mr *mr = odp->private, *imr = mr->parent; + struct ib_umem_odp *odp_imr = to_ib_umem_odp(imr->umem); + int srcu_key; mr->parent = NULL; synchronize_srcu(&mr->dev->mr_srcu); - ib_umem_odp_release(odp); - if (imr->live) + if (smp_load_acquire(&imr->live)) { + srcu_key = srcu_read_lock(&mr->dev->mr_srcu); + mutex_lock(&odp_imr->umem_mutex); mlx5_ib_update_xlt(imr, idx, 1, 0, MLX5_IB_UPD_XLT_INDIRECT | MLX5_IB_UPD_XLT_ATOMIC); + mutex_unlock(&odp_imr->umem_mutex); + srcu_read_unlock(&mr->dev->mr_srcu, srcu_key); + } + ib_umem_odp_release(odp); mlx5_mr_cache_free(mr->dev, mr); if (atomic_dec_and_test(&imr->num_leaf_free)) @@ -278,7 +308,6 @@ void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start, idx - blk_start_idx + 1, 0, MLX5_IB_UPD_XLT_ZAP | MLX5_IB_UPD_XLT_ATOMIC); - mutex_unlock(&umem_odp->umem_mutex); /* * We are now sure that the device will not access the * memory. We can safely unmap it, and mark it as dirty if @@ -289,10 +318,12 @@ void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start, if (unlikely(!umem_odp->npages && mr->parent && !umem_odp->dying)) { - WRITE_ONCE(umem_odp->dying, 1); + WRITE_ONCE(mr->live, 0); + umem_odp->dying = 1; atomic_inc(&mr->parent->num_leaf_free); schedule_work(&umem_odp->work); } + mutex_unlock(&umem_odp->umem_mutex); } void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) @@ -429,8 +460,6 @@ static struct mlx5_ib_mr *implicit_mr_alloc(struct ib_pd *pd, mr->ibmr.lkey = mr->mmkey.key; mr->ibmr.rkey = mr->mmkey.key; - mr->live = 1; - mlx5_ib_dbg(dev, "key %x dev %p mr %p\n", mr->mmkey.key, dev->mdev, mr); @@ -484,6 +513,8 @@ next_mr: mtt->parent = mr; INIT_WORK(&odp->work, mr_leaf_free_action); + smp_store_release(&mtt->live, 1); + if (!nentries) start_idx = addr >> MLX5_IMR_MTT_SHIFT; nentries++; @@ -536,6 +567,7 @@ struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd, init_waitqueue_head(&imr->q_leaf_free); atomic_set(&imr->num_leaf_free, 0); atomic_set(&imr->num_pending_prefetch, 0); + smp_store_release(&imr->live, 1); return imr; } @@ -555,15 +587,19 @@ void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *imr) if (mr->parent != imr) continue; + mutex_lock(&umem_odp->umem_mutex); ib_umem_odp_unmap_dma_pages(umem_odp, ib_umem_start(umem_odp), ib_umem_end(umem_odp)); - if (umem_odp->dying) + if (umem_odp->dying) { + mutex_unlock(&umem_odp->umem_mutex); continue; + } - WRITE_ONCE(umem_odp->dying, 1); + umem_odp->dying = 1; atomic_inc(&imr->num_leaf_free); schedule_work(&umem_odp->work); + mutex_unlock(&umem_odp->umem_mutex); } up_read(&per_mm->umem_rwsem); @@ -773,7 +809,7 @@ next_mr: switch (mmkey->type) { case MLX5_MKEY_MR: mr = container_of(mmkey, struct mlx5_ib_mr, mmkey); - if (!mr->live || !mr->ibmr.pd) { + if (!smp_load_acquire(&mr->live) || !mr->ibmr.pd) { mlx5_ib_dbg(dev, "got dead MR\n"); ret = -EFAULT; goto srcu_unlock; @@ -1641,12 +1677,12 @@ static bool num_pending_prefetch_inc(struct ib_pd *pd, mr = container_of(mmkey, struct mlx5_ib_mr, mmkey); - if (mr->ibmr.pd != pd) { + if (!smp_load_acquire(&mr->live)) { ret = false; break; } - if (!mr->live) { + if (mr->ibmr.pd != pd) { ret = false; break; } diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c index 6cac0c88cf39..36cdfbdbd325 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c @@ -230,8 +230,6 @@ static void pvrdma_free_srq(struct pvrdma_dev *dev, struct pvrdma_srq *srq) pvrdma_page_dir_cleanup(dev, &srq->pdir); - kfree(srq); - atomic_dec(&dev->num_srqs); } diff --git a/drivers/infiniband/sw/siw/siw_qp.c b/drivers/infiniband/sw/siw/siw_qp.c index 430314c8abd9..52d402f39df9 100644 --- a/drivers/infiniband/sw/siw/siw_qp.c +++ b/drivers/infiniband/sw/siw/siw_qp.c @@ -182,12 +182,19 @@ void siw_qp_llp_close(struct siw_qp *qp) */ void siw_qp_llp_write_space(struct sock *sk) { - struct siw_cep *cep = sk_to_cep(sk); + struct siw_cep *cep; - cep->sk_write_space(sk); + read_lock(&sk->sk_callback_lock); + + cep = sk_to_cep(sk); + if (cep) { + cep->sk_write_space(sk); - if (!test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) - (void)siw_sq_start(cep->qp); + if (!test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) + (void)siw_sq_start(cep->qp); + } + + read_unlock(&sk->sk_callback_lock); } static int siw_qp_readq_init(struct siw_qp *qp, int irq_size, int orq_size) diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index cfca3c70599b..21f90a887485 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -643,8 +643,7 @@ static int v4l_stk_release(struct file *fp) dev->owner = NULL; } - if (is_present(dev)) - usb_autopm_put_interface(dev->interface); + usb_autopm_put_interface(dev->interface); mutex_unlock(&dev->lock); return v4l2_fh_release(fp); } diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 47ae84afac2e..1b1a794d639d 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -527,6 +527,7 @@ static int fastrpc_dma_buf_attach(struct dma_buf *dmabuf, FASTRPC_PHYS(buffer->phys), buffer->size); if (ret < 0) { dev_err(buffer->dev, "failed to get scatterlist from DMA API\n"); + kfree(a); return -EINVAL; } diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 32e9b1aed2ca..0a2b99e1af45 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -218,13 +218,21 @@ static void mei_mkhi_fix(struct mei_cl_device *cldev) { int ret; + /* No need to enable the client if nothing is needed from it */ + if (!cldev->bus->fw_f_fw_ver_supported && + !cldev->bus->hbm_f_os_supported) + return; + ret = mei_cldev_enable(cldev); if (ret) return; - ret = mei_fwver(cldev); - if (ret < 0) - dev_err(&cldev->dev, "FW version command failed %d\n", ret); + if (cldev->bus->fw_f_fw_ver_supported) { + ret = mei_fwver(cldev); + if (ret < 0) + dev_err(&cldev->dev, "FW version command failed %d\n", + ret); + } if (cldev->bus->hbm_f_os_supported) { ret = mei_osver(cldev); diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 77f7dff7098d..c09f8bb49495 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -79,6 +79,9 @@ #define MEI_DEV_ID_CNP_H 0xA360 /* Cannon Point H */ #define MEI_DEV_ID_CNP_H_4 0xA364 /* Cannon Point H 4 (iTouch) */ +#define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */ +#define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ + #define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ #define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index abe1b1f4362f..c4f6991d3028 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1355,6 +1355,8 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) #define MEI_CFG_FW_SPS \ .quirk_probe = mei_me_fw_type_sps +#define MEI_CFG_FW_VER_SUPP \ + .fw_ver_supported = 1 #define MEI_CFG_ICH_HFS \ .fw_status.count = 0 @@ -1392,31 +1394,41 @@ static const struct mei_cfg mei_me_ich10_cfg = { MEI_CFG_ICH10_HFS, }; -/* PCH devices */ -static const struct mei_cfg mei_me_pch_cfg = { +/* PCH6 devices */ +static const struct mei_cfg mei_me_pch6_cfg = { MEI_CFG_PCH_HFS, }; +/* PCH7 devices */ +static const struct mei_cfg mei_me_pch7_cfg = { + MEI_CFG_PCH_HFS, + MEI_CFG_FW_VER_SUPP, +}; + /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { MEI_CFG_PCH_HFS, + MEI_CFG_FW_VER_SUPP, MEI_CFG_FW_NM, }; /* PCH8 Lynx Point and newer devices */ static const struct mei_cfg mei_me_pch8_cfg = { MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, }; /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ static const struct mei_cfg mei_me_pch8_sps_cfg = { MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, MEI_CFG_FW_SPS, }; /* Cannon Lake and newer devices */ static const struct mei_cfg mei_me_pch12_cfg = { MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, }; @@ -1428,7 +1440,8 @@ static const struct mei_cfg *const mei_cfg_list[] = { [MEI_ME_UNDEF_CFG] = NULL, [MEI_ME_ICH_CFG] = &mei_me_ich_cfg, [MEI_ME_ICH10_CFG] = &mei_me_ich10_cfg, - [MEI_ME_PCH_CFG] = &mei_me_pch_cfg, + [MEI_ME_PCH6_CFG] = &mei_me_pch6_cfg, + [MEI_ME_PCH7_CFG] = &mei_me_pch7_cfg, [MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg, [MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg, [MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg, @@ -1473,6 +1486,8 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, mei_device_init(dev, &pdev->dev, &mei_me_hw_ops); hw->cfg = cfg; + dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; + return dev; } diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 08c84a0de4a8..1d8794828cbc 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -20,11 +20,13 @@ * @fw_status: FW status * @quirk_probe: device exclusion quirk * @dma_size: device DMA buffers size + * @fw_ver_supported: is fw version retrievable from FW */ struct mei_cfg { const struct mei_fw_status fw_status; bool (*quirk_probe)(struct pci_dev *pdev); size_t dma_size[DMA_DSCR_NUM]; + u32 fw_ver_supported:1; }; @@ -62,7 +64,8 @@ struct mei_me_hw { * @MEI_ME_UNDEF_CFG: Lower sentinel. * @MEI_ME_ICH_CFG: I/O Controller Hub legacy devices. * @MEI_ME_ICH10_CFG: I/O Controller Hub platforms Gen10 - * @MEI_ME_PCH_CFG: Platform Controller Hub platforms (Up to Gen8). + * @MEI_ME_PCH6_CFG: Platform Controller Hub platforms (Gen6). + * @MEI_ME_PCH7_CFG: Platform Controller Hub platforms (Gen7). * @MEI_ME_PCH_CPT_PBG_CFG:Platform Controller Hub workstations * with quirk for Node Manager exclusion. * @MEI_ME_PCH8_CFG: Platform Controller Hub Gen8 and newer @@ -77,7 +80,8 @@ enum mei_cfg_idx { MEI_ME_UNDEF_CFG, MEI_ME_ICH_CFG, MEI_ME_ICH10_CFG, - MEI_ME_PCH_CFG, + MEI_ME_PCH6_CFG, + MEI_ME_PCH7_CFG, MEI_ME_PCH_CPT_PBG_CFG, MEI_ME_PCH8_CFG, MEI_ME_PCH8_SPS_CFG, diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index f71a023aed3c..0f2141178299 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -426,6 +426,8 @@ struct mei_fw_version { * * @fw_ver : FW versions * + * @fw_f_fw_ver_supported : fw feature: fw version supported + * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients * @me_clients_map : FW clients bit map @@ -506,6 +508,8 @@ struct mei_device { struct mei_fw_version fw_ver[MEI_MAX_FW_VER_BLOCKS]; + unsigned int fw_f_fw_ver_supported:1; + struct rw_semaphore me_clients_rwsem; struct list_head me_clients; DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index d5a92c6eadb3..3dca63eddaa0 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -61,13 +61,13 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, MEI_ME_ICH10_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH6_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH6_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH7_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH7_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH7_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, MEI_ME_PCH8_SPS_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, MEI_ME_PCH8_SPS_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, MEI_ME_PCH8_CFG)}, @@ -96,6 +96,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_4, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH12_CFG)}, diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 97a97a9ccc36..e10b76089048 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -134,16 +134,15 @@ static void au_write_buf16(struct nand_chip *this, const u_char *buf, int len) /** * au_read_buf16 - read chip data into buffer - * @mtd: MTD device structure + * @this: NAND chip object * @buf: buffer to store date * @len: number of bytes to read * * read function for 16bit buswidth */ -static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +static void au_read_buf16(struct nand_chip *this, u_char *buf, int len) { int i; - struct nand_chip *this = mtd_to_nand(mtd); u16 *p = (u16 *) buf; len >>= 1; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1d8621d43160..7acf4a93b592 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -487,7 +487,7 @@ static int write_sr(struct spi_nor *nor, u8 val) SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); return spi_mem_exec_op(nor->spimem, &op); } diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 684aa51684db..b00274caae4f 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -705,7 +705,7 @@ qca8k_setup(struct dsa_switch *ds) BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); /* Setup connection between CPU port & user ports */ - for (i = 0; i < DSA_MAX_PORTS; i++) { + for (i = 0; i < QCA8K_NUM_PORTS; i++) { /* CPU port gets connected to all user ports of the switch */ if (dsa_is_cpu_port(ds, i)) { qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), @@ -1077,7 +1077,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev) if (id != QCA8K_ID_QCA8337) return -ENODEV; - priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); + priv->ds = dsa_switch_alloc(&mdiodev->dev, QCA8K_NUM_PORTS); if (!priv->ds) return -ENOMEM; diff --git a/drivers/net/dsa/rtl8366.c b/drivers/net/dsa/rtl8366.c index ca3d17e43ed8..ac88caca5ad4 100644 --- a/drivers/net/dsa/rtl8366.c +++ b/drivers/net/dsa/rtl8366.c @@ -339,10 +339,12 @@ int rtl8366_vlan_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct realtek_smi *smi = ds->priv; + u16 vid; int ret; - if (!smi->ops->is_vlan_valid(smi, port)) - return -EINVAL; + for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++) + if (!smi->ops->is_vlan_valid(smi, vid)) + return -EINVAL; dev_info(smi->dev, "prepare VLANs %04x..%04x\n", vlan->vid_begin, vlan->vid_end); @@ -370,8 +372,9 @@ void rtl8366_vlan_add(struct dsa_switch *ds, int port, u16 vid; int ret; - if (!smi->ops->is_vlan_valid(smi, port)) - return; + for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++) + if (!smi->ops->is_vlan_valid(smi, vid)) + return; dev_info(smi->dev, "add VLAN on port %d, %s, %s\n", port, diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/rtl8366rb.c index a268085ffad2..f5cc8b0a7c74 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/rtl8366rb.c @@ -507,7 +507,8 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) irq = of_irq_get(intc, 0); if (irq <= 0) { dev_err(smi->dev, "failed to get parent IRQ\n"); - return irq ? irq : -EINVAL; + ret = irq ? irq : -EINVAL; + goto out_put_node; } /* This clears the IRQ status register */ @@ -515,7 +516,7 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) &val); if (ret) { dev_err(smi->dev, "can't read interrupt status\n"); - return ret; + goto out_put_node; } /* Fetch IRQ edge information from the descriptor */ @@ -537,7 +538,7 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) val); if (ret) { dev_err(smi->dev, "could not configure IRQ polarity\n"); - return ret; + goto out_put_node; } ret = devm_request_threaded_irq(smi->dev, irq, NULL, @@ -545,7 +546,7 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) "RTL8366RB", smi); if (ret) { dev_err(smi->dev, "unable to request irq: %d\n", ret); - return ret; + goto out_put_node; } smi->irqdomain = irq_domain_add_linear(intc, RTL8366RB_NUM_INTERRUPT, @@ -553,12 +554,15 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) smi); if (!smi->irqdomain) { dev_err(smi->dev, "failed to create IRQ domain\n"); - return -EINVAL; + ret = -EINVAL; + goto out_put_node; } for (i = 0; i < smi->num_ports; i++) irq_set_parent(irq_create_mapping(smi->irqdomain, i), irq); - return 0; +out_put_node: + of_node_put(intc); + return ret; } static int rtl8366rb_set_addr(struct realtek_smi *smi) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index b9def744bcb3..7687ddcae159 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1897,7 +1897,9 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds, return sja1105_static_config_reload(priv); } -/* Caller must hold priv->tagger_data.meta_lock */ +/* Must be called only with priv->tagger_data.state bit + * SJA1105_HWTS_RX_EN cleared + */ static int sja1105_change_rxtstamping(struct sja1105_private *priv, bool on) { @@ -1954,16 +1956,17 @@ static int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, break; } - if (rx_on != priv->tagger_data.hwts_rx_en) { - spin_lock(&priv->tagger_data.meta_lock); + if (rx_on != test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) { + clear_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state); + rc = sja1105_change_rxtstamping(priv, rx_on); - spin_unlock(&priv->tagger_data.meta_lock); if (rc < 0) { dev_err(ds->dev, "Failed to change RX timestamping: %d\n", rc); - return -EFAULT; + return rc; } - priv->tagger_data.hwts_rx_en = rx_on; + if (rx_on) + set_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state); } if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) @@ -1982,7 +1985,7 @@ static int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, config.tx_type = HWTSTAMP_TX_ON; else config.tx_type = HWTSTAMP_TX_OFF; - if (priv->tagger_data.hwts_rx_en) + if (test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; else config.rx_filter = HWTSTAMP_FILTER_NONE; @@ -2005,12 +2008,12 @@ static void sja1105_rxtstamp_work(struct work_struct *work) mutex_lock(&priv->ptp_lock); - now = priv->tstamp_cc.read(&priv->tstamp_cc); - while ((skb = skb_dequeue(&data->skb_rxtstamp_queue)) != NULL) { struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); u64 ts; + now = priv->tstamp_cc.read(&priv->tstamp_cc); + *shwt = (struct skb_shared_hwtstamps) {0}; ts = SJA1105_SKB_CB(skb)->meta_tstamp; @@ -2031,7 +2034,7 @@ static bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, struct sja1105_private *priv = ds->priv; struct sja1105_tagger_data *data = &priv->tagger_data; - if (!data->hwts_rx_en) + if (!test_bit(SJA1105_HWTS_RX_EN, &data->state)) return false; /* We need to read the full PTP clock to reconstruct the Rx @@ -2201,6 +2204,7 @@ static int sja1105_probe(struct spi_device *spi) tagger_data = &priv->tagger_data; skb_queue_head_init(&tagger_data->skb_rxtstamp_queue); INIT_WORK(&tagger_data->rxtstamp_work, sja1105_rxtstamp_work); + spin_lock_init(&tagger_data->meta_lock); /* Connections between dsa_port and sja1105_port */ for (i = 0; i < SJA1105_NUM_PORTS; i++) { diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 84dc603138cf..58dd37ecde17 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -409,7 +409,8 @@ int sja1105_static_config_upload(struct sja1105_private *priv) rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len); if (rc < 0) { dev_err(dev, "Invalid config, cannot upload\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } /* Prevent PHY jabbering during switch reset by inhibiting * Tx on all ports and waiting for current packet to drain. @@ -418,7 +419,8 @@ int sja1105_static_config_upload(struct sja1105_private *priv) rc = sja1105_inhibit_tx(priv, port_bitmap, true); if (rc < 0) { dev_err(dev, "Failed to inhibit Tx on ports\n"); - return -ENXIO; + rc = -ENXIO; + goto out; } /* Wait for an eventual egress packet to finish transmission * (reach IFG). It is guaranteed that a second one will not diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c index 7548247455d7..1b1a09095c0d 100644 --- a/drivers/net/ethernet/atheros/ag71xx.c +++ b/drivers/net/ethernet/atheros/ag71xx.c @@ -526,7 +526,7 @@ static int ag71xx_mdio_probe(struct ag71xx *ag) struct device *dev = &ag->pdev->dev; struct net_device *ndev = ag->ndev; static struct mii_bus *mii_bus; - struct device_node *np; + struct device_node *np, *mnp; int err; np = dev->of_node; @@ -571,7 +571,9 @@ static int ag71xx_mdio_probe(struct ag71xx *ag) msleep(200); } - err = of_mdiobus_register(mii_bus, np); + mnp = of_get_child_by_name(np, "mdio"); + err = of_mdiobus_register(mii_bus, mnp); + of_node_put(mnp); if (err) goto mdio_err_put_clk; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c index 5b602243d573..a4dead4ab0ed 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c @@ -137,13 +137,12 @@ static int uldrx_handler(struct sge_rspq *q, const __be64 *rsp, static int alloc_uld_rxqs(struct adapter *adap, struct sge_uld_rxq_info *rxq_info, bool lro) { - struct sge *s = &adap->sge; unsigned int nq = rxq_info->nrxq + rxq_info->nciq; + int i, err, msi_idx, que_idx = 0, bmap_idx = 0; struct sge_ofld_rxq *q = rxq_info->uldrxq; unsigned short *ids = rxq_info->rspq_id; - unsigned int bmap_idx = 0; + struct sge *s = &adap->sge; unsigned int per_chan; - int i, err, msi_idx, que_idx = 0; per_chan = rxq_info->nrxq / adap->params.nports; @@ -161,6 +160,10 @@ static int alloc_uld_rxqs(struct adapter *adap, if (msi_idx >= 0) { bmap_idx = get_msix_idx_from_bmap(adap); + if (bmap_idx < 0) { + err = -ENOSPC; + goto freeout; + } msi_idx = adap->msix_info_ulds[bmap_idx].idx; } err = t4_sge_alloc_rxq(adap, &q->rspq, false, diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c index 3e863a71c513..7df5d7d211d4 100644 --- a/drivers/net/ethernet/hisilicon/hns_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -148,11 +148,15 @@ static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, { u32 time_cnt; u32 reg_value; + int ret; regmap_write(mdio_dev->subctrl_vbase, cfg_reg, set_val); for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) { - regmap_read(mdio_dev->subctrl_vbase, st_reg, ®_value); + ret = regmap_read(mdio_dev->subctrl_vbase, st_reg, ®_value); + if (ret) + return ret; + reg_value &= st_msk; if ((!!check_st) == (!!reg_value)) break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 9231b39d18b2..c501bf2a0252 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -112,17 +112,11 @@ int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, u32 out[MLX5_ST_SZ_DW(destroy_mkey_out)] = {0}; u32 in[MLX5_ST_SZ_DW(destroy_mkey_in)] = {0}; struct xarray *mkeys = &dev->priv.mkey_table; - struct mlx5_core_mkey *deleted_mkey; unsigned long flags; xa_lock_irqsave(mkeys, flags); - deleted_mkey = __xa_erase(mkeys, mlx5_base_mkey(mkey->key)); + __xa_erase(mkeys, mlx5_base_mkey(mkey->key)); xa_unlock_irqrestore(mkeys, flags); - if (!deleted_mkey) { - mlx5_core_dbg(dev, "failed xarray delete of mkey 0x%x\n", - mlx5_base_mkey(mkey->key)); - return -ENOENT; - } MLX5_SET(destroy_mkey_in, in, opcode, MLX5_CMD_OP_DESTROY_MKEY); MLX5_SET(destroy_mkey_in, in, mkey_index, mlx5_mkey_to_idx(mkey->key)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c index 913f1e5aaaf2..d7c7467e2d53 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c @@ -137,7 +137,8 @@ dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool, icm_mr->icm_start_addr = icm_mr->dm.addr; - align_diff = icm_mr->icm_start_addr % align_base; + /* align_base is always a power of 2 */ + align_diff = icm_mr->icm_start_addr & (align_base - 1); if (align_diff) icm_mr->used_length = align_base - align_diff; diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c index b063eb78fa0c..aac115136720 100644 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ b/drivers/net/ethernet/mscc/ocelot_board.c @@ -388,13 +388,14 @@ static int mscc_ocelot_probe(struct platform_device *pdev) continue; phy = of_phy_find_device(phy_node); + of_node_put(phy_node); if (!phy) continue; err = ocelot_probe_port(ocelot, port, regs, phy); if (err) { of_node_put(portnp); - return err; + goto out_put_ports; } phy_mode = of_get_phy_mode(portnp); @@ -422,7 +423,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev) "invalid phy mode for port%d, (Q)SGMII only\n", port); of_node_put(portnp); - return -EINVAL; + err = -EINVAL; + goto out_put_ports; } serdes = devm_of_phy_get(ocelot->dev, portnp, NULL); @@ -435,7 +437,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev) "missing SerDes phys for port%d\n", port); - goto err_probe_ports; + of_node_put(portnp); + goto out_put_ports; } ocelot->ports[port]->serdes = serdes; @@ -447,9 +450,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Ocelot switch probed\n"); - return 0; - -err_probe_ports: +out_put_ports: + of_node_put(ports); return err; } diff --git a/drivers/net/ethernet/pensando/Kconfig b/drivers/net/ethernet/pensando/Kconfig index bd0583e409df..d25b88f53de4 100644 --- a/drivers/net/ethernet/pensando/Kconfig +++ b/drivers/net/ethernet/pensando/Kconfig @@ -20,6 +20,7 @@ if NET_VENDOR_PENSANDO config IONIC tristate "Pensando Ethernet IONIC Support" depends on 64BIT && PCI + select NET_DEVLINK help This enables the support for the Pensando family of Ethernet adapters. More specific information on this driver can be diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 457444894d80..b4b8ba00ee01 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -2787,6 +2787,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n", err); + dev_kfree_skb_irq(skb); ql_free_large_buffers(qdev); return -ENOMEM; } diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 55db7fbd43cc..f9e6744d8fd6 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -282,7 +282,6 @@ struct netsec_desc_ring { void *vaddr; u16 head, tail; u16 xdp_xmit; /* netsec_xdp_xmit packets */ - bool is_xdp; struct page_pool *page_pool; struct xdp_rxq_info xdp_rxq; spinlock_t lock; /* XDP tx queue locking */ @@ -634,8 +633,7 @@ static bool netsec_clean_tx_dring(struct netsec_priv *priv) unsigned int bytes; int cnt = 0; - if (dring->is_xdp) - spin_lock(&dring->lock); + spin_lock(&dring->lock); bytes = 0; entry = dring->vaddr + DESC_SZ * tail; @@ -682,8 +680,8 @@ next: entry = dring->vaddr + DESC_SZ * tail; cnt++; } - if (dring->is_xdp) - spin_unlock(&dring->lock); + + spin_unlock(&dring->lock); if (!cnt) return false; @@ -799,9 +797,6 @@ static void netsec_set_tx_de(struct netsec_priv *priv, de->data_buf_addr_lw = lower_32_bits(desc->dma_addr); de->buf_len_info = (tx_ctrl->tcp_seg_len << 16) | desc->len; de->attr = attr; - /* under spin_lock if using XDP */ - if (!dring->is_xdp) - dma_wmb(); dring->desc[idx] = *desc; if (desc->buf_type == TYPE_NETSEC_SKB) @@ -1123,12 +1118,10 @@ static netdev_tx_t netsec_netdev_start_xmit(struct sk_buff *skb, u16 tso_seg_len = 0; int filled; - if (dring->is_xdp) - spin_lock_bh(&dring->lock); + spin_lock_bh(&dring->lock); filled = netsec_desc_used(dring); if (netsec_check_stop_tx(priv, filled)) { - if (dring->is_xdp) - spin_unlock_bh(&dring->lock); + spin_unlock_bh(&dring->lock); net_warn_ratelimited("%s %s Tx queue full\n", dev_name(priv->dev), ndev->name); return NETDEV_TX_BUSY; @@ -1161,8 +1154,7 @@ static netdev_tx_t netsec_netdev_start_xmit(struct sk_buff *skb, tx_desc.dma_addr = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); if (dma_mapping_error(priv->dev, tx_desc.dma_addr)) { - if (dring->is_xdp) - spin_unlock_bh(&dring->lock); + spin_unlock_bh(&dring->lock); netif_err(priv, drv, priv->ndev, "%s: DMA mapping failed\n", __func__); ndev->stats.tx_dropped++; @@ -1177,8 +1169,7 @@ static netdev_tx_t netsec_netdev_start_xmit(struct sk_buff *skb, netdev_sent_queue(priv->ndev, skb->len); netsec_set_tx_de(priv, dring, &tx_ctrl, &tx_desc, skb); - if (dring->is_xdp) - spin_unlock_bh(&dring->lock); + spin_unlock_bh(&dring->lock); netsec_write(priv, NETSEC_REG_NRM_TX_PKTCNT, 1); /* submit another tx */ return NETDEV_TX_OK; @@ -1262,7 +1253,6 @@ err: static void netsec_setup_tx_dring(struct netsec_priv *priv) { struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX]; - struct bpf_prog *xdp_prog = READ_ONCE(priv->xdp_prog); int i; for (i = 0; i < DESC_NUM; i++) { @@ -1275,12 +1265,6 @@ static void netsec_setup_tx_dring(struct netsec_priv *priv) */ de->attr = 1U << NETSEC_TX_SHIFT_OWN_FIELD; } - - if (xdp_prog) - dring->is_xdp = true; - else - dring->is_xdp = false; - } static int netsec_setup_rx_dring(struct netsec_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 9b4b5f69fc02..2cb9c53f93b8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -401,8 +401,11 @@ static void dwmac4_set_filter(struct mac_device_info *hw, int numhashregs = (hw->multicast_filter_bins >> 5); int mcbitslog2 = hw->mcast_bits_log2; unsigned int value; + u32 mc_filter[8]; int i; + memset(mc_filter, 0, sizeof(mc_filter)); + value = readl(ioaddr + GMAC_PACKET_FILTER); value &= ~GMAC_PACKET_FILTER_HMC; value &= ~GMAC_PACKET_FILTER_HPF; @@ -416,16 +419,13 @@ static void dwmac4_set_filter(struct mac_device_info *hw, /* Pass all multi */ value |= GMAC_PACKET_FILTER_PM; /* Set all the bits of the HASH tab */ - for (i = 0; i < numhashregs; i++) - writel(0xffffffff, ioaddr + GMAC_HASH_TAB(i)); + memset(mc_filter, 0xff, sizeof(mc_filter)); } else if (!netdev_mc_empty(dev)) { struct netdev_hw_addr *ha; - u32 mc_filter[8]; /* Hash filter for multicast */ value |= GMAC_PACKET_FILTER_HMC; - memset(mc_filter, 0, sizeof(mc_filter)); netdev_for_each_mc_addr(ha, dev) { /* The upper n bits of the calculated CRC are used to * index the contents of the hash table. The number of @@ -440,10 +440,11 @@ static void dwmac4_set_filter(struct mac_device_info *hw, */ mc_filter[bit_nr >> 5] |= (1 << (bit_nr & 0x1f)); } - for (i = 0; i < numhashregs; i++) - writel(mc_filter[i], ioaddr + GMAC_HASH_TAB(i)); } + for (i = 0; i < numhashregs; i++) + writel(mc_filter[i], ioaddr + GMAC_HASH_TAB(i)); + value |= GMAC_PACKET_FILTER_HPF; /* Handle multiple unicast addresses */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 5923ca62d793..99037386080a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -84,7 +84,7 @@ #define XGMAC_TSIE BIT(12) #define XGMAC_LPIIE BIT(5) #define XGMAC_PMTIE BIT(4) -#define XGMAC_INT_DEFAULT_EN (XGMAC_LPIIE | XGMAC_PMTIE | XGMAC_TSIE) +#define XGMAC_INT_DEFAULT_EN (XGMAC_LPIIE | XGMAC_PMTIE) #define XGMAC_Qx_TX_FLOW_CTRL(x) (0x00000070 + (x) * 4) #define XGMAC_PT GENMASK(31, 16) #define XGMAC_PT_SHIFT 16 @@ -122,6 +122,7 @@ #define XGMAC_HWFEAT_GMIISEL BIT(1) #define XGMAC_HW_FEATURE1 0x00000120 #define XGMAC_HWFEAT_L3L4FNUM GENMASK(30, 27) +#define XGMAC_HWFEAT_HASHTBLSZ GENMASK(25, 24) #define XGMAC_HWFEAT_RSSEN BIT(20) #define XGMAC_HWFEAT_TSOEN BIT(18) #define XGMAC_HWFEAT_SPHEN BIT(17) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 2b277b2c586b..5031398e612c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -472,7 +472,7 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw, dwxgmac2_set_mchash(ioaddr, mc_filter, mcbitslog2); /* Handle multiple unicast addresses */ - if (netdev_uc_count(dev) > XGMAC_ADDR_MAX) { + if (netdev_uc_count(dev) > hw->unicast_filter_entries) { value |= XGMAC_FILTER_PR; } else { struct netdev_hw_addr *ha; @@ -523,8 +523,8 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw, struct stmmac_rss *cfg, u32 num_rxq) { void __iomem *ioaddr = hw->pcsr; + u32 value, *key; int i, ret; - u32 value; value = readl(ioaddr + XGMAC_RSS_CTRL); if (!cfg || !cfg->enable) { @@ -533,8 +533,9 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw, return 0; } - for (i = 0; i < (sizeof(cfg->key) / sizeof(u32)); i++) { - ret = dwxgmac2_rss_write_reg(ioaddr, true, i, cfg->key[i]); + key = (u32 *)cfg->key; + for (i = 0; i < (ARRAY_SIZE(cfg->key) / sizeof(u32)); i++) { + ret = dwxgmac2_rss_write_reg(ioaddr, true, i, key[i]); if (ret) return ret; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 53c4a40d8386..965cbe3e6f51 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -380,6 +380,7 @@ static void dwxgmac2_get_hw_feature(void __iomem *ioaddr, /* MAC HW feature 1 */ hw_cap = readl(ioaddr + XGMAC_HW_FEATURE1); dma_cap->l3l4fnum = (hw_cap & XGMAC_HWFEAT_L3L4FNUM) >> 27; + dma_cap->hash_tb_sz = (hw_cap & XGMAC_HWFEAT_HASHTBLSZ) >> 24; dma_cap->rssen = (hw_cap & XGMAC_HWFEAT_RSSEN) >> 20; dma_cap->tsoen = (hw_cap & XGMAC_HWFEAT_TSOEN) >> 18; dma_cap->sphen = (hw_cap & XGMAC_HWFEAT_SPHEN) >> 17; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d3232738fb25..c76a1336a451 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -629,6 +629,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; ptp_v2 = PTP_TCR_TSVER2ENA; snap_type_sel = PTP_TCR_SNAPTYPSEL_1; + ts_event_en = PTP_TCR_TSEVNTENA; ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; ptp_over_ethernet = PTP_TCR_TSIPENA; @@ -4715,11 +4716,9 @@ int stmmac_suspend(struct device *dev) if (!ndev || !netif_running(ndev)) return 0; - mutex_lock(&priv->lock); + phylink_mac_change(priv->phylink, false); - rtnl_lock(); - phylink_stop(priv->phylink); - rtnl_unlock(); + mutex_lock(&priv->lock); netif_device_detach(ndev); stmmac_stop_all_queues(priv); @@ -4734,6 +4733,12 @@ int stmmac_suspend(struct device *dev) stmmac_pmt(priv, priv->hw, priv->wolopts); priv->irq_wake = 1; } else { + mutex_unlock(&priv->lock); + rtnl_lock(); + phylink_stop(priv->phylink); + rtnl_unlock(); + mutex_lock(&priv->lock); + stmmac_mac_set(priv, priv->ioaddr, false); pinctrl_pm_select_sleep_state(priv->device); /* Disable clock in case of PWM is off */ @@ -4824,12 +4829,16 @@ int stmmac_resume(struct device *dev) stmmac_start_all_queues(priv); - rtnl_lock(); - phylink_start(priv->phylink); - rtnl_unlock(); - mutex_unlock(&priv->lock); + if (!device_may_wakeup(priv->device)) { + rtnl_lock(); + phylink_start(priv->phylink); + rtnl_unlock(); + } + + phylink_mac_change(priv->phylink, true); + return 0; } EXPORT_SYMBOL_GPL(stmmac_resume); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index 5f66f6161629..cc76a42c7466 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -1564,10 +1564,6 @@ static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue) struct stmmac_packet_attrs attr = { }; int size = priv->dma_buf_sz; - /* Only XGMAC has SW support for multiple RX descs in same packet */ - if (priv->plat->has_xgmac) - size = priv->dev->max_mtu; - attr.dst = priv->dev->dev_addr; attr.max_size = size - ETH_FCS_LEN; attr.queue_mapping = queue; diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index ceddb424f887..0dd0ba915ab9 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -1137,10 +1137,11 @@ static void atusb_disconnect(struct usb_interface *interface) ieee802154_unregister_hw(atusb->hw); + usb_put_dev(atusb->usb_dev); + ieee802154_free_hw(atusb->hw); usb_set_intfdata(interface, NULL); - usb_put_dev(atusb->usb_dev); pr_debug("%s done\n", __func__); } diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index 11402dc347db..430c93786153 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -3145,12 +3145,12 @@ static int ca8210_probe(struct spi_device *spi_device) goto error; } + priv->spi->dev.platform_data = pdata; ret = ca8210_get_platform_data(priv->spi, pdata); if (ret) { dev_crit(&spi_device->dev, "ca8210_get_platform_data failed\n"); goto error; } - priv->spi->dev.platform_data = pdata; ret = ca8210_dev_com_init(priv); if (ret) { diff --git a/drivers/net/ieee802154/mcr20a.c b/drivers/net/ieee802154/mcr20a.c index 17f2300e63ee..8dc04e2590b1 100644 --- a/drivers/net/ieee802154/mcr20a.c +++ b/drivers/net/ieee802154/mcr20a.c @@ -800,7 +800,7 @@ mcr20a_handle_rx_read_buf_complete(void *context) if (!skb) return; - memcpy(skb_put(skb, len), lp->rx_buf, len); + __skb_put_data(skb, lp->rx_buf, len); ieee802154_rx_irqsafe(lp->hw, skb, lp->rx_lqi[0]); print_hex_dump_debug("mcr20a rx: ", DUMP_PREFIX_OFFSET, 16, 1, diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 2aa7b2e60046..1eb5d4fb8925 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -15,6 +15,15 @@ #include <linux/of_gpio.h> #include <linux/gpio/consumer.h> +#define AT803X_SPECIFIC_STATUS 0x11 +#define AT803X_SS_SPEED_MASK (3 << 14) +#define AT803X_SS_SPEED_1000 (2 << 14) +#define AT803X_SS_SPEED_100 (1 << 14) +#define AT803X_SS_SPEED_10 (0 << 14) +#define AT803X_SS_DUPLEX BIT(13) +#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) +#define AT803X_SS_MDIX BIT(6) + #define AT803X_INTR_ENABLE 0x12 #define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) #define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) @@ -357,6 +366,64 @@ static int at803x_aneg_done(struct phy_device *phydev) return aneg_done; } +static int at803x_read_status(struct phy_device *phydev) +{ + int ss, err, old_link = phydev->link; + + /* Update the link, but return if there was an error */ + err = genphy_update_link(phydev); + if (err) + return err; + + /* why bother the PHY if nothing can have changed */ + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + return 0; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + err = genphy_read_lpa(phydev); + if (err < 0) + return err; + + /* Read the AT8035 PHY-Specific Status register, which indicates the + * speed and duplex that the PHY is actually using, irrespective of + * whether we are in autoneg mode or not. + */ + ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); + if (ss < 0) + return ss; + + if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { + switch (ss & AT803X_SS_SPEED_MASK) { + case AT803X_SS_SPEED_10: + phydev->speed = SPEED_10; + break; + case AT803X_SS_SPEED_100: + phydev->speed = SPEED_100; + break; + case AT803X_SS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + } + if (ss & AT803X_SS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + if (ss & AT803X_SS_MDIX) + phydev->mdix = ETH_TP_MDI_X; + else + phydev->mdix = ETH_TP_MDI; + } + + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) + phy_resolve_aneg_pause(phydev); + + return 0; +} + static struct phy_driver at803x_driver[] = { { /* ATHEROS 8035 */ @@ -370,6 +437,7 @@ static struct phy_driver at803x_driver[] = { .suspend = at803x_suspend, .resume = at803x_resume, /* PHY_GBIT_FEATURES */ + .read_status = at803x_read_status, .ack_interrupt = at803x_ack_interrupt, .config_intr = at803x_config_intr, }, { @@ -399,6 +467,7 @@ static struct phy_driver at803x_driver[] = { .suspend = at803x_suspend, .resume = at803x_resume, /* PHY_GBIT_FEATURES */ + .read_status = at803x_read_status, .aneg_done = at803x_aneg_done, .ack_interrupt = &at803x_ack_interrupt, .config_intr = &at803x_config_intr, diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c index e282600bd83e..c1d345c3cab3 100644 --- a/drivers/net/phy/mdio_device.c +++ b/drivers/net/phy/mdio_device.c @@ -121,7 +121,7 @@ void mdio_device_reset(struct mdio_device *mdiodev, int value) return; if (mdiodev->reset_gpio) - gpiod_set_value(mdiodev->reset_gpio, value); + gpiod_set_value_cansleep(mdiodev->reset_gpio, value); if (mdiodev->reset_ctrl) { if (value) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 369903d9b6ec..9412669b579c 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -283,6 +283,18 @@ void of_set_phy_eee_broken(struct phy_device *phydev) phydev->eee_broken_modes = broken; } +void phy_resolve_aneg_pause(struct phy_device *phydev) +{ + if (phydev->duplex == DUPLEX_FULL) { + phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + phydev->lp_advertising); + phydev->asym_pause = linkmode_test_bit( + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + phydev->lp_advertising); + } +} +EXPORT_SYMBOL_GPL(phy_resolve_aneg_pause); + /** * phy_resolve_aneg_linkmode - resolve the advertisements into phy settings * @phydev: The phy_device struct @@ -305,13 +317,7 @@ void phy_resolve_aneg_linkmode(struct phy_device *phydev) break; } - if (phydev->duplex == DUPLEX_FULL) { - phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->lp_advertising); - phydev->asym_pause = linkmode_test_bit( - ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->lp_advertising); - } + phy_resolve_aneg_pause(phydev); } EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7c92afd36bbe..119e6f466056 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -457,6 +457,11 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) val); change_autoneg = true; break; + case MII_CTRL1000: + mii_ctrl1000_mod_linkmode_adv_t(phydev->advertising, + val); + change_autoneg = true; + break; default: /* do nothing */ break; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index d347ddcac45b..9d2bbb13293e 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1783,32 +1783,9 @@ done: } EXPORT_SYMBOL(genphy_update_link); -/** - * genphy_read_status - check the link status and update current link state - * @phydev: target phy_device struct - * - * Description: Check the link, then figure out the current state - * by comparing what we advertise with what the link partner - * advertises. Start by checking the gigabit possibilities, - * then move on to 10/100. - */ -int genphy_read_status(struct phy_device *phydev) +int genphy_read_lpa(struct phy_device *phydev) { - int lpa, lpagb, err, old_link = phydev->link; - - /* Update the link, but return if there was an error */ - err = genphy_update_link(phydev); - if (err) - return err; - - /* why bother the PHY if nothing can have changed */ - if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) - return 0; - - phydev->speed = SPEED_UNKNOWN; - phydev->duplex = DUPLEX_UNKNOWN; - phydev->pause = 0; - phydev->asym_pause = 0; + int lpa, lpagb; if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { if (phydev->is_gigabit_capable) { @@ -1838,6 +1815,44 @@ int genphy_read_status(struct phy_device *phydev) return lpa; mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); + } + + return 0; +} +EXPORT_SYMBOL(genphy_read_lpa); + +/** + * genphy_read_status - check the link status and update current link state + * @phydev: target phy_device struct + * + * Description: Check the link, then figure out the current state + * by comparing what we advertise with what the link partner + * advertises. Start by checking the gigabit possibilities, + * then move on to 10/100. + */ +int genphy_read_status(struct phy_device *phydev) +{ + int err, old_link = phydev->link; + + /* Update the link, but return if there was an error */ + err = genphy_update_link(phydev); + if (err) + return err; + + /* why bother the PHY if nothing can have changed */ + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + return 0; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + err = genphy_read_lpa(phydev); + if (err < 0) + return err; + + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { phy_resolve_aneg_linkmode(phydev); } else if (phydev->autoneg == AUTONEG_DISABLE) { int bmcr = phy_read(phydev, MII_BMCR); diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 734de7de03f7..e1fabb3e3246 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -238,7 +238,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb_dst_drop(skb); skb_dst_set(skb, &rt->dst); - nf_reset(skb); + nf_reset_ct(skb); skb->ip_summed = CHECKSUM_NONE; ip_select_ident(net, skb, NULL); @@ -358,7 +358,7 @@ static int pptp_rcv(struct sk_buff *skb) po = lookup_chan(htons(header->call_id), iph->saddr); if (po) { skb_dst_drop(skb); - nf_reset(skb); + nf_reset_ct(skb); return sk_receive_skb(sk_pppox(po), skb, 0); } drop: diff --git a/drivers/net/tun.c b/drivers/net/tun.c index aab0be40d443..812dc3a65efb 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1104,7 +1104,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) */ skb_orphan(skb); - nf_reset(skb); + nf_reset_ct(skb); if (ptr_ring_produce(&tfile->tx_ring, skb)) goto drop; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index ce78714f536f..a505b2ab88b8 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2620,14 +2620,18 @@ static struct hso_device *hso_create_bulk_serial_device( */ if (serial->tiocmget) { tiocmget = serial->tiocmget; + tiocmget->endp = hso_get_ep(interface, + USB_ENDPOINT_XFER_INT, + USB_DIR_IN); + if (!tiocmget->endp) { + dev_err(&interface->dev, "Failed to find INT IN ep\n"); + goto exit; + } + tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL); if (tiocmget->urb) { mutex_init(&tiocmget->mutex); init_waitqueue_head(&tiocmget->waitq); - tiocmget->endp = hso_get_ep( - interface, - USB_ENDPOINT_XFER_INT, - USB_DIR_IN); } else hso_free_tiomget(serial); } diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index b6dc5d714b5e..3d77cd402ba9 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1350,6 +1350,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1e2d, 0x0082, 4)}, /* Cinterion PHxx,PXxx (2 RmNet) */ {QMI_FIXED_INTF(0x1e2d, 0x0082, 5)}, /* Cinterion PHxx,PXxx (2 RmNet) */ {QMI_FIXED_INTF(0x1e2d, 0x0083, 4)}, /* Cinterion PHxx,PXxx (1 RmNet + USB Audio)*/ + {QMI_QUIRK_SET_DTR(0x1e2d, 0x00b0, 4)}, /* Cinterion CLS8 */ {QMI_FIXED_INTF(0x413c, 0x81a2, 8)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a3, 8)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 08726090570e..cee9fef925cd 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4799,10 +4799,9 @@ static int rtl8152_reset_resume(struct usb_interface *intf) struct r8152 *tp = usb_get_intfdata(intf); clear_bit(SELECTIVE_SUSPEND, &tp->flags); - mutex_lock(&tp->control); tp->rtl_ops.init(tp); queue_delayed_work(system_long_wq, &tp->hw_phy_work, 0); - mutex_unlock(&tp->control); + set_ethernet_addr(tp); return rtl8152_resume(intf); } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ba98e0971b84..5a635f028bdc 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1585,7 +1585,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) /* Don't wait up for transmitted skbs to be freed. */ if (!use_napi) { skb_orphan(skb); - nf_reset(skb); + nf_reset_ct(skb); } /* If running out of space, stop queue to avoid getting packets that we diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index a4b38a980c3c..ee52bde058df 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -366,7 +366,7 @@ static int vrf_finish_output6(struct net *net, struct sock *sk, struct neighbour *neigh; int ret; - nf_reset(skb); + nf_reset_ct(skb); skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; @@ -459,7 +459,7 @@ static struct sk_buff *vrf_ip6_out_direct(struct net_device *vrf_dev, /* reset skb device */ if (likely(err == 1)) - nf_reset(skb); + nf_reset_ct(skb); else skb = NULL; @@ -560,7 +560,7 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s bool is_v6gw = false; int ret = -EINVAL; - nf_reset(skb); + nf_reset_ct(skb); /* Be paranoid, rather than too clever. */ if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { @@ -670,7 +670,7 @@ static struct sk_buff *vrf_ip_out_direct(struct net_device *vrf_dev, /* reset skb device */ if (likely(err == 1)) - nf_reset(skb); + nf_reset_ct(skb); else skb = NULL; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 635956024e88..45c73a6f09a1 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1261,8 +1261,8 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, skb_orphan(skb); skb_dst_drop(skb); skb->mark = 0; - secpath_reset(skb); - nf_reset(skb); + skb_ext_reset(skb); + nf_reset_ct(skb); /* * Get absolute mactime here so all HWs RX at the "same time", and diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index e14ec75b61d6..482c6c8b0fb7 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -887,9 +887,9 @@ static int xennet_set_skb_gso(struct sk_buff *skb, return 0; } -static RING_IDX xennet_fill_frags(struct netfront_queue *queue, - struct sk_buff *skb, - struct sk_buff_head *list) +static int xennet_fill_frags(struct netfront_queue *queue, + struct sk_buff *skb, + struct sk_buff_head *list) { RING_IDX cons = queue->rx.rsp_cons; struct sk_buff *nskb; @@ -908,7 +908,7 @@ static RING_IDX xennet_fill_frags(struct netfront_queue *queue, if (unlikely(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) { queue->rx.rsp_cons = ++cons + skb_queue_len(list); kfree_skb(nskb); - return ~0U; + return -ENOENT; } skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, @@ -919,7 +919,9 @@ static RING_IDX xennet_fill_frags(struct netfront_queue *queue, kfree_skb(nskb); } - return cons; + queue->rx.rsp_cons = cons; + + return 0; } static int checksum_setup(struct net_device *dev, struct sk_buff *skb) @@ -1045,8 +1047,7 @@ err: skb->data_len = rx->status; skb->len += rx->status; - i = xennet_fill_frags(queue, skb, &tmpq); - if (unlikely(i == ~0U)) + if (unlikely(xennet_fill_frags(queue, skb, &tmpq))) goto err; if (rx->flags & XEN_NETRXF_csum_blank) @@ -1056,7 +1057,7 @@ err: __skb_queue_tail(&rxq, skb); - queue->rx.rsp_cons = ++i; + i = ++queue->rx.rsp_cons; work_done++; } diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 108f60b46804..fd7dea36c3b6 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -102,10 +102,13 @@ static void nvme_set_queue_dying(struct nvme_ns *ns) */ if (!ns->disk || test_and_set_bit(NVME_NS_DEAD, &ns->flags)) return; - revalidate_disk(ns->disk); blk_set_queue_dying(ns->queue); /* Forcibly unquiesce queues to avoid blocking dispatch */ blk_mq_unquiesce_queue(ns->queue); + /* + * Revalidate after unblocking dispatchers that may be holding bd_butex + */ + revalidate_disk(ns->disk); } static void nvme_queue_scan(struct nvme_ctrl *ctrl) @@ -847,7 +850,7 @@ out: static int nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, void __user *ubuffer, unsigned bufflen, void __user *meta_buffer, unsigned meta_len, - u32 meta_seed, u32 *result, unsigned timeout) + u32 meta_seed, u64 *result, unsigned timeout) { bool write = nvme_is_write(cmd); struct nvme_ns *ns = q->queuedata; @@ -888,7 +891,7 @@ static int nvme_submit_user_cmd(struct request_queue *q, else ret = nvme_req(req)->status; if (result) - *result = le32_to_cpu(nvme_req(req)->result.u32); + *result = le64_to_cpu(nvme_req(req)->result.u64); if (meta && !ret && !write) { if (copy_to_user(meta_buffer, meta, meta_len)) ret = -EFAULT; @@ -1335,6 +1338,54 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns, struct nvme_command c; unsigned timeout = 0; u32 effects; + u64 result; + int status; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (copy_from_user(&cmd, ucmd, sizeof(cmd))) + return -EFAULT; + if (cmd.flags) + return -EINVAL; + + memset(&c, 0, sizeof(c)); + c.common.opcode = cmd.opcode; + c.common.flags = cmd.flags; + c.common.nsid = cpu_to_le32(cmd.nsid); + c.common.cdw2[0] = cpu_to_le32(cmd.cdw2); + c.common.cdw2[1] = cpu_to_le32(cmd.cdw3); + c.common.cdw10 = cpu_to_le32(cmd.cdw10); + c.common.cdw11 = cpu_to_le32(cmd.cdw11); + c.common.cdw12 = cpu_to_le32(cmd.cdw12); + c.common.cdw13 = cpu_to_le32(cmd.cdw13); + c.common.cdw14 = cpu_to_le32(cmd.cdw14); + c.common.cdw15 = cpu_to_le32(cmd.cdw15); + + if (cmd.timeout_ms) + timeout = msecs_to_jiffies(cmd.timeout_ms); + + effects = nvme_passthru_start(ctrl, ns, cmd.opcode); + status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c, + (void __user *)(uintptr_t)cmd.addr, cmd.data_len, + (void __user *)(uintptr_t)cmd.metadata, + cmd.metadata_len, 0, &result, timeout); + nvme_passthru_end(ctrl, effects); + + if (status >= 0) { + if (put_user(result, &ucmd->result)) + return -EFAULT; + } + + return status; +} + +static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns, + struct nvme_passthru_cmd64 __user *ucmd) +{ + struct nvme_passthru_cmd64 cmd; + struct nvme_command c; + unsigned timeout = 0; + u32 effects; int status; if (!capable(CAP_SYS_ADMIN)) @@ -1405,6 +1456,41 @@ static void nvme_put_ns_from_disk(struct nvme_ns_head *head, int idx) srcu_read_unlock(&head->srcu, idx); } +static bool is_ctrl_ioctl(unsigned int cmd) +{ + if (cmd == NVME_IOCTL_ADMIN_CMD || cmd == NVME_IOCTL_ADMIN64_CMD) + return true; + if (is_sed_ioctl(cmd)) + return true; + return false; +} + +static int nvme_handle_ctrl_ioctl(struct nvme_ns *ns, unsigned int cmd, + void __user *argp, + struct nvme_ns_head *head, + int srcu_idx) +{ + struct nvme_ctrl *ctrl = ns->ctrl; + int ret; + + nvme_get_ctrl(ns->ctrl); + nvme_put_ns_from_disk(head, srcu_idx); + + switch (cmd) { + case NVME_IOCTL_ADMIN_CMD: + ret = nvme_user_cmd(ctrl, NULL, argp); + break; + case NVME_IOCTL_ADMIN64_CMD: + ret = nvme_user_cmd64(ctrl, NULL, argp); + break; + default: + ret = sed_ioctl(ctrl->opal_dev, cmd, argp); + break; + } + nvme_put_ctrl(ctrl); + return ret; +} + static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { @@ -1422,20 +1508,8 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, * seperately and drop the ns SRCU reference early. This avoids a * deadlock when deleting namespaces using the passthrough interface. */ - if (cmd == NVME_IOCTL_ADMIN_CMD || is_sed_ioctl(cmd)) { - struct nvme_ctrl *ctrl = ns->ctrl; - - nvme_get_ctrl(ns->ctrl); - nvme_put_ns_from_disk(head, srcu_idx); - - if (cmd == NVME_IOCTL_ADMIN_CMD) - ret = nvme_user_cmd(ctrl, NULL, argp); - else - ret = sed_ioctl(ctrl->opal_dev, cmd, argp); - - nvme_put_ctrl(ctrl); - return ret; - } + if (is_ctrl_ioctl(cmd)) + return nvme_handle_ctrl_ioctl(ns, cmd, argp, head, srcu_idx); switch (cmd) { case NVME_IOCTL_ID: @@ -1448,6 +1522,9 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, case NVME_IOCTL_SUBMIT_IO: ret = nvme_submit_io(ns, argp); break; + case NVME_IOCTL_IO64_CMD: + ret = nvme_user_cmd64(ns->ctrl, ns, argp); + break; default: if (ns->ndev) ret = nvme_nvm_ioctl(ns, cmd, arg); @@ -2289,6 +2366,16 @@ static const struct nvme_core_quirk_entry core_quirks[] = { .vid = 0x14a4, .fr = "22301111", .quirks = NVME_QUIRK_SIMPLE_SUSPEND, + }, + { + /* + * This Kingston E8FK11.T firmware version has no interrupt + * after resume with actions related to suspend to idle + * https://bugzilla.kernel.org/show_bug.cgi?id=204887 + */ + .vid = 0x2646, + .fr = "E8FK11.T", + .quirks = NVME_QUIRK_SIMPLE_SUSPEND, } }; @@ -2540,8 +2627,9 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) list_add_tail(&subsys->entry, &nvme_subsystems); } - if (sysfs_create_link(&subsys->dev.kobj, &ctrl->device->kobj, - dev_name(ctrl->device))) { + ret = sysfs_create_link(&subsys->dev.kobj, &ctrl->device->kobj, + dev_name(ctrl->device)); + if (ret) { dev_err(ctrl->device, "failed to create sysfs link from subsystem.\n"); goto out_put_subsystem; @@ -2838,6 +2926,8 @@ static long nvme_dev_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case NVME_IOCTL_ADMIN_CMD: return nvme_user_cmd(ctrl, NULL, argp); + case NVME_IOCTL_ADMIN64_CMD: + return nvme_user_cmd64(ctrl, NULL, argp); case NVME_IOCTL_IO_CMD: return nvme_dev_user_cmd(ctrl, argp); case NVME_IOCTL_RESET: @@ -3045,6 +3135,8 @@ static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL); nvme_show_int_function(cntlid); nvme_show_int_function(numa_node); +nvme_show_int_function(queue_count); +nvme_show_int_function(sqsize); static ssize_t nvme_sysfs_delete(struct device *dev, struct device_attribute *attr, const char *buf, @@ -3125,6 +3217,8 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_address.attr, &dev_attr_state.attr, &dev_attr_numa_node.attr, + &dev_attr_queue_count.attr, + &dev_attr_sqsize.attr, NULL }; diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index b5013c101b35..38a83ef5bcd3 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -221,6 +221,7 @@ struct nvme_ctrl { u16 oacs; u16 nssa; u16 nr_streams; + u16 sqsize; u32 max_namespaces; atomic_t abort_limit; u8 vwc; @@ -269,7 +270,6 @@ struct nvme_ctrl { u16 hmmaxd; /* Fabrics only */ - u16 sqsize; u32 ioccsz; u32 iorcsz; u16 icdoff; diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index c0808f9eb8ab..bb88681f4dc3 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2946,11 +2946,21 @@ static int nvme_suspend(struct device *dev) if (ret < 0) goto unfreeze; + /* + * A saved state prevents pci pm from generically controlling the + * device's power. If we're using protocol specific settings, we don't + * want pci interfering. + */ + pci_save_state(pdev); + ret = nvme_set_power_state(ctrl, ctrl->npss); if (ret < 0) goto unfreeze; if (ret) { + /* discard the saved state */ + pci_load_saved_state(pdev, NULL); + /* * Clearing npss forces a controller reset on resume. The * correct value will be resdicovered then. @@ -2958,14 +2968,7 @@ static int nvme_suspend(struct device *dev) nvme_dev_disable(ndev, true); ctrl->npss = 0; ret = 0; - goto unfreeze; } - /* - * A saved state prevents pci pm from generically controlling the - * device's power. If we're using protocol specific settings, we don't - * want pci interfering. - */ - pci_save_state(pdev); unfreeze: nvme_unfreeze(ctrl); return ret; @@ -3090,6 +3093,9 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_LIGHTNVM, }, { PCI_DEVICE(0x10ec, 0x5762), /* ADATA SX6000LNP */ .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN, }, + { PCI_DEVICE(0x1cc1, 0x8201), /* ADATA SX8200PNP 512GB */ + .driver_data = NVME_QUIRK_NO_DEEPEST_PS | + NVME_QUIRK_IGNORE_DEV_SUBNQN, }, { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index dfa07bb9dfeb..4d280160dd3f 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -427,7 +427,7 @@ static void nvme_rdma_destroy_queue_ib(struct nvme_rdma_queue *queue) static int nvme_rdma_get_max_fr_pages(struct ib_device *ibdev) { return min_t(u32, NVME_RDMA_MAX_SEGMENTS, - ibdev->attrs.max_fast_reg_page_list_len); + ibdev->attrs.max_fast_reg_page_list_len - 1); } static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue) @@ -437,7 +437,7 @@ static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue) const int cq_factor = send_wr_factor + 1; /* + RECV */ int comp_vector, idx = nvme_rdma_queue_idx(queue); enum ib_poll_context poll_ctx; - int ret; + int ret, pages_per_mr; queue->device = nvme_rdma_find_get_device(queue->cm_id); if (!queue->device) { @@ -479,10 +479,16 @@ static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue) goto out_destroy_qp; } + /* + * Currently we don't use SG_GAPS MR's so if the first entry is + * misaligned we'll end up using two entries for a single data page, + * so one additional entry is required. + */ + pages_per_mr = nvme_rdma_get_max_fr_pages(ibdev) + 1; ret = ib_mr_pool_init(queue->qp, &queue->qp->rdma_mrs, queue->queue_size, IB_MR_TYPE_MEM_REG, - nvme_rdma_get_max_fr_pages(ibdev), 0); + pages_per_mr, 0); if (ret) { dev_err(queue->ctrl->ctrl.device, "failed to initialize MR pool sized %d for QID %d\n", @@ -614,7 +620,8 @@ static int nvme_rdma_start_queue(struct nvme_rdma_ctrl *ctrl, int idx) if (!ret) { set_bit(NVME_RDMA_Q_LIVE, &queue->flags); } else { - __nvme_rdma_stop_queue(queue); + if (test_bit(NVME_RDMA_Q_ALLOCATED, &queue->flags)) + __nvme_rdma_stop_queue(queue); dev_info(ctrl->ctrl.device, "failed to connect queue: %d ret=%d\n", idx, ret); } @@ -820,8 +827,8 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl, if (error) goto out_stop_queue; - ctrl->ctrl.max_hw_sectors = - (ctrl->max_fr_pages - 1) << (ilog2(SZ_4K) - 9); + ctrl->ctrl.max_segments = ctrl->max_fr_pages; + ctrl->ctrl.max_hw_sectors = ctrl->max_fr_pages << (ilog2(SZ_4K) - 9); blk_mq_unquiesce_queue(ctrl->ctrl.admin_q); diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 4ffd5957637a..385a5212c10f 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1042,7 +1042,7 @@ static void nvme_tcp_io_work(struct work_struct *w) { struct nvme_tcp_queue *queue = container_of(w, struct nvme_tcp_queue, io_work); - unsigned long start = jiffies + msecs_to_jiffies(1); + unsigned long deadline = jiffies + msecs_to_jiffies(1); do { bool pending = false; @@ -1067,7 +1067,7 @@ static void nvme_tcp_io_work(struct work_struct *w) if (!pending) return; - } while (time_after(jiffies, start)); /* quota is exhausted */ + } while (!time_after(jiffies, deadline)); /* quota is exhausted */ queue_work_on(queue->io_cpu, nvme_tcp_wq, &queue->io_work); } diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c index de0bff70ebb6..32008d85172b 100644 --- a/drivers/nvme/target/io-cmd-bdev.c +++ b/drivers/nvme/target/io-cmd-bdev.c @@ -11,10 +11,10 @@ void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id) { const struct queue_limits *ql = &bdev_get_queue(bdev)->limits; - /* Number of physical blocks per logical block. */ - const u32 ppl = ql->physical_block_size / ql->logical_block_size; - /* Physical blocks per logical block, 0's based. */ - const __le16 ppl0b = to0based(ppl); + /* Number of logical blocks per physical block. */ + const u32 lpp = ql->physical_block_size / ql->logical_block_size; + /* Logical blocks per physical block, 0's based. */ + const __le16 lpp0b = to0based(lpp); /* * For NVMe 1.2 and later, bit 1 indicates that the fields NAWUN, @@ -25,9 +25,9 @@ void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id) * field from the identify controller data structure should be used. */ id->nsfeat |= 1 << 1; - id->nawun = ppl0b; - id->nawupf = ppl0b; - id->nacwu = ppl0b; + id->nawun = lpp0b; + id->nawupf = lpp0b; + id->nacwu = lpp0b; /* * Bit 4 indicates that the fields NPWG, NPWA, NPDG, NPDA, and @@ -36,7 +36,7 @@ void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id) */ id->nsfeat |= 1 << 4; /* NPWG = Namespace Preferred Write Granularity. 0's based */ - id->npwg = ppl0b; + id->npwg = lpp0b; /* NPWA = Namespace Preferred Write Alignment. 0's based */ id->npwa = id->npwg; /* NPDG = Namespace Preferred Deallocate Granularity. 0's based */ diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index bf4f03474e89..d535080b781f 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -348,8 +348,7 @@ static int nvmet_tcp_map_data(struct nvmet_tcp_cmd *cmd) return 0; err: - if (cmd->req.sg_cnt) - sgl_free(cmd->req.sg); + sgl_free(cmd->req.sg); return NVME_SC_INTERNAL; } @@ -554,8 +553,7 @@ static int nvmet_try_send_data(struct nvmet_tcp_cmd *cmd) if (queue->nvme_sq.sqhd_disabled) { kfree(cmd->iov); - if (cmd->req.sg_cnt) - sgl_free(cmd->req.sg); + sgl_free(cmd->req.sg); } return 1; @@ -586,8 +584,7 @@ static int nvmet_try_send_response(struct nvmet_tcp_cmd *cmd, return -EAGAIN; kfree(cmd->iov); - if (cmd->req.sg_cnt) - sgl_free(cmd->req.sg); + sgl_free(cmd->req.sg); cmd->queue->snd_cmd = NULL; nvmet_tcp_put_cmd(cmd); return 1; @@ -1310,8 +1307,7 @@ static void nvmet_tcp_finish_cmd(struct nvmet_tcp_cmd *cmd) nvmet_req_uninit(&cmd->req); nvmet_tcp_unmap_pdu_iovec(cmd); kfree(cmd->iov); - if (cmd->req.sg_cnt) - sgl_free(cmd->req.sg); + sgl_free(cmd->req.sg); } static void nvmet_tcp_uninit_data_in_cmds(struct nvmet_tcp_queue *queue) diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index c61f00b72e15..a577218d1ab7 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -507,6 +507,8 @@ int ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base, ptp_qoriq->regs.etts_regs = base + ETTS_REGS_OFFSET; } + spin_lock_init(&ptp_qoriq->lock); + ktime_get_real_ts64(&now); ptp_qoriq_settime(&ptp_qoriq->caps, &now); @@ -514,7 +516,6 @@ int ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base, (ptp_qoriq->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT | (ptp_qoriq->cksel & CKSEL_MASK) << CKSEL_SHIFT; - spin_lock_init(&ptp_qoriq->lock); spin_lock_irqsave(&ptp_qoriq->lock, flags); regs = &ptp_qoriq->regs; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e3a2518503ed..bd21655c37a6 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -508,15 +508,6 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. -config PWM_TIPWMSS - bool - default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) - help - PWM Subsystem driver support for AM33xx SOC. - - PWM submodules require PWM config space access from submodule - drivers and require common parent driver support. - config PWM_TWL tristate "TWL4030/6030 PWM support" depends on TWL4030_CORE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 26326adf71d7..9a475073dafc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -50,7 +50,6 @@ obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o -obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index fc53e1e221f0..c94184d080f8 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1553,8 +1553,8 @@ static int dasd_eckd_read_vol_info(struct dasd_device *device) if (rc == 0) { memcpy(&private->vsq, vsq, sizeof(*vsq)); } else { - dev_warn(&device->cdev->dev, - "Reading the volume storage information failed with rc=%d\n", rc); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Reading the volume storage information failed with rc=%d", rc); } if (useglobal) @@ -1737,8 +1737,8 @@ static int dasd_eckd_read_ext_pool_info(struct dasd_device *device) if (rc == 0) { dasd_eckd_cpy_ext_pool_data(device, lcq); } else { - dev_warn(&device->cdev->dev, - "Reading the logical configuration failed with rc=%d\n", rc); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Reading the logical configuration failed with rc=%d", rc); } dasd_sfree_request(cqr, cqr->memdev); @@ -2020,14 +2020,10 @@ dasd_eckd_check_characteristics(struct dasd_device *device) dasd_eckd_read_features(device); /* Read Volume Information */ - rc = dasd_eckd_read_vol_info(device); - if (rc) - goto out_err3; + dasd_eckd_read_vol_info(device); /* Read Extent Pool Information */ - rc = dasd_eckd_read_ext_pool_info(device); - if (rc) - goto out_err3; + dasd_eckd_read_ext_pool_info(device); /* Read Device Characteristics */ rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, @@ -2059,9 +2055,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) if (readonly) set_bit(DASD_FLAG_DEVICE_RO, &device->flags); - if (dasd_eckd_is_ese(device)) - dasd_set_feature(device->cdev, DASD_FEATURE_DISCARD, 1); - dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " "with %d cylinders, %d heads, %d sectors%s\n", private->rdc_data.dev_type, @@ -3695,14 +3688,6 @@ static int dasd_eckd_release_space(struct dasd_device *device, return -EINVAL; } -static struct dasd_ccw_req * -dasd_eckd_build_cp_discard(struct dasd_device *device, struct dasd_block *block, - struct request *req, sector_t first_trk, - sector_t last_trk) -{ - return dasd_eckd_dso_ras(device, block, req, first_trk, last_trk, 1); -} - static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( struct dasd_device *startdev, struct dasd_block *block, @@ -4447,10 +4432,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, cmdwtd = private->features.feature[12] & 0x40; use_prefix = private->features.feature[8] & 0x01; - if (req_op(req) == REQ_OP_DISCARD) - return dasd_eckd_build_cp_discard(startdev, block, req, - first_trk, last_trk); - cqr = NULL; if (cdlspecial || dasd_page_cache) { /* do nothing, just fall through to the cmd mode single case */ @@ -4729,14 +4710,12 @@ static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, struct dasd_block *block, struct request *req) { - struct dasd_device *startdev = NULL; struct dasd_eckd_private *private; - struct dasd_ccw_req *cqr; + struct dasd_device *startdev; unsigned long flags; + struct dasd_ccw_req *cqr; - /* Discard requests can only be processed on base devices */ - if (req_op(req) != REQ_OP_DISCARD) - startdev = dasd_alias_get_start_dev(base); + startdev = dasd_alias_get_start_dev(base); if (!startdev) startdev = base; private = startdev->private; @@ -5663,14 +5642,10 @@ static int dasd_eckd_restore_device(struct dasd_device *device) dasd_eckd_read_features(device); /* Read Volume Information */ - rc = dasd_eckd_read_vol_info(device); - if (rc) - goto out_err2; + dasd_eckd_read_vol_info(device); /* Read Extent Pool Information */ - rc = dasd_eckd_read_ext_pool_info(device); - if (rc) - goto out_err2; + dasd_eckd_read_ext_pool_info(device); /* Read Device Characteristics */ rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, @@ -6521,20 +6496,8 @@ static void dasd_eckd_setup_blk_queue(struct dasd_block *block) unsigned int logical_block_size = block->bp_block; struct request_queue *q = block->request_queue; struct dasd_device *device = block->base; - struct dasd_eckd_private *private; - unsigned int max_discard_sectors; - unsigned int max_bytes; - unsigned int ext_bytes; /* Extent Size in Bytes */ - int recs_per_trk; - int trks_per_cyl; - int ext_limit; - int ext_size; /* Extent Size in Cylinders */ int max; - private = device->private; - trks_per_cyl = private->rdc_data.trk_per_cyl; - recs_per_trk = recs_per_track(&private->rdc_data, 0, logical_block_size); - if (device->features & DASD_FEATURE_USERAW) { /* * the max_blocks value for raw_track access is 256 @@ -6555,28 +6518,6 @@ static void dasd_eckd_setup_blk_queue(struct dasd_block *block) /* With page sized segments each segment can be translated into one idaw/tidaw */ blk_queue_max_segment_size(q, PAGE_SIZE); blk_queue_segment_boundary(q, PAGE_SIZE - 1); - - if (dasd_eckd_is_ese(device)) { - /* - * Depending on the extent size, up to UINT_MAX bytes can be - * accepted. However, neither DASD_ECKD_RAS_EXTS_MAX nor the - * device limits should be exceeded. - */ - ext_size = dasd_eckd_ext_size(device); - ext_limit = min(private->real_cyl / ext_size, DASD_ECKD_RAS_EXTS_MAX); - ext_bytes = ext_size * trks_per_cyl * recs_per_trk * - logical_block_size; - max_bytes = UINT_MAX - (UINT_MAX % ext_bytes); - if (max_bytes / ext_bytes > ext_limit) - max_bytes = ext_bytes * ext_limit; - - max_discard_sectors = max_bytes / 512; - - blk_queue_max_discard_sectors(q, max_discard_sectors); - blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); - q->limits.discard_granularity = ext_bytes; - q->limits.discard_alignment = ext_bytes; - } } static struct ccw_driver dasd_eckd_driver = { diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index ba7d2480613b..dcdaba689b20 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -113,6 +113,7 @@ struct subchannel { enum sch_todo todo; struct work_struct todo_work; struct schib_config config; + u64 dma_mask; char *driver_override; /* Driver name to force a match */ } __attribute__ ((aligned(8))); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 1fbfb0a93f5f..831850435c23 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -232,7 +232,12 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid, * belong to a subchannel need to fit 31 bit width (e.g. ccw). */ sch->dev.coherent_dma_mask = DMA_BIT_MASK(31); - sch->dev.dma_mask = &sch->dev.coherent_dma_mask; + /* + * But we don't have such restrictions imposed on the stuff that + * is handled by the streaming API. + */ + sch->dma_mask = DMA_BIT_MASK(64); + sch->dev.dma_mask = &sch->dma_mask; return sch; err: diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 131430bd48d9..0c6245fc7706 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -710,7 +710,7 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch) if (!cdev->private) goto err_priv; cdev->dev.coherent_dma_mask = sch->dev.coherent_dma_mask; - cdev->dev.dma_mask = &cdev->dev.coherent_dma_mask; + cdev->dev.dma_mask = sch->dev.dma_mask; dma_pool = cio_gp_dma_create(&cdev->dev, 1); if (!dma_pool) goto err_dma_pool; diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index f4ca1d29d61b..cd164886132f 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -113,7 +113,7 @@ static void set_impl_params(struct qdio_irq *irq_ptr, irq_ptr->qib.pfmt = qib_param_field_format; if (qib_param_field) memcpy(irq_ptr->qib.parm, qib_param_field, - QDIO_MAX_BUFFERS_PER_Q); + sizeof(irq_ptr->qib.parm)); if (!input_slib_elements) goto output; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index a7868c8133ee..dda274351c21 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4715,8 +4715,7 @@ static int qeth_qdio_establish(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "qdioest"); - qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q, - GFP_KERNEL); + qib_param_field = kzalloc(FIELD_SIZEOF(struct qib, parm), GFP_KERNEL); if (!qib_param_field) { rc = -ENOMEM; goto out_free_nothing; diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index da00ca5fa5dc..401743e2b429 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1923,6 +1923,7 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req, struct fcoe_fcp_rsp_payload *fcp_rsp; struct bnx2fc_rport *tgt = io_req->tgt; struct scsi_cmnd *sc_cmd; + u16 scope = 0, qualifier = 0; /* scsi_cmd_cmpl is called with tgt lock held */ @@ -1990,12 +1991,30 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req, if (io_req->cdb_status == SAM_STAT_TASK_SET_FULL || io_req->cdb_status == SAM_STAT_BUSY) { - /* Set the jiffies + retry_delay_timer * 100ms - for the rport/tgt */ - tgt->retry_delay_timestamp = jiffies + - fcp_rsp->retry_delay_timer * HZ / 10; + /* Newer array firmware with BUSY or + * TASK_SET_FULL may return a status that needs + * the scope bits masked. + * Or a huge delay timestamp up to 27 minutes + * can result. + */ + if (fcp_rsp->retry_delay_timer) { + /* Upper 2 bits */ + scope = fcp_rsp->retry_delay_timer + & 0xC000; + /* Lower 14 bits */ + qualifier = fcp_rsp->retry_delay_timer + & 0x3FFF; + } + if (scope > 0 && qualifier > 0 && + qualifier <= 0x3FEF) { + /* Set the jiffies + + * retry_delay_timer * 100ms + * for the rport/tgt + */ + tgt->retry_delay_timestamp = jiffies + + (qualifier * HZ / 10); + } } - } if (io_req->fcp_resid) scsi_set_resid(sc_cmd, io_req->fcp_resid); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index d1513fdf1e00..0847e682797b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -3683,7 +3683,7 @@ void hisi_sas_debugfs_work_handler(struct work_struct *work) } EXPORT_SYMBOL_GPL(hisi_sas_debugfs_work_handler); -void hisi_sas_debugfs_release(struct hisi_hba *hisi_hba) +static void hisi_sas_debugfs_release(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; int i; @@ -3705,7 +3705,7 @@ void hisi_sas_debugfs_release(struct hisi_hba *hisi_hba) devm_kfree(dev, hisi_hba->debugfs_port_reg[i]); } -int hisi_sas_debugfs_alloc(struct hisi_hba *hisi_hba) +static int hisi_sas_debugfs_alloc(struct hisi_hba *hisi_hba) { const struct hisi_sas_hw *hw = hisi_hba->hw; struct device *dev = hisi_hba->dev; @@ -3796,7 +3796,7 @@ fail: return -ENOMEM; } -void hisi_sas_debugfs_bist_init(struct hisi_hba *hisi_hba) +static void hisi_sas_debugfs_bist_init(struct hisi_hba *hisi_hba) { hisi_hba->debugfs_bist_dentry = debugfs_create_dir("bist", hisi_hba->debugfs_dir); diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 45a66048801b..ff6d4aa92421 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -4183,11 +4183,11 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) */ if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ && pdev->subsystem_device == 0xC000) - return -ENODEV; + goto out_disable_device; /* Now check the magic signature byte */ pci_read_config_word(pdev, PCI_CONF_AMISIG, &magic); if (magic != HBA_SIGNATURE_471 && magic != HBA_SIGNATURE) - return -ENODEV; + goto out_disable_device; /* Ok it is probably a megaraid */ } diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 1659d35cd37b..59ca98f12afd 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -596,7 +596,7 @@ static void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type) tmp_prio = get->operational.app_prio.fcoe; if (qedf_default_prio > -1) qedf->prio = qedf_default_prio; - else if (tmp_prio < 0 || tmp_prio > 7) { + else if (tmp_prio > 7) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "FIP/FCoE prio %d out of range, setting to %d.\n", tmp_prio, QEDF_DEFAULT_PRIO); diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 8190c2a27584..30bafd9d21e9 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -2920,6 +2920,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) struct qla_hw_data *ha = vha->hw; uint16_t id = vha->vp_idx; + set_bit(VPORT_DELETE, &vha->dpc_flags); + while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) || test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) msleep(1000); diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 873a6aef1c5c..6ffa9877c28b 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2396,6 +2396,7 @@ typedef struct fc_port { unsigned int query:1; unsigned int id_changed:1; unsigned int scan_needed:1; + unsigned int n2n_flag:1; struct completion nvme_del_done; uint32_t nvme_prli_service_param; @@ -2446,7 +2447,6 @@ typedef struct fc_port { uint8_t fc4_type; uint8_t fc4f_nvme; uint8_t scan_state; - uint8_t n2n_flag; unsigned long last_queue_full; unsigned long last_ramp_up; @@ -3036,6 +3036,7 @@ enum scan_flags_t { enum fc4type_t { FS_FC4TYPE_FCP = BIT_0, FS_FC4TYPE_NVME = BIT_1, + FS_FCP_IS_N2N = BIT_7, }; struct fab_scan_rp { @@ -4394,6 +4395,7 @@ typedef struct scsi_qla_host { #define IOCB_WORK_ACTIVE 31 #define SET_ZIO_THRESHOLD_NEEDED 32 #define ISP_ABORT_TO_ROM 33 +#define VPORT_DELETE 34 unsigned long pci_flags; #define PFLG_DISCONNECTED 0 /* PCI device removed */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index dc0e36676313..5298ed10059f 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -3102,7 +3102,8 @@ int qla24xx_post_gpnid_work(struct scsi_qla_host *vha, port_id_t *id) { struct qla_work_evt *e; - if (test_bit(UNLOADING, &vha->dpc_flags)) + if (test_bit(UNLOADING, &vha->dpc_flags) || + (vha->vp_idx && test_bit(VPORT_DELETE, &vha->dpc_flags))) return 0; e = qla2x00_alloc_work(vha, QLA_EVT_GPNID); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 643d2324082e..1d041313ec52 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -746,12 +746,15 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, break; default: if ((id.b24 != fcport->d_id.b24 && - fcport->d_id.b24) || + fcport->d_id.b24 && + fcport->loop_id != FC_NO_LOOP_ID) || (fcport->loop_id != FC_NO_LOOP_ID && fcport->loop_id != loop_id)) { ql_dbg(ql_dbg_disc, vha, 0x20e3, "%s %d %8phC post del sess\n", __func__, __LINE__, fcport->port_name); + if (fcport->n2n_flag) + fcport->d_id.b24 = 0; qlt_schedule_sess_for_deletion(fcport); return; } @@ -759,6 +762,8 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, } fcport->loop_id = loop_id; + if (fcport->n2n_flag) + fcport->d_id.b24 = id.b24; wwn = wwn_to_u64(fcport->port_name); qlt_find_sess_invalidate_other(vha, wwn, @@ -972,7 +977,7 @@ static void qla24xx_async_gnl_sp_done(srb_t *sp, int res) wwn = wwn_to_u64(e->port_name); ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0x20e8, - "%s %8phC %02x:%02x:%02x state %d/%d lid %x \n", + "%s %8phC %02x:%02x:%02x CLS %x/%x lid %x \n", __func__, (void *)&wwn, e->port_id[2], e->port_id[1], e->port_id[0], e->current_login_state, e->last_login_state, (loop_id & 0x7fff)); @@ -1499,7 +1504,8 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) (fcport->fw_login_state == DSC_LS_PRLI_PEND))) return 0; - if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) { + if (fcport->fw_login_state == DSC_LS_PLOGI_COMP && + !N2N_TOPO(vha->hw)) { if (time_before_eq(jiffies, fcport->plogi_nack_done_deadline)) { set_bit(RELOGIN_NEEDED, &vha->dpc_flags); return 0; @@ -1570,8 +1576,9 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) qla24xx_post_gpdb_work(vha, fcport, 0); } else { ql_dbg(ql_dbg_disc, vha, 0x2118, - "%s %d %8phC post NVMe PRLI\n", - __func__, __LINE__, fcport->port_name); + "%s %d %8phC post %s PRLI\n", + __func__, __LINE__, fcport->port_name, + fcport->fc4f_nvme ? "NVME" : "FC"); qla24xx_post_prli_work(vha, fcport); } break; @@ -1853,17 +1860,38 @@ qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea) break; } - if (ea->fcport->n2n_flag) { + if (ea->fcport->fc4f_nvme) { ql_dbg(ql_dbg_disc, vha, 0x2118, "%s %d %8phC post fc4 prli\n", __func__, __LINE__, ea->fcport->port_name); ea->fcport->fc4f_nvme = 0; - ea->fcport->n2n_flag = 0; qla24xx_post_prli_work(vha, ea->fcport); + return; + } + + /* at this point both PRLI NVME & PRLI FCP failed */ + if (N2N_TOPO(vha->hw)) { + if (ea->fcport->n2n_link_reset_cnt < 3) { + ea->fcport->n2n_link_reset_cnt++; + /* + * remote port is not sending Plogi. Reset + * link to kick start his state machine + */ + set_bit(N2N_LINK_RESET, &vha->dpc_flags); + } else { + ql_log(ql_log_warn, vha, 0x2119, + "%s %d %8phC Unable to reconnect\n", + __func__, __LINE__, ea->fcport->port_name); + } + } else { + /* + * switch connect. login failed. Take connection + * down and allow relogin to retrigger + */ + ea->fcport->flags &= ~FCF_ASYNC_SENT; + ea->fcport->keep_nport_handle = 0; + qlt_schedule_sess_for_deletion(ea->fcport); } - ql_dbg(ql_dbg_disc, vha, 0x2119, - "%s %d %8phC unhandle event of %x\n", - __func__, __LINE__, ea->fcport->port_name, ea->data[0]); break; } } @@ -3190,7 +3218,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) for (j = 0; j < 2; j++, fwdt++) { if (!fwdt->template) { - ql_log(ql_log_warn, vha, 0x00ba, + ql_dbg(ql_dbg_init, vha, 0x00ba, "-> fwdt%u no template\n", j); continue; } @@ -4986,28 +5014,47 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) unsigned long flags; /* Inititae N2N login. */ - if (test_and_clear_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags)) { - /* borrowing */ - u32 *bp, i, sz; - - memset(ha->init_cb, 0, ha->init_cb_size); - sz = min_t(int, sizeof(struct els_plogi_payload), - ha->init_cb_size); - rval = qla24xx_get_port_login_templ(vha, ha->init_cb_dma, - (void *)ha->init_cb, sz); - if (rval == QLA_SUCCESS) { - bp = (uint32_t *)ha->init_cb; - for (i = 0; i < sz/4 ; i++, bp++) - *bp = cpu_to_be32(*bp); + if (N2N_TOPO(ha)) { + if (test_and_clear_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags)) { + /* borrowing */ + u32 *bp, i, sz; + + memset(ha->init_cb, 0, ha->init_cb_size); + sz = min_t(int, sizeof(struct els_plogi_payload), + ha->init_cb_size); + rval = qla24xx_get_port_login_templ(vha, + ha->init_cb_dma, (void *)ha->init_cb, sz); + if (rval == QLA_SUCCESS) { + bp = (uint32_t *)ha->init_cb; + for (i = 0; i < sz/4 ; i++, bp++) + *bp = cpu_to_be32(*bp); - memcpy(&ha->plogi_els_payld.data, (void *)ha->init_cb, - sizeof(ha->plogi_els_payld.data)); - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - } else { - ql_dbg(ql_dbg_init, vha, 0x00d1, - "PLOGI ELS param read fail.\n"); + memcpy(&ha->plogi_els_payld.data, + (void *)ha->init_cb, + sizeof(ha->plogi_els_payld.data)); + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + } else { + ql_dbg(ql_dbg_init, vha, 0x00d1, + "PLOGI ELS param read fail.\n"); + goto skip_login; + } + } + + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->n2n_flag) { + qla24xx_fcport_handle_login(vha, fcport); + return QLA_SUCCESS; + } + } +skip_login: + spin_lock_irqsave(&vha->work_lock, flags); + vha->scan.scan_retry++; + spin_unlock_irqrestore(&vha->work_lock, flags); + + if (vha->scan.scan_retry < MAX_SCAN_RETRIES) { + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); } - return QLA_SUCCESS; } found_devs = 0; diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index e92e52aa6e9b..518eb954cf42 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -2656,9 +2656,10 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; els_iocb->port_id[1] = sp->fcport->d_id.b.area; els_iocb->port_id[2] = sp->fcport->d_id.b.domain; - els_iocb->s_id[0] = vha->d_id.b.al_pa; - els_iocb->s_id[1] = vha->d_id.b.area; - els_iocb->s_id[2] = vha->d_id.b.domain; + /* For SID the byte order is different than DID */ + els_iocb->s_id[1] = vha->d_id.b.al_pa; + els_iocb->s_id[2] = vha->d_id.b.area; + els_iocb->s_id[0] = vha->d_id.b.domain; if (elsio->u.els_logo.els_cmd == ELS_DCMD_PLOGI) { els_iocb->control_flags = 0; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 4c858e2d0ea8..1cc6913f76c4 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2249,7 +2249,7 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105a, + ql_dbg(ql_dbg_disc, vha, 0x105a, "Entered %s.\n", __func__); if (IS_CNA_CAPABLE(vha->hw)) { @@ -3883,14 +3883,24 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, case TOPO_N2N: ha->current_topology = ISP_CFG_N; spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + list_for_each_entry(fcport, &vha->vp_fcports, list) { + fcport->scan_state = QLA_FCPORT_SCAN; + fcport->n2n_flag = 0; + } + fcport = qla2x00_find_fcport_by_wwpn(vha, rptid_entry->u.f1.port_name, 1); spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); if (fcport) { fcport->plogi_nack_done_deadline = jiffies + HZ; - fcport->dm_login_expire = jiffies + 3*HZ; + fcport->dm_login_expire = jiffies + 2*HZ; fcport->scan_state = QLA_FCPORT_FOUND; + fcport->n2n_flag = 1; + fcport->keep_nport_handle = 1; + if (vha->flags.nvme_enabled) + fcport->fc4f_nvme = 1; + switch (fcport->disc_state) { case DSC_DELETED: set_bit(RELOGIN_NEEDED, @@ -3924,7 +3934,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, rptid_entry->u.f1.port_name, rptid_entry->u.f1.node_name, NULL, - FC4_TYPE_UNKNOWN); + FS_FCP_IS_N2N); } /* if our portname is higher then initiate N2N login */ @@ -4023,6 +4033,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, list_for_each_entry(fcport, &vha->vp_fcports, list) { fcport->scan_state = QLA_FCPORT_SCAN; + fcport->n2n_flag = 0; } fcport = qla2x00_find_fcport_by_wwpn(vha, @@ -4032,6 +4043,14 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, fcport->login_retry = vha->hw->login_retry_count; fcport->plogi_nack_done_deadline = jiffies + HZ; fcport->scan_state = QLA_FCPORT_FOUND; + fcport->keep_nport_handle = 1; + fcport->n2n_flag = 1; + fcport->d_id.b.domain = + rptid_entry->u.f2.remote_nport_id[2]; + fcport->d_id.b.area = + rptid_entry->u.f2.remote_nport_id[1]; + fcport->d_id.b.al_pa = + rptid_entry->u.f2.remote_nport_id[0]; } } } diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 1a9a11ae7285..6afad68e5ba2 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -66,6 +66,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) uint16_t vp_id; struct qla_hw_data *ha = vha->hw; unsigned long flags = 0; + u8 i; mutex_lock(&ha->vport_lock); /* @@ -75,8 +76,9 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) * ensures no active vp_list traversal while the vport is removed * from the queue) */ - wait_event_timeout(vha->vref_waitq, !atomic_read(&vha->vref_count), - 10*HZ); + for (i = 0; i < 10 && atomic_read(&vha->vref_count); i++) + wait_event_timeout(vha->vref_waitq, + atomic_read(&vha->vref_count), HZ); spin_lock_irqsave(&ha->vport_slock, flags); if (atomic_read(&vha->vref_count)) { @@ -262,6 +264,9 @@ qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb) spin_lock_irqsave(&ha->vport_slock, flags); list_for_each_entry(vha, &ha->vp_list, list) { if (vha->vp_idx) { + if (test_bit(VPORT_DELETE, &vha->dpc_flags)) + continue; + atomic_inc(&vha->vref_count); spin_unlock_irqrestore(&ha->vport_slock, flags); @@ -300,6 +305,20 @@ qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb) int qla2x00_vp_abort_isp(scsi_qla_host_t *vha) { + fc_port_t *fcport; + + /* + * To exclusively reset vport, we need to log it out first. + * Note: This control_vp can fail if ISP reset is already + * issued, this is expected, as the vp would be already + * logged out due to ISP reset. + */ + if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { + qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); + list_for_each_entry(fcport, &vha->vp_fcports, list) + fcport->logout_on_delete = 0; + } + /* * Physical port will do most of the abort and recovery work. We can * just treat it as a loop down @@ -312,16 +331,9 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha) atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); } - /* - * To exclusively reset vport, we need to log it out first. Note: this - * control_vp can fail if ISP reset is already issued, this is - * expected, as the vp would be already logged out due to ISP reset. - */ - if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) - qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); - ql_dbg(ql_dbg_taskm, vha, 0x801d, "Scheduling enable of Vport %d.\n", vha->vp_idx); + return qla24xx_enable_vp(vha); } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 73db01e3b4e4..3568031c6504 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1115,9 +1115,15 @@ static inline int test_fcport_count(scsi_qla_host_t *vha) void qla2x00_wait_for_sess_deletion(scsi_qla_host_t *vha) { + u8 i; + qla2x00_mark_all_devices_lost(vha, 0); - wait_event_timeout(vha->fcport_waitQ, test_fcport_count(vha), 10*HZ); + for (i = 0; i < 10; i++) + wait_event_timeout(vha->fcport_waitQ, test_fcport_count(vha), + HZ); + + flush_workqueue(vha->hw->wq); } /* @@ -5036,6 +5042,10 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) memcpy(fcport->port_name, e->u.new_sess.port_name, WWN_SIZE); + + if (e->u.new_sess.fc4_type & FS_FCP_IS_N2N) + fcport->n2n_flag = 1; + } else { ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %8phC mem alloc fail.\n", @@ -5134,11 +5144,9 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) if (dfcp) qlt_schedule_sess_for_deletion(tfcp); - - if (N2N_TOPO(vha->hw)) - fcport->flags &= ~FCF_FABRIC_DEVICE; - if (N2N_TOPO(vha->hw)) { + fcport->flags &= ~FCF_FABRIC_DEVICE; + fcport->keep_nport_handle = 1; if (vha->flags.nvme_enabled) { fcport->fc4f_nvme = 1; fcport->n2n_flag = 1; diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 0ffda6171614..a06e56224a55 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -953,7 +953,7 @@ void qlt_free_session_done(struct work_struct *work) struct qla_hw_data *ha = vha->hw; unsigned long flags; bool logout_started = false; - scsi_qla_host_t *base_vha; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); struct qlt_plogi_ack_t *own = sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]; @@ -1020,6 +1020,7 @@ void qlt_free_session_done(struct work_struct *work) if (logout_started) { bool traced = false; + u16 cnt = 0; while (!READ_ONCE(sess->logout_completed)) { if (!traced) { @@ -1029,6 +1030,9 @@ void qlt_free_session_done(struct work_struct *work) traced = true; } msleep(100); + cnt++; + if (cnt > 200) + break; } ql_dbg(ql_dbg_disc, vha, 0xf087, @@ -1101,6 +1105,7 @@ void qlt_free_session_done(struct work_struct *work) } spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + sess->free_pending = 0; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, "Unregistration of sess %p %8phC finished fcp_cnt %d\n", @@ -1109,17 +1114,9 @@ void qlt_free_session_done(struct work_struct *work) if (tgt && (tgt->sess_count == 0)) wake_up_all(&tgt->waitQ); - if (vha->fcport_count == 0) - wake_up_all(&vha->fcport_waitQ); - - base_vha = pci_get_drvdata(ha->pdev); - - sess->free_pending = 0; - - if (test_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags)) - return; - - if ((!tgt || !tgt->tgt_stop) && !LOOP_TRANSITION(vha)) { + if (!test_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags) && + !(vha->vp_idx && test_bit(VPORT_DELETE, &vha->dpc_flags)) && + (!tgt || !tgt->tgt_stop) && !LOOP_TRANSITION(vha)) { switch (vha->host->active_mode) { case MODE_INITIATOR: case MODE_DUAL: @@ -1132,6 +1129,9 @@ void qlt_free_session_done(struct work_struct *work) break; } } + + if (vha->fcport_count == 0) + wake_up_all(&vha->fcport_waitQ); } /* ha->tgt.sess_lock supposed to be held on entry */ @@ -1161,7 +1161,7 @@ void qlt_unreg_sess(struct fc_port *sess) sess->last_login_gen = sess->login_gen; INIT_WORK(&sess->free_work, qlt_free_session_done); - schedule_work(&sess->free_work); + queue_work(sess->vha->hw->wq, &sess->free_work); } EXPORT_SYMBOL(qlt_unreg_sess); diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index ed8b9ac805e6..542d2bac2922 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1837,8 +1837,7 @@ static int storvsc_probe(struct hv_device *device, /* * Set the number of HW queues we are supporting. */ - if (stor_device->num_sc != 0) - host->nr_hw_queues = stor_device->num_sc + 1; + host->nr_hw_queues = num_present_cpus(); /* * Set the error handler work queue. diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 034dd9cb9ec8..11a87f51c442 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -8143,6 +8143,9 @@ int ufshcd_shutdown(struct ufs_hba *hba) { int ret = 0; + if (!hba->is_powered) + goto out; + if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba)) goto out; diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6f1fa4c849a1..a490141a0e88 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -125,4 +125,6 @@ source "drivers/staging/exfat/Kconfig" source "drivers/staging/qlge/Kconfig" +source "drivers/staging/wfx/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index a90f9b308c8d..4cb548a0ff87 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_UWB) += uwb/ obj-$(CONFIG_USB_WUSB) += wusbcore/ obj-$(CONFIG_EXFAT_FS) += exfat/ obj-$(CONFIG_QLGE) += qlge/ +obj-$(CONFIG_WFX) += wfx/ diff --git a/drivers/staging/board/armadillo800eva.c b/drivers/staging/board/armadillo800eva.c index 962cc0c79988..0225234dd7aa 100644 --- a/drivers/staging/board/armadillo800eva.c +++ b/drivers/staging/board/armadillo800eva.c @@ -50,16 +50,8 @@ static struct sh_mobile_lcdc_info lcdc0_info = { }; static struct resource lcdc0_resources[] = { - [0] = { - .name = "LCD0", - .start = 0xfe940000, - .end = 0xfe943fff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = 177 + 32, - .flags = IORESOURCE_IRQ, - }, + DEFINE_RES_MEM_NAMED(0xfe940000, 0x4000, "LCD0"), + DEFINE_RES_IRQ(177 + 32), }; static struct platform_device lcdc0_device = { diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 15b7a82f4b1e..e52a64be93f3 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -135,7 +135,6 @@ static int clk_wzrd_probe(struct platform_device *pdev) unsigned long rate; const char *clk_name; struct clk_wzrd *clk_wzrd; - struct resource *mem; struct device_node *np = pdev->dev.of_node; clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL); @@ -143,8 +142,7 @@ static int clk_wzrd_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, clk_wzrd); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - clk_wzrd->base = devm_ioremap_resource(&pdev->dev, mem); + clk_wzrd->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(clk_wzrd->base)) return PTR_ERR(clk_wzrd->base); diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c index caf4d4df4bd3..f7c365b70106 100644 --- a/drivers/staging/comedi/drivers/dt3000.c +++ b/drivers/staging/comedi/drivers/dt3000.c @@ -508,12 +508,11 @@ static int dt3k_ai_insn_read(struct comedi_device *dev, unsigned int *data) { int i; - unsigned int chan, gain, aref; + unsigned int chan, gain; chan = CR_CHAN(insn->chanspec); gain = CR_RANGE(insn->chanspec); /* XXX docs don't explain how to select aref */ - aref = CR_AREF(insn->chanspec); for (i = 0; i < insn->n; i++) data[i] = dt3k_readsingle(dev, DPR_SUBSYS_AI, chan, gain); diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c index 147481bf680c..9e0c19eb867c 100644 --- a/drivers/staging/emxx_udc/emxx_udc.c +++ b/drivers/staging/emxx_udc/emxx_udc.c @@ -3078,7 +3078,6 @@ static int nbu2ss_drv_probe(struct platform_device *pdev) { int status = -ENODEV; struct nbu2ss_udc *udc; - struct resource *r; int irq; void __iomem *mmio_base; @@ -3088,8 +3087,7 @@ static int nbu2ss_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, udc); /* require I/O memory and IRQ to be provided as resources */ - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmio_base = devm_ioremap_resource(&pdev->dev, r); + mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mmio_base)) return PTR_ERR(mmio_base); diff --git a/drivers/staging/exfat/Kconfig b/drivers/staging/exfat/Kconfig index 290dbfc7ace1..ce32dfe33bec 100644 --- a/drivers/staging/exfat/Kconfig +++ b/drivers/staging/exfat/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 config EXFAT_FS tristate "exFAT fs support" depends on BLOCK @@ -6,7 +7,7 @@ config EXFAT_FS This adds support for the exFAT file system. config EXFAT_DONT_MOUNT_VFAT - bool "Prohibit mounting of fat/vfat filesysems by exFAT" + bool "Prohibit mounting of fat/vfat filesystems by exFAT" depends on EXFAT_FS default y help diff --git a/drivers/staging/exfat/Makefile b/drivers/staging/exfat/Makefile index 84944dfbae28..6c90aec83feb 100644 --- a/drivers/staging/exfat/Makefile +++ b/drivers/staging/exfat/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-or-later obj-$(CONFIG_EXFAT_FS) += exfat.o diff --git a/drivers/staging/exfat/TODO b/drivers/staging/exfat/TODO index a3eb282f9efc..b60e50b9cf4e 100644 --- a/drivers/staging/exfat/TODO +++ b/drivers/staging/exfat/TODO @@ -3,6 +3,15 @@ same for ffsWriteFile. exfat_core.c - fs_sync(sb,0) all over the place looks fishy as hell. There's only one place that calls it with a non-zero argument. +Randomly removing fs_sync() calls is *not* the right answer, especially +if the removal then leaves a call to fs_set_vol_flags(VOL_CLEAN), as that +says the file system is clean and synced when we *know* it isn't. +The proper fix here is to go through and actually analyze how DELAYED_SYNC +should work, and any time we're setting VOL_CLEAN, ensure the file system +has in fact been synced to disk. In other words, changing the 'false' to +'true' is probably more correct. Also, it's likely that the one current +place where it actually does an bdev_sync isn't sufficient in the DELAYED_SYNC +case. ffsTruncateFile - if (old_size <= new_size) { That doesn't look right. How did it ever work? Are they relying on lazy diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 6c12f2d79f4d..3abab33e932c 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ diff --git a/drivers/staging/exfat/exfat_blkdev.c b/drivers/staging/exfat/exfat_blkdev.c index f086c75e7076..81d20e6241c6 100644 --- a/drivers/staging/exfat/exfat_blkdev.c +++ b/drivers/staging/exfat/exfat_blkdev.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c index 1565ce65d39f..e1b001718709 100644 --- a/drivers/staging/exfat/exfat_cache.c +++ b/drivers/staging/exfat/exfat_cache.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index b3e9cf725cf5..79174e5c4145 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ diff --git a/drivers/staging/exfat/exfat_nls.c b/drivers/staging/exfat/exfat_nls.c index 03cb8290b5d2..a5c4b68925fb 100644 --- a/drivers/staging/exfat/exfat_nls.c +++ b/drivers/staging/exfat/exfat_nls.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 5f6caee819a6..86ace780a60b 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ @@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/time.h> #include <linux/slab.h> +#include <linux/mm.h> #include <linux/seq_file.h> #include <linux/pagemap.h> #include <linux/mpage.h> @@ -458,7 +459,7 @@ static int ffsUmountVol(struct super_block *sb) /* acquire the lock for file system critical section */ down(&p_fs->v_sem); - fs_sync(sb, false); + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); if (p_fs->vol_type == EXFAT) { @@ -666,8 +667,8 @@ static int ffsCreateFile(struct inode *inode, char *path, u8 mode, /* create a new file */ ret = create_file(inode, &dir, &uni_name, mode, fid); -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -1039,8 +1040,8 @@ static int ffsWriteFile(struct inode *inode, struct file_id_t *fid, release_entry_set(es); } -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -1179,8 +1180,8 @@ static int ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) if (fid->rwoffset > fid->size) fid->rwoffset = fid->size; -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -1327,8 +1328,8 @@ static int ffsMoveFile(struct inode *old_parent_inode, struct file_id_t *fid, num_entries + 1); } out: -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -1389,8 +1390,8 @@ static int ffsRemoveFile(struct inode *inode, struct file_id_t *fid) fid->start_clu = CLUSTER_32(~0); fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -1478,8 +1479,8 @@ static int ffsSetAttr(struct inode *inode, u32 attr) release_entry_set(es); } -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -1916,8 +1917,8 @@ static int ffsCreateDir(struct inode *inode, char *path, struct file_id_t *fid) ret = create_dir(inode, &dir, &uni_name, fid); -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -2177,8 +2178,8 @@ static int ffsRemoveDir(struct inode *inode, struct file_id_t *fid) fid->start_clu = CLUSTER_32(~0); fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; -#ifdef CONFIG_EXFAT_DELAYED_SYNC - fs_sync(sb, false); +#ifndef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, true); fs_set_vol_flags(sb, VOL_CLEAN); #endif @@ -3450,7 +3451,7 @@ static void exfat_free_super(struct exfat_sb_info *sbi) kfree(sbi->options.iocharset); /* mutex_init is in exfat_fill_super function. only for 3.7+ */ mutex_destroy(&sbi->s_lock); - kfree(sbi); + kvfree(sbi); } static void exfat_put_super(struct super_block *sb) @@ -3845,7 +3846,7 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent) * the filesystem, since we're only just about to mount * it and have no inodes etc active! */ - sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); + sbi = kvzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) return -ENOMEM; mutex_init(&sbi->s_lock); diff --git a/drivers/staging/exfat/exfat_upcase.c b/drivers/staging/exfat/exfat_upcase.c index 366082fb3dab..b91a1faa0e50 100644 --- a/drivers/staging/exfat/exfat_upcase.c +++ b/drivers/staging/exfat/exfat_upcase.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. */ diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig index 8ec524a95ec8..cb61c2a772bd 100644 --- a/drivers/staging/fbtft/Kconfig +++ b/drivers/staging/fbtft/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 menuconfig FB_TFT tristate "Support for small TFT LCD display modules" - depends on FB && SPI + depends on FB && SPI && OF depends on GPIOLIB || COMPILE_TEST select FB_SYS_FILLRECT select FB_SYS_COPYAREA @@ -199,13 +199,3 @@ config FB_TFT_WATTEROTT depends on FB_TFT help Generic Framebuffer support for WATTEROTT - -config FB_FLEX - tristate "Generic FB driver for TFT LCD displays" - depends on FB_TFT - help - Generic Framebuffer support for TFT LCD displays. - -config FB_TFT_FBTFT_DEVICE - tristate "Module to for adding FBTFT devices" - depends on FB_TFT diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile index 6bc03311c9c7..27af43f32f81 100644 --- a/drivers/staging/fbtft/Makefile +++ b/drivers/staging/fbtft/Makefile @@ -36,7 +36,3 @@ obj-$(CONFIG_FB_TFT_UC1611) += fb_uc1611.o obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o -obj-$(CONFIG_FB_FLEX) += flexfb.o - -# Device modules -obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index cf5700a2ea66..63d36f21eb98 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -317,7 +317,7 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) /* special case, needed ? */ if (y == -1) { y = 0; - height = info->var.yres - 1; + height = info->var.yres; } /* Mark display lines/area as dirty */ @@ -714,7 +714,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, if (par->gamma.curves && gamma) { if (fbtft_gamma_parse_str(par, par->gamma.curves, gamma, strlen(gamma))) - goto alloc_fail; + goto release_framebuf; } /* Transmit buffer */ @@ -731,7 +731,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, if (txbuflen > 0) { txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL); if (!txbuf) - goto alloc_fail; + goto release_framebuf; par->txbuf.buf = txbuf; par->txbuf.len = txbuflen; } @@ -753,6 +753,9 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, return info; +release_framebuf: + framebuffer_release(info); + alloc_fail: vfree(vmem); diff --git a/drivers/staging/fbtft/fbtft_device.c b/drivers/staging/fbtft/fbtft_device.c deleted file mode 100644 index 44e1410eb3fe..000000000000 --- a/drivers/staging/fbtft/fbtft_device.c +++ /dev/null @@ -1,1261 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * - * Copyright (C) 2013, Noralf Tronnes - */ - -#define pr_fmt(fmt) "fbtft_device: " fmt -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/gpio/consumer.h> -#include <linux/spi/spi.h> -#include <video/mipi_display.h> - -#include "fbtft.h" - -#define MAX_GPIOS 32 - -static struct spi_device *spi_device; -static struct platform_device *p_device; - -static char *name; -module_param(name, charp, 0000); -MODULE_PARM_DESC(name, - "Devicename (required). name=list => list all supported devices."); - -static unsigned int rotate; -module_param(rotate, uint, 0000); -MODULE_PARM_DESC(rotate, - "Angle to rotate display counter clockwise: 0, 90, 180, 270"); - -static unsigned int busnum; -module_param(busnum, uint, 0000); -MODULE_PARM_DESC(busnum, "SPI bus number (default=0)"); - -static unsigned int cs; -module_param(cs, uint, 0000); -MODULE_PARM_DESC(cs, "SPI chip select (default=0)"); - -static unsigned int speed; -module_param(speed, uint, 0000); -MODULE_PARM_DESC(speed, "SPI speed (override device default)"); - -static int mode = -1; -module_param(mode, int, 0000); -MODULE_PARM_DESC(mode, "SPI mode (override device default)"); - -static unsigned int fps; -module_param(fps, uint, 0000); -MODULE_PARM_DESC(fps, "Frames per second (override driver default)"); - -static char *gamma; -module_param(gamma, charp, 0000); -MODULE_PARM_DESC(gamma, - "String representation of Gamma Curve(s). Driver specific."); - -static int txbuflen; -module_param(txbuflen, int, 0000); -MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)"); - -static int bgr = -1; -module_param(bgr, int, 0000); -MODULE_PARM_DESC(bgr, - "BGR bit (supported by some drivers)."); - -static unsigned int startbyte; -module_param(startbyte, uint, 0000); -MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays."); - -static bool custom; -module_param(custom, bool, 0000); -MODULE_PARM_DESC(custom, "Add a custom display device. Use speed= argument to make it a SPI device, else platform_device"); - -static unsigned int width; -module_param(width, uint, 0000); -MODULE_PARM_DESC(width, "Display width, used with the custom argument"); - -static unsigned int height; -module_param(height, uint, 0000); -MODULE_PARM_DESC(height, "Display height, used with the custom argument"); - -static unsigned int buswidth = 8; -module_param(buswidth, uint, 0000); -MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument"); - -static s16 init[FBTFT_MAX_INIT_SEQUENCE]; -static int init_num; -module_param_array(init, short, &init_num, 0000); -MODULE_PARM_DESC(init, "Init sequence, used with the custom argument"); - -static unsigned long debug; -module_param(debug, ulong, 0000); -MODULE_PARM_DESC(debug, - "level: 0-7 (the remaining 29 bits is for advanced usage)"); - -static unsigned int verbose = 3; -module_param(verbose, uint, 0000); -MODULE_PARM_DESC(verbose, - "0 silent, >1 show devices, >2 show devices before (default=3)"); - -struct fbtft_device_display { - char *name; - struct spi_board_info *spi; - struct platform_device *pdev; -}; - -static void fbtft_device_pdev_release(struct device *dev); - -static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len); -static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, - int xs, int ys, int xe, int ye); - -#define ADAFRUIT18_GAMMA \ - "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \ - "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10" - -#define CBERRY28_GAMMA \ - "D0 00 14 15 13 2C 42 43 4E 09 16 14 18 21\n" \ - "D0 00 14 15 13 0B 43 55 53 0C 17 14 23 20" - -static const s16 cberry28_init_sequence[] = { - /* turn off sleep mode */ - -1, MIPI_DCS_EXIT_SLEEP_MODE, - -2, 120, - - /* set pixel format to RGB-565 */ - -1, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT, - - -1, 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33, - - /* - * VGH = 13.26V - * VGL = -10.43V - */ - -1, 0xB7, 0x35, - - /* - * VDV and VRH register values come from command write - * (instead of NVM) - */ - -1, 0xC2, 0x01, 0xFF, - - /* - * VAP = 4.7V + (VCOM + VCOM offset + 0.5 * VDV) - * VAN = -4.7V + (VCOM + VCOM offset + 0.5 * VDV) - */ - -1, 0xC3, 0x17, - - /* VDV = 0V */ - -1, 0xC4, 0x20, - - /* VCOM = 0.675V */ - -1, 0xBB, 0x17, - - /* VCOM offset = 0V */ - -1, 0xC5, 0x20, - - /* - * AVDD = 6.8V - * AVCL = -4.8V - * VDS = 2.3V - */ - -1, 0xD0, 0xA4, 0xA1, - - -1, MIPI_DCS_SET_DISPLAY_ON, - - -3, -}; - -static const s16 hy28b_init_sequence[] = { - -1, 0x00e7, 0x0010, -1, 0x0000, 0x0001, - -1, 0x0001, 0x0100, -1, 0x0002, 0x0700, - -1, 0x0003, 0x1030, -1, 0x0004, 0x0000, - -1, 0x0008, 0x0207, -1, 0x0009, 0x0000, - -1, 0x000a, 0x0000, -1, 0x000c, 0x0001, - -1, 0x000d, 0x0000, -1, 0x000f, 0x0000, - -1, 0x0010, 0x0000, -1, 0x0011, 0x0007, - -1, 0x0012, 0x0000, -1, 0x0013, 0x0000, - -2, 50, -1, 0x0010, 0x1590, -1, 0x0011, - 0x0227, -2, 50, -1, 0x0012, 0x009c, -2, 50, - -1, 0x0013, 0x1900, -1, 0x0029, 0x0023, - -1, 0x002b, 0x000e, -2, 50, - -1, 0x0020, 0x0000, -1, 0x0021, 0x0000, - -2, 50, -1, 0x0050, 0x0000, - -1, 0x0051, 0x00ef, -1, 0x0052, 0x0000, - -1, 0x0053, 0x013f, -1, 0x0060, 0xa700, - -1, 0x0061, 0x0001, -1, 0x006a, 0x0000, - -1, 0x0080, 0x0000, -1, 0x0081, 0x0000, - -1, 0x0082, 0x0000, -1, 0x0083, 0x0000, - -1, 0x0084, 0x0000, -1, 0x0085, 0x0000, - -1, 0x0090, 0x0010, -1, 0x0092, 0x0000, - -1, 0x0093, 0x0003, -1, 0x0095, 0x0110, - -1, 0x0097, 0x0000, -1, 0x0098, 0x0000, - -1, 0x0007, 0x0133, -1, 0x0020, 0x0000, - -1, 0x0021, 0x0000, -2, 100, -3 }; - -#define HY28B_GAMMA \ - "04 1F 4 7 7 0 7 7 6 0\n" \ - "0F 00 1 7 4 0 0 0 6 7" - -static const s16 pitft_init_sequence[] = { - -1, MIPI_DCS_SOFT_RESET, - -2, 5, - -1, MIPI_DCS_SET_DISPLAY_OFF, - -1, 0xEF, 0x03, 0x80, 0x02, - -1, 0xCF, 0x00, 0xC1, 0x30, - -1, 0xED, 0x64, 0x03, 0x12, 0x81, - -1, 0xE8, 0x85, 0x00, 0x78, - -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, - -1, 0xF7, 0x20, - -1, 0xEA, 0x00, 0x00, - -1, 0xC0, 0x23, - -1, 0xC1, 0x10, - -1, 0xC5, 0x3E, 0x28, - -1, 0xC7, 0x86, - -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55, - -1, 0xB1, 0x00, 0x18, - -1, 0xB6, 0x08, 0x82, 0x27, - -1, 0xF2, 0x00, - -1, MIPI_DCS_SET_GAMMA_CURVE, 0x01, - -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, - 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, - -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, - 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, - -1, MIPI_DCS_EXIT_SLEEP_MODE, - -2, 100, - -1, MIPI_DCS_SET_DISPLAY_ON, - -2, 20, - -3 -}; - -static const s16 waveshare32b_init_sequence[] = { - -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, - -1, 0xCF, 0x00, 0xC1, 0x30, - -1, 0xE8, 0x85, 0x00, 0x78, - -1, 0xEA, 0x00, 0x00, - -1, 0xED, 0x64, 0x03, 0x12, 0x81, - -1, 0xF7, 0x20, - -1, 0xC0, 0x23, - -1, 0xC1, 0x10, - -1, 0xC5, 0x3E, 0x28, - -1, 0xC7, 0x86, - -1, MIPI_DCS_SET_ADDRESS_MODE, 0x28, - -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55, - -1, 0xB1, 0x00, 0x18, - -1, 0xB6, 0x08, 0x82, 0x27, - -1, 0xF2, 0x00, - -1, MIPI_DCS_SET_GAMMA_CURVE, 0x01, - -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, - 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, - -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, - 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, - -1, MIPI_DCS_EXIT_SLEEP_MODE, - -2, 120, - -1, MIPI_DCS_SET_DISPLAY_ON, - -1, MIPI_DCS_WRITE_MEMORY_START, - -3 -}; - -#define PIOLED_GAMMA "0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 " \ - "2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 " \ - "3 3 3 4 4 4 4 4 4 4 4 4 4 4 4" - -/* Supported displays in alphabetical order */ -static struct fbtft_device_display displays[] = { - { - .name = "adafruit18", - .spi = &(struct spi_board_info) { - .modalias = "fb_st7735r", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .gamma = ADAFRUIT18_GAMMA, - } - } - }, { - .name = "adafruit18_green", - .spi = &(struct spi_board_info) { - .modalias = "fb_st7735r", - .max_speed_hz = 4000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .fbtftops.set_addr_win = - adafruit18_green_tab_set_addr_win, - }, - .bgr = true, - .gamma = ADAFRUIT18_GAMMA, - } - } - }, { - .name = "adafruit22", - .spi = &(struct spi_board_info) { - .modalias = "fb_hx8340bn", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 9, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "adafruit22a", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9340", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "adafruit28", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9341", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "adafruit13m", - .spi = &(struct spi_board_info) { - .modalias = "fb_ssd1306", - .max_speed_hz = 16000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "admatec_c-berry28", - .spi = &(struct spi_board_info) { - .modalias = "fb_st7789v", - .max_speed_hz = 48000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .init_sequence = cberry28_init_sequence, - }, - .gamma = CBERRY28_GAMMA, - } - } - }, { - .name = "agm1264k-fl", - .pdev = &(struct platform_device) { - .name = "fb_agm1264k-fl", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = FBTFT_ONBOARD_BACKLIGHT, - }, - }, - } - } - }, { - .name = "dogs102", - .spi = &(struct spi_board_info) { - .modalias = "fb_uc1701", - .max_speed_hz = 8000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - .bgr = true, - } - } - }, { - .name = "er_tftm050_2", - .spi = &(struct spi_board_info) { - .modalias = "fb_ra8875", - .max_speed_hz = 5000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .width = 480, - .height = 272, - }, - .bgr = true, - } - } - }, { - .name = "er_tftm070_5", - .spi = &(struct spi_board_info) { - .modalias = "fb_ra8875", - .max_speed_hz = 5000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .width = 800, - .height = 480, - }, - .bgr = true, - } - } - }, { - .name = "ew24ha0", - .spi = &(struct spi_board_info) { - .modalias = "fb_uc1611", - .max_speed_hz = 32000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "ew24ha0_9bit", - .spi = &(struct spi_board_info) { - .modalias = "fb_uc1611", - .max_speed_hz = 32000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 9, - }, - } - } - }, { - .name = "flexfb", - .spi = &(struct spi_board_info) { - .modalias = "flexfb", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - } - }, { - .name = "flexpfb", - .pdev = &(struct platform_device) { - .name = "flexpfb", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - } - } - }, { - .name = "freetronicsoled128", - .spi = &(struct spi_board_info) { - .modalias = "fb_ssd1351", - .max_speed_hz = 20000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = FBTFT_ONBOARD_BACKLIGHT, - }, - .bgr = true, - } - } - }, { - .name = "hx8353d", - .spi = &(struct spi_board_info) { - .modalias = "fb_hx8353d", - .max_speed_hz = 16000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - } - } - }, { - .name = "hy28a", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9320", - .max_speed_hz = 32000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .startbyte = 0x70, - .bgr = true, - } - } - }, { - .name = "hy28b", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9325", - .max_speed_hz = 48000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .init_sequence = hy28b_init_sequence, - }, - .startbyte = 0x70, - .bgr = true, - .fps = 50, - .gamma = HY28B_GAMMA, - } - } - }, { - .name = "ili9481", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9481", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .regwidth = 16, - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "itdb24", - .pdev = &(struct platform_device) { - .name = "fb_s6d1121", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = false, - }, - } - } - }, { - .name = "itdb28", - .pdev = &(struct platform_device) { - .name = "fb_ili9325", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - }, - } - } - }, { - .name = "itdb28_spi", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9325", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "mi0283qt-2", - .spi = &(struct spi_board_info) { - .modalias = "fb_hx8347d", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .startbyte = 0x70, - .bgr = true, - } - } - }, { - .name = "mi0283qt-9a", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9341", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 9, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "mi0283qt-v2", - .spi = &(struct spi_board_info) { - .modalias = "fb_watterott", - .max_speed_hz = 4000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - } - } - }, { - .name = "nokia3310", - .spi = &(struct spi_board_info) { - .modalias = "fb_pcd8544", - .max_speed_hz = 400000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "nokia3310a", - .spi = &(struct spi_board_info) { - .modalias = "fb_tls8204", - .max_speed_hz = 1000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "nokia5110", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9163", - .max_speed_hz = 12000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "piscreen", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9486", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .regwidth = 16, - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "pitft", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9340", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .chip_select = 0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .init_sequence = pitft_init_sequence, - }, - .bgr = true, - } - } - }, { - .name = "pioled", - .spi = &(struct spi_board_info) { - .modalias = "fb_ssd1351", - .max_speed_hz = 20000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - .bgr = true, - .gamma = PIOLED_GAMMA - } - } - }, { - .name = "rpi-display", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9341", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "s6d02a1", - .spi = &(struct spi_board_info) { - .modalias = "fb_s6d02a1", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "sainsmart18", - .spi = &(struct spi_board_info) { - .modalias = "fb_st7735r", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "sainsmart32", - .pdev = &(struct platform_device) { - .name = "fb_ssd1289", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 16, - .txbuflen = -2, /* disable buffer */ - .backlight = 1, - .fbtftops.write = write_gpio16_wr_slow, - }, - .bgr = true, - }, - }, - } - }, { - .name = "sainsmart32_fast", - .pdev = &(struct platform_device) { - .name = "fb_ssd1289", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 16, - .txbuflen = -2, /* disable buffer */ - .backlight = 1, - }, - .bgr = true, - }, - }, - } - }, { - .name = "sainsmart32_latched", - .pdev = &(struct platform_device) { - .name = "fb_ssd1289", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 16, - .txbuflen = -2, /* disable buffer */ - .backlight = 1, - .fbtftops.write = - fbtft_write_gpio16_wr_latched, - }, - .bgr = true, - }, - }, - } - }, { - .name = "sainsmart32_spi", - .spi = &(struct spi_board_info) { - .modalias = "fb_ssd1289", - .max_speed_hz = 16000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "spidev", - .spi = &(struct spi_board_info) { - .modalias = "spidev", - .max_speed_hz = 500000, - .bus_num = 0, - .chip_select = 0, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - } - } - }, { - .name = "ssd1331", - .spi = &(struct spi_board_info) { - .modalias = "fb_ssd1331", - .max_speed_hz = 20000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "tinylcd35", - .spi = &(struct spi_board_info) { - .modalias = "fb_tinylcd", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "tm022hdh26", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9341", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "tontec35_9481", /* boards before 02 July 2014 */ - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9481", - .max_speed_hz = 128000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "tontec35_9486", /* boards after 02 July 2014 */ - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9486", - .max_speed_hz = 128000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - }, - .bgr = true, - } - } - }, { - .name = "upd161704", - .spi = &(struct spi_board_info) { - .modalias = "fb_upd161704", - .max_speed_hz = 32000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - .name = "waveshare32b", - .spi = &(struct spi_board_info) { - .modalias = "fb_ili9340", - .max_speed_hz = 48000000, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - .backlight = 1, - .init_sequence = - waveshare32b_init_sequence, - }, - .bgr = true, - } - } - }, { - .name = "waveshare22", - .spi = &(struct spi_board_info) { - .modalias = "fb_bd663474", - .max_speed_hz = 32000000, - .mode = SPI_MODE_3, - .platform_data = &(struct fbtft_platform_data) { - .display = { - .buswidth = 8, - }, - } - } - }, { - /* This should be the last item. - * Used with the custom argument - */ - .name = "", - .spi = &(struct spi_board_info) { - .modalias = "", - .max_speed_hz = 0, - .mode = SPI_MODE_0, - .platform_data = &(struct fbtft_platform_data) { - } - }, - .pdev = &(struct platform_device) { - .name = "", - .id = 0, - .dev = { - .release = fbtft_device_pdev_release, - .platform_data = &(struct fbtft_platform_data) { - }, - }, - }, - } -}; - -static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len) -{ - u16 data; - int i; -#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO - static u16 prev_data; -#endif - - fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, - "%s(len=%zu): ", __func__, len); - - while (len) { - data = *(u16 *)buf; - - /* Start writing by pulling down /WR */ - gpiod_set_value(par->gpio.wr, 0); - - /* Set data */ -#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO - if (data == prev_data) { - gpiod_set_value(par->gpio.wr, 0); /* used as delay */ - } else { - for (i = 0; i < 16; i++) { - if ((data & 1) != (prev_data & 1)) - gpiod_set_value(par->gpio.db[i], - data & 1); - data >>= 1; - prev_data >>= 1; - } - } -#else - for (i = 0; i < 16; i++) { - gpiod_set_value(par->gpio.db[i], data & 1); - data >>= 1; - } -#endif - - /* Pullup /WR */ - gpiod_set_value(par->gpio.wr, 1); - -#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO - prev_data = *(u16 *)buf; -#endif - buf += 2; - len -= 2; - } - - return 0; -} - -static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, - int xs, int ys, int xe, int ye) -{ - write_reg(par, 0x2A, 0, xs + 2, 0, xe + 2); - write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1); - write_reg(par, 0x2C); -} - -static void fbtft_device_pdev_release(struct device *dev) -{ -/* Needed to silence this message: - * Device 'xxx' does not have a release() function, - * it is broken and must be fixed - */ -} - -static int spi_device_found(struct device *dev, void *data) -{ - struct spi_device *spi = to_spi_device(dev); - - dev_info(dev, "%s %s %dkHz %d bits mode=0x%02X\n", spi->modalias, - dev_name(dev), spi->max_speed_hz / 1000, spi->bits_per_word, - spi->mode); - - return 0; -} - -static void pr_spi_devices(void) -{ - pr_debug("SPI devices registered:\n"); - bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found); -} - -static int p_device_found(struct device *dev, void *data) -{ - struct platform_device - *pdev = to_platform_device(dev); - - if (strstr(pdev->name, "fb")) - dev_info(dev, "%s id=%d pdata? %s\n", pdev->name, pdev->id, - pdev->dev.platform_data ? "yes" : "no"); - - return 0; -} - -static void pr_p_devices(void) -{ - pr_debug("'fb' Platform devices registered:\n"); - bus_for_each_dev(&platform_bus_type, NULL, NULL, p_device_found); -} - -#ifdef MODULE -static void fbtft_device_spi_delete(struct spi_master *master, unsigned int cs) -{ - struct device *dev; - char str[32]; - - snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs); - - dev = bus_find_device_by_name(&spi_bus_type, NULL, str); - if (dev) { - if (verbose) - dev_info(dev, "Deleting %s\n", str); - device_del(dev); - } -} - -static int fbtft_device_spi_device_register(struct spi_board_info *spi) -{ - struct spi_master *master; - - master = spi_busnum_to_master(spi->bus_num); - if (!master) { - pr_err("spi_busnum_to_master(%d) returned NULL\n", - spi->bus_num); - return -EINVAL; - } - /* make sure it's available */ - fbtft_device_spi_delete(master, spi->chip_select); - spi_device = spi_new_device(master, spi); - put_device(&master->dev); - if (!spi_device) { - dev_err(&master->dev, "spi_new_device() returned NULL\n"); - return -EPERM; - } - return 0; -} -#else -static int fbtft_device_spi_device_register(struct spi_board_info *spi) -{ - return spi_register_board_info(spi, 1); -} -#endif - -static int __init fbtft_device_init(void) -{ - struct spi_board_info *spi = NULL; - struct fbtft_platform_data *pdata; - bool found = false; - int i = 0; - int ret = 0; - - if (!name) { -#ifdef MODULE - pr_err("missing module parameter: 'name'\n"); - return -EINVAL; -#else - return 0; -#endif - } - - if (init_num > FBTFT_MAX_INIT_SEQUENCE) { - pr_err("init parameter: exceeded max array size: %d\n", - FBTFT_MAX_INIT_SEQUENCE); - return -EINVAL; - } - - if (verbose > 2) { - pr_spi_devices(); /* print list of registered SPI devices */ - pr_p_devices(); /* print list of 'fb' platform devices */ - } - - pr_debug("name='%s', busnum=%d, cs=%d\n", name, busnum, cs); - - if (rotate > 0 && rotate < 4) { - rotate = (4 - rotate) * 90; - pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n", - rotate); - } - if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) { - pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n", - rotate); - rotate = 0; - } - - /* name=list lists all supported displays */ - if (strcmp(name, "list") == 0) { - pr_info("Supported displays:\n"); - - for (i = 0; i < ARRAY_SIZE(displays); i++) - pr_info("%s\n", displays[i].name); - return -ECANCELED; - } - - if (custom) { - i = ARRAY_SIZE(displays) - 1; - displays[i].name = name; - if (speed == 0) { - displays[i].pdev->name = name; - displays[i].spi = NULL; - } else { - size_t len; - - len = strlcpy(displays[i].spi->modalias, name, - SPI_NAME_SIZE); - if (len >= SPI_NAME_SIZE) - pr_warn("modalias (name) truncated to: %s\n", - displays[i].spi->modalias); - displays[i].pdev = NULL; - } - } - - for (i = 0; i < ARRAY_SIZE(displays); i++) { - if (strncmp(name, displays[i].name, SPI_NAME_SIZE) == 0) { - if (displays[i].spi) { - spi = displays[i].spi; - spi->chip_select = cs; - spi->bus_num = busnum; - if (speed) - spi->max_speed_hz = speed; - if (mode != -1) - spi->mode = mode; - pdata = (void *)spi->platform_data; - } else if (displays[i].pdev) { - p_device = displays[i].pdev; - pdata = p_device->dev.platform_data; - } else { - pr_err("broken displays array\n"); - return -EINVAL; - } - - pdata->rotate = rotate; - if (bgr == 0) - pdata->bgr = false; - else if (bgr == 1) - pdata->bgr = true; - if (startbyte) - pdata->startbyte = startbyte; - if (gamma) - pdata->gamma = gamma; - pdata->display.debug = debug; - if (fps) - pdata->fps = fps; - if (txbuflen) - pdata->txbuflen = txbuflen; - if (init_num) - pdata->display.init_sequence = init; - if (custom) { - pdata->display.width = width; - pdata->display.height = height; - pdata->display.buswidth = buswidth; - pdata->display.backlight = 1; - } - - if (displays[i].spi) { - ret = fbtft_device_spi_device_register(spi); - if (ret) { - pr_err("failed to register SPI device\n"); - return ret; - } - } else { - ret = platform_device_register(p_device); - if (ret < 0) { - pr_err("platform_device_register() returned %d\n", - ret); - return ret; - } - } - found = true; - break; - } - } - - if (!found) { - pr_err("display not supported: '%s'\n", name); - return -EINVAL; - } - - if (spi_device && (verbose > 1)) - pr_spi_devices(); - if (p_device && (verbose > 1)) - pr_p_devices(); - - return 0; -} - -static void __exit fbtft_device_exit(void) -{ - if (spi_device) { - device_del(&spi_device->dev); - kfree(spi_device); - } - - if (p_device) - platform_device_unregister(p_device); -} - -arch_initcall(fbtft_device_init); -module_exit(fbtft_device_exit); - -MODULE_DESCRIPTION("Add a FBTFT device."); -MODULE_AUTHOR("Noralf Tronnes"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c deleted file mode 100644 index 3747321011fa..000000000000 --- a/drivers/staging/fbtft/flexfb.c +++ /dev/null @@ -1,851 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Generic FB driver for TFT LCD displays - * - * Copyright (C) 2013 Noralf Tronnes - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/vmalloc.h> -#include <linux/gpio/consumer.h> -#include <linux/spi/spi.h> -#include <linux/delay.h> - -#include "fbtft.h" - -#define DRVNAME "flexfb" - -static char *chip; -module_param(chip, charp, 0000); -MODULE_PARM_DESC(chip, "LCD controller"); - -static unsigned int width; -module_param(width, uint, 0000); -MODULE_PARM_DESC(width, "Display width"); - -static unsigned int height; -module_param(height, uint, 0000); -MODULE_PARM_DESC(height, "Display height"); - -static s16 init[512]; -static int init_num; -module_param_array(init, short, &init_num, 0000); -MODULE_PARM_DESC(init, "Init sequence"); - -static unsigned int setaddrwin; -module_param(setaddrwin, uint, 0000); -MODULE_PARM_DESC(setaddrwin, "Which set_addr_win() implementation to use"); - -static unsigned int buswidth = 8; -module_param(buswidth, uint, 0000); -MODULE_PARM_DESC(buswidth, "Width of databus (default: 8)"); - -static unsigned int regwidth = 8; -module_param(regwidth, uint, 0000); -MODULE_PARM_DESC(regwidth, "Width of controller register (default: 8)"); - -static bool nobacklight; -module_param(nobacklight, bool, 0000); -MODULE_PARM_DESC(nobacklight, "Turn off backlight functionality."); - -static bool latched; -module_param(latched, bool, 0000); -MODULE_PARM_DESC(latched, "Use with latched 16-bit databus"); - -static const s16 *initp; -static int initp_num; - -/* default init sequences */ -static const s16 st7735r_init[] = { - -1, 0x01, - -2, 150, - -1, 0x11, - -2, 500, - -1, 0xB1, 0x01, 0x2C, 0x2D, - -1, 0xB2, 0x01, 0x2C, 0x2D, - -1, 0xB3, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D, - -1, 0xB4, 0x07, - -1, 0xC0, 0xA2, 0x02, 0x84, - -1, 0xC1, 0xC5, - -1, 0xC2, 0x0A, 0x00, - -1, 0xC3, 0x8A, 0x2A, - -1, 0xC4, 0x8A, 0xEE, - -1, 0xC5, 0x0E, - -1, 0x20, - -1, 0x36, 0xC0, - -1, 0x3A, 0x05, - -1, 0xE0, 0x0f, 0x1a, 0x0f, 0x18, 0x2f, 0x28, 0x20, 0x22, - 0x1f, 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10, - -1, 0xE1, 0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, - 0x30, 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10, - -1, 0x29, - -2, 100, - -1, 0x13, - -2, 10, - -3 -}; - -static const s16 ssd1289_init[] = { - -1, 0x00, 0x0001, - -1, 0x03, 0xA8A4, - -1, 0x0C, 0x0000, - -1, 0x0D, 0x080C, - -1, 0x0E, 0x2B00, - -1, 0x1E, 0x00B7, - -1, 0x01, 0x2B3F, - -1, 0x02, 0x0600, - -1, 0x10, 0x0000, - -1, 0x11, 0x6070, - -1, 0x05, 0x0000, - -1, 0x06, 0x0000, - -1, 0x16, 0xEF1C, - -1, 0x17, 0x0003, - -1, 0x07, 0x0233, - -1, 0x0B, 0x0000, - -1, 0x0F, 0x0000, - -1, 0x41, 0x0000, - -1, 0x42, 0x0000, - -1, 0x48, 0x0000, - -1, 0x49, 0x013F, - -1, 0x4A, 0x0000, - -1, 0x4B, 0x0000, - -1, 0x44, 0xEF00, - -1, 0x45, 0x0000, - -1, 0x46, 0x013F, - -1, 0x30, 0x0707, - -1, 0x31, 0x0204, - -1, 0x32, 0x0204, - -1, 0x33, 0x0502, - -1, 0x34, 0x0507, - -1, 0x35, 0x0204, - -1, 0x36, 0x0204, - -1, 0x37, 0x0502, - -1, 0x3A, 0x0302, - -1, 0x3B, 0x0302, - -1, 0x23, 0x0000, - -1, 0x24, 0x0000, - -1, 0x25, 0x8000, - -1, 0x4f, 0x0000, - -1, 0x4e, 0x0000, - -1, 0x22, - -3 -}; - -static const s16 hx8340bn_init[] = { - -1, 0xC1, 0xFF, 0x83, 0x40, - -1, 0x11, - -2, 150, - -1, 0xCA, 0x70, 0x00, 0xD9, - -1, 0xB0, 0x01, 0x11, - -1, 0xC9, 0x90, 0x49, 0x10, 0x28, 0x28, 0x10, 0x00, 0x06, - -2, 20, - -1, 0xC2, 0x60, 0x71, 0x01, 0x0E, 0x05, 0x02, 0x09, 0x31, 0x0A, - -1, 0xC3, 0x67, 0x30, 0x61, 0x17, 0x48, 0x07, 0x05, 0x33, - -2, 10, - -1, 0xB5, 0x35, 0x20, 0x45, - -1, 0xB4, 0x33, 0x25, 0x4C, - -2, 10, - -1, 0x3A, 0x05, - -1, 0x29, - -2, 10, - -3 -}; - -static const s16 ili9225_init[] = { - -1, 0x0001, 0x011C, - -1, 0x0002, 0x0100, - -1, 0x0003, 0x1030, - -1, 0x0008, 0x0808, - -1, 0x000C, 0x0000, - -1, 0x000F, 0x0A01, - -1, 0x0020, 0x0000, - -1, 0x0021, 0x0000, - -2, 50, - -1, 0x0010, 0x0A00, - -1, 0x0011, 0x1038, - -2, 50, - -1, 0x0012, 0x1121, - -1, 0x0013, 0x004E, - -1, 0x0014, 0x676F, - -1, 0x0030, 0x0000, - -1, 0x0031, 0x00DB, - -1, 0x0032, 0x0000, - -1, 0x0033, 0x0000, - -1, 0x0034, 0x00DB, - -1, 0x0035, 0x0000, - -1, 0x0036, 0x00AF, - -1, 0x0037, 0x0000, - -1, 0x0038, 0x00DB, - -1, 0x0039, 0x0000, - -1, 0x0050, 0x0000, - -1, 0x0051, 0x060A, - -1, 0x0052, 0x0D0A, - -1, 0x0053, 0x0303, - -1, 0x0054, 0x0A0D, - -1, 0x0055, 0x0A06, - -1, 0x0056, 0x0000, - -1, 0x0057, 0x0303, - -1, 0x0058, 0x0000, - -1, 0x0059, 0x0000, - -2, 50, - -1, 0x0007, 0x1017, - -2, 50, - -3 -}; - -static const s16 ili9320_init[] = { - -1, 0x00E5, 0x8000, - -1, 0x0000, 0x0001, - -1, 0x0001, 0x0100, - -1, 0x0002, 0x0700, - -1, 0x0003, 0x1030, - -1, 0x0004, 0x0000, - -1, 0x0008, 0x0202, - -1, 0x0009, 0x0000, - -1, 0x000A, 0x0000, - -1, 0x000C, 0x0000, - -1, 0x000D, 0x0000, - -1, 0x000F, 0x0000, - -1, 0x0010, 0x0000, - -1, 0x0011, 0x0007, - -1, 0x0012, 0x0000, - -1, 0x0013, 0x0000, - -2, 200, - -1, 0x0010, 0x17B0, - -1, 0x0011, 0x0031, - -2, 50, - -1, 0x0012, 0x0138, - -2, 50, - -1, 0x0013, 0x1800, - -1, 0x0029, 0x0008, - -2, 50, - -1, 0x0020, 0x0000, - -1, 0x0021, 0x0000, - -1, 0x0030, 0x0000, - -1, 0x0031, 0x0505, - -1, 0x0032, 0x0004, - -1, 0x0035, 0x0006, - -1, 0x0036, 0x0707, - -1, 0x0037, 0x0105, - -1, 0x0038, 0x0002, - -1, 0x0039, 0x0707, - -1, 0x003C, 0x0704, - -1, 0x003D, 0x0807, - -1, 0x0050, 0x0000, - -1, 0x0051, 0x00EF, - -1, 0x0052, 0x0000, - -1, 0x0053, 0x013F, - -1, 0x0060, 0x2700, - -1, 0x0061, 0x0001, - -1, 0x006A, 0x0000, - -1, 0x0080, 0x0000, - -1, 0x0081, 0x0000, - -1, 0x0082, 0x0000, - -1, 0x0083, 0x0000, - -1, 0x0084, 0x0000, - -1, 0x0085, 0x0000, - -1, 0x0090, 0x0010, - -1, 0x0092, 0x0000, - -1, 0x0093, 0x0003, - -1, 0x0095, 0x0110, - -1, 0x0097, 0x0000, - -1, 0x0098, 0x0000, - -1, 0x0007, 0x0173, - -3 -}; - -static const s16 ili9325_init[] = { - -1, 0x00E3, 0x3008, - -1, 0x00E7, 0x0012, - -1, 0x00EF, 0x1231, - -1, 0x0001, 0x0100, - -1, 0x0002, 0x0700, - -1, 0x0003, 0x1030, - -1, 0x0004, 0x0000, - -1, 0x0008, 0x0207, - -1, 0x0009, 0x0000, - -1, 0x000A, 0x0000, - -1, 0x000C, 0x0000, - -1, 0x000D, 0x0000, - -1, 0x000F, 0x0000, - -1, 0x0010, 0x0000, - -1, 0x0011, 0x0007, - -1, 0x0012, 0x0000, - -1, 0x0013, 0x0000, - -2, 200, - -1, 0x0010, 0x1690, - -1, 0x0011, 0x0223, - -2, 50, - -1, 0x0012, 0x000D, - -2, 50, - -1, 0x0013, 0x1200, - -1, 0x0029, 0x000A, - -1, 0x002B, 0x000C, - -2, 50, - -1, 0x0020, 0x0000, - -1, 0x0021, 0x0000, - -1, 0x0030, 0x0000, - -1, 0x0031, 0x0506, - -1, 0x0032, 0x0104, - -1, 0x0035, 0x0207, - -1, 0x0036, 0x000F, - -1, 0x0037, 0x0306, - -1, 0x0038, 0x0102, - -1, 0x0039, 0x0707, - -1, 0x003C, 0x0702, - -1, 0x003D, 0x1604, - -1, 0x0050, 0x0000, - -1, 0x0051, 0x00EF, - -1, 0x0052, 0x0000, - -1, 0x0053, 0x013F, - -1, 0x0060, 0xA700, - -1, 0x0061, 0x0001, - -1, 0x006A, 0x0000, - -1, 0x0080, 0x0000, - -1, 0x0081, 0x0000, - -1, 0x0082, 0x0000, - -1, 0x0083, 0x0000, - -1, 0x0084, 0x0000, - -1, 0x0085, 0x0000, - -1, 0x0090, 0x0010, - -1, 0x0092, 0x0600, - -1, 0x0007, 0x0133, - -3 -}; - -static const s16 ili9341_init[] = { - -1, 0x28, - -2, 20, - -1, 0xCF, 0x00, 0x83, 0x30, - -1, 0xED, 0x64, 0x03, 0x12, 0x81, - -1, 0xE8, 0x85, 0x01, 0x79, - -1, 0xCB, 0x39, 0x2c, 0x00, 0x34, 0x02, - -1, 0xF7, 0x20, - -1, 0xEA, 0x00, 0x00, - -1, 0xC0, 0x26, - -1, 0xC1, 0x11, - -1, 0xC5, 0x35, 0x3E, - -1, 0xC7, 0xBE, - -1, 0xB1, 0x00, 0x1B, - -1, 0xB6, 0x0a, 0x82, 0x27, 0x00, - -1, 0xB7, 0x07, - -1, 0x3A, 0x55, - -1, 0x36, 0x48, - -1, 0x11, - -2, 120, - -1, 0x29, - -2, 20, - -3 -}; - -static const s16 ssd1351_init[] = { - -1, 0xfd, 0x12, - -1, 0xfd, 0xb1, - -1, 0xae, - -1, 0xb3, 0xf1, - -1, 0xca, 0x7f, - -1, 0xa0, 0x74, - -1, 0x15, 0x00, 0x7f, - -1, 0x75, 0x00, 0x7f, - -1, 0xa1, 0x00, - -1, 0xa2, 0x00, - -1, 0xb5, 0x00, - -1, 0xab, 0x01, - -1, 0xb1, 0x32, - -1, 0xb4, 0xa0, 0xb5, 0x55, - -1, 0xbb, 0x17, - -1, 0xbe, 0x05, - -1, 0xc1, 0xc8, 0x80, 0xc8, - -1, 0xc7, 0x0f, - -1, 0xb6, 0x01, - -1, 0xa6, - -1, 0xaf, - -3 -}; - -/** - * struct flexfb_lcd_controller - Describes the LCD controller properties - * @name: Model name of the chip - * @width: Width of display in pixels - * @height: Height of display in pixels - * @setaddrwin: Which set_addr_win() implementation to use - * @regwidth: LCD Controller Register width in bits - * @init_seq: LCD initialization sequence - * @init_seq_sz: Size of LCD initialization sequence - */ -struct flexfb_lcd_controller { - const char *name; - unsigned int width; - unsigned int height; - unsigned int setaddrwin; - unsigned int regwidth; - const s16 *init_seq; - int init_seq_sz; -}; - -static const struct flexfb_lcd_controller flexfb_chip_table[] = { - { - .name = "st7735r", - .width = 120, - .height = 160, - .init_seq = st7735r_init, - .init_seq_sz = ARRAY_SIZE(st7735r_init), - }, - { - .name = "hx8340bn", - .width = 176, - .height = 220, - .init_seq = hx8340bn_init, - .init_seq_sz = ARRAY_SIZE(hx8340bn_init), - }, - { - .name = "ili9225", - .width = 176, - .height = 220, - .regwidth = 16, - .init_seq = ili9225_init, - .init_seq_sz = ARRAY_SIZE(ili9225_init), - }, - { - .name = "ili9320", - .width = 240, - .height = 320, - .setaddrwin = 1, - .regwidth = 16, - .init_seq = ili9320_init, - .init_seq_sz = ARRAY_SIZE(ili9320_init), - }, - { - .name = "ili9325", - .width = 240, - .height = 320, - .setaddrwin = 1, - .regwidth = 16, - .init_seq = ili9325_init, - .init_seq_sz = ARRAY_SIZE(ili9325_init), - }, - { - .name = "ili9341", - .width = 240, - .height = 320, - .init_seq = ili9341_init, - .init_seq_sz = ARRAY_SIZE(ili9341_init), - }, - { - .name = "ssd1289", - .width = 240, - .height = 320, - .setaddrwin = 2, - .regwidth = 16, - .init_seq = ssd1289_init, - .init_seq_sz = ARRAY_SIZE(ssd1289_init), - }, - { - .name = "ssd1351", - .width = 128, - .height = 128, - .setaddrwin = 3, - .init_seq = ssd1351_init, - .init_seq_sz = ARRAY_SIZE(ssd1351_init), - }, -}; - -/* ili9320, ili9325 */ -static void flexfb_set_addr_win_1(struct fbtft_par *par, - int xs, int ys, int xe, int ye) -{ - switch (par->info->var.rotate) { - /* R20h = Horizontal GRAM Start Address */ - /* R21h = Vertical GRAM Start Address */ - case 0: - write_reg(par, 0x0020, xs); - write_reg(par, 0x0021, ys); - break; - case 180: - write_reg(par, 0x0020, width - 1 - xs); - write_reg(par, 0x0021, height - 1 - ys); - break; - case 270: - write_reg(par, 0x0020, width - 1 - ys); - write_reg(par, 0x0021, xs); - break; - case 90: - write_reg(par, 0x0020, ys); - write_reg(par, 0x0021, height - 1 - xs); - break; - } - write_reg(par, 0x0022); /* Write Data to GRAM */ -} - -/* ssd1289 */ -static void flexfb_set_addr_win_2(struct fbtft_par *par, - int xs, int ys, int xe, int ye) -{ - switch (par->info->var.rotate) { - /* R4Eh - Set GDDRAM X address counter */ - /* R4Fh - Set GDDRAM Y address counter */ - case 0: - write_reg(par, 0x4e, xs); - write_reg(par, 0x4f, ys); - break; - case 180: - write_reg(par, 0x4e, par->info->var.xres - 1 - xs); - write_reg(par, 0x4f, par->info->var.yres - 1 - ys); - break; - case 270: - write_reg(par, 0x4e, par->info->var.yres - 1 - ys); - write_reg(par, 0x4f, xs); - break; - case 90: - write_reg(par, 0x4e, ys); - write_reg(par, 0x4f, par->info->var.xres - 1 - xs); - break; - } - - /* R22h - RAM data write */ - write_reg(par, 0x22, 0); -} - -/* ssd1351 */ -static void set_addr_win_3(struct fbtft_par *par, - int xs, int ys, int xe, int ye) -{ - write_reg(par, 0x15, xs, xe); - write_reg(par, 0x75, ys, ye); - write_reg(par, 0x5C); -} - -static int flexfb_verify_gpios_dc(struct fbtft_par *par) -{ - fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); - - if (!par->gpio.dc) { - dev_err(par->info->device, - "Missing info about 'dc' gpio. Aborting.\n"); - return -EINVAL; - } - - return 0; -} - -static int flexfb_verify_gpios_db(struct fbtft_par *par) -{ - int i; - int num_db = buswidth; - - fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); - - if (!par->gpio.dc) { - dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n"); - return -EINVAL; - } - if (!par->gpio.wr) { - dev_err(par->info->device, "Missing info about 'wr' gpio. Aborting.\n"); - return -EINVAL; - } - if (latched && !par->gpio.latch) { - dev_err(par->info->device, "Missing info about 'latch' gpio. Aborting.\n"); - return -EINVAL; - } - if (latched) - num_db = buswidth / 2; - for (i = 0; i < num_db; i++) { - if (!par->gpio.db[i]) { - dev_err(par->info->device, - "Missing info about 'db%02d' gpio. Aborting.\n", - i); - return -EINVAL; - } - } - - return 0; -} - -static void flexfb_chip_load_param(const struct flexfb_lcd_controller *chip) -{ - if (!width) - width = chip->width; - if (!height) - height = chip->height; - setaddrwin = chip->setaddrwin; - if (chip->regwidth) - regwidth = chip->regwidth; - if (!init_num) { - initp = chip->init_seq; - initp_num = chip->init_seq_sz; - } -} - -static struct fbtft_display flex_display = { }; - -static int flexfb_chip_init(const struct device *dev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(flexfb_chip_table); i++) - if (!strcmp(chip, flexfb_chip_table[i].name)) { - flexfb_chip_load_param(&flexfb_chip_table[i]); - return 0; - } - - dev_err(dev, "chip=%s is not supported\n", chip); - - return -EINVAL; -} - -static int flexfb_probe_common(struct spi_device *sdev, - struct platform_device *pdev) -{ - struct device *dev; - struct fb_info *info; - struct fbtft_par *par; - int ret; - - initp = init; - initp_num = init_num; - - if (sdev) - dev = &sdev->dev; - else - dev = &pdev->dev; - - fbtft_init_dbg(dev, "%s(%s)\n", __func__, - sdev ? "'SPI device'" : "'Platform device'"); - - if (chip) { - ret = flexfb_chip_init(dev); - if (ret) - return ret; - } - - if (width == 0 || height == 0) { - dev_err(dev, "argument(s) missing: width and height has to be set.\n"); - return -EINVAL; - } - flex_display.width = width; - flex_display.height = height; - fbtft_init_dbg(dev, "Display resolution: %dx%d\n", width, height); - fbtft_init_dbg(dev, "chip = %s\n", chip ? chip : "not set"); - fbtft_init_dbg(dev, "setaddrwin = %d\n", setaddrwin); - fbtft_init_dbg(dev, "regwidth = %d\n", regwidth); - fbtft_init_dbg(dev, "buswidth = %d\n", buswidth); - - info = fbtft_framebuffer_alloc(&flex_display, dev, dev->platform_data); - if (!info) - return -ENOMEM; - - par = info->par; - if (sdev) - par->spi = sdev; - else - par->pdev = pdev; - if (!par->init_sequence) - par->init_sequence = initp; - par->fbtftops.init_display = fbtft_init_display; - - /* registerwrite functions */ - switch (regwidth) { - case 8: - par->fbtftops.write_register = fbtft_write_reg8_bus8; - break; - case 16: - par->fbtftops.write_register = fbtft_write_reg16_bus8; - break; - default: - dev_err(dev, - "argument 'regwidth': %d is not supported.\n", - regwidth); - return -EINVAL; - } - - /* bus functions */ - if (sdev) { - par->fbtftops.write = fbtft_write_spi; - switch (buswidth) { - case 8: - par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; - if (!par->startbyte) - par->fbtftops.verify_gpios = flexfb_verify_gpios_dc; - break; - case 9: - if (regwidth == 16) { - dev_err(dev, "argument 'regwidth': %d is not supported with buswidth=%d and SPI.\n", - regwidth, buswidth); - return -EINVAL; - } - par->fbtftops.write_register = fbtft_write_reg8_bus9; - par->fbtftops.write_vmem = fbtft_write_vmem16_bus9; - if (par->spi->master->bits_per_word_mask - & SPI_BPW_MASK(9)) { - par->spi->bits_per_word = 9; - break; - } - - dev_warn(dev, - "9-bit SPI not available, emulating using 8-bit.\n"); - /* allocate buffer with room for dc bits */ - par->extra = devm_kzalloc(par->info->device, - par->txbuf.len - + (par->txbuf.len / 8) + 8, - GFP_KERNEL); - if (!par->extra) { - ret = -ENOMEM; - goto out_release; - } - par->fbtftops.write = fbtft_write_spi_emulate_9; - - break; - default: - dev_err(dev, - "argument 'buswidth': %d is not supported with SPI.\n", - buswidth); - return -EINVAL; - } - } else { - par->fbtftops.verify_gpios = flexfb_verify_gpios_db; - switch (buswidth) { - case 8: - par->fbtftops.write = fbtft_write_gpio8_wr; - par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; - break; - case 16: - par->fbtftops.write_register = fbtft_write_reg16_bus16; - if (latched) - par->fbtftops.write = fbtft_write_gpio16_wr_latched; - else - par->fbtftops.write = fbtft_write_gpio16_wr; - par->fbtftops.write_vmem = fbtft_write_vmem16_bus16; - break; - default: - dev_err(dev, - "argument 'buswidth': %d is not supported with parallel.\n", - buswidth); - return -EINVAL; - } - } - - /* set_addr_win function */ - switch (setaddrwin) { - case 0: - /* use default */ - break; - case 1: - par->fbtftops.set_addr_win = flexfb_set_addr_win_1; - break; - case 2: - par->fbtftops.set_addr_win = flexfb_set_addr_win_2; - break; - case 3: - par->fbtftops.set_addr_win = set_addr_win_3; - break; - default: - dev_err(dev, "argument 'setaddrwin': unknown value %d.\n", - setaddrwin); - return -EINVAL; - } - - if (!nobacklight) - par->fbtftops.register_backlight = fbtft_register_backlight; - - ret = fbtft_register_framebuffer(info); - if (ret < 0) - goto out_release; - - return 0; - -out_release: - fbtft_framebuffer_release(info); - - return ret; -} - -static int flexfb_remove_common(struct device *dev, struct fb_info *info) -{ - struct fbtft_par *par; - - if (!info) - return -EINVAL; - par = info->par; - if (par) - fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par, "%s()\n", - __func__); - fbtft_unregister_framebuffer(info); - fbtft_framebuffer_release(info); - - return 0; -} - -static int flexfb_probe_spi(struct spi_device *spi) -{ - return flexfb_probe_common(spi, NULL); -} - -static int flexfb_remove_spi(struct spi_device *spi) -{ - struct fb_info *info = spi_get_drvdata(spi); - - return flexfb_remove_common(&spi->dev, info); -} - -static int flexfb_probe_pdev(struct platform_device *pdev) -{ - return flexfb_probe_common(NULL, pdev); -} - -static int flexfb_remove_pdev(struct platform_device *pdev) -{ - struct fb_info *info = platform_get_drvdata(pdev); - - return flexfb_remove_common(&pdev->dev, info); -} - -static struct spi_driver flexfb_spi_driver = { - .driver = { - .name = DRVNAME, - }, - .probe = flexfb_probe_spi, - .remove = flexfb_remove_spi, -}; - -static const struct platform_device_id flexfb_platform_ids[] = { - { "flexpfb", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(platform, flexfb_platform_ids); - -static struct platform_driver flexfb_platform_driver = { - .driver = { - .name = DRVNAME, - }, - .id_table = flexfb_platform_ids, - .probe = flexfb_probe_pdev, - .remove = flexfb_remove_pdev, -}; - -static int __init flexfb_init(void) -{ - int ret, ret2; - - ret = spi_register_driver(&flexfb_spi_driver); - ret2 = platform_driver_register(&flexfb_platform_driver); - if (ret < 0) - return ret; - return ret2; -} - -static void __exit flexfb_exit(void) -{ - spi_unregister_driver(&flexfb_spi_driver); - platform_driver_unregister(&flexfb_platform_driver); -} - -/* ------------------------------------------------------------------------- */ - -module_init(flexfb_init); -module_exit(flexfb_exit); - -MODULE_DESCRIPTION("Generic FB driver for TFT LCD displays"); -MODULE_AUTHOR("Noralf Tronnes"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/fieldbus/anybuss/anybuss-client.h b/drivers/staging/fieldbus/anybuss/anybuss-client.h index 0c4b6a1ffe10..8ee1f1baccf1 100644 --- a/drivers/staging/fieldbus/anybuss/anybuss-client.h +++ b/drivers/staging/fieldbus/anybuss/anybuss-client.h @@ -12,6 +12,9 @@ #include <linux/types.h> #include <linux/poll.h> +/* move to <linux/fieldbus_dev.h> when taking this out of staging */ +#include "../fieldbus_dev.h" + struct anybuss_host; struct anybuss_client { @@ -61,12 +64,6 @@ anybuss_set_drvdata(struct anybuss_client *client, void *data) int anybuss_set_power(struct anybuss_client *client, bool power_on); -enum anybuss_offl_mode { - AB_OFFL_MODE_CLEAR = 0, - AB_OFFL_MODE_FREEZE, - AB_OFFL_MODE_SET -}; - struct anybuss_memcfg { u16 input_io; u16 input_dpram; @@ -76,7 +73,7 @@ struct anybuss_memcfg { u16 output_dpram; u16 output_total; - enum anybuss_offl_mode offl_mode; + enum fieldbus_dev_offl_mode offl_mode; }; int anybuss_start_init(struct anybuss_client *client, diff --git a/drivers/staging/fieldbus/anybuss/hms-profinet.c b/drivers/staging/fieldbus/anybuss/hms-profinet.c index 5446843e35f4..31c43a0a5776 100644 --- a/drivers/staging/fieldbus/anybuss/hms-profinet.c +++ b/drivers/staging/fieldbus/anybuss/hms-profinet.c @@ -96,7 +96,7 @@ static int __profi_enable(struct profi_priv *priv) .output_io = 220, .output_dpram = PROFI_DPRAM_SIZE, .output_total = PROFI_DPRAM_SIZE, - .offl_mode = AB_OFFL_MODE_CLEAR, + .offl_mode = FIELDBUS_DEV_OFFL_MODE_CLEAR, }; /* diff --git a/drivers/staging/fieldbus/anybuss/host.c b/drivers/staging/fieldbus/anybuss/host.c index f69dc4930457..549cb7d51af8 100644 --- a/drivers/staging/fieldbus/anybuss/host.c +++ b/drivers/staging/fieldbus/anybuss/host.c @@ -1022,13 +1022,13 @@ int anybuss_start_init(struct anybuss_client *client, }; switch (cfg->offl_mode) { - case AB_OFFL_MODE_CLEAR: + case FIELDBUS_DEV_OFFL_MODE_CLEAR: op_mode = 0; break; - case AB_OFFL_MODE_FREEZE: + case FIELDBUS_DEV_OFFL_MODE_FREEZE: op_mode = OP_MODE_FBFC; break; - case AB_OFFL_MODE_SET: + case FIELDBUS_DEV_OFFL_MODE_SET: op_mode = OP_MODE_FBS; break; default: diff --git a/drivers/staging/fieldbus/dev_core.c b/drivers/staging/fieldbus/dev_core.c index f6f5b92ba914..1ba0234cc60d 100644 --- a/drivers/staging/fieldbus/dev_core.c +++ b/drivers/staging/fieldbus/dev_core.c @@ -23,9 +23,6 @@ static dev_t fieldbus_devt; static DEFINE_IDA(fieldbus_ida); static DEFINE_MUTEX(fieldbus_mtx); -static const char ctrl_enabled[] = "enabled"; -static const char ctrl_disabled[] = "disabled"; - static ssize_t online_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/fieldbus/fieldbus_dev.h b/drivers/staging/fieldbus/fieldbus_dev.h index a10fc3b446dc..301dca3b8d71 100644 --- a/drivers/staging/fieldbus/fieldbus_dev.h +++ b/drivers/staging/fieldbus/fieldbus_dev.h @@ -15,6 +15,12 @@ enum fieldbus_dev_type { FIELDBUS_DEV_TYPE_PROFINET, }; +enum fieldbus_dev_offl_mode { + FIELDBUS_DEV_OFFL_MODE_CLEAR = 0, + FIELDBUS_DEV_OFFL_MODE_FREEZE, + FIELDBUS_DEV_OFFL_MODE_SET +}; + /** * struct fieldbus_dev - Fieldbus device * @read_area: [DRIVER] function to read the process data area of the diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 82099db4bf0c..a480409090c0 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -7,7 +7,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/gpio.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index e6b660489165..bf3e2a9cc07f 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -155,6 +155,11 @@ * The DOUT/RDY output must also be wired to an interrupt capable GPIO. */ +enum { + AD7192_SYSCALIB_ZERO_SCALE, + AD7192_SYSCALIB_FULL_SCALE, +}; + struct ad7192_state { struct regulator *avdd; struct regulator *dvdd; @@ -169,10 +174,80 @@ struct ad7192_state { u8 devid; u8 clock_sel; struct mutex lock; /* protect sensor state */ + u8 syscalib_mode[8]; struct ad_sigma_delta sd; }; +static const char * const ad7192_syscalib_modes[] = { + [AD7192_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7192_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7192_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + st->syscalib_mode[chan->channel] = mode; + + return 0; +} + +static int ad7192_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + return st->syscalib_mode[chan->channel]; +} + +static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7192_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, temp; + + ret = strtobool(buf, &sys_calib); + if (ret) + return ret; + + temp = st->syscalib_mode[chan->channel]; + if (sys_calib) { + if (temp == AD7192_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_FULL, + chan->address); + } + + return ret ? ret : len; +} + +static const struct iio_enum ad7192_syscalib_mode_enum = { + .items = ad7192_syscalib_modes, + .num_items = ARRAY_SIZE(ad7192_syscalib_modes), + .set = ad7192_set_syscalib_mode, + .get = ad7192_get_syscalib_mode +}; + +static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7192_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7192_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", &ad7192_syscalib_mode_enum), + {} +}; + static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd) { return container_of(sd, struct ad7192_state, sd); @@ -770,9 +845,11 @@ static int ad7192_channels_config(struct iio_dev *indio_dev) *chan = channels[i]; chan->info_mask_shared_by_all |= BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY); - if (chan->type != IIO_TEMP) + if (chan->type != IIO_TEMP) { chan->info_mask_shared_by_type_available |= BIT(IIO_CHAN_INFO_SCALE); + chan->ext_info = ad7192_calibsys_ext_info; + } chan++; } diff --git a/drivers/staging/isdn/avm/b1.c b/drivers/staging/isdn/avm/b1.c index 40ca1e8fa09f..32ec8cf31fd0 100644 --- a/drivers/staging/isdn/avm/b1.c +++ b/drivers/staging/isdn/avm/b1.c @@ -261,9 +261,10 @@ int b1_loaded(avmcard *card) b1_put_byte(base, SEND_POLL); for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { if (b1_rx_full(base)) { - if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { + ans = b1_get_byte(base); + if (ans == RECEIVE_POLL) return 1; - } + printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n", card->name, ans); return 0; @@ -284,8 +285,9 @@ int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) int retval; b1_reset(port); + retval = b1_load_t4file(card, &data->firmware); - if ((retval = b1_load_t4file(card, &data->firmware))) { + if (retval) { b1_reset(port); printk(KERN_ERR "%s: failed to load t4file!!\n", card->name); @@ -295,7 +297,8 @@ int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) b1_disable_irq(port); if (data->configuration.len > 0 && data->configuration.data) { - if ((retval = b1_load_config(card, &data->configuration))) { + retval = b1_load_config(card, &data->configuration); + if (retval) { b1_reset(port); printk(KERN_ERR "%s: failed to load config!!\n", card->name); @@ -525,7 +528,9 @@ irqreturn_t b1_interrupt(int interrupt, void *devptr) MsgLen = 30; CAPIMSG_SETLEN(card->msgbuf, 30); } - if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + + skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC); + if (!skb) { printk(KERN_ERR "%s: incoming packet dropped\n", card->name); } else { @@ -539,7 +544,9 @@ irqreturn_t b1_interrupt(int interrupt, void *devptr) ApplId = (unsigned) b1_get_word(card->port); MsgLen = b1_get_slice(card->port, card->msgbuf); - if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + skb = alloc_skb(MsgLen, GFP_ATOMIC); + + if (!skb) { printk(KERN_ERR "%s: incoming packet dropped\n", card->name); spin_unlock_irqrestore(&card->lock, flags); @@ -663,11 +670,17 @@ int b1_proc_show(struct seq_file *m, void *v) seq_printf(m, "%-16s %s\n", "type", s); if (card->cardtype == avm_t1isa) seq_printf(m, "%-16s %d\n", "cardnr", card->cardnr); - if ((s = cinfo->version[VER_DRIVER]) != NULL) + + s = cinfo->version[VER_DRIVER]; + if (s) seq_printf(m, "%-16s %s\n", "ver_driver", s); - if ((s = cinfo->version[VER_CARDTYPE]) != NULL) + + s = cinfo->version[VER_CARDTYPE]; + if (s) seq_printf(m, "%-16s %s\n", "ver_cardtype", s); - if ((s = cinfo->version[VER_SERIAL]) != NULL) + + s = cinfo->version[VER_SERIAL]; + if (s) seq_printf(m, "%-16s %s\n", "ver_serial", s); if (card->cardtype != avm_m1) { @@ -784,13 +797,15 @@ static int __init b1_init(void) char *p; char rev[32]; - if ((p = strchr(revision, ':')) != NULL && p[1]) { + p = strchr(revision, ':'); + if (p && p[1]) { strlcpy(rev, p + 2, 32); - if ((p = strchr(rev, '$')) != NULL && p > rev) + p = strchr(rev, '$'); + if (p && p > rev) *(p - 1) = 0; - } else + } else { strcpy(rev, "1.0"); - + } printk(KERN_INFO "b1: revision %s\n", rev); return 0; diff --git a/drivers/staging/isdn/gigaset/interface.c b/drivers/staging/isdn/gigaset/interface.c index 17fa615a8c68..9ddadd07e707 100644 --- a/drivers/staging/isdn/gigaset/interface.c +++ b/drivers/staging/isdn/gigaset/interface.c @@ -518,7 +518,7 @@ void gigaset_if_init(struct cardstate *cs) if (!IS_ERR(cs->tty_dev)) dev_set_drvdata(cs->tty_dev, cs); else { - pr_warning("could not register device to the tty subsystem\n"); + pr_warn("could not register device to the tty subsystem\n"); cs->tty_dev = NULL; } mutex_unlock(&cs->mutex); diff --git a/drivers/staging/kpc2000/kpc2000_spi.c b/drivers/staging/kpc2000/kpc2000_spi.c index 3be33c450cab..6ba94b0131da 100644 --- a/drivers/staging/kpc2000/kpc2000_spi.c +++ b/drivers/staging/kpc2000/kpc2000_spi.c @@ -162,14 +162,12 @@ union kp_spi_ffctrl { kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx) { u64 __iomem *addr = cs->base; - u64 val; addr += idx; if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)) return cs->conf_cache; - val = readq(addr); - return val; + return readq(addr); } static inline void diff --git a/drivers/staging/mt7621-pci/pci-mt7621.c b/drivers/staging/mt7621-pci/pci-mt7621.c index 6b98827da57f..3633c924848e 100644 --- a/drivers/staging/mt7621-pci/pci-mt7621.c +++ b/drivers/staging/mt7621-pci/pci-mt7621.c @@ -29,15 +29,14 @@ #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/reset.h> +#include <linux/sys_soc.h> #include <mt7621.h> #include <ralink_regs.h> #include "../../pci/pci.h" /* sysctl */ -#define MT7621_CHIP_REV_ID 0x0c #define MT7621_GPIO_MODE 0x60 -#define CHIP_REV_MT7621_E2 0x0101 /* MediaTek specific configuration registers */ #define PCIE_FTS_NUM 0x70c @@ -126,6 +125,8 @@ struct mt7621_pcie_port { * @ports: pointer to PCIe port information * @perst: gpio reset * @rst: pointer to pcie reset + * @resets_inverted: depends on chip revision + * reset lines are inverted. */ struct mt7621_pcie { void __iomem *base; @@ -140,6 +141,7 @@ struct mt7621_pcie { struct list_head ports; struct gpio_desc *perst; struct reset_control *rst; + bool resets_inverted; }; static inline u32 pcie_read(struct mt7621_pcie *pcie, u32 reg) @@ -229,9 +231,9 @@ static inline void mt7621_pcie_port_clk_disable(struct mt7621_pcie_port *port) static inline void mt7621_control_assert(struct mt7621_pcie_port *port) { - u32 chip_rev_id = rt_sysc_r32(MT7621_CHIP_REV_ID); + struct mt7621_pcie *pcie = port->pcie; - if ((chip_rev_id & 0xFFFF) == CHIP_REV_MT7621_E2) + if (pcie->resets_inverted) reset_control_assert(port->pcie_rst); else reset_control_deassert(port->pcie_rst); @@ -239,9 +241,9 @@ static inline void mt7621_control_assert(struct mt7621_pcie_port *port) static inline void mt7621_control_deassert(struct mt7621_pcie_port *port) { - u32 chip_rev_id = rt_sysc_r32(MT7621_CHIP_REV_ID); + struct mt7621_pcie *pcie = port->pcie; - if ((chip_rev_id & 0xFFFF) == CHIP_REV_MT7621_E2) + if (pcie->resets_inverted) reset_control_deassert(port->pcie_rst); else reset_control_assert(port->pcie_rst); @@ -641,9 +643,14 @@ static int mt7621_pcie_register_host(struct pci_host_bridge *host, return pci_host_probe(host); } +static const struct soc_device_attribute mt7621_pci_quirks_match[] = { + { .soc_id = "mt7621", .revision = "E2" } +}; + static int mt7621_pci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct soc_device_attribute *attr; struct mt7621_pcie *pcie; struct pci_host_bridge *bridge; int err; @@ -661,6 +668,10 @@ static int mt7621_pci_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); INIT_LIST_HEAD(&pcie->ports); + attr = soc_device_match(mt7621_pci_quirks_match); + if (attr) + pcie->resets_inverted = true; + err = mt7621_pcie_parse_dt(pcie); if (err) { dev_err(dev, "Parsing DT failed\n"); diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index ffac0c4b3f5c..c798672d61b2 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -65,7 +65,7 @@ int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } void cvm_oct_note_carrier(struct octeon_ethernet *priv, - cvmx_helper_link_info_t li) + union cvmx_helper_link_info li) { if (li.s.link_up) { pr_notice_ratelimited("%s: %u Mbps %s duplex, port %d, queue %d\n", @@ -81,7 +81,7 @@ void cvm_oct_note_carrier(struct octeon_ethernet *priv, void cvm_oct_adjust_link(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - cvmx_helper_link_info_t link_info; + union cvmx_helper_link_info link_info; link_info.u64 = 0; link_info.s.link_up = dev->phydev->link ? 1 : 0; @@ -106,7 +106,7 @@ int cvm_oct_common_stop(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); int interface = INTERFACE(priv->port); - cvmx_helper_link_info_t link_info; + union cvmx_helper_link_info link_info; union cvmx_gmxx_prtx_cfg gmx_cfg; int index = INDEX(priv->port); diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c index d91fd5ce9e68..0c4fac31540a 100644 --- a/drivers/staging/octeon/ethernet-rgmii.c +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -53,7 +53,7 @@ static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable) static void cvm_oct_check_preamble_errors(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - cvmx_helper_link_info_t link_info; + union cvmx_helper_link_info link_info; unsigned long flags; link_info.u64 = priv->link_info; @@ -103,7 +103,7 @@ static void cvm_oct_check_preamble_errors(struct net_device *dev) static void cvm_oct_rgmii_poll(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - cvmx_helper_link_info_t link_info; + union cvmx_helper_link_info link_info; bool status_change; link_info = cvmx_helper_link_get(priv->port); diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 0e65955c746b..2c16230f993c 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -60,7 +60,7 @@ static irqreturn_t cvm_oct_do_interrupt(int irq, void *napi_id) * * Returns Non-zero if the packet can be dropped, zero otherwise. */ -static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work) +static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) { int port; @@ -135,7 +135,7 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work) return 0; } -static void copy_segments_to_skb(cvmx_wqe_t *work, struct sk_buff *skb) +static void copy_segments_to_skb(struct cvmx_wqe *work, struct sk_buff *skb) { int segments = work->word2.s.bufs; union cvmx_buf_ptr segment_ptr = work->packet_ptr; @@ -215,7 +215,7 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget) struct sk_buff *skb = NULL; struct sk_buff **pskb = NULL; int skb_in_hw; - cvmx_wqe_t *work; + struct cvmx_wqe *work; int port; if (USE_ASYNC_IOBDMA && did_work_request) diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index c64728fc21f2..5114273826ec 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -127,7 +127,7 @@ static void cvm_oct_free_tx_skbs(struct net_device *dev) */ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) { - cvmx_pko_command_word0_t pko_command; + union cvmx_pko_command_word0 pko_command; union cvmx_buf_ptr hw_buffer; u64 old_scratch; u64 old_scratch2; @@ -261,11 +261,11 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) /* Build the PKO buffer pointer */ hw_buffer.u64 = 0; if (skb_shinfo(skb)->nr_frags == 0) { - hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data); + hw_buffer.s.addr = XKPHYS_TO_PHYS((uintptr_t)skb->data); hw_buffer.s.pool = 0; hw_buffer.s.size = skb->len; } else { - hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data); + hw_buffer.s.addr = XKPHYS_TO_PHYS((uintptr_t)skb->data); hw_buffer.s.pool = 0; hw_buffer.s.size = skb_headlen(skb); CVM_OCT_SKB_CB(skb)[0] = hw_buffer.u64; @@ -273,11 +273,12 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) skb_frag_t *fs = skb_shinfo(skb)->frags + i; hw_buffer.s.addr = - XKPHYS_TO_PHYS((u64)skb_frag_address(fs)); + XKPHYS_TO_PHYS((uintptr_t)skb_frag_address(fs)); hw_buffer.s.size = skb_frag_size(fs); CVM_OCT_SKB_CB(skb)[i + 1] = hw_buffer.u64; } - hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)CVM_OCT_SKB_CB(skb)); + hw_buffer.s.addr = + XKPHYS_TO_PHYS((uintptr_t)CVM_OCT_SKB_CB(skb)); hw_buffer.s.size = skb_shinfo(skb)->nr_frags + 1; pko_command.s.segs = skb_shinfo(skb)->nr_frags + 1; pko_command.s.gather = 1; @@ -349,10 +350,8 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) */ dst_release(skb_dst(skb)); skb_dst_set(skb, NULL); -#ifdef CONFIG_XFRM - secpath_reset(skb); -#endif - nf_reset(skb); + skb_ext_reset(skb); + nf_reset_ct(skb); #ifdef CONFIG_NET_SCHED skb->tc_index = 0; @@ -515,7 +514,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) void *copy_location; /* Get a work queue entry */ - cvmx_wqe_t *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL); + struct cvmx_wqe *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL); if (unlikely(!work)) { printk_ratelimited("%s: Failed to allocate a work queue entry\n", diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index cf8e9a23ebf9..f42c3816ce49 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -172,7 +172,7 @@ static void cvm_oct_configure_common_hw(void) */ int cvm_oct_free_work(void *work_queue_entry) { - cvmx_wqe_t *work = work_queue_entry; + struct cvmx_wqe *work = work_queue_entry; int segments = work->word2.s.bufs; union cvmx_buf_ptr segment_ptr = work->packet_ptr; @@ -460,7 +460,7 @@ int cvm_oct_common_open(struct net_device *dev, struct octeon_ethernet *priv = netdev_priv(dev); int interface = INTERFACE(priv->port); int index = INDEX(priv->port); - cvmx_helper_link_info_t link_info; + union cvmx_helper_link_info link_info; int rv; rv = cvm_oct_phy_setup_device(dev); @@ -496,7 +496,7 @@ int cvm_oct_common_open(struct net_device *dev, void cvm_oct_link_poll(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - cvmx_helper_link_info_t link_info; + union cvmx_helper_link_info link_info; link_info = cvmx_helper_link_get(priv->port); if (link_info.u64 == priv->link_info) diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index a8a864b40913..a6140705706f 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -14,7 +14,7 @@ #include <linux/of.h> #include <linux/phy.h> -#ifdef CONFIG_MIPS +#ifdef CONFIG_CAVIUM_OCTEON_SOC #include <asm/octeon/octeon.h> @@ -91,7 +91,7 @@ int cvm_oct_common_stop(struct net_device *dev); int cvm_oct_common_open(struct net_device *dev, void (*link_poll)(struct net_device *)); void cvm_oct_note_carrier(struct octeon_ethernet *priv, - cvmx_helper_link_info_t li); + union cvmx_helper_link_info li); void cvm_oct_link_poll(struct net_device *dev); extern int always_use_pow; diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index a4ac3bfb62a8..b07f5e24acbc 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -1,5 +1,8 @@ #define CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE 512 -#define XKPHYS_TO_PHYS(p) (p) + +#ifndef XKPHYS_TO_PHYS +# define XKPHYS_TO_PHYS(p) (p) +#endif #define OCTEON_IRQ_WORKQ0 0 #define OCTEON_IRQ_RML 0 @@ -38,7 +41,7 @@ #define CVMX_NPI_RSL_INT_BLOCKS 0 #define CVMX_POW_WQ_INT_PC 0 -typedef union { +union cvmx_pip_wqe_word2 { uint64_t u64; struct { uint64_t bufs:8; @@ -114,13 +117,13 @@ typedef union { uint64_t err_code:8; } snoip; -} cvmx_pip_wqe_word2; +}; union cvmx_pip_wqe_word0 { struct { uint64_t next_ptr:40; uint8_t unused; - uint16_t hw_chksum; + __wsum hw_chksum; } cn38xx; struct { uint64_t pknd:6; /* 0..5 */ @@ -180,15 +183,15 @@ union cvmx_buf_ptr { } s; }; -typedef struct { +struct cvmx_wqe { union cvmx_wqe_word0 word0; union cvmx_wqe_word1 word1; - cvmx_pip_wqe_word2 word2; + union cvmx_pip_wqe_word2 word2; union cvmx_buf_ptr packet_ptr; uint8_t packet_data[96]; -} cvmx_wqe_t; +}; -typedef union { +union cvmx_helper_link_info { uint64_t u64; struct { uint64_t reserved_20_63:44; @@ -196,18 +199,18 @@ typedef union { uint64_t full_duplex:1; /**< 1 if the link is full duplex */ uint64_t speed:18; /**< Speed of the link in Mbps */ } s; -} cvmx_helper_link_info_t; +}; -typedef enum { +enum cvmx_fau_reg_32 { CVMX_FAU_REG_32_START = 0, -} cvmx_fau_reg_32_t; +}; -typedef enum { +enum cvmx_fau_op_size { CVMX_FAU_OP_SIZE_8 = 0, CVMX_FAU_OP_SIZE_16 = 1, CVMX_FAU_OP_SIZE_32 = 2, CVMX_FAU_OP_SIZE_64 = 3 -} cvmx_fau_op_size_t; +}; typedef enum { CVMX_SPI_MODE_UNKNOWN = 0, @@ -1134,7 +1137,7 @@ union cvmx_npi_rsl_int_blocks { } cn50xx; }; -typedef union { +union cvmx_pko_command_word0 { uint64_t u64; struct { uint64_t total_bytes:16; @@ -1154,7 +1157,7 @@ typedef union { uint64_t size0:2; uint64_t size1:2; } s; -} cvmx_pko_command_word0_t; +}; union cvmx_ciu_timx { uint64_t u64; @@ -1175,16 +1178,18 @@ union cvmx_gmxx_rxx_rx_inbnd { } s; }; -static inline int32_t cvmx_fau_fetch_and_add32(cvmx_fau_reg_32_t reg, +static inline int32_t cvmx_fau_fetch_and_add32(enum cvmx_fau_reg_32 reg, int32_t value) { return value; } -static inline void cvmx_fau_atomic_add32(cvmx_fau_reg_32_t reg, int32_t value) +static inline void cvmx_fau_atomic_add32(enum cvmx_fau_reg_32 reg, + int32_t value) { } -static inline void cvmx_fau_atomic_write32(cvmx_fau_reg_32_t reg, int32_t value) +static inline void cvmx_fau_atomic_write32(enum cvmx_fau_reg_32 reg, + int32_t value) { } static inline uint64_t cvmx_scratch_read64(uint64_t address) @@ -1195,14 +1200,14 @@ static inline uint64_t cvmx_scratch_read64(uint64_t address) static inline void cvmx_scratch_write64(uint64_t address, uint64_t value) { } -static inline int cvmx_wqe_get_grp(cvmx_wqe_t *work) +static inline int cvmx_wqe_get_grp(struct cvmx_wqe *work) { return 0; } static inline void *cvmx_phys_to_ptr(uint64_t physical_address) { - return (void *)(physical_address); + return (void *)(uintptr_t)(physical_address); } static inline uint64_t cvmx_ptr_to_phys(void *ptr) @@ -1264,15 +1269,15 @@ static inline cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int return 0; } -static inline cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port) +static inline union cvmx_helper_link_info cvmx_helper_link_get(int ipd_port) { - cvmx_helper_link_info_t ret = { .u64 = 0 }; + union cvmx_helper_link_info ret = { .u64 = 0 }; return ret; } static inline int cvmx_helper_link_set(int ipd_port, - cvmx_helper_link_info_t link_info) + union cvmx_helper_link_info link_info) { return 0; } @@ -1342,14 +1347,14 @@ static inline void cvmx_pow_work_request_async(int scr_addr, cvmx_pow_wait_t wait) { } -static inline cvmx_wqe_t *cvmx_pow_work_response_async(int scr_addr) +static inline struct cvmx_wqe *cvmx_pow_work_response_async(int scr_addr) { - cvmx_wqe_t *wqe = (void *)(unsigned long)scr_addr; + struct cvmx_wqe *wqe = (void *)(unsigned long)scr_addr; return wqe; } -static inline cvmx_wqe_t *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) +static inline struct cvmx_wqe *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) { return (void *)(unsigned long)wait; } @@ -1361,7 +1366,7 @@ static inline int cvmx_spi_restart_interface(int interface, } static inline void cvmx_fau_async_fetch_and_add32(uint64_t scraddr, - cvmx_fau_reg_32_t reg, + enum cvmx_fau_reg_32 reg, int32_t value) { } @@ -1379,7 +1384,7 @@ static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, { } static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(uint64_t port, - uint64_t queue, cvmx_pko_command_word0_t pko_command, + uint64_t queue, union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, cvmx_pko_lock_t use_locking) { cvmx_pko_status_t ret = 0; @@ -1387,21 +1392,21 @@ static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(uint64_t port, return ret; } -static inline void cvmx_wqe_set_port(cvmx_wqe_t *work, int port) +static inline void cvmx_wqe_set_port(struct cvmx_wqe *work, int port) { } -static inline void cvmx_wqe_set_qos(cvmx_wqe_t *work, int qos) +static inline void cvmx_wqe_set_qos(struct cvmx_wqe *work, int qos) { } -static inline int cvmx_wqe_get_qos(cvmx_wqe_t *work) +static inline int cvmx_wqe_get_qos(struct cvmx_wqe *work) { return 0; } -static inline void cvmx_wqe_set_grp(cvmx_wqe_t *work, int grp) +static inline void cvmx_wqe_set_grp(struct cvmx_wqe *work, int grp) { } -static inline void cvmx_pow_work_submit(cvmx_wqe_t *wqp, uint32_t tag, +static inline void cvmx_pow_work_submit(struct cvmx_wqe *wqp, uint32_t tag, enum cvmx_pow_tag_type tag_type, uint64_t qos, uint64_t grp) { } diff --git a/drivers/staging/olpc_dcon/Kconfig b/drivers/staging/olpc_dcon/Kconfig index f5c716bb3413..d1a0dea09ef0 100644 --- a/drivers/staging/olpc_dcon/Kconfig +++ b/drivers/staging/olpc_dcon/Kconfig @@ -3,7 +3,7 @@ config FB_OLPC_DCON tristate "One Laptop Per Child Display CONtroller support" depends on OLPC && FB depends on I2C - depends on (GPIO_CS5535 || GPIO_CS5535=n) + depends on GPIO_CS5535 && ACPI select BACKLIGHT_CLASS_DEVICE help In order to support very low power operation, the XO laptop uses a @@ -15,22 +15,3 @@ config FB_OLPC_DCON This controller is only available on OLPC platforms. Unless you have one of these platforms, you will want to say 'N'. -config FB_OLPC_DCON_1 - bool "OLPC XO-1 DCON support" - depends on FB_OLPC_DCON && GPIO_CS5535 - default y - help - Enable support for the DCON in XO-1 model laptops. The kernel - communicates with the DCON using model-specific code. If you - have an XO-1 (or if you're unsure what model you have), you should - say 'Y'. - -config FB_OLPC_DCON_1_5 - bool "OLPC XO-1.5 DCON support" - depends on FB_OLPC_DCON && ACPI - default y - help - Enable support for the DCON in XO-1.5 model laptops. The kernel - communicates with the DCON using model-specific code. If you - have an XO-1.5 (or if you're unsure what model you have), you - should say 'Y'. diff --git a/drivers/staging/olpc_dcon/Makefile b/drivers/staging/olpc_dcon/Makefile index cb1248c5c162..734b2ce26066 100644 --- a/drivers/staging/olpc_dcon/Makefile +++ b/drivers/staging/olpc_dcon/Makefile @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -olpc-dcon-objs += olpc_dcon.o -olpc-dcon-$(CONFIG_FB_OLPC_DCON_1) += olpc_dcon_xo_1.o -olpc-dcon-$(CONFIG_FB_OLPC_DCON_1_5) += olpc_dcon_xo_1_5.o +olpc-dcon-objs += olpc_dcon.o olpc_dcon_xo_1.o olpc_dcon_xo_1_5.o obj-$(CONFIG_FB_OLPC_DCON) += olpc-dcon.o diff --git a/drivers/staging/olpc_dcon/TODO b/drivers/staging/olpc_dcon/TODO index d8296f2ae872..7c263358b44a 100644 --- a/drivers/staging/olpc_dcon/TODO +++ b/drivers/staging/olpc_dcon/TODO @@ -8,7 +8,6 @@ TODO: internals, but isn't properly integrated, is not the correct solution. - see if vx855 gpio API can be made similar enough to cs5535 so we can share more code - - allow simultaneous XO-1 and XO-1.5 support Please send patches to Greg Kroah-Hartman <greg@kroah.com> and copy: diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c index a254238be181..a0d6d90f4cc8 100644 --- a/drivers/staging/olpc_dcon/olpc_dcon.c +++ b/drivers/staging/olpc_dcon/olpc_dcon.c @@ -790,15 +790,11 @@ static struct i2c_driver dcon_driver = { static int __init olpc_dcon_init(void) { -#ifdef CONFIG_FB_OLPC_DCON_1_5 /* XO-1.5 */ if (olpc_board_at_least(olpc_board(0xd0))) pdata = &dcon_pdata_xo_1_5; -#endif -#ifdef CONFIG_FB_OLPC_DCON_1 - if (!pdata) + else pdata = &dcon_pdata_xo_1; -#endif return i2c_add_driver(&dcon_driver); } diff --git a/drivers/staging/olpc_dcon/olpc_dcon.h b/drivers/staging/olpc_dcon/olpc_dcon.h index 22d976a09785..41bd1360b56e 100644 --- a/drivers/staging/olpc_dcon/olpc_dcon.h +++ b/drivers/staging/olpc_dcon/olpc_dcon.h @@ -106,12 +106,7 @@ struct dcon_gpio { irqreturn_t dcon_interrupt(int irq, void *id); -#ifdef CONFIG_FB_OLPC_DCON_1 extern struct dcon_platform_data dcon_pdata_xo_1; -#endif - -#ifdef CONFIG_FB_OLPC_DCON_1_5 extern struct dcon_platform_data dcon_pdata_xo_1_5; -#endif #endif diff --git a/drivers/staging/qlge/TODO b/drivers/staging/qlge/TODO index 51c509084e80..f93f7428f5d5 100644 --- a/drivers/staging/qlge/TODO +++ b/drivers/staging/qlge/TODO @@ -1,6 +1,3 @@ -* reception stalls permanently (until admin intervention) if the rx buffer - queues become empty because of allocation failures (ex. under memory - pressure) * commit 7c734359d350 ("qlge: Size RX buffers based on MTU.", v2.6.33-rc1) introduced dead code in the receive routines, which should be rewritten anyways by the admission of the author himself, see the comment above diff --git a/drivers/staging/qlge/qlge.h b/drivers/staging/qlge/qlge.h index ad7c5eb8a3b6..6ec7e3ce3863 100644 --- a/drivers/staging/qlge/qlge.h +++ b/drivers/staging/qlge/qlge.h @@ -34,8 +34,13 @@ #define NUM_TX_RING_ENTRIES 256 #define NUM_RX_RING_ENTRIES 256 -#define NUM_SMALL_BUFFERS 512 -#define NUM_LARGE_BUFFERS 512 +/* Use the same len for sbq and lbq. Note that it seems like the device might + * support different sizes. + */ +#define QLGE_BQ_SHIFT 9 +#define QLGE_BQ_LEN BIT(QLGE_BQ_SHIFT) +#define QLGE_BQ_SIZE (QLGE_BQ_LEN * sizeof(__le64)) + #define DB_PAGE_SIZE 4096 /* Calculate the number of (4k) pages required to @@ -46,8 +51,8 @@ (((x * sizeof(u64)) % DB_PAGE_SIZE) ? 1 : 0)) #define RX_RING_SHADOW_SPACE (sizeof(u64) + \ - MAX_DB_PAGES_PER_BQ(NUM_SMALL_BUFFERS) * sizeof(u64) + \ - MAX_DB_PAGES_PER_BQ(NUM_LARGE_BUFFERS) * sizeof(u64)) + MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN) * sizeof(u64) + \ + MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN) * sizeof(u64)) #define LARGE_BUFFER_MAX_SIZE 8192 #define LARGE_BUFFER_MIN_SIZE 2048 @@ -77,6 +82,11 @@ #define LSD(x) ((u32)((u64)(x))) #define MSD(x) ((u32)((((u64)(x)) >> 32))) +/* In some cases, the device interprets a value of 0x0000 as 65536. These + * cases are marked using the following macro. + */ +#define QLGE_FIT16(value) ((u16)(value)) + /* MPI test register definitions. This register * is used for determining alternate NIC function's * PCI->func number. @@ -1358,25 +1368,6 @@ struct tx_ring_desc { struct tx_ring_desc *next; }; -struct page_chunk { - struct page *page; /* master page */ - char *va; /* virt addr for this chunk */ - u64 map; /* mapping for master */ - unsigned int offset; /* offset for this chunk */ - unsigned int last_flag; /* flag set for last chunk in page */ -}; - -struct bq_desc { - union { - struct page_chunk pg_chunk; - struct sk_buff *skb; - } p; - __le64 *addr; - u32 index; - DEFINE_DMA_UNMAP_ADDR(mapaddr); - DEFINE_DMA_UNMAP_LEN(maplen); -}; - #define QL_TXQ_IDX(qdev, skb) (smp_processor_id()%(qdev->tx_ring_count)) struct tx_ring { @@ -1406,15 +1397,68 @@ struct tx_ring { u64 tx_errors; }; -/* - * Type of inbound queue. - */ -enum { - DEFAULT_Q = 2, /* Handles slow queue and chip/MPI events. */ - TX_Q = 3, /* Handles outbound completions. */ - RX_Q = 4, /* Handles inbound completions. */ +struct qlge_page_chunk { + struct page *page; + void *va; /* virt addr including offset */ + unsigned int offset; }; +struct qlge_bq_desc { + union { + /* for large buffers */ + struct qlge_page_chunk pg_chunk; + /* for small buffers */ + struct sk_buff *skb; + } p; + dma_addr_t dma_addr; + /* address in ring where the buffer address is written for the device */ + __le64 *buf_ptr; + u32 index; +}; + +/* buffer queue */ +struct qlge_bq { + __le64 *base; + dma_addr_t base_dma; + __le64 *base_indirect; + dma_addr_t base_indirect_dma; + struct qlge_bq_desc *queue; + /* prod_idx is the index of the first buffer that may NOT be used by + * hw, ie. one after the last. Advanced by sw. + */ + void __iomem *prod_idx_db_reg; + /* next index where sw should refill a buffer for hw */ + u16 next_to_use; + /* next index where sw expects to find a buffer filled by hw */ + u16 next_to_clean; + enum { + QLGE_SB, /* small buffer */ + QLGE_LB, /* large buffer */ + } type; +}; + +#define QLGE_BQ_CONTAINER(bq) \ +({ \ + typeof(bq) _bq = bq; \ + (struct rx_ring *)((char *)_bq - (_bq->type == QLGE_SB ? \ + offsetof(struct rx_ring, sbq) : \ + offsetof(struct rx_ring, lbq))); \ +}) + +/* Experience shows that the device ignores the low 4 bits of the tail index. + * Refill up to a x16 multiple. + */ +#define QLGE_BQ_ALIGN(index) ALIGN_DOWN(index, 16) + +#define QLGE_BQ_WRAP(index) ((index) & (QLGE_BQ_LEN - 1)) + +#define QLGE_BQ_HW_OWNED(bq) \ +({ \ + typeof(bq) _bq = bq; \ + QLGE_BQ_WRAP(QLGE_BQ_ALIGN((_bq)->next_to_use) - \ + (_bq)->next_to_clean); \ +}) + struct rx_ring { struct cqicb cqicb; /* The chip's completion queue init control block. */ @@ -1432,40 +1476,17 @@ struct rx_ring { void __iomem *valid_db_reg; /* PCI doorbell mem area + 0x04 */ /* Large buffer queue elements. */ - u32 lbq_len; /* entry count */ - u32 lbq_size; /* size in bytes of queue */ - u32 lbq_buf_size; - void *lbq_base; - dma_addr_t lbq_base_dma; - void *lbq_base_indirect; - dma_addr_t lbq_base_indirect_dma; - struct page_chunk pg_chunk; /* current page for chunks */ - struct bq_desc *lbq; /* array of control blocks */ - void __iomem *lbq_prod_idx_db_reg; /* PCI doorbell mem area + 0x18 */ - u32 lbq_prod_idx; /* current sw prod idx */ - u32 lbq_curr_idx; /* next entry we expect */ - u32 lbq_clean_idx; /* beginning of new descs */ - u32 lbq_free_cnt; /* free buffer desc cnt */ + struct qlge_bq lbq; + struct qlge_page_chunk master_chunk; + dma_addr_t chunk_dma_addr; /* Small buffer queue elements. */ - u32 sbq_len; /* entry count */ - u32 sbq_size; /* size in bytes of queue */ - u32 sbq_buf_size; - void *sbq_base; - dma_addr_t sbq_base_dma; - void *sbq_base_indirect; - dma_addr_t sbq_base_indirect_dma; - struct bq_desc *sbq; /* array of control blocks */ - void __iomem *sbq_prod_idx_db_reg; /* PCI doorbell mem area + 0x1c */ - u32 sbq_prod_idx; /* current sw prod idx */ - u32 sbq_curr_idx; /* next entry we expect */ - u32 sbq_clean_idx; /* beginning of new descs */ - u32 sbq_free_cnt; /* free buffer desc cnt */ + struct qlge_bq sbq; /* Misc. handler elements. */ - u32 type; /* Type of queue, tx, rx. */ u32 irq; /* Which vector this ring is assigned. */ u32 cpu; /* Which CPU this should run on. */ + struct delayed_work refill_work; char name[IFNAMSIZ + 5]; struct napi_struct napi; u8 reserved; @@ -1982,11 +2003,6 @@ struct intr_context { u32 intr_dis_mask; /* value/mask used to disable this intr */ u32 intr_read_mask; /* value/mask used to read this intr */ char name[IFNAMSIZ * 2]; - atomic_t irq_cnt; /* irq_cnt is used in single vector - * environment. It's incremented for each - * irq handler that is scheduled. When each - * handler finishes it decrements irq_cnt and - * enables interrupts if it's zero. */ irq_handler_t handler; }; @@ -2074,7 +2090,6 @@ struct ql_adapter { u32 port; /* Port number this adapter */ spinlock_t adapter_lock; - spinlock_t hw_lock; spinlock_t stats_lock; /* PCI Bus Relative Register Addresses */ @@ -2115,6 +2130,7 @@ struct ql_adapter { struct rx_ring rx_ring[MAX_RX_RINGS]; struct tx_ring tx_ring[MAX_TX_RINGS]; unsigned int lbq_buf_order; + u32 lbq_buf_size; int rx_csum; u32 default_rx_queue; @@ -2235,7 +2251,6 @@ void ql_mpi_reset_work(struct work_struct *work); void ql_mpi_core_to_log(struct work_struct *work); int ql_wait_reg_rdy(struct ql_adapter *qdev, u32 reg, u32 bit, u32 ebit); void ql_queue_asic_error(struct ql_adapter *qdev); -u32 ql_enable_completion_interrupt(struct ql_adapter *qdev, u32 intr); void ql_set_ethtool_ops(struct net_device *ndev); int ql_read_xgmac_reg64(struct ql_adapter *qdev, u32 reg, u64 *data); void ql_mpi_idc_work(struct work_struct *work); diff --git a/drivers/staging/qlge/qlge_dbg.c b/drivers/staging/qlge/qlge_dbg.c index 31389ab8bdf7..019b7e6a1b7a 100644 --- a/drivers/staging/qlge/qlge_dbg.c +++ b/drivers/staging/qlge/qlge_dbg.c @@ -7,7 +7,7 @@ /* Read a NIC register from the alternate function. */ static u32 ql_read_other_func_reg(struct ql_adapter *qdev, - u32 reg) + u32 reg) { u32 register_to_read; u32 reg_val; @@ -26,7 +26,7 @@ static u32 ql_read_other_func_reg(struct ql_adapter *qdev, /* Write a NIC register from the alternate function. */ static int ql_write_other_func_reg(struct ql_adapter *qdev, - u32 reg, u32 reg_val) + u32 reg, u32 reg_val) { u32 register_to_read; int status = 0; @@ -41,7 +41,7 @@ static int ql_write_other_func_reg(struct ql_adapter *qdev, } static int ql_wait_other_func_reg_rdy(struct ql_adapter *qdev, u32 reg, - u32 bit, u32 err_bit) + u32 bit, u32 err_bit) { u32 temp; int count = 10; @@ -61,22 +61,22 @@ static int ql_wait_other_func_reg_rdy(struct ql_adapter *qdev, u32 reg, } static int ql_read_other_func_serdes_reg(struct ql_adapter *qdev, u32 reg, - u32 *data) + u32 *data) { int status; /* wait for reg to come ready */ status = ql_wait_other_func_reg_rdy(qdev, XG_SERDES_ADDR / 4, - XG_SERDES_ADDR_RDY, 0); + XG_SERDES_ADDR_RDY, 0); if (status) goto exit; /* set up for reg read */ - ql_write_other_func_reg(qdev, XG_SERDES_ADDR/4, reg | PROC_ADDR_R); + ql_write_other_func_reg(qdev, XG_SERDES_ADDR / 4, reg | PROC_ADDR_R); /* wait for reg to come ready */ status = ql_wait_other_func_reg_rdy(qdev, XG_SERDES_ADDR / 4, - XG_SERDES_ADDR_RDY, 0); + XG_SERDES_ADDR_RDY, 0); if (status) goto exit; @@ -111,8 +111,8 @@ exit: } static void ql_get_both_serdes(struct ql_adapter *qdev, u32 addr, - u32 *direct_ptr, u32 *indirect_ptr, - unsigned int direct_valid, unsigned int indirect_valid) + u32 *direct_ptr, u32 *indirect_ptr, + bool direct_valid, bool indirect_valid) { unsigned int status; @@ -133,16 +133,15 @@ static void ql_get_both_serdes(struct ql_adapter *qdev, u32 addr, } static int ql_get_serdes_regs(struct ql_adapter *qdev, - struct ql_mpi_coredump *mpi_coredump) + struct ql_mpi_coredump *mpi_coredump) { int status; - unsigned int xfi_direct_valid, xfi_indirect_valid, xaui_direct_valid; - unsigned int xaui_indirect_valid, i; + bool xfi_direct_valid = false, xfi_indirect_valid = false; + bool xaui_direct_valid = true, xaui_indirect_valid = true; + unsigned int i; u32 *direct_ptr, temp; u32 *indirect_ptr; - xfi_direct_valid = xfi_indirect_valid = 0; - xaui_direct_valid = xaui_indirect_valid = 1; /* The XAUI needs to be read out per port */ status = ql_read_other_func_serdes_reg(qdev, @@ -152,7 +151,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_indirect_valid = 0; + xaui_indirect_valid = false; status = ql_read_serdes_reg(qdev, XG_SERDES_XAUI_HSS_PCS_START, &temp); @@ -161,7 +160,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_direct_valid = 0; + xaui_direct_valid = false; /* * XFI register is shared so only need to read one @@ -176,18 +175,18 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, /* now see if i'm NIC 1 or NIC 2 */ if (qdev->func & 1) /* I'm NIC 2, so the indirect (NIC1) xfi is up. */ - xfi_indirect_valid = 1; + xfi_indirect_valid = true; else - xfi_direct_valid = 1; + xfi_direct_valid = true; } if ((temp & XG_SERDES_ADDR_XFI2_PWR_UP) == XG_SERDES_ADDR_XFI2_PWR_UP) { /* now see if i'm NIC 1 or NIC 2 */ if (qdev->func & 1) /* I'm NIC 2, so the indirect (NIC1) xfi is up. */ - xfi_direct_valid = 1; + xfi_direct_valid = true; else - xfi_indirect_valid = 1; + xfi_indirect_valid = true; } /* Get XAUI_AN register block. */ @@ -203,7 +202,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, for (i = 0; i <= 0x000000034; i += 4, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xaui_direct_valid, xaui_indirect_valid); + xaui_direct_valid, xaui_indirect_valid); /* Get XAUI_HSS_PCS register block. */ if (qdev->func & 1) { @@ -220,7 +219,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, for (i = 0x800; i <= 0x880; i += 4, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xaui_direct_valid, xaui_indirect_valid); + xaui_direct_valid, xaui_indirect_valid); /* Get XAUI_XFI_AN register block. */ if (qdev->func & 1) { @@ -233,7 +232,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, for (i = 0x1000; i <= 0x1034; i += 4, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xfi_direct_valid, xfi_indirect_valid); + xfi_direct_valid, xfi_indirect_valid); /* Get XAUI_XFI_TRAIN register block. */ if (qdev->func & 1) { @@ -248,7 +247,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, for (i = 0x1050; i <= 0x107c; i += 4, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xfi_direct_valid, xfi_indirect_valid); + xfi_direct_valid, xfi_indirect_valid); /* Get XAUI_XFI_HSS_PCS register block. */ if (qdev->func & 1) { @@ -265,7 +264,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, for (i = 0x1800; i <= 0x1838; i += 4, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xfi_direct_valid, xfi_indirect_valid); + xfi_direct_valid, xfi_indirect_valid); /* Get XAUI_XFI_HSS_TX register block. */ if (qdev->func & 1) { @@ -280,7 +279,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, } for (i = 0x1c00; i <= 0x1c1f; i++, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xfi_direct_valid, xfi_indirect_valid); + xfi_direct_valid, xfi_indirect_valid); /* Get XAUI_XFI_HSS_RX register block. */ if (qdev->func & 1) { @@ -296,7 +295,7 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, for (i = 0x1c40; i <= 0x1c5f; i++, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xfi_direct_valid, xfi_indirect_valid); + xfi_direct_valid, xfi_indirect_valid); /* Get XAUI_XFI_HSS_PLL register block. */ @@ -313,18 +312,18 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, } for (i = 0x1e00; i <= 0x1e1f; i++, direct_ptr++, indirect_ptr++) ql_get_both_serdes(qdev, i, direct_ptr, indirect_ptr, - xfi_direct_valid, xfi_indirect_valid); + xfi_direct_valid, xfi_indirect_valid); return 0; } static int ql_read_other_func_xgmac_reg(struct ql_adapter *qdev, u32 reg, - u32 *data) + u32 *data) { int status = 0; /* wait for reg to come ready */ status = ql_wait_other_func_reg_rdy(qdev, XGMAC_ADDR / 4, - XGMAC_ADDR_RDY, XGMAC_ADDR_XME); + XGMAC_ADDR_RDY, XGMAC_ADDR_XME); if (status) goto exit; @@ -333,7 +332,7 @@ static int ql_read_other_func_xgmac_reg(struct ql_adapter *qdev, u32 reg, /* wait for reg to come ready */ status = ql_wait_other_func_reg_rdy(qdev, XGMAC_ADDR / 4, - XGMAC_ADDR_RDY, XGMAC_ADDR_XME); + XGMAC_ADDR_RDY, XGMAC_ADDR_XME); if (status) goto exit; @@ -347,17 +346,17 @@ exit: * skipping unused locations. */ static int ql_get_xgmac_regs(struct ql_adapter *qdev, u32 *buf, - unsigned int other_function) + unsigned int other_function) { int status = 0; int i; for (i = PAUSE_SRC_LO; i < XGMAC_REGISTER_END; i += 4, buf++) { /* We're reading 400 xgmac registers, but we filter out - * serveral locations that are non-responsive to reads. + * several locations that are non-responsive to reads. */ if ((i == 0x00000114) || - (i == 0x00000118) || + (i == 0x00000118) || (i == 0x0000013c) || (i == 0x00000140) || (i > 0x00000150 && i < 0x000001fc) || @@ -389,7 +388,6 @@ static int ql_get_xgmac_regs(struct ql_adapter *qdev, u32 *buf, static int ql_get_ets_regs(struct ql_adapter *qdev, u32 *buf) { - int status = 0; int i; for (i = 0; i < 8; i++, buf++) { @@ -402,7 +400,7 @@ static int ql_get_ets_regs(struct ql_adapter *qdev, u32 *buf) *buf = ql_read32(qdev, CNA_ETS); } - return status; + return 0; } static void ql_get_intr_states(struct ql_adapter *qdev, u32 *buf) @@ -411,7 +409,7 @@ static void ql_get_intr_states(struct ql_adapter *qdev, u32 *buf) for (i = 0; i < qdev->rx_ring_count; i++, buf++) { ql_write32(qdev, INTR_EN, - qdev->intr_context[i].intr_read_mask); + qdev->intr_context[i].intr_read_mask); *buf = ql_read32(qdev, INTR_EN); } } @@ -427,7 +425,7 @@ static int ql_get_cam_entries(struct ql_adapter *qdev, u32 *buf) for (i = 0; i < 16; i++) { status = ql_get_mac_addr_reg(qdev, - MAC_ADDR_TYPE_CAM_MAC, i, value); + MAC_ADDR_TYPE_CAM_MAC, i, value); if (status) { netif_err(qdev, drv, qdev->ndev, "Failed read of mac index register\n"); @@ -439,7 +437,7 @@ static int ql_get_cam_entries(struct ql_adapter *qdev, u32 *buf) } for (i = 0; i < 32; i++) { status = ql_get_mac_addr_reg(qdev, - MAC_ADDR_TYPE_MULTI_MAC, i, value); + MAC_ADDR_TYPE_MULTI_MAC, i, value); if (status) { netif_err(qdev, drv, qdev->ndev, "Failed read of mac index register\n"); @@ -498,7 +496,7 @@ end: /* Read the MPI Processor core registers */ static int ql_get_mpi_regs(struct ql_adapter *qdev, u32 *buf, - u32 offset, u32 count) + u32 offset, u32 count) { int i, status = 0; for (i = 0; i < count; i++, buf++) { @@ -511,7 +509,7 @@ static int ql_get_mpi_regs(struct ql_adapter *qdev, u32 *buf, /* Read the ASIC probe dump */ static unsigned int *ql_get_probe(struct ql_adapter *qdev, u32 clock, - u32 valid, u32 *buf) + u32 valid, u32 *buf) { u32 module, mux_sel, probe, lo_val, hi_val; @@ -546,13 +544,13 @@ static int ql_get_probe_dump(struct ql_adapter *qdev, unsigned int *buf) /* First we have to enable the probe mux */ ql_write_mpi_reg(qdev, MPI_TEST_FUNC_PRB_CTL, MPI_TEST_FUNC_PRB_EN); buf = ql_get_probe(qdev, PRB_MX_ADDR_SYS_CLOCK, - PRB_MX_ADDR_VALID_SYS_MOD, buf); + PRB_MX_ADDR_VALID_SYS_MOD, buf); buf = ql_get_probe(qdev, PRB_MX_ADDR_PCI_CLOCK, - PRB_MX_ADDR_VALID_PCI_MOD, buf); + PRB_MX_ADDR_VALID_PCI_MOD, buf); buf = ql_get_probe(qdev, PRB_MX_ADDR_XGM_CLOCK, - PRB_MX_ADDR_VALID_XGM_MOD, buf); + PRB_MX_ADDR_VALID_XGM_MOD, buf); buf = ql_get_probe(qdev, PRB_MX_ADDR_FC_CLOCK, - PRB_MX_ADDR_VALID_FC_MOD, buf); + PRB_MX_ADDR_VALID_FC_MOD, buf); return 0; } @@ -667,7 +665,7 @@ static void ql_get_mac_protocol_registers(struct ql_adapter *qdev, u32 *buf) result_index = 0; while ((result_index & MAC_ADDR_MR) == 0) { result_index = ql_read32(qdev, - MAC_ADDR_IDX); + MAC_ADDR_IDX); } result_data = ql_read32(qdev, MAC_ADDR_DATA); *buf = result_index; @@ -741,7 +739,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Insert the global header */ memset(&(mpi_coredump->mpi_global_header), 0, - sizeof(struct mpi_coredump_global_header)); + sizeof(struct mpi_coredump_global_header)); mpi_coredump->mpi_global_header.cookie = MPI_COREDUMP_COOKIE; mpi_coredump->mpi_global_header.headerSize = sizeof(struct mpi_coredump_global_header); @@ -752,23 +750,23 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get generic NIC reg dump */ ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr, - NIC1_CONTROL_SEG_NUM, + NIC1_CONTROL_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic_regs), "NIC1 Registers"); ql_build_coredump_seg_header(&mpi_coredump->nic2_regs_seg_hdr, - NIC2_CONTROL_SEG_NUM, + NIC2_CONTROL_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic2_regs), "NIC2 Registers"); /* Get XGMac registers. (Segment 18, Rev C. step 21) */ ql_build_coredump_seg_header(&mpi_coredump->xgmac1_seg_hdr, - NIC1_XGMAC_SEG_NUM, + NIC1_XGMAC_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->xgmac1), "NIC1 XGMac Registers"); ql_build_coredump_seg_header(&mpi_coredump->xgmac2_seg_hdr, - NIC2_XGMAC_SEG_NUM, + NIC2_XGMAC_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->xgmac2), "NIC2 XGMac Registers"); @@ -799,97 +797,97 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Rev C. Step 20a */ ql_build_coredump_seg_header(&mpi_coredump->xaui_an_hdr, - XAUI_AN_SEG_NUM, + XAUI_AN_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xaui_an), "XAUI AN Registers"); /* Rev C. Step 20b */ ql_build_coredump_seg_header(&mpi_coredump->xaui_hss_pcs_hdr, - XAUI_HSS_PCS_SEG_NUM, + XAUI_HSS_PCS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xaui_hss_pcs), "XAUI HSS PCS Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi_an_hdr, XFI_AN_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) + + sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xfi_an), "XFI AN Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi_train_hdr, - XFI_TRAIN_SEG_NUM, + XFI_TRAIN_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xfi_train), "XFI TRAIN Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_pcs_hdr, - XFI_HSS_PCS_SEG_NUM, + XFI_HSS_PCS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xfi_hss_pcs), "XFI HSS PCS Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_tx_hdr, - XFI_HSS_TX_SEG_NUM, + XFI_HSS_TX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xfi_hss_tx), "XFI HSS TX Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_rx_hdr, - XFI_HSS_RX_SEG_NUM, + XFI_HSS_RX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xfi_hss_rx), "XFI HSS RX Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_pll_hdr, - XFI_HSS_PLL_SEG_NUM, + XFI_HSS_PLL_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes_xfi_hss_pll), "XFI HSS PLL Registers"); ql_build_coredump_seg_header(&mpi_coredump->xaui2_an_hdr, - XAUI2_AN_SEG_NUM, + XAUI2_AN_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xaui_an), "XAUI2 AN Registers"); ql_build_coredump_seg_header(&mpi_coredump->xaui2_hss_pcs_hdr, - XAUI2_HSS_PCS_SEG_NUM, + XAUI2_HSS_PCS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xaui_hss_pcs), "XAUI2 HSS PCS Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi2_an_hdr, - XFI2_AN_SEG_NUM, + XFI2_AN_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xfi_an), "XFI2 AN Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi2_train_hdr, - XFI2_TRAIN_SEG_NUM, + XFI2_TRAIN_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xfi_train), "XFI2 TRAIN Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_pcs_hdr, - XFI2_HSS_PCS_SEG_NUM, + XFI2_HSS_PCS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xfi_hss_pcs), "XFI2 HSS PCS Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_tx_hdr, - XFI2_HSS_TX_SEG_NUM, + XFI2_HSS_TX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xfi_hss_tx), "XFI2 HSS TX Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_rx_hdr, - XFI2_HSS_RX_SEG_NUM, + XFI2_HSS_RX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xfi_hss_rx), "XFI2 HSS RX Registers"); ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_pll_hdr, - XFI2_HSS_PLL_SEG_NUM, + XFI2_HSS_PLL_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->serdes2_xfi_hss_pll), "XFI2 HSS PLL Registers"); @@ -903,7 +901,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) } ql_build_coredump_seg_header(&mpi_coredump->core_regs_seg_hdr, - CORE_SEG_NUM, + CORE_SEG_NUM, sizeof(mpi_coredump->core_regs_seg_hdr) + sizeof(mpi_coredump->mpi_core_regs) + sizeof(mpi_coredump->mpi_core_sh_regs), @@ -922,7 +920,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the Test Logic Registers */ ql_build_coredump_seg_header(&mpi_coredump->test_logic_regs_seg_hdr, - TEST_LOGIC_SEG_NUM, + TEST_LOGIC_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->test_logic_regs), "Test Logic Regs"); @@ -933,7 +931,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the RMII Registers */ ql_build_coredump_seg_header(&mpi_coredump->rmii_regs_seg_hdr, - RMII_SEG_NUM, + RMII_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->rmii_regs), "RMII Registers"); @@ -944,7 +942,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the FCMAC1 Registers */ ql_build_coredump_seg_header(&mpi_coredump->fcmac1_regs_seg_hdr, - FCMAC1_SEG_NUM, + FCMAC1_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->fcmac1_regs), "FCMAC1 Registers"); @@ -956,7 +954,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the FCMAC2 Registers */ ql_build_coredump_seg_header(&mpi_coredump->fcmac2_regs_seg_hdr, - FCMAC2_SEG_NUM, + FCMAC2_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->fcmac2_regs), "FCMAC2 Registers"); @@ -968,7 +966,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the FC1 MBX Registers */ ql_build_coredump_seg_header(&mpi_coredump->fc1_mbx_regs_seg_hdr, - FC1_MBOX_SEG_NUM, + FC1_MBOX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->fc1_mbx_regs), "FC1 MBox Regs"); @@ -979,7 +977,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the IDE Registers */ ql_build_coredump_seg_header(&mpi_coredump->ide_regs_seg_hdr, - IDE_SEG_NUM, + IDE_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->ide_regs), "IDE Registers"); @@ -990,7 +988,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the NIC1 MBX Registers */ ql_build_coredump_seg_header(&mpi_coredump->nic1_mbx_regs_seg_hdr, - NIC1_MBOX_SEG_NUM, + NIC1_MBOX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic1_mbx_regs), "NIC1 MBox Regs"); @@ -1001,7 +999,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the SMBus Registers */ ql_build_coredump_seg_header(&mpi_coredump->smbus_regs_seg_hdr, - SMBUS_SEG_NUM, + SMBUS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->smbus_regs), "SMBus Registers"); @@ -1012,7 +1010,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the FC2 MBX Registers */ ql_build_coredump_seg_header(&mpi_coredump->fc2_mbx_regs_seg_hdr, - FC2_MBOX_SEG_NUM, + FC2_MBOX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->fc2_mbx_regs), "FC2 MBox Regs"); @@ -1023,7 +1021,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the NIC2 MBX Registers */ ql_build_coredump_seg_header(&mpi_coredump->nic2_mbx_regs_seg_hdr, - NIC2_MBOX_SEG_NUM, + NIC2_MBOX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic2_mbx_regs), "NIC2 MBox Regs"); @@ -1034,7 +1032,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the I2C Registers */ ql_build_coredump_seg_header(&mpi_coredump->i2c_regs_seg_hdr, - I2C_SEG_NUM, + I2C_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->i2c_regs), "I2C Registers"); @@ -1045,7 +1043,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the MEMC Registers */ ql_build_coredump_seg_header(&mpi_coredump->memc_regs_seg_hdr, - MEMC_SEG_NUM, + MEMC_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->memc_regs), "MEMC Registers"); @@ -1056,7 +1054,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the PBus Registers */ ql_build_coredump_seg_header(&mpi_coredump->pbus_regs_seg_hdr, - PBUS_SEG_NUM, + PBUS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->pbus_regs), "PBUS Registers"); @@ -1067,7 +1065,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the MDE Registers */ ql_build_coredump_seg_header(&mpi_coredump->mde_regs_seg_hdr, - MDE_SEG_NUM, + MDE_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->mde_regs), "MDE Registers"); @@ -1077,7 +1075,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) goto err; ql_build_coredump_seg_header(&mpi_coredump->misc_nic_seg_hdr, - MISC_NIC_INFO_SEG_NUM, + MISC_NIC_INFO_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->misc_nic_info), "MISC NIC INFO"); @@ -1089,14 +1087,14 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Segment 31 */ /* Get indexed register values. */ ql_build_coredump_seg_header(&mpi_coredump->intr_states_seg_hdr, - INTR_STATES_SEG_NUM, + INTR_STATES_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->intr_states), "INTR States"); ql_get_intr_states(qdev, &mpi_coredump->intr_states[0]); ql_build_coredump_seg_header(&mpi_coredump->cam_entries_seg_hdr, - CAM_ENTRIES_SEG_NUM, + CAM_ENTRIES_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->cam_entries), "CAM Entries"); @@ -1105,18 +1103,18 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) goto err; ql_build_coredump_seg_header(&mpi_coredump->nic_routing_words_seg_hdr, - ROUTING_WORDS_SEG_NUM, + ROUTING_WORDS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic_routing_words), "Routing Words"); status = ql_get_routing_entries(qdev, - &mpi_coredump->nic_routing_words[0]); + &mpi_coredump->nic_routing_words[0]); if (status) goto err; /* Segment 34 (Rev C. step 23) */ ql_build_coredump_seg_header(&mpi_coredump->ets_seg_hdr, - ETS_SEG_NUM, + ETS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->ets), "ETS Registers"); @@ -1125,24 +1123,24 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) goto err; ql_build_coredump_seg_header(&mpi_coredump->probe_dump_seg_hdr, - PROBE_DUMP_SEG_NUM, + PROBE_DUMP_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->probe_dump), "Probe Dump"); ql_get_probe_dump(qdev, &mpi_coredump->probe_dump[0]); ql_build_coredump_seg_header(&mpi_coredump->routing_reg_seg_hdr, - ROUTING_INDEX_SEG_NUM, + ROUTING_INDEX_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->routing_regs), "Routing Regs"); status = ql_get_routing_index_registers(qdev, - &mpi_coredump->routing_regs[0]); + &mpi_coredump->routing_regs[0]); if (status) goto err; ql_build_coredump_seg_header(&mpi_coredump->mac_prot_reg_seg_hdr, - MAC_PROTOCOL_SEG_NUM, + MAC_PROTOCOL_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->mac_prot_regs), "MAC Prot Regs"); @@ -1150,7 +1148,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Get the semaphore registers for all 5 functions */ ql_build_coredump_seg_header(&mpi_coredump->sem_regs_seg_hdr, - SEM_REGS_SEG_NUM, + SEM_REGS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->sem_regs), "Sem Registers"); @@ -1176,12 +1174,12 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) } ql_build_coredump_seg_header(&mpi_coredump->code_ram_seg_hdr, - WCS_RAM_SEG_NUM, + WCS_RAM_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->code_ram), "WCS RAM"); status = ql_dump_risc_ram_area(qdev, &mpi_coredump->code_ram[0], - CODE_RAM_ADDR, CODE_RAM_CNT); + CODE_RAM_ADDR, CODE_RAM_CNT); if (status) { netif_err(qdev, drv, qdev->ndev, "Failed Dump of CODE RAM. Status = 0x%.08x\n", @@ -1191,12 +1189,12 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) /* Insert the segment header */ ql_build_coredump_seg_header(&mpi_coredump->memc_ram_seg_hdr, - MEMC_RAM_SEG_NUM, + MEMC_RAM_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->memc_ram), "MEMC RAM"); status = ql_dump_risc_ram_area(qdev, &mpi_coredump->memc_ram[0], - MEMC_RAM_ADDR, MEMC_RAM_CNT); + MEMC_RAM_ADDR, MEMC_RAM_CNT); if (status) { netif_err(qdev, drv, qdev->ndev, "Failed Dump of MEMC RAM. Status = 0x%.08x\n", @@ -1231,7 +1229,7 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, memset(&(mpi_coredump->mpi_global_header), 0, - sizeof(struct mpi_coredump_global_header)); + sizeof(struct mpi_coredump_global_header)); mpi_coredump->mpi_global_header.cookie = MPI_COREDUMP_COOKIE; mpi_coredump->mpi_global_header.headerSize = sizeof(struct mpi_coredump_global_header); @@ -1243,7 +1241,7 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, /* segment 16 */ ql_build_coredump_seg_header(&mpi_coredump->misc_nic_seg_hdr, - MISC_NIC_INFO_SEG_NUM, + MISC_NIC_INFO_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->misc_nic_info), "MISC NIC INFO"); @@ -1254,7 +1252,7 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, /* Segment 16, Rev C. Step 18 */ ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr, - NIC1_CONTROL_SEG_NUM, + NIC1_CONTROL_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic_regs), "NIC Registers"); @@ -1265,14 +1263,14 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, /* Segment 31 */ /* Get indexed register values. */ ql_build_coredump_seg_header(&mpi_coredump->intr_states_seg_hdr, - INTR_STATES_SEG_NUM, + INTR_STATES_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->intr_states), "INTR States"); ql_get_intr_states(qdev, &mpi_coredump->intr_states[0]); ql_build_coredump_seg_header(&mpi_coredump->cam_entries_seg_hdr, - CAM_ENTRIES_SEG_NUM, + CAM_ENTRIES_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->cam_entries), "CAM Entries"); @@ -1281,18 +1279,18 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, return; ql_build_coredump_seg_header(&mpi_coredump->nic_routing_words_seg_hdr, - ROUTING_WORDS_SEG_NUM, + ROUTING_WORDS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->nic_routing_words), "Routing Words"); status = ql_get_routing_entries(qdev, - &mpi_coredump->nic_routing_words[0]); + &mpi_coredump->nic_routing_words[0]); if (status) return; /* Segment 34 (Rev C. step 23) */ ql_build_coredump_seg_header(&mpi_coredump->ets_seg_hdr, - ETS_SEG_NUM, + ETS_SEG_NUM, sizeof(struct mpi_coredump_segment_header) + sizeof(mpi_coredump->ets), "ETS Registers"); @@ -1630,6 +1628,7 @@ void ql_dump_qdev(struct ql_adapter *qdev) DUMP_QDEV_FIELD(qdev, "0x%08x", xg_sem_mask); DUMP_QDEV_FIELD(qdev, "0x%08x", port_link_up); DUMP_QDEV_FIELD(qdev, "0x%08x", port_init); + DUMP_QDEV_FIELD(qdev, "%u", lbq_buf_size); } #endif @@ -1730,16 +1729,24 @@ void ql_dump_cqicb(struct cqicb *cqicb) le16_to_cpu(cqicb->sbq_len)); } +static const char *qlge_rx_ring_type_name(struct rx_ring *rx_ring) +{ + struct ql_adapter *qdev = rx_ring->qdev; + + if (rx_ring->cq_id < qdev->rss_ring_count) + return "RX COMPLETION"; + else + return "TX COMPLETION"; +}; + void ql_dump_rx_ring(struct rx_ring *rx_ring) { if (rx_ring == NULL) return; pr_err("===================== Dumping rx_ring %d ===============\n", rx_ring->cq_id); - pr_err("Dumping rx_ring %d, type = %s%s%s\n", - rx_ring->cq_id, rx_ring->type == DEFAULT_Q ? "DEFAULT" : "", - rx_ring->type == TX_Q ? "OUTBOUND COMPLETIONS" : "", - rx_ring->type == RX_Q ? "INBOUND_COMPLETIONS" : ""); + pr_err("Dumping rx_ring %d, type = %s\n", rx_ring->cq_id, + qlge_rx_ring_type_name(rx_ring)); pr_err("rx_ring->cqicb = %p\n", &rx_ring->cqicb); pr_err("rx_ring->cq_base = %p\n", rx_ring->cq_base); pr_err("rx_ring->cq_base_dma = %llx\n", @@ -1758,41 +1765,33 @@ void ql_dump_rx_ring(struct rx_ring *rx_ring) pr_err("rx_ring->curr_entry = %p\n", rx_ring->curr_entry); pr_err("rx_ring->valid_db_reg = %p\n", rx_ring->valid_db_reg); - pr_err("rx_ring->lbq_base = %p\n", rx_ring->lbq_base); - pr_err("rx_ring->lbq_base_dma = %llx\n", - (unsigned long long) rx_ring->lbq_base_dma); - pr_err("rx_ring->lbq_base_indirect = %p\n", - rx_ring->lbq_base_indirect); - pr_err("rx_ring->lbq_base_indirect_dma = %llx\n", - (unsigned long long) rx_ring->lbq_base_indirect_dma); - pr_err("rx_ring->lbq = %p\n", rx_ring->lbq); - pr_err("rx_ring->lbq_len = %d\n", rx_ring->lbq_len); - pr_err("rx_ring->lbq_size = %d\n", rx_ring->lbq_size); - pr_err("rx_ring->lbq_prod_idx_db_reg = %p\n", - rx_ring->lbq_prod_idx_db_reg); - pr_err("rx_ring->lbq_prod_idx = %d\n", rx_ring->lbq_prod_idx); - pr_err("rx_ring->lbq_curr_idx = %d\n", rx_ring->lbq_curr_idx); + pr_err("rx_ring->lbq.base = %p\n", rx_ring->lbq.base); + pr_err("rx_ring->lbq.base_dma = %llx\n", + (unsigned long long)rx_ring->lbq.base_dma); + pr_err("rx_ring->lbq.base_indirect = %p\n", + rx_ring->lbq.base_indirect); + pr_err("rx_ring->lbq.base_indirect_dma = %llx\n", + (unsigned long long)rx_ring->lbq.base_indirect_dma); + pr_err("rx_ring->lbq = %p\n", rx_ring->lbq.queue); + pr_err("rx_ring->lbq.prod_idx_db_reg = %p\n", + rx_ring->lbq.prod_idx_db_reg); + pr_err("rx_ring->lbq.next_to_use = %d\n", rx_ring->lbq.next_to_use); + pr_err("rx_ring->lbq.next_to_clean = %d\n", rx_ring->lbq.next_to_clean); pr_err("rx_ring->lbq_clean_idx = %d\n", rx_ring->lbq_clean_idx); pr_err("rx_ring->lbq_free_cnt = %d\n", rx_ring->lbq_free_cnt); - pr_err("rx_ring->lbq_buf_size = %d\n", rx_ring->lbq_buf_size); - - pr_err("rx_ring->sbq_base = %p\n", rx_ring->sbq_base); - pr_err("rx_ring->sbq_base_dma = %llx\n", - (unsigned long long) rx_ring->sbq_base_dma); - pr_err("rx_ring->sbq_base_indirect = %p\n", - rx_ring->sbq_base_indirect); - pr_err("rx_ring->sbq_base_indirect_dma = %llx\n", - (unsigned long long) rx_ring->sbq_base_indirect_dma); - pr_err("rx_ring->sbq = %p\n", rx_ring->sbq); - pr_err("rx_ring->sbq_len = %d\n", rx_ring->sbq_len); - pr_err("rx_ring->sbq_size = %d\n", rx_ring->sbq_size); - pr_err("rx_ring->sbq_prod_idx_db_reg addr = %p\n", - rx_ring->sbq_prod_idx_db_reg); - pr_err("rx_ring->sbq_prod_idx = %d\n", rx_ring->sbq_prod_idx); - pr_err("rx_ring->sbq_curr_idx = %d\n", rx_ring->sbq_curr_idx); - pr_err("rx_ring->sbq_clean_idx = %d\n", rx_ring->sbq_clean_idx); - pr_err("rx_ring->sbq_free_cnt = %d\n", rx_ring->sbq_free_cnt); - pr_err("rx_ring->sbq_buf_size = %d\n", rx_ring->sbq_buf_size); + + pr_err("rx_ring->sbq.base = %p\n", rx_ring->sbq.base); + pr_err("rx_ring->sbq.base_dma = %llx\n", + (unsigned long long)rx_ring->sbq.base_dma); + pr_err("rx_ring->sbq.base_indirect = %p\n", + rx_ring->sbq.base_indirect); + pr_err("rx_ring->sbq.base_indirect_dma = %llx\n", + (unsigned long long)rx_ring->sbq.base_indirect_dma); + pr_err("rx_ring->sbq = %p\n", rx_ring->sbq.queue); + pr_err("rx_ring->sbq.prod_idx_db_reg addr = %p\n", + rx_ring->sbq.prod_idx_db_reg); + pr_err("rx_ring->sbq.next_to_use = %d\n", rx_ring->sbq.next_to_use); + pr_err("rx_ring->sbq.next_to_clean = %d\n", rx_ring->sbq.next_to_clean); pr_err("rx_ring->cq_id = %d\n", rx_ring->cq_id); pr_err("rx_ring->irq = %d\n", rx_ring->irq); pr_err("rx_ring->cpu = %d\n", rx_ring->cpu); @@ -1806,7 +1805,7 @@ void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id) pr_err("%s: Enter\n", __func__); ptr = kmalloc(size, GFP_ATOMIC); - if (ptr == NULL) + if (!ptr) return; if (ql_write_cfg(qdev, ptr, size, bit, q_id)) { @@ -1992,7 +1991,7 @@ void ql_dump_ib_mac_rsp(struct ib_mac_iocb_rsp *ib_mac_rsp) le16_to_cpu(ib_mac_rsp->vlan_id)); pr_err("flags4 = %s%s%s\n", - ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HV ? "HV " : "", + ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HV ? "HV " : "", ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HS ? "HS " : "", ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HL ? "HL " : ""); diff --git a/drivers/staging/qlge/qlge_main.c b/drivers/staging/qlge/qlge_main.c index 6cae33072496..0c381d91faa6 100644 --- a/drivers/staging/qlge/qlge_main.c +++ b/drivers/staging/qlge/qlge_main.c @@ -625,75 +625,26 @@ static void ql_disable_interrupts(struct ql_adapter *qdev) ql_write32(qdev, INTR_EN, (INTR_EN_EI << 16)); } -/* If we're running with multiple MSI-X vectors then we enable on the fly. - * Otherwise, we may have multiple outstanding workers and don't want to - * enable until the last one finishes. In this case, the irq_cnt gets - * incremented every time we queue a worker and decremented every time - * a worker finishes. Once it hits zero we enable the interrupt. - */ -u32 ql_enable_completion_interrupt(struct ql_adapter *qdev, u32 intr) +static void ql_enable_completion_interrupt(struct ql_adapter *qdev, u32 intr) { - u32 var = 0; - unsigned long hw_flags = 0; - struct intr_context *ctx = qdev->intr_context + intr; - - if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags) && intr)) { - /* Always enable if we're MSIX multi interrupts and - * it's not the default (zeroeth) interrupt. - */ - ql_write32(qdev, INTR_EN, - ctx->intr_en_mask); - var = ql_read32(qdev, STS); - return var; - } + struct intr_context *ctx = &qdev->intr_context[intr]; - spin_lock_irqsave(&qdev->hw_lock, hw_flags); - if (atomic_dec_and_test(&ctx->irq_cnt)) { - ql_write32(qdev, INTR_EN, - ctx->intr_en_mask); - var = ql_read32(qdev, STS); - } - spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); - return var; + ql_write32(qdev, INTR_EN, ctx->intr_en_mask); } -static u32 ql_disable_completion_interrupt(struct ql_adapter *qdev, u32 intr) +static void ql_disable_completion_interrupt(struct ql_adapter *qdev, u32 intr) { - u32 var = 0; - struct intr_context *ctx; - - /* HW disables for us if we're MSIX multi interrupts and - * it's not the default (zeroeth) interrupt. - */ - if (likely(test_bit(QL_MSIX_ENABLED, &qdev->flags) && intr)) - return 0; + struct intr_context *ctx = &qdev->intr_context[intr]; - ctx = qdev->intr_context + intr; - spin_lock(&qdev->hw_lock); - if (!atomic_read(&ctx->irq_cnt)) { - ql_write32(qdev, INTR_EN, - ctx->intr_dis_mask); - var = ql_read32(qdev, STS); - } - atomic_inc(&ctx->irq_cnt); - spin_unlock(&qdev->hw_lock); - return var; + ql_write32(qdev, INTR_EN, ctx->intr_dis_mask); } static void ql_enable_all_completion_interrupts(struct ql_adapter *qdev) { int i; - for (i = 0; i < qdev->intr_count; i++) { - /* The enable call does a atomic_dec_and_test - * and enables only if the result is zero. - * So we precharge it here. - */ - if (unlikely(!test_bit(QL_MSIX_ENABLED, &qdev->flags) || - i == 0)) - atomic_set(&qdev->intr_context[i].irq_cnt, 1); - ql_enable_completion_interrupt(qdev, i); - } + for (i = 0; i < qdev->intr_count; i++) + ql_enable_completion_interrupt(qdev, i); } static int ql_validate_flash(struct ql_adapter *qdev, u32 size, const char *str) @@ -1027,48 +978,32 @@ static inline unsigned int ql_lbq_block_size(struct ql_adapter *qdev) return PAGE_SIZE << qdev->lbq_buf_order; } -/* Get the next large buffer. */ -static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring) +static struct qlge_bq_desc *qlge_get_curr_buf(struct qlge_bq *bq) { - struct bq_desc *lbq_desc = &rx_ring->lbq[rx_ring->lbq_curr_idx]; - rx_ring->lbq_curr_idx++; - if (rx_ring->lbq_curr_idx == rx_ring->lbq_len) - rx_ring->lbq_curr_idx = 0; - rx_ring->lbq_free_cnt++; - return lbq_desc; + struct qlge_bq_desc *bq_desc; + + bq_desc = &bq->queue[bq->next_to_clean]; + bq->next_to_clean = QLGE_BQ_WRAP(bq->next_to_clean + 1); + + return bq_desc; } -static struct bq_desc *ql_get_curr_lchunk(struct ql_adapter *qdev, - struct rx_ring *rx_ring) +static struct qlge_bq_desc *ql_get_curr_lchunk(struct ql_adapter *qdev, + struct rx_ring *rx_ring) { - struct bq_desc *lbq_desc = ql_get_curr_lbuf(rx_ring); + struct qlge_bq_desc *lbq_desc = qlge_get_curr_buf(&rx_ring->lbq); - pci_dma_sync_single_for_cpu(qdev->pdev, - dma_unmap_addr(lbq_desc, mapaddr), - rx_ring->lbq_buf_size, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(qdev->pdev, lbq_desc->dma_addr, + qdev->lbq_buf_size, PCI_DMA_FROMDEVICE); - /* If it's the last chunk of our master page then - * we unmap it. - */ - if ((lbq_desc->p.pg_chunk.offset + rx_ring->lbq_buf_size) - == ql_lbq_block_size(qdev)) - pci_unmap_page(qdev->pdev, - lbq_desc->p.pg_chunk.map, - ql_lbq_block_size(qdev), - PCI_DMA_FROMDEVICE); - return lbq_desc; -} + if ((lbq_desc->p.pg_chunk.offset + qdev->lbq_buf_size) == + ql_lbq_block_size(qdev)) { + /* last chunk of the master page */ + pci_unmap_page(qdev->pdev, lbq_desc->dma_addr, + ql_lbq_block_size(qdev), PCI_DMA_FROMDEVICE); + } -/* Get the next small buffer. */ -static struct bq_desc *ql_get_curr_sbuf(struct rx_ring *rx_ring) -{ - struct bq_desc *sbq_desc = &rx_ring->sbq[rx_ring->sbq_curr_idx]; - rx_ring->sbq_curr_idx++; - if (rx_ring->sbq_curr_idx == rx_ring->sbq_len) - rx_ring->sbq_curr_idx = 0; - rx_ring->sbq_free_cnt++; - return sbq_desc; + return lbq_desc; } /* Update an rx ring index. */ @@ -1087,178 +1022,192 @@ static void ql_write_cq_idx(struct rx_ring *rx_ring) ql_write_db_reg(rx_ring->cnsmr_idx, rx_ring->cnsmr_idx_db_reg); } -static int ql_get_next_chunk(struct ql_adapter *qdev, struct rx_ring *rx_ring, - struct bq_desc *lbq_desc) +static const char * const bq_type_name[] = { + [QLGE_SB] = "sbq", + [QLGE_LB] = "lbq", +}; + +/* return 0 or negative error */ +static int qlge_refill_sb(struct rx_ring *rx_ring, + struct qlge_bq_desc *sbq_desc, gfp_t gfp) { - if (!rx_ring->pg_chunk.page) { - u64 map; - rx_ring->pg_chunk.page = alloc_pages(__GFP_COMP | GFP_ATOMIC, - qdev->lbq_buf_order); - if (unlikely(!rx_ring->pg_chunk.page)) { - netif_err(qdev, drv, qdev->ndev, - "page allocation failed.\n"); + struct ql_adapter *qdev = rx_ring->qdev; + struct sk_buff *skb; + + if (sbq_desc->p.skb) + return 0; + + netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, + "ring %u sbq: getting new skb for index %d.\n", + rx_ring->cq_id, sbq_desc->index); + + skb = __netdev_alloc_skb(qdev->ndev, SMALL_BUFFER_SIZE, gfp); + if (!skb) + return -ENOMEM; + skb_reserve(skb, QLGE_SB_PAD); + + sbq_desc->dma_addr = pci_map_single(qdev->pdev, skb->data, + SMALL_BUF_MAP_SIZE, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(qdev->pdev, sbq_desc->dma_addr)) { + netif_err(qdev, ifup, qdev->ndev, "PCI mapping failed.\n"); + dev_kfree_skb_any(skb); + return -EIO; + } + *sbq_desc->buf_ptr = cpu_to_le64(sbq_desc->dma_addr); + + sbq_desc->p.skb = skb; + return 0; +} + +/* return 0 or negative error */ +static int qlge_refill_lb(struct rx_ring *rx_ring, + struct qlge_bq_desc *lbq_desc, gfp_t gfp) +{ + struct ql_adapter *qdev = rx_ring->qdev; + struct qlge_page_chunk *master_chunk = &rx_ring->master_chunk; + + if (!master_chunk->page) { + struct page *page; + dma_addr_t dma_addr; + + page = alloc_pages(gfp | __GFP_COMP, qdev->lbq_buf_order); + if (unlikely(!page)) return -ENOMEM; - } - rx_ring->pg_chunk.offset = 0; - map = pci_map_page(qdev->pdev, rx_ring->pg_chunk.page, - 0, ql_lbq_block_size(qdev), + dma_addr = pci_map_page(qdev->pdev, page, 0, + ql_lbq_block_size(qdev), PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(qdev->pdev, map)) { - __free_pages(rx_ring->pg_chunk.page, - qdev->lbq_buf_order); - rx_ring->pg_chunk.page = NULL; + if (pci_dma_mapping_error(qdev->pdev, dma_addr)) { + __free_pages(page, qdev->lbq_buf_order); netif_err(qdev, drv, qdev->ndev, "PCI mapping failed.\n"); - return -ENOMEM; + return -EIO; } - rx_ring->pg_chunk.map = map; - rx_ring->pg_chunk.va = page_address(rx_ring->pg_chunk.page); + master_chunk->page = page; + master_chunk->va = page_address(page); + master_chunk->offset = 0; + rx_ring->chunk_dma_addr = dma_addr; } - /* Copy the current master pg_chunk info - * to the current descriptor. - */ - lbq_desc->p.pg_chunk = rx_ring->pg_chunk; + lbq_desc->p.pg_chunk = *master_chunk; + lbq_desc->dma_addr = rx_ring->chunk_dma_addr; + *lbq_desc->buf_ptr = cpu_to_le64(lbq_desc->dma_addr + + lbq_desc->p.pg_chunk.offset); /* Adjust the master page chunk for next * buffer get. */ - rx_ring->pg_chunk.offset += rx_ring->lbq_buf_size; - if (rx_ring->pg_chunk.offset == ql_lbq_block_size(qdev)) { - rx_ring->pg_chunk.page = NULL; - lbq_desc->p.pg_chunk.last_flag = 1; + master_chunk->offset += qdev->lbq_buf_size; + if (master_chunk->offset == ql_lbq_block_size(qdev)) { + master_chunk->page = NULL; } else { - rx_ring->pg_chunk.va += rx_ring->lbq_buf_size; - get_page(rx_ring->pg_chunk.page); - lbq_desc->p.pg_chunk.last_flag = 0; + master_chunk->va += qdev->lbq_buf_size; + get_page(master_chunk->page); } + return 0; } -/* Process (refill) a large buffer queue. */ -static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) + +/* return 0 or negative error */ +static int qlge_refill_bq(struct qlge_bq *bq, gfp_t gfp) { - u32 clean_idx = rx_ring->lbq_clean_idx; - u32 start_idx = clean_idx; - struct bq_desc *lbq_desc; - u64 map; + struct rx_ring *rx_ring = QLGE_BQ_CONTAINER(bq); + struct ql_adapter *qdev = rx_ring->qdev; + struct qlge_bq_desc *bq_desc; + int refill_count; + int retval; int i; - while (rx_ring->lbq_free_cnt > 32) { - for (i = (rx_ring->lbq_clean_idx % 16); i < 16; i++) { - netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, - "lbq: try cleaning clean_idx = %d.\n", - clean_idx); - lbq_desc = &rx_ring->lbq[clean_idx]; - if (ql_get_next_chunk(qdev, rx_ring, lbq_desc)) { - rx_ring->lbq_clean_idx = clean_idx; - netif_err(qdev, ifup, qdev->ndev, - "Could not get a page chunk, i=%d, clean_idx =%d .\n", - i, clean_idx); - return; - } + refill_count = QLGE_BQ_WRAP(QLGE_BQ_ALIGN(bq->next_to_clean - 1) - + bq->next_to_use); + if (!refill_count) + return 0; + + i = bq->next_to_use; + bq_desc = &bq->queue[i]; + i -= QLGE_BQ_LEN; + do { + netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, + "ring %u %s: try cleaning idx %d\n", + rx_ring->cq_id, bq_type_name[bq->type], i); - map = lbq_desc->p.pg_chunk.map + - lbq_desc->p.pg_chunk.offset; - dma_unmap_addr_set(lbq_desc, mapaddr, map); - dma_unmap_len_set(lbq_desc, maplen, - rx_ring->lbq_buf_size); - *lbq_desc->addr = cpu_to_le64(map); - - pci_dma_sync_single_for_device(qdev->pdev, map, - rx_ring->lbq_buf_size, - PCI_DMA_FROMDEVICE); - clean_idx++; - if (clean_idx == rx_ring->lbq_len) - clean_idx = 0; + if (bq->type == QLGE_SB) + retval = qlge_refill_sb(rx_ring, bq_desc, gfp); + else + retval = qlge_refill_lb(rx_ring, bq_desc, gfp); + if (retval < 0) { + netif_err(qdev, ifup, qdev->ndev, + "ring %u %s: Could not get a page chunk, idx %d\n", + rx_ring->cq_id, bq_type_name[bq->type], i); + break; } - rx_ring->lbq_clean_idx = clean_idx; - rx_ring->lbq_prod_idx += 16; - if (rx_ring->lbq_prod_idx == rx_ring->lbq_len) - rx_ring->lbq_prod_idx = 0; - rx_ring->lbq_free_cnt -= 16; - } + bq_desc++; + i++; + if (unlikely(!i)) { + bq_desc = &bq->queue[0]; + i -= QLGE_BQ_LEN; + } + refill_count--; + } while (refill_count); + i += QLGE_BQ_LEN; - if (start_idx != clean_idx) { - netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, - "lbq: updating prod idx = %d.\n", - rx_ring->lbq_prod_idx); - ql_write_db_reg(rx_ring->lbq_prod_idx, - rx_ring->lbq_prod_idx_db_reg); + if (bq->next_to_use != i) { + if (QLGE_BQ_ALIGN(bq->next_to_use) != QLGE_BQ_ALIGN(i)) { + netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, + "ring %u %s: updating prod idx = %d.\n", + rx_ring->cq_id, bq_type_name[bq->type], + i); + ql_write_db_reg(i, bq->prod_idx_db_reg); + } + bq->next_to_use = i; } + + return retval; } -/* Process (refill) a small buffer queue. */ -static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) +static void ql_update_buffer_queues(struct rx_ring *rx_ring, gfp_t gfp, + unsigned long delay) { - u32 clean_idx = rx_ring->sbq_clean_idx; - u32 start_idx = clean_idx; - struct bq_desc *sbq_desc; - u64 map; - int i; - - while (rx_ring->sbq_free_cnt > 16) { - for (i = (rx_ring->sbq_clean_idx % 16); i < 16; i++) { - sbq_desc = &rx_ring->sbq[clean_idx]; - netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, - "sbq: try cleaning clean_idx = %d.\n", - clean_idx); - if (sbq_desc->p.skb == NULL) { - netif_printk(qdev, rx_status, KERN_DEBUG, - qdev->ndev, - "sbq: getting new skb for index %d.\n", - sbq_desc->index); - sbq_desc->p.skb = - netdev_alloc_skb(qdev->ndev, - SMALL_BUFFER_SIZE); - if (sbq_desc->p.skb == NULL) { - rx_ring->sbq_clean_idx = clean_idx; - return; - } - skb_reserve(sbq_desc->p.skb, QLGE_SB_PAD); - map = pci_map_single(qdev->pdev, - sbq_desc->p.skb->data, - rx_ring->sbq_buf_size, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(qdev->pdev, map)) { - netif_err(qdev, ifup, qdev->ndev, - "PCI mapping failed.\n"); - rx_ring->sbq_clean_idx = clean_idx; - dev_kfree_skb_any(sbq_desc->p.skb); - sbq_desc->p.skb = NULL; - return; - } - dma_unmap_addr_set(sbq_desc, mapaddr, map); - dma_unmap_len_set(sbq_desc, maplen, - rx_ring->sbq_buf_size); - *sbq_desc->addr = cpu_to_le64(map); - } + bool sbq_fail, lbq_fail; - clean_idx++; - if (clean_idx == rx_ring->sbq_len) - clean_idx = 0; - } - rx_ring->sbq_clean_idx = clean_idx; - rx_ring->sbq_prod_idx += 16; - if (rx_ring->sbq_prod_idx == rx_ring->sbq_len) - rx_ring->sbq_prod_idx = 0; - rx_ring->sbq_free_cnt -= 16; - } + sbq_fail = !!qlge_refill_bq(&rx_ring->sbq, gfp); + lbq_fail = !!qlge_refill_bq(&rx_ring->lbq, gfp); - if (start_idx != clean_idx) { - netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, - "sbq: updating prod idx = %d.\n", - rx_ring->sbq_prod_idx); - ql_write_db_reg(rx_ring->sbq_prod_idx, - rx_ring->sbq_prod_idx_db_reg); - } + /* Minimum number of buffers needed to be able to receive at least one + * frame of any format: + * sbq: 1 for header + 1 for data + * lbq: mtu 9000 / lb size + * Below this, the queue might stall. + */ + if ((sbq_fail && QLGE_BQ_HW_OWNED(&rx_ring->sbq) < 2) || + (lbq_fail && QLGE_BQ_HW_OWNED(&rx_ring->lbq) < + DIV_ROUND_UP(9000, LARGE_BUFFER_MAX_SIZE))) + /* Allocations can take a long time in certain cases (ex. + * reclaim). Therefore, use a workqueue for long-running + * work items. + */ + queue_delayed_work_on(smp_processor_id(), system_long_wq, + &rx_ring->refill_work, delay); } -static void ql_update_buffer_queues(struct ql_adapter *qdev, - struct rx_ring *rx_ring) +static void qlge_slow_refill(struct work_struct *work) { - ql_update_sbq(qdev, rx_ring); - ql_update_lbq(qdev, rx_ring); + struct rx_ring *rx_ring = container_of(work, struct rx_ring, + refill_work.work); + struct napi_struct *napi = &rx_ring->napi; + + napi_disable(napi); + ql_update_buffer_queues(rx_ring, GFP_KERNEL, HZ / 2); + napi_enable(napi); + + local_bh_disable(); + /* napi_disable() might have prevented incomplete napi work from being + * rescheduled. + */ + napi_schedule(napi); + /* trigger softirq processing */ + local_bh_enable(); } /* Unmaps tx buffers. Can be called from send() if a pci mapping @@ -1495,7 +1444,7 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev, u16 vlan_id) { struct sk_buff *skb; - struct bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); + struct qlge_bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); struct napi_struct *napi = &rx_ring->napi; /* Frame error, so drop the packet. */ @@ -1544,7 +1493,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev, struct net_device *ndev = qdev->ndev; struct sk_buff *skb = NULL; void *addr; - struct bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); + struct qlge_bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); struct napi_struct *napi = &rx_ring->napi; size_t hlen = ETH_HLEN; @@ -1634,10 +1583,9 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev, u32 length, u16 vlan_id) { + struct qlge_bq_desc *sbq_desc = qlge_get_curr_buf(&rx_ring->sbq); struct net_device *ndev = qdev->ndev; - struct sk_buff *skb = NULL; - struct sk_buff *new_skb = NULL; - struct bq_desc *sbq_desc = ql_get_curr_sbuf(rx_ring); + struct sk_buff *skb, *new_skb; skb = sbq_desc->p.skb; /* Allocate new_skb and copy */ @@ -1648,17 +1596,11 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev, } skb_reserve(new_skb, NET_IP_ALIGN); - pci_dma_sync_single_for_cpu(qdev->pdev, - dma_unmap_addr(sbq_desc, mapaddr), - dma_unmap_len(sbq_desc, maplen), - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(qdev->pdev, sbq_desc->dma_addr, + SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); skb_put_data(new_skb, skb->data, length); - pci_dma_sync_single_for_device(qdev->pdev, - dma_unmap_addr(sbq_desc, mapaddr), - dma_unmap_len(sbq_desc, maplen), - PCI_DMA_FROMDEVICE); skb = new_skb; /* Frame error, so drop the packet. */ @@ -1759,11 +1701,10 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, struct rx_ring *rx_ring, struct ib_mac_iocb_rsp *ib_mac_rsp) { - struct bq_desc *lbq_desc; - struct bq_desc *sbq_desc; - struct sk_buff *skb = NULL; u32 length = le32_to_cpu(ib_mac_rsp->data_len); u32 hdr_len = le32_to_cpu(ib_mac_rsp->hdr_len); + struct qlge_bq_desc *lbq_desc, *sbq_desc; + struct sk_buff *skb = NULL; size_t hlen = ETH_HLEN; /* @@ -1776,11 +1717,9 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, /* * Headers fit nicely into a small buffer. */ - sbq_desc = ql_get_curr_sbuf(rx_ring); - pci_unmap_single(qdev->pdev, - dma_unmap_addr(sbq_desc, mapaddr), - dma_unmap_len(sbq_desc, maplen), - PCI_DMA_FROMDEVICE); + sbq_desc = qlge_get_curr_buf(&rx_ring->sbq); + pci_unmap_single(qdev->pdev, sbq_desc->dma_addr, + SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); skb = sbq_desc->p.skb; ql_realign_skb(skb, hdr_len); skb_put(skb, hdr_len); @@ -1808,35 +1747,22 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, * from the "data" small buffer to the "header" small * buffer. */ - sbq_desc = ql_get_curr_sbuf(rx_ring); + sbq_desc = qlge_get_curr_buf(&rx_ring->sbq); pci_dma_sync_single_for_cpu(qdev->pdev, - dma_unmap_addr - (sbq_desc, mapaddr), - dma_unmap_len - (sbq_desc, maplen), + sbq_desc->dma_addr, + SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); skb_put_data(skb, sbq_desc->p.skb->data, length); - pci_dma_sync_single_for_device(qdev->pdev, - dma_unmap_addr - (sbq_desc, - mapaddr), - dma_unmap_len - (sbq_desc, - maplen), - PCI_DMA_FROMDEVICE); } else { netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, "%d bytes in a single small buffer.\n", length); - sbq_desc = ql_get_curr_sbuf(rx_ring); + sbq_desc = qlge_get_curr_buf(&rx_ring->sbq); skb = sbq_desc->p.skb; ql_realign_skb(skb, length); skb_put(skb, length); - pci_unmap_single(qdev->pdev, - dma_unmap_addr(sbq_desc, - mapaddr), - dma_unmap_len(sbq_desc, - maplen), + pci_unmap_single(qdev->pdev, sbq_desc->dma_addr, + SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); sbq_desc->p.skb = NULL; } @@ -1873,10 +1799,8 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, "No skb available, drop the packet.\n"); return NULL; } - pci_unmap_page(qdev->pdev, - dma_unmap_addr(lbq_desc, - mapaddr), - dma_unmap_len(lbq_desc, maplen), + pci_unmap_page(qdev->pdev, lbq_desc->dma_addr, + qdev->lbq_buf_size, PCI_DMA_FROMDEVICE); skb_reserve(skb, NET_IP_ALIGN); netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, @@ -1907,11 +1831,9 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, * eventually be in trouble. */ int size, i = 0; - sbq_desc = ql_get_curr_sbuf(rx_ring); - pci_unmap_single(qdev->pdev, - dma_unmap_addr(sbq_desc, mapaddr), - dma_unmap_len(sbq_desc, maplen), - PCI_DMA_FROMDEVICE); + sbq_desc = qlge_get_curr_buf(&rx_ring->sbq); + pci_unmap_single(qdev->pdev, sbq_desc->dma_addr, + SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); if (!(ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HS)) { /* * This is an non TCP/UDP IP frame, so @@ -1931,8 +1853,7 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, } do { lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); - size = (length < rx_ring->lbq_buf_size) ? length : - rx_ring->lbq_buf_size; + size = min(length, qdev->lbq_buf_size); netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, "Adding page %d to skb for %d bytes.\n", @@ -2286,7 +2207,7 @@ static int ql_clean_inbound_rx_ring(struct rx_ring *rx_ring, int budget) if (count == budget) break; } - ql_update_buffer_queues(qdev, rx_ring); + ql_update_buffer_queues(rx_ring, GFP_ATOMIC, 0); ql_write_cq_idx(rx_ring); return count; } @@ -2500,21 +2421,22 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) u32 var; int work_done = 0; - spin_lock(&qdev->hw_lock); - if (atomic_read(&qdev->intr_context[0].irq_cnt)) { - netif_printk(qdev, intr, KERN_DEBUG, qdev->ndev, - "Shared Interrupt, Not ours!\n"); - spin_unlock(&qdev->hw_lock); - return IRQ_NONE; - } - spin_unlock(&qdev->hw_lock); + /* Experience shows that when using INTx interrupts, interrupts must + * be masked manually. + * When using MSI mode, INTR_EN_EN must be explicitly disabled + * (even though it is auto-masked), otherwise a later command to + * enable it is not effective. + */ + if (!test_bit(QL_MSIX_ENABLED, &qdev->flags)) + ql_disable_completion_interrupt(qdev, 0); - var = ql_disable_completion_interrupt(qdev, intr_context->intr); + var = ql_read32(qdev, STS); /* * Check for fatal error. */ if (var & STS_FE) { + ql_disable_completion_interrupt(qdev, 0); ql_queue_asic_error(qdev); netdev_err(qdev->ndev, "Got fatal error, STS = %x.\n", var); var = ql_read32(qdev, ERR_STS); @@ -2534,7 +2456,6 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) */ netif_err(qdev, intr, qdev->ndev, "Got MPI processor interrupt.\n"); - ql_disable_completion_interrupt(qdev, intr_context->intr); ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); queue_delayed_work_on(smp_processor_id(), qdev->workqueue, &qdev->mpi_work, 0); @@ -2550,11 +2471,18 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) if (var & intr_context->irq_mask) { netif_info(qdev, intr, qdev->ndev, "Waking handler for rx_ring[0].\n"); - ql_disable_completion_interrupt(qdev, intr_context->intr); napi_schedule(&rx_ring->napi); work_done++; + } else { + /* Experience shows that the device sometimes signals an + * interrupt but no work is scheduled from this function. + * Nevertheless, the interrupt is auto-masked. Therefore, we + * systematically re-enable the interrupt if we didn't + * schedule napi. + */ + ql_enable_completion_interrupt(qdev, 0); } - ql_enable_completion_interrupt(qdev, intr_context->intr); + return work_done ? IRQ_HANDLED : IRQ_NONE; } @@ -2820,54 +2748,46 @@ pci_alloc_err: static void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring) { - struct bq_desc *lbq_desc; + struct qlge_bq *lbq = &rx_ring->lbq; + unsigned int last_offset; - uint32_t curr_idx, clean_idx; + last_offset = ql_lbq_block_size(qdev) - qdev->lbq_buf_size; + while (lbq->next_to_clean != lbq->next_to_use) { + struct qlge_bq_desc *lbq_desc = + &lbq->queue[lbq->next_to_clean]; - curr_idx = rx_ring->lbq_curr_idx; - clean_idx = rx_ring->lbq_clean_idx; - while (curr_idx != clean_idx) { - lbq_desc = &rx_ring->lbq[curr_idx]; - - if (lbq_desc->p.pg_chunk.last_flag) { - pci_unmap_page(qdev->pdev, - lbq_desc->p.pg_chunk.map, - ql_lbq_block_size(qdev), + if (lbq_desc->p.pg_chunk.offset == last_offset) + pci_unmap_page(qdev->pdev, lbq_desc->dma_addr, + ql_lbq_block_size(qdev), PCI_DMA_FROMDEVICE); - lbq_desc->p.pg_chunk.last_flag = 0; - } - put_page(lbq_desc->p.pg_chunk.page); - lbq_desc->p.pg_chunk.page = NULL; - - if (++curr_idx == rx_ring->lbq_len) - curr_idx = 0; + lbq->next_to_clean = QLGE_BQ_WRAP(lbq->next_to_clean + 1); } - if (rx_ring->pg_chunk.page) { - pci_unmap_page(qdev->pdev, rx_ring->pg_chunk.map, - ql_lbq_block_size(qdev), PCI_DMA_FROMDEVICE); - put_page(rx_ring->pg_chunk.page); - rx_ring->pg_chunk.page = NULL; + + if (rx_ring->master_chunk.page) { + pci_unmap_page(qdev->pdev, rx_ring->chunk_dma_addr, + ql_lbq_block_size(qdev), PCI_DMA_FROMDEVICE); + put_page(rx_ring->master_chunk.page); + rx_ring->master_chunk.page = NULL; } } static void ql_free_sbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring) { int i; - struct bq_desc *sbq_desc; - for (i = 0; i < rx_ring->sbq_len; i++) { - sbq_desc = &rx_ring->sbq[i]; + for (i = 0; i < QLGE_BQ_LEN; i++) { + struct qlge_bq_desc *sbq_desc = &rx_ring->sbq.queue[i]; + if (sbq_desc == NULL) { netif_err(qdev, ifup, qdev->ndev, "sbq_desc %d is NULL.\n", i); return; } if (sbq_desc->p.skb) { - pci_unmap_single(qdev->pdev, - dma_unmap_addr(sbq_desc, mapaddr), - dma_unmap_len(sbq_desc, maplen), + pci_unmap_single(qdev->pdev, sbq_desc->dma_addr, + SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); dev_kfree_skb(sbq_desc->p.skb); sbq_desc->p.skb = NULL; @@ -2881,89 +2801,83 @@ static void ql_free_sbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring static void ql_free_rx_buffers(struct ql_adapter *qdev) { int i; - struct rx_ring *rx_ring; for (i = 0; i < qdev->rx_ring_count; i++) { - rx_ring = &qdev->rx_ring[i]; - if (rx_ring->lbq) + struct rx_ring *rx_ring = &qdev->rx_ring[i]; + + if (rx_ring->lbq.queue) ql_free_lbq_buffers(qdev, rx_ring); - if (rx_ring->sbq) + if (rx_ring->sbq.queue) ql_free_sbq_buffers(qdev, rx_ring); } } static void ql_alloc_rx_buffers(struct ql_adapter *qdev) { - struct rx_ring *rx_ring; int i; - for (i = 0; i < qdev->rx_ring_count; i++) { - rx_ring = &qdev->rx_ring[i]; - if (rx_ring->type != TX_Q) - ql_update_buffer_queues(qdev, rx_ring); - } + for (i = 0; i < qdev->rss_ring_count; i++) + ql_update_buffer_queues(&qdev->rx_ring[i], GFP_KERNEL, + HZ / 2); } -static void ql_init_lbq_ring(struct ql_adapter *qdev, - struct rx_ring *rx_ring) +static int qlge_init_bq(struct qlge_bq *bq) { + struct rx_ring *rx_ring = QLGE_BQ_CONTAINER(bq); + struct ql_adapter *qdev = rx_ring->qdev; + struct qlge_bq_desc *bq_desc; + __le64 *buf_ptr; int i; - struct bq_desc *lbq_desc; - __le64 *bq = rx_ring->lbq_base; - memset(rx_ring->lbq, 0, rx_ring->lbq_len * sizeof(struct bq_desc)); - for (i = 0; i < rx_ring->lbq_len; i++) { - lbq_desc = &rx_ring->lbq[i]; - memset(lbq_desc, 0, sizeof(*lbq_desc)); - lbq_desc->index = i; - lbq_desc->addr = bq; - bq++; + bq->base = pci_alloc_consistent(qdev->pdev, QLGE_BQ_SIZE, + &bq->base_dma); + if (!bq->base) { + netif_err(qdev, ifup, qdev->ndev, + "ring %u %s allocation failed.\n", rx_ring->cq_id, + bq_type_name[bq->type]); + return -ENOMEM; } -} -static void ql_init_sbq_ring(struct ql_adapter *qdev, - struct rx_ring *rx_ring) -{ - int i; - struct bq_desc *sbq_desc; - __le64 *bq = rx_ring->sbq_base; + bq->queue = kmalloc_array(QLGE_BQ_LEN, sizeof(struct qlge_bq_desc), + GFP_KERNEL); + if (!bq->queue) + return -ENOMEM; - memset(rx_ring->sbq, 0, rx_ring->sbq_len * sizeof(struct bq_desc)); - for (i = 0; i < rx_ring->sbq_len; i++) { - sbq_desc = &rx_ring->sbq[i]; - memset(sbq_desc, 0, sizeof(*sbq_desc)); - sbq_desc->index = i; - sbq_desc->addr = bq; - bq++; + buf_ptr = bq->base; + bq_desc = &bq->queue[0]; + for (i = 0; i < QLGE_BQ_LEN; i++, buf_ptr++, bq_desc++) { + bq_desc->p.skb = NULL; + bq_desc->index = i; + bq_desc->buf_ptr = buf_ptr; } + + return 0; } static void ql_free_rx_resources(struct ql_adapter *qdev, struct rx_ring *rx_ring) { /* Free the small buffer queue. */ - if (rx_ring->sbq_base) { - pci_free_consistent(qdev->pdev, - rx_ring->sbq_size, - rx_ring->sbq_base, rx_ring->sbq_base_dma); - rx_ring->sbq_base = NULL; + if (rx_ring->sbq.base) { + pci_free_consistent(qdev->pdev, QLGE_BQ_SIZE, + rx_ring->sbq.base, rx_ring->sbq.base_dma); + rx_ring->sbq.base = NULL; } /* Free the small buffer queue control blocks. */ - kfree(rx_ring->sbq); - rx_ring->sbq = NULL; + kfree(rx_ring->sbq.queue); + rx_ring->sbq.queue = NULL; /* Free the large buffer queue. */ - if (rx_ring->lbq_base) { - pci_free_consistent(qdev->pdev, - rx_ring->lbq_size, - rx_ring->lbq_base, rx_ring->lbq_base_dma); - rx_ring->lbq_base = NULL; + if (rx_ring->lbq.base) { + pci_free_consistent(qdev->pdev, QLGE_BQ_SIZE, + rx_ring->lbq.base, rx_ring->lbq.base_dma); + rx_ring->lbq.base = NULL; } /* Free the large buffer queue control blocks. */ - kfree(rx_ring->lbq); - rx_ring->lbq = NULL; + kfree(rx_ring->lbq.queue); + rx_ring->lbq.queue = NULL; /* Free the rx queue. */ if (rx_ring->cq_base) { @@ -2992,62 +2906,13 @@ static int ql_alloc_rx_resources(struct ql_adapter *qdev, return -ENOMEM; } - if (rx_ring->sbq_len) { - /* - * Allocate small buffer queue. - */ - rx_ring->sbq_base = - pci_alloc_consistent(qdev->pdev, rx_ring->sbq_size, - &rx_ring->sbq_base_dma); - - if (rx_ring->sbq_base == NULL) { - netif_err(qdev, ifup, qdev->ndev, - "Small buffer queue allocation failed.\n"); - goto err_mem; - } - - /* - * Allocate small buffer queue control blocks. - */ - rx_ring->sbq = kmalloc_array(rx_ring->sbq_len, - sizeof(struct bq_desc), - GFP_KERNEL); - if (rx_ring->sbq == NULL) - goto err_mem; - - ql_init_sbq_ring(qdev, rx_ring); - } - - if (rx_ring->lbq_len) { - /* - * Allocate large buffer queue. - */ - rx_ring->lbq_base = - pci_alloc_consistent(qdev->pdev, rx_ring->lbq_size, - &rx_ring->lbq_base_dma); - - if (rx_ring->lbq_base == NULL) { - netif_err(qdev, ifup, qdev->ndev, - "Large buffer queue allocation failed.\n"); - goto err_mem; - } - /* - * Allocate large buffer queue control blocks. - */ - rx_ring->lbq = kmalloc_array(rx_ring->lbq_len, - sizeof(struct bq_desc), - GFP_KERNEL); - if (rx_ring->lbq == NULL) - goto err_mem; - - ql_init_lbq_ring(qdev, rx_ring); + if (rx_ring->cq_id < qdev->rss_ring_count && + (qlge_init_bq(&rx_ring->sbq) || qlge_init_bq(&rx_ring->lbq))) { + ql_free_rx_resources(qdev, rx_ring); + return -ENOMEM; } return 0; - -err_mem: - ql_free_rx_resources(qdev, rx_ring); - return -ENOMEM; } static void ql_tx_ring_clean(struct ql_adapter *qdev) @@ -3133,7 +2998,6 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) void __iomem *doorbell_area = qdev->doorbell_area + (DB_PAGE_SIZE * (128 + rx_ring->cq_id)); int err = 0; - u16 bq_len; u64 tmp; __le64 *base_indirect_ptr; int page_entries; @@ -3144,12 +3008,12 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) *rx_ring->prod_idx_sh_reg = 0; shadow_reg += sizeof(u64); shadow_reg_dma += sizeof(u64); - rx_ring->lbq_base_indirect = shadow_reg; - rx_ring->lbq_base_indirect_dma = shadow_reg_dma; - shadow_reg += (sizeof(u64) * MAX_DB_PAGES_PER_BQ(rx_ring->lbq_len)); - shadow_reg_dma += (sizeof(u64) * MAX_DB_PAGES_PER_BQ(rx_ring->lbq_len)); - rx_ring->sbq_base_indirect = shadow_reg; - rx_ring->sbq_base_indirect_dma = shadow_reg_dma; + rx_ring->lbq.base_indirect = shadow_reg; + rx_ring->lbq.base_indirect_dma = shadow_reg_dma; + shadow_reg += (sizeof(u64) * MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN)); + shadow_reg_dma += (sizeof(u64) * MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN)); + rx_ring->sbq.base_indirect = shadow_reg; + rx_ring->sbq.base_indirect_dma = shadow_reg_dma; /* PCI doorbell mem area + 0x00 for consumer index register */ rx_ring->cnsmr_idx_db_reg = (u32 __iomem *) doorbell_area; @@ -3160,16 +3024,16 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) rx_ring->valid_db_reg = doorbell_area + 0x04; /* PCI doorbell mem area + 0x18 for large buffer consumer */ - rx_ring->lbq_prod_idx_db_reg = (u32 __iomem *) (doorbell_area + 0x18); + rx_ring->lbq.prod_idx_db_reg = (u32 __iomem *)(doorbell_area + 0x18); /* PCI doorbell mem area + 0x1c */ - rx_ring->sbq_prod_idx_db_reg = (u32 __iomem *) (doorbell_area + 0x1c); + rx_ring->sbq.prod_idx_db_reg = (u32 __iomem *)(doorbell_area + 0x1c); memset((void *)cqicb, 0, sizeof(struct cqicb)); cqicb->msix_vect = rx_ring->irq; - bq_len = (rx_ring->cq_len == 65536) ? 0 : (u16) rx_ring->cq_len; - cqicb->len = cpu_to_le16(bq_len | LEN_V | LEN_CPP_CONT); + cqicb->len = cpu_to_le16(QLGE_FIT16(rx_ring->cq_len) | LEN_V | + LEN_CPP_CONT); cqicb->addr = cpu_to_le64(rx_ring->cq_base_dma); @@ -3181,59 +3045,42 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) cqicb->flags = FLAGS_LC | /* Load queue base address */ FLAGS_LV | /* Load MSI-X vector */ FLAGS_LI; /* Load irq delay values */ - if (rx_ring->lbq_len) { + if (rx_ring->cq_id < qdev->rss_ring_count) { cqicb->flags |= FLAGS_LL; /* Load lbq values */ - tmp = (u64)rx_ring->lbq_base_dma; - base_indirect_ptr = rx_ring->lbq_base_indirect; + tmp = (u64)rx_ring->lbq.base_dma; + base_indirect_ptr = rx_ring->lbq.base_indirect; page_entries = 0; do { *base_indirect_ptr = cpu_to_le64(tmp); tmp += DB_PAGE_SIZE; base_indirect_ptr++; page_entries++; - } while (page_entries < MAX_DB_PAGES_PER_BQ(rx_ring->lbq_len)); - cqicb->lbq_addr = - cpu_to_le64(rx_ring->lbq_base_indirect_dma); - bq_len = (rx_ring->lbq_buf_size == 65536) ? 0 : - (u16) rx_ring->lbq_buf_size; - cqicb->lbq_buf_size = cpu_to_le16(bq_len); - bq_len = (rx_ring->lbq_len == 65536) ? 0 : - (u16) rx_ring->lbq_len; - cqicb->lbq_len = cpu_to_le16(bq_len); - rx_ring->lbq_prod_idx = 0; - rx_ring->lbq_curr_idx = 0; - rx_ring->lbq_clean_idx = 0; - rx_ring->lbq_free_cnt = rx_ring->lbq_len; - } - if (rx_ring->sbq_len) { + } while (page_entries < MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN)); + cqicb->lbq_addr = cpu_to_le64(rx_ring->lbq.base_indirect_dma); + cqicb->lbq_buf_size = + cpu_to_le16(QLGE_FIT16(qdev->lbq_buf_size)); + cqicb->lbq_len = cpu_to_le16(QLGE_FIT16(QLGE_BQ_LEN)); + rx_ring->lbq.next_to_use = 0; + rx_ring->lbq.next_to_clean = 0; + cqicb->flags |= FLAGS_LS; /* Load sbq values */ - tmp = (u64)rx_ring->sbq_base_dma; - base_indirect_ptr = rx_ring->sbq_base_indirect; + tmp = (u64)rx_ring->sbq.base_dma; + base_indirect_ptr = rx_ring->sbq.base_indirect; page_entries = 0; do { *base_indirect_ptr = cpu_to_le64(tmp); tmp += DB_PAGE_SIZE; base_indirect_ptr++; page_entries++; - } while (page_entries < MAX_DB_PAGES_PER_BQ(rx_ring->sbq_len)); + } while (page_entries < MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN)); cqicb->sbq_addr = - cpu_to_le64(rx_ring->sbq_base_indirect_dma); - cqicb->sbq_buf_size = - cpu_to_le16((u16)(rx_ring->sbq_buf_size)); - bq_len = (rx_ring->sbq_len == 65536) ? 0 : - (u16) rx_ring->sbq_len; - cqicb->sbq_len = cpu_to_le16(bq_len); - rx_ring->sbq_prod_idx = 0; - rx_ring->sbq_curr_idx = 0; - rx_ring->sbq_clean_idx = 0; - rx_ring->sbq_free_cnt = rx_ring->sbq_len; - } - switch (rx_ring->type) { - case TX_Q: - cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs); - cqicb->pkt_delay = cpu_to_le16(qdev->tx_max_coalesced_frames); - break; - case RX_Q: + cpu_to_le64(rx_ring->sbq.base_indirect_dma); + cqicb->sbq_buf_size = cpu_to_le16(SMALL_BUFFER_SIZE); + cqicb->sbq_len = cpu_to_le16(QLGE_FIT16(QLGE_BQ_LEN)); + rx_ring->sbq.next_to_use = 0; + rx_ring->sbq.next_to_clean = 0; + } + if (rx_ring->cq_id < qdev->rss_ring_count) { /* Inbound completion handling rx_rings run in * separate NAPI contexts. */ @@ -3241,10 +3088,9 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) 64); cqicb->irq_delay = cpu_to_le16(qdev->rx_coalesce_usecs); cqicb->pkt_delay = cpu_to_le16(qdev->rx_max_coalesced_frames); - break; - default: - netif_printk(qdev, ifup, KERN_DEBUG, qdev->ndev, - "Invalid rx_ring->type = %d.\n", rx_ring->type); + } else { + cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs); + cqicb->pkt_delay = cpu_to_le16(qdev->tx_max_coalesced_frames); } err = ql_write_cfg(qdev, cqicb, sizeof(struct cqicb), CFG_LCQ, rx_ring->cq_id); @@ -3366,6 +3212,7 @@ msi: } } qlge_irq_type = LEG_IRQ; + set_bit(QL_LEGACY_ENABLED, &qdev->flags); netif_printk(qdev, ifup, KERN_DEBUG, qdev->ndev, "Running with legacy interrupts.\n"); } @@ -3509,6 +3356,16 @@ static void ql_resolve_queues_to_irqs(struct ql_adapter *qdev) intr_context->intr_dis_mask = INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK | INTR_EN_TYPE_DISABLE; + if (test_bit(QL_LEGACY_ENABLED, &qdev->flags)) { + /* Experience shows that when using INTx interrupts, + * the device does not always auto-mask INTR_EN_EN. + * Moreover, masking INTR_EN_EN manually does not + * immediately prevent interrupt generation. + */ + intr_context->intr_en_mask |= INTR_EN_EI << 16 | + INTR_EN_EI; + intr_context->intr_dis_mask |= INTR_EN_EI << 16; + } intr_context->intr_read_mask = INTR_EN_TYPE_MASK | INTR_EN_INTR_MASK | INTR_EN_TYPE_READ; /* @@ -3557,7 +3414,6 @@ static int ql_request_irq(struct ql_adapter *qdev) ql_resolve_queues_to_irqs(qdev); for (i = 0; i < qdev->intr_count; i++, intr_context++) { - atomic_set(&intr_context->irq_cnt, 0); if (test_bit(QL_MSIX_ENABLED, &qdev->flags)) { status = request_irq(qdev->msi_x_entry[i].vector, intr_context->handler, @@ -3591,12 +3447,7 @@ static int ql_request_irq(struct ql_adapter *qdev) goto err_irq; netif_err(qdev, ifup, qdev->ndev, - "Hooked intr %d, queue type %s, with name %s.\n", - i, - qdev->rx_ring[0].type == DEFAULT_Q ? - "DEFAULT_Q" : - qdev->rx_ring[0].type == TX_Q ? "TX_Q" : - qdev->rx_ring[0].type == RX_Q ? "RX_Q" : "", + "Hooked intr 0, queue type RX_Q, with name %s.\n", intr_context->name); } intr_context->hooked = 1; @@ -4072,6 +3923,7 @@ static int ql_get_adapter_resources(struct ql_adapter *qdev) static int qlge_close(struct net_device *ndev) { struct ql_adapter *qdev = netdev_priv(ndev); + int i; /* If we hit pci_channel_io_perm_failure * failure condition, then we already @@ -4089,21 +3941,31 @@ static int qlge_close(struct net_device *ndev) */ while (!test_bit(QL_ADAPTER_UP, &qdev->flags)) msleep(1); + + /* Make sure refill_work doesn't re-enable napi */ + for (i = 0; i < qdev->rss_ring_count; i++) + cancel_delayed_work_sync(&qdev->rx_ring[i].refill_work); + ql_adapter_down(qdev); ql_release_adapter_resources(qdev); return 0; } +static void qlge_set_lb_size(struct ql_adapter *qdev) +{ + if (qdev->ndev->mtu <= 1500) + qdev->lbq_buf_size = LARGE_BUFFER_MIN_SIZE; + else + qdev->lbq_buf_size = LARGE_BUFFER_MAX_SIZE; + qdev->lbq_buf_order = get_order(qdev->lbq_buf_size); +} + static int ql_configure_rings(struct ql_adapter *qdev) { int i; struct rx_ring *rx_ring; struct tx_ring *tx_ring; int cpu_cnt = min(MAX_CPUS, (int)num_online_cpus()); - unsigned int lbq_buf_len = (qdev->ndev->mtu > 1500) ? - LARGE_BUFFER_MAX_SIZE : LARGE_BUFFER_MIN_SIZE; - - qdev->lbq_buf_order = get_order(lbq_buf_len); /* In a perfect world we have one RSS ring for each CPU * and each has it's own vector. To do that we ask for @@ -4148,15 +4010,10 @@ static int ql_configure_rings(struct ql_adapter *qdev) rx_ring->cq_len = qdev->rx_ring_size; rx_ring->cq_size = rx_ring->cq_len * sizeof(struct ql_net_rsp_iocb); - rx_ring->lbq_len = NUM_LARGE_BUFFERS; - rx_ring->lbq_size = - rx_ring->lbq_len * sizeof(__le64); - rx_ring->lbq_buf_size = (u16)lbq_buf_len; - rx_ring->sbq_len = NUM_SMALL_BUFFERS; - rx_ring->sbq_size = - rx_ring->sbq_len * sizeof(__le64); - rx_ring->sbq_buf_size = SMALL_BUF_MAP_SIZE; - rx_ring->type = RX_Q; + rx_ring->lbq.type = QLGE_LB; + rx_ring->sbq.type = QLGE_SB; + INIT_DELAYED_WORK(&rx_ring->refill_work, + &qlge_slow_refill); } else { /* * Outbound queue handles outbound completions only. @@ -4165,13 +4022,6 @@ static int ql_configure_rings(struct ql_adapter *qdev) rx_ring->cq_len = qdev->tx_ring_size; rx_ring->cq_size = rx_ring->cq_len * sizeof(struct ql_net_rsp_iocb); - rx_ring->lbq_len = 0; - rx_ring->lbq_size = 0; - rx_ring->lbq_buf_size = 0; - rx_ring->sbq_len = 0; - rx_ring->sbq_size = 0; - rx_ring->sbq_buf_size = 0; - rx_ring->type = TX_Q; } } return 0; @@ -4186,6 +4036,7 @@ static int qlge_open(struct net_device *ndev) if (err) return err; + qlge_set_lb_size(qdev); err = ql_configure_rings(qdev); if (err) return err; @@ -4207,9 +4058,7 @@ error_up: static int ql_change_rx_buffers(struct ql_adapter *qdev) { - struct rx_ring *rx_ring; - int i, status; - u32 lbq_buf_len; + int status; /* Wait for an outstanding reset to complete. */ if (!test_bit(QL_ADAPTER_UP, &qdev->flags)) { @@ -4232,16 +4081,7 @@ static int ql_change_rx_buffers(struct ql_adapter *qdev) if (status) goto error; - /* Get the new rx buffer size. */ - lbq_buf_len = (qdev->ndev->mtu > 1500) ? - LARGE_BUFFER_MAX_SIZE : LARGE_BUFFER_MIN_SIZE; - qdev->lbq_buf_order = get_order(lbq_buf_len); - - for (i = 0; i < qdev->rss_ring_count; i++) { - rx_ring = &qdev->rx_ring[i]; - /* Set the new size. */ - rx_ring->lbq_buf_size = lbq_buf_len; - } + qlge_set_lb_size(qdev); status = ql_adapter_up(qdev); if (status) @@ -4642,7 +4482,6 @@ static int ql_init_device(struct pci_dev *pdev, struct net_device *ndev, goto err_out2; } qdev->msg_enable = netif_msg_init(debug, default_msg); - spin_lock_init(&qdev->hw_lock); spin_lock_init(&qdev->stats_lock); if (qlge_mpi_coredump) { diff --git a/drivers/staging/qlge/qlge_mpi.c b/drivers/staging/qlge/qlge_mpi.c index 957c72985a06..9e422bbbb6ab 100644 --- a/drivers/staging/qlge/qlge_mpi.c +++ b/drivers/staging/qlge/qlge_mpi.c @@ -1257,7 +1257,6 @@ void ql_mpi_work(struct work_struct *work) /* End polled mode for MPI */ ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16) | INTR_MASK_PI); mutex_unlock(&qdev->mpi_mutex); - ql_enable_completion_interrupt(qdev, 0); } void ql_mpi_reset_work(struct work_struct *work) diff --git a/drivers/staging/rtl8188eu/core/rtw_ap.c b/drivers/staging/rtl8188eu/core/rtw_ap.c index 51a5b71f8c25..9aa44c921aca 100644 --- a/drivers/staging/rtl8188eu/core/rtw_ap.c +++ b/drivers/staging/rtl8188eu/core/rtw_ap.c @@ -560,29 +560,24 @@ void update_sta_info_apmode(struct adapter *padapter, struct sta_info *psta) static void update_hw_ht_param(struct adapter *padapter) { - unsigned char max_AMPDU_len; - unsigned char min_MPDU_spacing; - struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; - struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; + u8 max_ampdu_len; + u8 min_mpdu_spacing; + struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; + struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; DBG_88E("%s\n", __func__); - /* handle A-MPDU parameter field */ - /* - ampdu_params_info [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k - ampdu_params_info [4:2]:Min MPDU Start Spacing - */ - max_AMPDU_len = pmlmeinfo->HT_caps.ampdu_params_info & 0x03; - - min_MPDU_spacing = (pmlmeinfo->HT_caps.ampdu_params_info & 0x1c) >> 2; - - rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_MIN_SPACE, (u8 *)(&min_MPDU_spacing)); + /* handle A-MPDU parameter field + * ampdu_params_info [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k + * ampdu_params_info [4:2]:Min MPDU Start Spacing + */ + max_ampdu_len = pmlmeinfo->HT_caps.ampdu_params_info & 0x03; + min_mpdu_spacing = (pmlmeinfo->HT_caps.ampdu_params_info & 0x1c) >> 2; - rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_FACTOR, (u8 *)(&max_AMPDU_len)); + rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_MIN_SPACE, &min_mpdu_spacing); + rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_FACTOR, &max_ampdu_len); - /* */ - /* Config SM Power Save setting */ - /* */ + /* Config SM Power Save setting */ pmlmeinfo->SM_PS = (le16_to_cpu(pmlmeinfo->HT_caps.cap_info) & 0x0C) >> 2; if (pmlmeinfo->SM_PS == WLAN_HT_CAP_SM_PS_STATIC) DBG_88E("%s(): WLAN_HT_CAP_SM_PS_STATIC\n", __func__); diff --git a/drivers/staging/rtl8188eu/core/rtw_efuse.c b/drivers/staging/rtl8188eu/core/rtw_efuse.c index 02c476f45b33..d191dbef0bb3 100644 --- a/drivers/staging/rtl8188eu/core/rtw_efuse.c +++ b/drivers/staging/rtl8188eu/core/rtw_efuse.c @@ -615,10 +615,9 @@ static bool hal_EfusePgPacketWrite1ByteHeader(struct adapter *pAdapter, u8 efuse static bool hal_EfusePgPacketWriteData(struct adapter *pAdapter, u8 efuseType, u16 *pAddr, struct pgpkt *pTargetPkt) { u16 efuse_addr = *pAddr; - u8 badworden = 0; + u8 badworden; u32 PgWriteSuccess = 0; - badworden = 0x0f; badworden = Efuse_WordEnableDataWrite(pAdapter, efuse_addr + 1, pTargetPkt->word_en, pTargetPkt->data); if (badworden == 0x0F) { /* write ok */ diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme.c b/drivers/staging/rtl8188eu/core/rtw_mlme.c index 1ec3b237212e..e764436e120f 100644 --- a/drivers/staging/rtl8188eu/core/rtw_mlme.c +++ b/drivers/staging/rtl8188eu/core/rtw_mlme.c @@ -2045,9 +2045,9 @@ void _rtw_roaming(struct adapter *padapter, struct wlan_network *tgt_network) while (1) { do_join_r = rtw_do_join(padapter); - if (do_join_r == _SUCCESS) { + if (do_join_r == _SUCCESS) break; - } + DBG_88E("roaming do_join return %d\n", do_join_r); pmlmepriv->to_roaming--; diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c index 18dc9fc1c04a..e984b4605e91 100644 --- a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c @@ -507,7 +507,7 @@ static void issue_probersp(struct adapter *padapter, unsigned char *da) pwps_ie = rtw_get_wps_ie(cur_network->ies+_FIXED_IE_LENGTH_, cur_network->ie_length-_FIXED_IE_LENGTH_, NULL, &wps_ielen); /* inerset & update wps_probe_resp_ie */ - if ((pmlmepriv->wps_probe_resp_ie != NULL) && pwps_ie && (wps_ielen > 0)) { + if (pmlmepriv->wps_probe_resp_ie && pwps_ie && wps_ielen > 0) { uint wps_offset, remainder_ielen; u8 *premainder_ie; diff --git a/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c b/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c index 7b16632048b7..03dc7e5fcc38 100644 --- a/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c +++ b/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c @@ -514,7 +514,7 @@ void rtw_init_pwrctrl_priv(struct adapter *padapter) pwrctrlpriv->power_mgnt = PS_MODE_ACTIVE; else pwrctrlpriv->power_mgnt = padapter->registrypriv.power_mgnt;/* PS_MODE_MIN; */ - pwrctrlpriv->bLeisurePs = (pwrctrlpriv->power_mgnt != PS_MODE_ACTIVE) ? true : false; + pwrctrlpriv->bLeisurePs = (pwrctrlpriv->power_mgnt != PS_MODE_ACTIVE); pwrctrlpriv->bFwCurrentInPSMode = false; @@ -621,7 +621,7 @@ int rtw_pm_set_lps(struct adapter *padapter, u8 mode) else pwrctrlpriv->LpsIdleCount = 2; pwrctrlpriv->power_mgnt = mode; - pwrctrlpriv->bLeisurePs = (pwrctrlpriv->power_mgnt != PS_MODE_ACTIVE) ? true : false; + pwrctrlpriv->bLeisurePs = (pwrctrlpriv->power_mgnt != PS_MODE_ACTIVE); } } else { ret = -EINVAL; diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c index 952f2ab51347..c37591657bac 100644 --- a/drivers/staging/rtl8188eu/core/rtw_xmit.c +++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c @@ -776,7 +776,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr memcpy(pwlanhdr->addr2, get_bssid(pmlmepriv), ETH_ALEN); memcpy(pwlanhdr->addr3, pattrib->src, ETH_ALEN); - if (psta->qos_option) + if (psta && psta->qos_option) qos_option = true; } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { @@ -784,7 +784,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN); memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), ETH_ALEN); - if (psta->qos_option) + if (psta && psta->qos_option) qos_option = true; } else { RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("fw_state:%x is not allowed to xmit frame\n", get_fwstate(pmlmepriv))); diff --git a/drivers/staging/rtl8188eu/hal/hal8188e_rate_adaptive.c b/drivers/staging/rtl8188eu/hal/hal8188e_rate_adaptive.c index 9ddd51685063..5792f491b59a 100644 --- a/drivers/staging/rtl8188eu/hal/hal8188e_rate_adaptive.c +++ b/drivers/staging/rtl8188eu/hal/hal8188e_rate_adaptive.c @@ -409,7 +409,7 @@ static int odm_ARFBRefresh_8188E(struct odm_dm_struct *dm_odm, struct odm_ra_inf pRaInfo->PTModeSS = 3; else if (pRaInfo->HighestRate > 0x0b) pRaInfo->PTModeSS = 2; - else if (pRaInfo->HighestRate > 0x0b) + else if (pRaInfo->HighestRate > 0x03) pRaInfo->PTModeSS = 1; else pRaInfo->PTModeSS = 0; diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c b/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c index 47352f210c0b..7646167a0b36 100644 --- a/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c +++ b/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c @@ -47,8 +47,6 @@ static u8 _is_fw_read_cmd_down(struct adapter *adapt, u8 msgbox_num) ******************************************/ static s32 FillH2CCmd_88E(struct adapter *adapt, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer) { - u8 bcmd_down = false; - s32 retry_cnts = 100; u8 h2c_box_num; u32 msgbox_addr; u32 msgbox_ex_addr; @@ -71,39 +69,34 @@ static s32 FillH2CCmd_88E(struct adapter *adapt, u8 ElementID, u32 CmdLen, u8 *p goto exit; /* pay attention to if race condition happened in H2C cmd setting. */ - do { - h2c_box_num = adapt->HalData->LastHMEBoxNum; - - if (!_is_fw_read_cmd_down(adapt, h2c_box_num)) { - DBG_88E(" fw read cmd failed...\n"); - goto exit; - } - - *(u8 *)(&h2c_cmd) = ElementID; - - if (CmdLen <= 3) { - memcpy((u8 *)(&h2c_cmd)+1, pCmdBuffer, CmdLen); - } else { - memcpy((u8 *)(&h2c_cmd)+1, pCmdBuffer, 3); - ext_cmd_len = CmdLen-3; - memcpy((u8 *)(&h2c_cmd_ex), pCmdBuffer+3, ext_cmd_len); + h2c_box_num = adapt->HalData->LastHMEBoxNum; - /* Write Ext command */ - msgbox_ex_addr = REG_HMEBOX_EXT_0 + (h2c_box_num * RTL88E_EX_MESSAGE_BOX_SIZE); - for (cmd_idx = 0; cmd_idx < ext_cmd_len; cmd_idx++) - usb_write8(adapt, msgbox_ex_addr+cmd_idx, *((u8 *)(&h2c_cmd_ex)+cmd_idx)); - } - /* Write command */ - msgbox_addr = REG_HMEBOX_0 + (h2c_box_num * RTL88E_MESSAGE_BOX_SIZE); - for (cmd_idx = 0; cmd_idx < RTL88E_MESSAGE_BOX_SIZE; cmd_idx++) - usb_write8(adapt, msgbox_addr+cmd_idx, *((u8 *)(&h2c_cmd)+cmd_idx)); + if (!_is_fw_read_cmd_down(adapt, h2c_box_num)) { + DBG_88E(" fw read cmd failed...\n"); + goto exit; + } - bcmd_down = true; + *(u8 *)(&h2c_cmd) = ElementID; - adapt->HalData->LastHMEBoxNum = - (h2c_box_num+1) % RTL88E_MAX_H2C_BOX_NUMS; + if (CmdLen <= 3) { + memcpy((u8 *)(&h2c_cmd) + 1, pCmdBuffer, CmdLen); + } else { + memcpy((u8 *)(&h2c_cmd) + 1, pCmdBuffer, 3); + ext_cmd_len = CmdLen - 3; + memcpy((u8 *)(&h2c_cmd_ex), pCmdBuffer + 3, ext_cmd_len); + + /* Write Ext command */ + msgbox_ex_addr = REG_HMEBOX_EXT_0 + (h2c_box_num * RTL88E_EX_MESSAGE_BOX_SIZE); + for (cmd_idx = 0; cmd_idx < ext_cmd_len; cmd_idx++) + usb_write8(adapt, msgbox_ex_addr + cmd_idx, *((u8 *)(&h2c_cmd_ex) + cmd_idx)); + } + /* Write command */ + msgbox_addr = REG_HMEBOX_0 + (h2c_box_num * RTL88E_MESSAGE_BOX_SIZE); + for (cmd_idx = 0; cmd_idx < RTL88E_MESSAGE_BOX_SIZE; cmd_idx++) + usb_write8(adapt, msgbox_addr + cmd_idx, *((u8 *)(&h2c_cmd) + cmd_idx)); - } while ((!bcmd_down) && (retry_cnts--)); + adapt->HalData->LastHMEBoxNum = + (h2c_box_num + 1) % RTL88E_MAX_H2C_BOX_NUMS; ret = _SUCCESS; diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c index 086f98d38cba..57ae0e83dd3e 100644 --- a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c +++ b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c @@ -552,7 +552,6 @@ void Hal_ReadAntennaDiversity88E(struct adapter *pAdapter, u8 *PROMContent, bool pHalData->AntDivCfg = 1; /* 0xC1[3] is ignored. */ } else { pHalData->AntDivCfg = 0; - pHalData->TRxAntDivType = pHalData->TRxAntDivType; /* The value in the driver setting of device manager. */ } DBG_88E("EEPROM : AntDivCfg = %x, TRxAntDivType = %x\n", pHalData->AntDivCfg, pHalData->TRxAntDivType); } diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 664d93a7f90d..4fac9dca798e 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -348,8 +348,10 @@ static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj, } padapter->HalData = kzalloc(sizeof(struct hal_data_8188e), GFP_KERNEL); - if (!padapter->HalData) - DBG_88E("cant not alloc memory for HAL DATA\n"); + if (!padapter->HalData) { + DBG_88E("Failed to allocate memory for HAL data\n"); + goto free_adapter; + } /* step read_chip_version */ rtw_hal_read_chip_version(padapter); diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index f932cb15e4e5..b08712a9c029 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -715,8 +715,8 @@ void rtl92e_set_wireless_mode(struct net_device *dev, u8 wireless_mode) if ((wireless_mode == WIRELESS_MODE_N_24G) || (wireless_mode == WIRELESS_MODE_N_5G)) { priv->rtllib->pHTInfo->bEnableHT = 1; - RT_TRACE(COMP_DBG, "%s(), wireless_mode:%x, bEnableHT = 1\n", - __func__, wireless_mode); + RT_TRACE(COMP_DBG, "%s(), wireless_mode:%x, bEnableHT = 1\n", + __func__, wireless_mode); } else { priv->rtllib->pHTInfo->bEnableHT = 0; RT_TRACE(COMP_DBG, "%s(), wireless_mode:%x, bEnableHT = 0\n", diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c index 5c33bcb0db2e..00fea127bdc3 100644 --- a/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c +++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c @@ -1620,7 +1620,7 @@ int ieee80211_parse_info_param(struct ieee80211_device *ieee, for (i = 0; i < network->rates_len; i++) { network->rates[i] = info_element->data[i]; #ifdef CONFIG_IEEE80211_DEBUG - p += snprintf(p, sizeof(rates_str) - + p += scnprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); #endif @@ -1647,7 +1647,7 @@ int ieee80211_parse_info_param(struct ieee80211_device *ieee, for (i = 0; i < network->rates_ex_len; i++) { network->rates_ex[i] = info_element->data[i]; #ifdef CONFIG_IEEE80211_DEBUG - p += snprintf(p, sizeof(rates_str) - + p += scnprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates_ex[i]); #endif diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c index 9dd5c04181ea..33c596f9ec96 100644 --- a/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c +++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c @@ -109,7 +109,7 @@ static inline char *rtl819x_translate_scan(struct ieee80211_device *ieee, /* Add basic and extended rates */ max_rate = 0; p = custom; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); for (i = 0, j = 0; i < network->rates_len; ) { if (j < network->rates_ex_len && ((network->rates_ex[j] & 0x7F) < @@ -119,12 +119,12 @@ static inline char *rtl819x_translate_scan(struct ieee80211_device *ieee, rate = network->rates[i++] & 0x7F; if (rate > max_rate) max_rate = rate; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); } for (; j < network->rates_ex_len; j++) { rate = network->rates_ex[j] & 0x7F; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); if (rate > max_rate) max_rate = rate; @@ -214,7 +214,7 @@ static inline char *rtl819x_translate_scan(struct ieee80211_device *ieee, * for given network. */ iwe.cmd = IWEVCUSTOM; p = custom; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Last beacon: %lums ago", (jiffies - network->last_scanned) / (HZ / 100)); iwe.u.data.length = p - custom; if (iwe.u.data.length) diff --git a/drivers/staging/rtl8192u/r8190_rtl8256.c b/drivers/staging/rtl8192u/r8190_rtl8256.c index b169460b9f26..63e0f7b1b852 100644 --- a/drivers/staging/rtl8192u/r8190_rtl8256.c +++ b/drivers/staging/rtl8192u/r8190_rtl8256.c @@ -43,8 +43,8 @@ void phy_set_rf8256_bandwidth(struct net_device *dev, enum ht_channel_width Band switch (Bandwidth) { case HT_CHANNEL_WIDTH_20: if (priv->card_8192_version == VERSION_819XU_A || - priv->card_8192_version == - VERSION_819XU_B) { /* 8256 D-cut, E-cut, xiong: consider it later! */ + priv->card_8192_version == VERSION_819XU_B) { + /* 8256 D-cut, E-cut, xiong: consider it later! */ rtl8192_phy_SetRFReg(dev, (enum rf90_radio_path_e)eRFPath, 0x0b, bMask12Bits, 0x100); /* phy para:1ba */ diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c index 2821411878ce..48f1591ed5b4 100644 --- a/drivers/staging/rtl8192u/r8192U_core.c +++ b/drivers/staging/rtl8192u/r8192U_core.c @@ -98,8 +98,6 @@ static char *ifname = "wlan%d"; static int hwwep = 1; /* default use hw. set 0 to use software security */ static int channels = 0x3fff; - - module_param(ifname, charp, 0644); module_param(hwwep, int, 0644); module_param(channels, int, 0644); @@ -112,7 +110,6 @@ static int rtl8192_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); static void rtl8192_usb_disconnect(struct usb_interface *intf); - static struct usb_driver rtl8192_usb_driver = { .name = RTL819XU_MODULE_NAME, /* Driver name */ .id_table = rtl8192_usb_id_tbl, /* PCI_ID table */ @@ -122,7 +119,6 @@ static struct usb_driver rtl8192_usb_driver = { .resume = NULL, /* PM resume fn */ }; - struct CHANNEL_LIST { u8 Channel[32]; u8 Len; @@ -207,9 +203,6 @@ static void rtl819x_set_channel_map(u8 channel_plan, struct r8192_priv *priv) } } - - - static void CamResetAllEntry(struct net_device *dev) { u32 ulcommand = 0; @@ -297,7 +290,6 @@ int write_nic_byte(struct net_device *dev, int indx, u8 data) return 0; } - int write_nic_word(struct net_device *dev, int indx, u16 data) { int status; @@ -324,7 +316,6 @@ int write_nic_word(struct net_device *dev, int indx, u16 data) return 0; } - int write_nic_dword(struct net_device *dev, int indx, u32 data) { int status; @@ -343,7 +334,6 @@ int write_nic_dword(struct net_device *dev, int indx, u32 data) usbdata, 4, HZ / 2); kfree(usbdata); - if (status < 0) { netdev_err(dev, "%s TimeOut! status: %d\n", __func__, status); return status; @@ -352,8 +342,6 @@ int write_nic_dword(struct net_device *dev, int indx, u32 data) return 0; } - - int read_nic_byte(struct net_device *dev, int indx, u8 *data) { int status; @@ -379,8 +367,6 @@ int read_nic_byte(struct net_device *dev, int indx, u8 *data) return 0; } - - int read_nic_word(struct net_device *dev, int indx, u16 *data) { int status; @@ -628,13 +614,13 @@ static void rtl8192_proc_init_one(struct net_device *dev) return; proc_create_single("stats-rx", S_IFREG | S_IRUGO, dir, - proc_get_stats_rx); + proc_get_stats_rx); proc_create_single("stats-tx", S_IFREG | S_IRUGO, dir, - proc_get_stats_tx); + proc_get_stats_tx); proc_create_single("stats-ap", S_IFREG | S_IRUGO, dir, - proc_get_stats_ap); + proc_get_stats_ap); proc_create_single("registers", S_IFREG | S_IRUGO, dir, - proc_get_registers); + proc_get_registers); } static void rtl8192_proc_remove_one(struct net_device *dev) @@ -788,7 +774,6 @@ void rtl8192_set_rxconf(struct net_device *dev) rxconf = rxconf | RCR_CBSSID; } - if (priv->ieee80211->iw_mode == IW_MODE_MONITOR) { rxconf = rxconf | RCR_AICV; rxconf = rxconf | RCR_APWRMGT; @@ -797,7 +782,6 @@ void rtl8192_set_rxconf(struct net_device *dev) if (priv->crcmon == 1 && priv->ieee80211->iw_mode == IW_MODE_MONITOR) rxconf = rxconf | RCR_ACRC32; - rxconf = rxconf & ~RX_FIFO_THRESHOLD_MASK; rxconf = rxconf | (RX_FIFO_THRESHOLD_NONE << RX_FIFO_THRESHOLD_SHIFT); rxconf = rxconf & ~MAX_RX_DMA_MASK; @@ -901,13 +885,11 @@ static u32 rtl819xusb_rx_command_packet(struct net_device *dev, return status; } - static void rtl8192_data_hard_stop(struct net_device *dev) { /* FIXME !! */ } - static void rtl8192_data_hard_resume(struct net_device *dev) { /* FIXME !! */ @@ -951,7 +933,6 @@ static int rtl8192_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); u8 queue_index = tcb_desc->queue_index; - spin_lock_irqsave(&priv->tx_lock, flags); memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); @@ -1123,7 +1104,6 @@ static void rtl8192_config_rate(struct net_device *dev, u16 *rate_config) } } - #define SHORT_SLOT_TIME 9 #define NON_SHORT_SLOT_TIME 20 @@ -1188,7 +1168,6 @@ static void rtl8192_net_update(struct net_device *dev) */ void rtl819xusb_beacon_tx(struct net_device *dev, u16 tx_rate) { - } short rtl819xU_tx_cmd(struct net_device *dev, struct sk_buff *skb) @@ -1232,6 +1211,8 @@ short rtl819xU_tx_cmd(struct net_device *dev, struct sk_buff *skb) return 0; DMESGE("Error TX CMD URB, error %d", status); + dev_kfree_skb(skb); + usb_free_urb(tx_urb); return -1; } @@ -1389,7 +1370,6 @@ static u8 MRateToHwRate8190Pci(u8 rate) return ret; } - static u8 QueryIsShort(u8 TxHT, u8 TxRate, struct cb_desc *tcb_desc) { u8 tmp_Short; @@ -1422,7 +1402,7 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb) (struct tx_fwinfo_819x_usb *)(skb->data + USB_HWDESC_HEADER_LEN); struct usb_device *udev = priv->udev; int pend; - int status; + int status, rt = -1; struct urb *tx_urb = NULL, *tx_urb_zero = NULL; unsigned int idx_pipe; @@ -1566,8 +1546,10 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb) } if (bSend0Byte) { tx_urb_zero = usb_alloc_urb(0, GFP_ATOMIC); - if (!tx_urb_zero) - return -ENOMEM; + if (!tx_urb_zero) { + rt = -ENOMEM; + goto error; + } usb_fill_bulk_urb(tx_urb_zero, udev, usb_sndbulkpipe(udev, idx_pipe), &zero, 0, tx_zero_isr, dev); @@ -1577,7 +1559,7 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb) "Error TX URB for zero byte %d, error %d", atomic_read(&priv->tx_pending[tcb_desc->queue_index]), status); - return -1; + goto error; } } netif_trans_update(dev); @@ -1588,7 +1570,12 @@ short rtl8192_tx(struct net_device *dev, struct sk_buff *skb) RT_TRACE(COMP_ERR, "Error TX URB %d, error %d", atomic_read(&priv->tx_pending[tcb_desc->queue_index]), status); - return -1; + +error: + dev_kfree_skb_any(skb); + usb_free_urb(tx_urb); + usb_free_urb(tx_urb_zero); + return rt; } static short rtl8192_usb_initendpoints(struct net_device *dev) @@ -1742,7 +1729,6 @@ static const struct ieee80211_qos_parameters def_qos_parameters = { {0, 0, 0, 0} /* tx_op_limit */ }; - static void rtl8192_update_beacon(struct work_struct *work) { struct r8192_priv *priv = container_of(work, struct r8192_priv, @@ -1913,15 +1899,12 @@ static int rtl8192_qos_association_resp(struct r8192_priv *priv, if (set_qos_param == 1) schedule_work(&priv->qos_activate); - return 0; } - -static int rtl8192_handle_assoc_response( - struct net_device *dev, - struct ieee80211_assoc_response_frame *resp, - struct ieee80211_network *network) +static int rtl8192_handle_assoc_response(struct net_device *dev, + struct ieee80211_assoc_response_frame *resp, + struct ieee80211_network *network) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -1929,7 +1912,6 @@ static int rtl8192_handle_assoc_response( return 0; } - static void rtl8192_update_ratr_table(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -2218,7 +2200,6 @@ static void rtl8192_init_priv_task(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); - INIT_WORK(&priv->reset_wq, rtl8192_restart); INIT_DELAYED_WORK(&priv->watch_dog_wq, @@ -2515,7 +2496,6 @@ static int rtl8192_read_eeprom_info(struct net_device *dev) break; } - if (priv->rf_type == RF_1T2R) RT_TRACE(COMP_EPROM, "\n1T2R config\n"); else @@ -2668,7 +2648,6 @@ static void rtl8192_hwconfig(struct net_device *dev) /* Set Auto Rate fallback control */ } - /* InitializeAdapter and PhyCfg */ static bool rtl8192_adapter_start(struct net_device *dev) { @@ -2803,14 +2782,12 @@ static bool rtl8192_adapter_start(struct net_device *dev) RT_TRACE(COMP_INIT, "%s():after phy RF config\n", __func__); } - if (priv->ieee80211->FwRWRF) /* We can force firmware to do RF-R/W */ priv->Rf_Mode = RF_OP_By_FW; else priv->Rf_Mode = RF_OP_By_SW_3wire; - rtl8192_phy_updateInitGain(dev); /*--set CCK and OFDM Block "ON"--*/ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bCCKEn, 0x1); @@ -2865,7 +2842,6 @@ static bool rtl8192_adapter_start(struct net_device *dev) } write_nic_byte(dev, 0x87, 0x0); - return init_status; } @@ -2996,7 +2972,6 @@ static RESET_TYPE RxCheckStuck(struct net_device *dev) return RESET_TYPE_NORESET; } - /** * This function is called by Checkforhang to check whether we should * ask OS to reset driver @@ -3052,8 +3027,6 @@ static void rtl8192_cancel_deferred_work(struct r8192_priv *priv); static int _rtl8192_up(struct net_device *dev); static int rtl8192_close(struct net_device *dev); - - static void CamRestoreAllEntry(struct net_device *dev) { u8 EntryId = 0; @@ -3070,7 +3043,6 @@ static void CamRestoreAllEntry(struct net_device *dev) RT_TRACE(COMP_SEC, "%s:\n", __func__); - if ((priv->ieee80211->pairwise_key_type == KEY_TYPE_WEP40) || (priv->ieee80211->pairwise_key_type == KEY_TYPE_WEP104)) { for (EntryId = 0; EntryId < 4; EntryId++) { @@ -3096,8 +3068,6 @@ static void CamRestoreAllEntry(struct net_device *dev) MacAddr, 0, NULL); } - - if (priv->ieee80211->group_key_type == KEY_TYPE_TKIP) { MacAddr = CAM_CONST_BROAD; for (EntryId = 1; EntryId < 4; EntryId++) { @@ -3134,7 +3104,6 @@ static void rtl819x_ifsilentreset(struct net_device *dev) int reset_status = 0; struct ieee80211_device *ieee = priv->ieee80211; - /* If we need to check CCK stop, please uncomment this line. */ /* bStuck = Adapter->HalFunc.CheckHWStopHandler(Adapter); */ @@ -3258,7 +3227,6 @@ static void rtl819x_update_rxcounts(struct r8192_priv *priv, u32 *TotalRxBcnNum, } } - static void rtl819x_watchdog_wqcallback(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -3369,7 +3337,6 @@ static int _rtl8192_up(struct net_device *dev) return 0; } - static int rtl8192_open(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -3381,7 +3348,6 @@ static int rtl8192_open(struct net_device *dev) return ret; } - int rtl8192_up(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -3392,7 +3358,6 @@ int rtl8192_up(struct net_device *dev) return _rtl8192_up(dev); } - static int rtl8192_close(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -3440,7 +3405,6 @@ int rtl8192_down(struct net_device *dev) deinit_hal_dm(dev); del_timer_sync(&priv->watch_dog_timer); - ieee80211_softmac_stop_protocol(priv->ieee80211); memset(&priv->ieee80211->current_network, 0, offsetof(struct ieee80211_network, list)); @@ -3449,7 +3413,6 @@ int rtl8192_down(struct net_device *dev) return 0; } - void rtl8192_commit(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -3495,7 +3458,6 @@ static void r8192_set_multicast(struct net_device *dev) priv->promisc = promisc; } - static int r8192_set_mac_adr(struct net_device *dev, void *mac) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -3525,7 +3487,6 @@ static int rtl8192_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) mutex_lock(&priv->wx_mutex); - if (p->length < sizeof(struct ieee_param) || !p->pointer) { ret = -EINVAL; goto out; @@ -3778,7 +3739,6 @@ static long rtl819x_translate_todbm(u8 signal_strength_index) return signal_power; } - /* We can not declare RSSI/EVM total value of sliding window to * be a local static. Otherwise, it may increase when we return from S3/S4. The * value will be kept in memory or disk. Declare the value in the adaptor @@ -3841,7 +3801,6 @@ static void rtl8192_process_phyinfo(struct r8192_priv *priv, u8 *buffer, if (!bcheck) return; - /* only rtl8190 supported * rtl8190_process_cck_rxpathsel(priv,pprevious_stats); */ @@ -3851,17 +3810,15 @@ static void rtl8192_process_phyinfo(struct r8192_priv *priv, u8 *buffer, /* record the general signal strength to the sliding window. */ - /* <2> Showed on UI for engineering * hardware does not provide rssi information for each rf path in CCK */ if (!pprevious_stats->bIsCCK && (pprevious_stats->bPacketToSelf || pprevious_stats->bToSelfBA)) { for (rfpath = RF90_PATH_A; rfpath < priv->NumTotalRFPath; rfpath++) { - if (!rtl8192_phy_CheckIsLegalRFPath( - priv->ieee80211->dev, rfpath)) + if (!rtl8192_phy_CheckIsLegalRFPath(priv->ieee80211->dev, + rfpath)) continue; - if (priv->stats.rx_rssi_percentage[rfpath] == 0) priv->stats.rx_rssi_percentage[rfpath] = pprevious_stats->RxMIMOSignalStrength[rfpath]; @@ -3881,7 +3838,6 @@ static void rtl8192_process_phyinfo(struct r8192_priv *priv, u8 *buffer, } } - /* Check PWDB. */ RT_TRACE(COMP_RXDESC, "Smooth %s PWDB = %d\n", pprevious_stats->bIsCCK ? "CCK" : "OFDM", @@ -3908,7 +3864,6 @@ static void rtl8192_process_phyinfo(struct r8192_priv *priv, u8 *buffer, pprevious_stats->bIsCCK ? "CCK" : "OFDM", pprevious_stats->RxPWDBAll); - if (pprevious_stats->bPacketToSelf || pprevious_stats->bPacketBeacon || pprevious_stats->bToSelfBA) { @@ -4083,7 +4038,6 @@ static void rtl8192_query_rxphystatus(struct r8192_priv *priv, u8 rf_rx_num = 0; u8 sq; - priv->stats.numqry_phystatus++; is_cck_rate = rx_hal_is_cck_rate(pdrvinfo); @@ -4192,8 +4146,7 @@ static void rtl8192_query_rxphystatus(struct r8192_priv *priv, else continue; - if (!rtl8192_phy_CheckIsLegalRFPath( - priv->ieee80211->dev, i)) + if (!rtl8192_phy_CheckIsLegalRFPath(priv->ieee80211->dev, i)) continue; rx_pwr[i] = @@ -4214,7 +4167,6 @@ static void rtl8192_query_rxphystatus(struct r8192_priv *priv, precord_stats->RxMIMOSignalStrength[i] = (u8)RSSI; } - /* (2)PWDB, Average PWDB calculated by hardware * (for rate adaptive) */ @@ -4259,7 +4211,6 @@ static void rtl8192_query_rxphystatus(struct r8192_priv *priv, evm & 0xff; } - /* record rx statistics for debug */ rxsc_sgien_exflg = pofdm_buf->rxsc_sgien_exflg; prxsc = (struct phy_ofdm_rx_status_rxsc_sgien_exintfflag *) @@ -4288,16 +4239,14 @@ static void rtl8192_query_rxphystatus(struct r8192_priv *priv, } } /* QueryRxPhyStatus8190Pci */ -static void rtl8192_record_rxdesc_forlateruse( - struct ieee80211_rx_stats *psrc_stats, - struct ieee80211_rx_stats *ptarget_stats) +static void rtl8192_record_rxdesc_forlateruse(struct ieee80211_rx_stats *psrc_stats, + struct ieee80211_rx_stats *ptarget_stats) { ptarget_stats->bIsAMPDU = psrc_stats->bIsAMPDU; ptarget_stats->bFirstMPDU = psrc_stats->bFirstMPDU; ptarget_stats->Seq_Num = psrc_stats->Seq_Num; } - static void TranslateRxSignalStuff819xUsb(struct sk_buff *skb, struct ieee80211_rx_stats *pstats, struct rx_drvinfo_819x_usb *pdrvinfo) @@ -4341,8 +4290,6 @@ static void TranslateRxSignalStuff819xUsb(struct sk_buff *skb, bToSelfBA = true; } - - if (bpacket_match_bssid) priv->stats.numpacket_matchbssid++; if (bpacket_toself) @@ -4383,7 +4330,6 @@ UpdateReceivedRateHistogramStatistics8190(struct net_device *dev, /* 1: short preamble/GI, 0: long preamble/GI */ u32 preamble_guardinterval; - if (stats->bCRC) rcvType = 2; else if (stats->bICV) @@ -4491,7 +4437,6 @@ UpdateReceivedRateHistogramStatistics8190(struct net_device *dev, priv->stats.received_rate_histogram[rcvType][rateIndex]++; } - static void query_rxdesc_status(struct sk_buff *skb, struct ieee80211_rx_stats *stats, bool bIsRxAggrSubframe) @@ -4526,8 +4471,7 @@ static void query_rxdesc_status(struct sk_buff *skb, * Driver info are written to the RxBuffer following rx desc */ if (stats->RxDrvInfoSize != 0) { - driver_info = (struct rx_drvinfo_819x_usb *)( - skb->data + driver_info = (struct rx_drvinfo_819x_usb *)(skb->data + sizeof(struct rx_desc_819x_usb) + stats->RxBufShift ); @@ -4556,7 +4500,6 @@ static void query_rxdesc_status(struct sk_buff *skb, stats->bShortPreamble = driver_info->SPLCP; - UpdateReceivedRateHistogramStatistics8190(dev, stats); stats->bIsAMPDU = (driver_info->PartAggr == 1); @@ -4569,7 +4512,7 @@ static void query_rxdesc_status(struct sk_buff *skb, /* Rx A-MPDU */ if (driver_info->FirstAGGR == 1 || driver_info->PartAggr == 1) RT_TRACE(COMP_RXDESC, - "driver_info->FirstAGGR = %d, driver_info->PartAggr = %d\n", + "driver_info->FirstAGGR = %d, driver_info->PartAggr = %d\n", driver_info->FirstAGGR, driver_info->PartAggr); } @@ -4636,9 +4579,8 @@ static void rtl8192_rx_nomal(struct sk_buff *skb) } } -static void rtl819xusb_process_received_packet( - struct net_device *dev, - struct ieee80211_rx_stats *pstats) +static void rtl819xusb_process_received_packet(struct net_device *dev, + struct ieee80211_rx_stats *pstats) { struct r8192_priv *priv = ieee80211_priv(dev); @@ -4671,8 +4613,6 @@ static void rtl819xusb_process_received_packet( #ifdef SW_CRC_CHECK SwCrcCheck(); #endif - - } static void query_rx_cmdpkt_desc_status(struct sk_buff *skb, @@ -4691,7 +4631,6 @@ static void query_rx_cmdpkt_desc_status(struct sk_buff *skb, stats->ntotalfrag = 1; } - static void rtl8192_rx_cmd(struct sk_buff *skb) { struct rtl8192_rx_info *info = (struct rtl8192_rx_info *)skb->cb; @@ -4759,7 +4698,6 @@ static const struct net_device_ops rtl8192_netdev_ops = { .ndo_start_xmit = ieee80211_xmit, }; - /**************************************************************************** * ---------------------------- USB_STUFF--------------------------- *****************************************************************************/ @@ -4815,7 +4753,6 @@ static int rtl8192_usb_probe(struct usb_interface *intf, RT_TRACE(COMP_INIT, "dev name=======> %s\n", dev->name); rtl8192_proc_init_one(dev); - RT_TRACE(COMP_INIT, "Driver probe completed\n"); return 0; @@ -4843,7 +4780,6 @@ static void rtl8192_cancel_deferred_work(struct r8192_priv *priv) cancel_work_sync(&priv->qos_activate); } - static void rtl8192_usb_disconnect(struct usb_interface *intf) { struct net_device *dev = usb_get_intfdata(intf); @@ -4907,7 +4843,6 @@ static int __init rtl8192_usb_module_init(void) return usb_register(&rtl8192_usb_driver); } - static void __exit rtl8192_usb_module_exit(void) { usb_deregister(&rtl8192_usb_driver); @@ -4949,7 +4884,6 @@ void EnableHWSecurityConfig8192(struct net_device *dev) write_nic_byte(dev, SECR, SECR_value); } - void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType, u8 *MacAddr, u8 DefaultKey, u32 *KeyContent) { @@ -4970,7 +4904,6 @@ void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType, else usConfig |= BIT(15) | (KeyType << 2) | KeyIndex; - for (i = 0; i < CAM_CONTENT_COUNT; i++) { TargetCommand = i + CAM_CONTENT_COUNT * EntryNo; TargetCommand |= BIT(31) | BIT(16); diff --git a/drivers/staging/rtl8712/rtl8712_led.c b/drivers/staging/rtl8712/rtl8712_led.c index db99129d3169..5901026949f2 100644 --- a/drivers/staging/rtl8712/rtl8712_led.c +++ b/drivers/staging/rtl8712/rtl8712_led.c @@ -75,7 +75,7 @@ static void BlinkWorkItemCallback(struct work_struct *work); * Initialize an LED_871x object. */ static void InitLed871x(struct _adapter *padapter, struct LED_871x *pLed, - enum LED_PIN_871x LedPin) + enum LED_PIN_871x LedPin) { pLed->padapter = padapter; pLed->LedPin = LedPin; diff --git a/drivers/staging/rtl8712/rtl8712_recv.c b/drivers/staging/rtl8712/rtl8712_recv.c index 9901815604f4..af12c16d6468 100644 --- a/drivers/staging/rtl8712/rtl8712_recv.c +++ b/drivers/staging/rtl8712/rtl8712_recv.c @@ -119,7 +119,7 @@ void r8712_init_recvbuf(struct _adapter *padapter, struct recv_buf *precvbuf) } void r8712_free_recvframe(union recv_frame *precvframe, - struct __queue *pfree_recv_queue) + struct __queue *pfree_recv_queue) { unsigned long irqL; struct _adapter *padapter = precvframe->u.hdr.adapter; diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c index 944336e0d2e2..363b82e3e7c6 100644 --- a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c +++ b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c @@ -142,9 +142,9 @@ static noinline_for_stack char *translate_scan_wpa(struct iw_request_info *info, memset(buf, 0, MAX_WPA_IE_LEN); n = sprintf(buf, "wpa_ie="); for (i = 0; i < wpa_len; i++) { - n += snprintf(buf + n, MAX_WPA_IE_LEN - n, + n += scnprintf(buf + n, MAX_WPA_IE_LEN - n, "%02x", wpa_ie[i]); - if (n >= MAX_WPA_IE_LEN) + if (n == MAX_WPA_IE_LEN-1) break; } memset(iwe, 0, sizeof(*iwe)); @@ -162,9 +162,9 @@ static noinline_for_stack char *translate_scan_wpa(struct iw_request_info *info, memset(buf, 0, MAX_WPA_IE_LEN); n = sprintf(buf, "rsn_ie="); for (i = 0; i < rsn_len; i++) { - n += snprintf(buf + n, MAX_WPA_IE_LEN - n, + n += scnprintf(buf + n, MAX_WPA_IE_LEN - n, "%02x", rsn_ie[i]); - if (n >= MAX_WPA_IE_LEN) + if (n == MAX_WPA_IE_LEN-1) break; } memset(iwe, 0, sizeof(*iwe)); diff --git a/drivers/staging/rtl8712/rtl871x_mp_ioctl.c b/drivers/staging/rtl8712/rtl871x_mp_ioctl.c index aa8f8500cbb2..29b85330815f 100644 --- a/drivers/staging/rtl8712/rtl871x_mp_ioctl.c +++ b/drivers/staging/rtl8712/rtl871x_mp_ioctl.c @@ -231,8 +231,7 @@ end_of_mp_stop_test: return _SUCCESS; } -uint oid_rt_pro_set_data_rate_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_data_rate_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -283,17 +282,15 @@ uint oid_rt_pro_stop_test_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); - uint status = RNDIS_STATUS_SUCCESS; if (poid_par_priv->type_of_oid != SET_OID) return RNDIS_STATUS_NOT_ACCEPTED; if (mp_stop_test(Adapter) == _FAIL) - status = RNDIS_STATUS_NOT_ACCEPTED; - return status; + return RNDIS_STATUS_NOT_ACCEPTED; + return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_channel_direct_call_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_channel_direct_call_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -328,8 +325,7 @@ uint oid_rt_pro_set_antenna_bb_hdl(struct oid_par_priv *poid_par_priv) return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_tx_power_control_hdl( - struct oid_par_priv *poid_par_priv) +uint oid_rt_pro_set_tx_power_control_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -347,71 +343,61 @@ uint oid_rt_pro_set_tx_power_control_hdl( return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_query_tx_packet_sent_hdl( - struct oid_par_priv *poid_par_priv) +uint oid_rt_pro_query_tx_packet_sent_hdl(struct oid_par_priv *poid_par_priv) { - uint status = RNDIS_STATUS_SUCCESS; struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); - if (poid_par_priv->type_of_oid != QUERY_OID) { - status = RNDIS_STATUS_NOT_ACCEPTED; - return status; - } + if (poid_par_priv->type_of_oid != QUERY_OID) + return RNDIS_STATUS_NOT_ACCEPTED; + if (poid_par_priv->information_buf_len == sizeof(u32)) { *(u32 *)poid_par_priv->information_buf = Adapter->mppriv.tx_pktcount; *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; } else { - status = RNDIS_STATUS_INVALID_LENGTH; + return RNDIS_STATUS_INVALID_LENGTH; } - return status; + return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_query_rx_packet_received_hdl( - struct oid_par_priv *poid_par_priv) +uint oid_rt_pro_query_rx_packet_received_hdl(struct oid_par_priv *poid_par_priv) { - uint status = RNDIS_STATUS_SUCCESS; struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); - if (poid_par_priv->type_of_oid != QUERY_OID) { - status = RNDIS_STATUS_NOT_ACCEPTED; - return status; - } + if (poid_par_priv->type_of_oid != QUERY_OID) + return RNDIS_STATUS_NOT_ACCEPTED; + if (poid_par_priv->information_buf_len == sizeof(u32)) { *(u32 *)poid_par_priv->information_buf = Adapter->mppriv.rx_pktcount; *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; } else { - status = RNDIS_STATUS_INVALID_LENGTH; + return RNDIS_STATUS_INVALID_LENGTH; } - return status; + return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_query_rx_packet_crc32_error_hdl( - struct oid_par_priv *poid_par_priv) +uint oid_rt_pro_query_rx_packet_crc32_error_hdl(struct oid_par_priv *poid_par_priv) { - uint status = RNDIS_STATUS_SUCCESS; struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); - if (poid_par_priv->type_of_oid != QUERY_OID) { - status = RNDIS_STATUS_NOT_ACCEPTED; - return status; - } + if (poid_par_priv->type_of_oid != QUERY_OID) + return RNDIS_STATUS_NOT_ACCEPTED; + if (poid_par_priv->information_buf_len == sizeof(u32)) { *(u32 *)poid_par_priv->information_buf = Adapter->mppriv.rx_crcerrpktcount; *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len; } else { - status = RNDIS_STATUS_INVALID_LENGTH; + return RNDIS_STATUS_INVALID_LENGTH; } - return status; + return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_reset_tx_packet_sent_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_reset_tx_packet_sent_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -422,10 +408,8 @@ uint oid_rt_pro_reset_tx_packet_sent_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_reset_rx_packet_received_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_reset_rx_packet_received_hdl(struct oid_par_priv *poid_par_priv) { - uint status = RNDIS_STATUS_SUCCESS; struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -435,13 +419,12 @@ uint oid_rt_pro_reset_rx_packet_received_hdl(struct oid_par_priv Adapter->mppriv.rx_pktcount = 0; Adapter->mppriv.rx_crcerrpktcount = 0; } else { - status = RNDIS_STATUS_INVALID_LENGTH; + return RNDIS_STATUS_INVALID_LENGTH; } - return status; + return RNDIS_STATUS_SUCCESS; } -uint oid_rt_reset_phy_rx_packet_count_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_reset_phy_rx_packet_count_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -452,8 +435,7 @@ uint oid_rt_reset_phy_rx_packet_count_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_get_phy_rx_packet_received_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_get_phy_rx_packet_received_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -468,8 +450,7 @@ uint oid_rt_get_phy_rx_packet_received_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_get_phy_rx_packet_crc32_error_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_get_phy_rx_packet_crc32_error_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -484,8 +465,7 @@ uint oid_rt_get_phy_rx_packet_crc32_error_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -497,8 +477,7 @@ uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_continuous_tx_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_continuous_tx_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -511,8 +490,7 @@ uint oid_rt_pro_set_continuous_tx_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_single_carrier_tx_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_single_carrier_tx_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -525,8 +503,7 @@ uint oid_rt_pro_set_single_carrier_tx_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_carrier_suppression_tx_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_carrier_suppression_tx_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -539,8 +516,7 @@ uint oid_rt_pro_set_carrier_suppression_tx_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_set_single_tone_tx_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_set_single_tone_tx_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -553,8 +529,7 @@ uint oid_rt_pro_set_single_tone_tx_hdl(struct oid_par_priv return RNDIS_STATUS_SUCCESS; } -uint oid_rt_pro_read_register_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_pro_read_register_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -735,8 +710,7 @@ uint oid_rt_pro_write_efuse_hdl(struct oid_par_priv *poid_par_priv) } /*----------------------------------------------------------------------*/ -uint oid_rt_get_efuse_current_size_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_get_efuse_current_size_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); @@ -829,8 +803,7 @@ uint oid_rt_set_bandwidth_hdl(struct oid_par_priv *poid_par_priv) return RNDIS_STATUS_SUCCESS; } -uint oid_rt_set_rx_packet_type_hdl(struct oid_par_priv - *poid_par_priv) +uint oid_rt_set_rx_packet_type_hdl(struct oid_par_priv *poid_par_priv) { struct _adapter *Adapter = (struct _adapter *) (poid_par_priv->adapter_context); diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index 6d18d23acdc0..7117d16a30f9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -216,8 +216,9 @@ void expire_timeout_chk(struct adapter *padapter) /* check auth_queue */ #ifdef DBG_EXPIRATION_CHK if (phead != plist) { - DBG_871X(FUNC_NDEV_FMT" auth_list, cnt:%u\n" - , FUNC_NDEV_ARG(padapter->pnetdev), pstapriv->auth_list_cnt); + DBG_871X(FUNC_NDEV_FMT " auth_list, cnt:%u\n", + FUNC_NDEV_ARG(padapter->pnetdev), + pstapriv->auth_list_cnt); } #endif while (phead != plist) { @@ -1446,14 +1447,14 @@ u8 rtw_ap_set_pairwise_key(struct adapter *padapter, struct sta_info *psta) u8 res = _SUCCESS; ph2c = rtw_zmalloc(sizeof(struct cmd_obj)); - if (ph2c == NULL) { + if (!ph2c) { res = _FAIL; goto exit; } psetstakey_para = rtw_zmalloc(sizeof(struct set_stakey_parm)); if (psetstakey_para == NULL) { - kfree((u8 *)ph2c); + kfree(ph2c); res = _FAIL; goto exit; } @@ -1496,7 +1497,7 @@ static int rtw_ap_set_key( } psetkeyparm = rtw_zmalloc(sizeof(struct setkey_parm)); if (psetkeyparm == NULL) { - kfree((unsigned char *)pcmd); + kfree(pcmd); res = _FAIL; goto exit; } diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 8d93c2f26890..e6fea9665dff 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -372,13 +372,13 @@ void rtw_free_cmd_obj(struct cmd_obj *pcmd) if ((pcmd->cmdcode != _JoinBss_CMD_) && (pcmd->cmdcode != _CreateBss_CMD_)) { /* free parmbuf in cmd_obj */ - kfree((unsigned char *)pcmd->parmbuf); + kfree(pcmd->parmbuf); } if (pcmd->rsp != NULL) { if (pcmd->rspsz != 0) { /* free rsp in cmd_obj */ - kfree((unsigned char *)pcmd->rsp); + kfree(pcmd->rsp); } } diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 34adf5789c98..3030ae5b6b6d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -19,7 +19,7 @@ int rtw_init_mlme_priv(struct adapter *padapter) int i; u8 *pbuf; struct wlan_network *pnetwork; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; int res = _SUCCESS; pmlmepriv->nic_hdl = (u8 *)padapter; @@ -40,7 +40,7 @@ int rtw_init_mlme_priv(struct adapter *padapter) pbuf = vzalloc(array_size(MAX_BSS_CNT, sizeof(struct wlan_network))); - if (pbuf == NULL) { + if (!pbuf) { res = _FAIL; goto exit; } @@ -112,9 +112,8 @@ void _rtw_free_mlme_priv(struct mlme_priv *pmlmepriv) { if (pmlmepriv) { rtw_free_mlme_priv_ie_data(pmlmepriv); - if (pmlmepriv->free_bss_buf) { + if (pmlmepriv->free_bss_buf) vfree(pmlmepriv->free_bss_buf); - } } } @@ -185,10 +184,10 @@ void _rtw_free_network(struct mlme_priv *pmlmepriv, struct wlan_network *pnetwor /* _irqL irqL; */ struct __queue *free_queue = &(pmlmepriv->free_bss_pool); - if (pnetwork == NULL) + if (!pnetwork) return; - if (pnetwork->fixed == true) + if (pnetwork->fixed) return; if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) || @@ -209,7 +208,6 @@ void _rtw_free_network(struct mlme_priv *pmlmepriv, struct wlan_network *pnetwor pmlmepriv->num_of_scanned--; - /* DBG_871X("_rtw_free_network:SSID =%s\n", pnetwork->network.Ssid.Ssid); */ spin_unlock_bh(&free_queue->lock); @@ -220,10 +218,10 @@ void _rtw_free_network_nolock(struct mlme_priv *pmlmepriv, struct wlan_network * struct __queue *free_queue = &(pmlmepriv->free_bss_pool); - if (pnetwork == NULL) + if (!pnetwork) return; - if (pnetwork->fixed == true) + if (pnetwork->fixed) return; /* spin_lock_irqsave(&free_queue->lock, irqL); */ @@ -301,12 +299,8 @@ void rtw_free_network_queue(struct adapter *padapter, u8 isfreeall) spin_unlock_bh(&scanned_queue->lock); } - - - sint rtw_if_up(struct adapter *padapter) { - sint res; if (padapter->bDriverStopped || padapter->bSurpriseRemoved || @@ -318,7 +312,6 @@ sint rtw_if_up(struct adapter *padapter) return res; } - void rtw_generate_random_ibss(u8 *pibss) { unsigned long curtime = jiffies; @@ -329,7 +322,6 @@ void rtw_generate_random_ibss(u8 *pibss) pibss[3] = (u8)(curtime & 0xff) ;/* p[0]; */ pibss[4] = (u8)((curtime>>8) & 0xff) ;/* p[1]; */ pibss[5] = (u8)((curtime>>16) & 0xff) ;/* p[2]; */ - return; } u8 *rtw_get_capability_from_ie(u8 *ie) @@ -337,7 +329,6 @@ u8 *rtw_get_capability_from_ie(u8 *ie) return ie + 8 + 2; } - u16 rtw_get_capability(struct wlan_bssid_ex *bss) { __le16 val; @@ -425,7 +416,6 @@ int is_same_network(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst, u8 fea memcpy((u8 *)&tmps, rtw_get_capability_from_ie(src->IEs), 2); memcpy((u8 *)&tmpd, rtw_get_capability_from_ie(dst->IEs), 2); - s_cap = le16_to_cpu(tmps); d_cap = le16_to_cpu(tmpd); @@ -467,7 +457,6 @@ struct wlan_network *rtw_get_oldest_wlan_network(struct __queue *scanned_queue) { struct list_head *plist, *phead; - struct wlan_network *pwlan = NULL; struct wlan_network *oldest = NULL; @@ -482,7 +471,7 @@ struct wlan_network *rtw_get_oldest_wlan_network(struct __queue *scanned_queue) pwlan = LIST_CONTAINOR(plist, struct wlan_network, list); - if (pwlan->fixed != true) { + if (!pwlan->fixed) { if (oldest == NULL || time_after(oldest->last_scanned, pwlan->last_scanned)) oldest = pwlan; } @@ -579,12 +568,8 @@ static void update_current_network(struct adapter *adapter, struct wlan_bssid_ex } } - /* - Caller must hold pmlmepriv->lock first. - - */ void rtw_update_scanned_network(struct adapter *adapter, struct wlan_bssid_ex *target) { @@ -625,7 +610,6 @@ void rtw_update_scanned_network(struct adapter *adapter, struct wlan_bssid_ex *t } - /* If we didn't find a match, then get a new network slot to initialize * with this beacon's information */ /* if (phead == plist) { */ @@ -634,7 +618,7 @@ void rtw_update_scanned_network(struct adapter *adapter, struct wlan_bssid_ex *t /* If there are no more slots, expire the oldest */ /* list_del_init(&oldest->list); */ pnetwork = oldest; - if (pnetwork == NULL) { + if (!pnetwork) { RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("\n\n\nsomething wrong here\n\n\n")); goto exit; } @@ -655,7 +639,7 @@ void rtw_update_scanned_network(struct adapter *adapter, struct wlan_bssid_ex *t pnetwork = rtw_alloc_network(pmlmepriv); /* will update scan_time */ - if (pnetwork == NULL) { + if (!pnetwork) { RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("\n\n\nsomething wrong here\n\n\n")); goto exit; } @@ -739,7 +723,7 @@ int rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwor privacy = pnetwork->network.Privacy; if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) { - if (rtw_get_wps_ie(pnetwork->network.IEs+_FIXED_IE_LENGTH_, pnetwork->network.IELength-_FIXED_IE_LENGTH_, NULL, &wps_ielen) != NULL) + if (rtw_get_wps_ie(pnetwork->network.IEs+_FIXED_IE_LENGTH_, pnetwork->network.IELength-_FIXED_IE_LENGTH_, NULL, &wps_ielen)) return true; else return false; @@ -754,15 +738,13 @@ int rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwor if (psecuritypriv->ndisauthtype == Ndis802_11AuthModeWPA2PSK) { p = rtw_get_ie(pnetwork->network.IEs + _BEACON_IE_OFFSET_, _RSN_IE_2_, &ie_len, (pnetwork->network.IELength - _BEACON_IE_OFFSET_)); - if (p && ie_len > 0) { + if (p && ie_len > 0) bselected = true; - } else { + else bselected = false; - } } } - if ((desired_encmode != Ndis802_11EncryptionDisabled) && (privacy == 0)) { DBG_871X("desired_encmode: %d, privacy: %d\n", desired_encmode, privacy); bselected = false; @@ -773,7 +755,6 @@ int rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwor bselected = false; } - return bselected; } @@ -783,7 +764,6 @@ void rtw_atimdone_event_callback(struct adapter *adapter, u8 *pbuf) RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("receive atimdone_event\n")); } - void rtw_survey_event_callback(struct adapter *adapter, u8 *pbuf) { u32 len; @@ -800,7 +780,6 @@ void rtw_survey_event_callback(struct adapter *adapter, u8 *pbuf) return; } - spin_lock_bh(&pmlmepriv->lock); /* update IBSS_network 's timestamp */ @@ -823,21 +802,16 @@ void rtw_survey_event_callback(struct adapter *adapter, u8 *pbuf) /* lock pmlmepriv->lock when you accessing network_q */ if ((check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) == false) { - if (pnetwork->Ssid.Ssid[0] == 0) { + if (pnetwork->Ssid.Ssid[0] == 0) pnetwork->Ssid.SsidLength = 0; - } rtw_add_network(adapter, pnetwork); } exit: spin_unlock_bh(&pmlmepriv->lock); - - return; } - - void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf) { u8 timer_cancelled = false; @@ -868,12 +842,11 @@ void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf) if (timer_cancelled) _cancel_timer(&pmlmepriv->scan_to_timer, &timer_cancelled); - spin_lock_bh(&pmlmepriv->lock); rtw_set_signal_stat_timer(&adapter->recvpriv); - if (pmlmepriv->to_join == true) { + if (pmlmepriv->to_join) { if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)) { if (check_fwstate(pmlmepriv, _FW_LINKED) == false) { set_fwstate(pmlmepriv, _FW_UNDER_LINKING); @@ -896,9 +869,8 @@ void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf) pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE; - if (rtw_createbss_cmd(adapter) != _SUCCESS) { - RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Error =>rtw_createbss_cmd status FAIL\n")); - } + if (rtw_createbss_cmd(adapter) != _SUCCESS) + RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Error =>rtw_createbss_cmd status FAIL\n")); pmlmepriv->to_join = false; } @@ -1009,7 +981,6 @@ static void find_network(struct adapter *adapter) else RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("rtw_free_assoc_resources : pwlan == NULL\n\n")); - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) && (adapter->stapriv.asoc_sta_count == 1)) rtw_free_network_nolock(adapter, pwlan); @@ -1169,9 +1140,8 @@ static struct sta_info *rtw_joinbss_update_stainfo(struct adapter *padapter, str struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; psta = rtw_get_stainfo(pstapriv, pnetwork->network.MacAddress); - if (psta == NULL) { + if (!psta) psta = rtw_alloc_stainfo(pstapriv, pnetwork->network.MacAddress); - } if (psta) { /* update ptarget_sta */ @@ -1189,7 +1159,6 @@ static struct sta_info *rtw_joinbss_update_stainfo(struct adapter *padapter, str psta->wireless_mode = pmlmeext->cur_wireless_mode; psta->raid = networktype_to_raid_ex(padapter, psta); - /* sta mode */ rtw_hal_set_odm_var(padapter, HAL_ODM_STA_INFO, psta, true); @@ -1221,7 +1190,6 @@ static struct sta_info *rtw_joinbss_update_stainfo(struct adapter *padapter, str padapter->securitypriv.wps_ie_len = 0; } - /* for A-MPDU Rx reordering buffer control for bmc_sta & sta_info */ /* if A-MPDU Rx is enabled, resetting rx_ordering_ctrl wstart_b(indicate_seq) to default value = 0xffff */ /* todo: check if AP can send A-MPDU packets */ @@ -1238,7 +1206,6 @@ static struct sta_info *rtw_joinbss_update_stainfo(struct adapter *padapter, str preorder_ctrl->wsize_b = 64;/* max_ampdu_sz;ex. 32(kbytes) -> wsize_b =32 */ } - bmc_sta = rtw_get_bcmc_stainfo(padapter); if (bmc_sta) { for (i = 0; i < 16 ; i++) { @@ -1272,7 +1239,6 @@ static void rtw_joinbss_update_network(struct adapter *padapter, struct wlan_net RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("\nfw_state:%x, BSSID:"MAC_FMT"\n" , get_fwstate(pmlmepriv), MAC_ARG(pnetwork->network.MacAddress))); - /* why not use ptarget_wlan?? */ memcpy(&cur_network->network, &pnetwork->network, pnetwork->network.Length); /* some IEs in pnetwork is wrong, so we should use ptarget_wlan IEs */ @@ -1281,7 +1247,6 @@ static void rtw_joinbss_update_network(struct adapter *padapter, struct wlan_net cur_network->aid = pnetwork->join_res; - rtw_set_signal_stat_timer(&padapter->recvpriv); padapter->recvpriv.signal_strength = ptarget_wlan->network.PhyInfo.SignalStrength; @@ -1349,12 +1314,10 @@ void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf) rtw_get_encrypt_decrypt_from_registrypriv(adapter); - - if (pmlmepriv->assoc_ssid.SsidLength == 0) { + if (pmlmepriv->assoc_ssid.SsidLength == 0) RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("@@@@@ joinbss event call back for Any SSid\n")); - } else { + else RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("@@@@@ rtw_joinbss_event_callback for SSid:%s\n", pmlmepriv->assoc_ssid.Ssid)); - } the_same_macaddr = !memcmp(pnetwork->network.MacAddress, cur_network->network.MacAddress, ETH_ALEN); @@ -1377,7 +1340,7 @@ void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf) if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { /* s1. find ptarget_wlan */ if (check_fwstate(pmlmepriv, _FW_LINKED)) { - if (the_same_macaddr == true) { + if (the_same_macaddr) { ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.MacAddress); } else { pcur_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.MacAddress); @@ -1412,11 +1375,10 @@ void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf) goto ignore_joinbss_callback; } - /* s3. find ptarget_sta & update ptarget_sta after update cur_network only for station mode */ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { ptarget_sta = rtw_joinbss_update_stainfo(adapter, pnetwork); - if (ptarget_sta == NULL) { + if (!ptarget_sta) { RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Can't update stainfo when joinbss_event callback\n")); spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); goto ignore_joinbss_callback; @@ -1432,7 +1394,6 @@ void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf) RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("adhoc mode, fw_state:%x", get_fwstate(pmlmepriv))); } - /* s5. Cancel assoc_timer */ _cancel_timer(&pmlmepriv->assoc_timer, &timer_cancelled); @@ -1506,7 +1467,7 @@ void rtw_sta_media_status_rpt(struct adapter *adapter, struct sta_info *psta, u3 { u16 media_status_rpt; - if (psta == NULL) + if (!psta) return; media_status_rpt = (u16)((psta->mac_id<<8)|mstatus); /* MACID|OPMODE:1 connect */ @@ -1564,7 +1525,7 @@ void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf) /* for AD-HOC mode */ psta = rtw_get_stainfo(&adapter->stapriv, pstassoc->macaddr); - if (psta != NULL) { + if (psta) { /* the sta have been in sta_info_queue => do nothing */ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Error: rtw_stassoc_event_callback: sta has been in sta_hash_queue\n")); @@ -1573,7 +1534,7 @@ void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf) } psta = rtw_alloc_stainfo(&adapter->stapriv, pstassoc->macaddr); - if (psta == NULL) { + if (!psta) { RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Can't alloc sta_info when rtw_stassoc_event_callback\n")); return; } @@ -1591,7 +1552,6 @@ void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf) if (adapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) psta->dot118021XPrivacy = adapter->securitypriv.dot11PrivacyAlgrthm; - psta->ieee8021x_blocked = false; spin_lock_bh(&pmlmepriv->lock); @@ -1612,7 +1572,6 @@ void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf) spin_unlock_bh(&pmlmepriv->lock); - mlmeext_sta_add_event_callback(adapter, psta); } @@ -1648,7 +1607,6 @@ void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf) if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) return; - mlmeext_sta_del_event_callback(adapter); spin_lock_bh(&pmlmepriv->lock); @@ -1726,13 +1684,8 @@ void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf) _clr_fwstate_(pmlmepriv, WIFI_ADHOC_STATE); } - if (rtw_createbss_cmd(adapter) != _SUCCESS) { - + if (rtw_createbss_cmd(adapter) != _SUCCESS) RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("***Error =>stadel_event_callback: rtw_createbss_cmd status FAIL***\n ")); - - } - - } } @@ -1750,7 +1703,6 @@ void rtw_cpwm_event_callback(struct adapter *padapter, u8 *pbuf) cpwm_int_hdl(padapter, preportpwrstate); } - void rtw_wmm_event_callback(struct adapter *padapter, u8 *pbuf) { WMMOnAssocRsp(padapter); @@ -1840,8 +1792,6 @@ void rtw_mlme_reset_auto_scan_int(struct adapter *adapter) mlme->auto_scan_int_ms = mlme->roam_scan_int_ms; } else mlme->auto_scan_int_ms = 0; /* disabled */ - - return; } static void rtw_auto_scan_handler(struct adapter *padapter) @@ -1859,7 +1809,7 @@ static void rtw_auto_scan_handler(struct adapter *padapter) goto exit; } - if (pmlmepriv->LinkDetectInfo.bBusyTraffic == true) { + if (pmlmepriv->LinkDetectInfo.bBusyTraffic) { DBG_871X(FUNC_ADPT_FMT" exit BusyTraffic\n", FUNC_ADPT_ARG(padapter)); goto exit; } @@ -1879,20 +1829,20 @@ void rtw_dynamic_check_timer_handler(struct adapter *adapter) if (!adapter) return; - if (adapter->hw_init_completed == false) + if (!adapter->hw_init_completed) return; - if ((adapter->bDriverStopped == true) || (adapter->bSurpriseRemoved == true)) + if (adapter->bDriverStopped || adapter->bSurpriseRemoved) return; - if (adapter->net_closed == true) + if (adapter->net_closed) return; if (is_primary_adapter(adapter)) DBG_871X("IsBtDisabled =%d, IsBtControlLps =%d\n", hal_btcoex_IsBtDisabled(adapter), hal_btcoex_IsBtControlLps(adapter)); - if ((adapter_to_pwrctl(adapter)->bFwCurrentInPSMode == true) - && (hal_btcoex_IsBtControlLps(adapter) == false) + if ((adapter_to_pwrctl(adapter)->bFwCurrentInPSMode) + && !(hal_btcoex_IsBtControlLps(adapter)) ) { u8 bEnterPS; @@ -1907,16 +1857,14 @@ void rtw_dynamic_check_timer_handler(struct adapter *adapter) } } else { - if (is_primary_adapter(adapter)) { + if (is_primary_adapter(adapter)) rtw_dynamic_chk_wk_cmd(adapter); - } } /* auto site survey */ rtw_auto_scan_handler(adapter); } - inline bool rtw_is_scan_deny(struct adapter *adapter) { struct mlme_priv *mlmepriv = &adapter->mlmepriv; @@ -1994,26 +1942,24 @@ int rtw_select_roaming_candidate(struct mlme_priv *mlme) { int ret = _FAIL; struct list_head *phead; - struct adapter *adapter; struct __queue *queue = &(mlme->scanned_queue); struct wlan_network *pnetwork = NULL; struct wlan_network *candidate = NULL; - if (mlme->cur_network_scanned == NULL) { + if (!mlme->cur_network_scanned) { rtw_warn_on(1); return ret; } spin_lock_bh(&(mlme->scanned_queue.lock)); phead = get_list_head(queue); - adapter = (struct adapter *)mlme->nic_hdl; mlme->pscanned = get_next(phead); while (phead != mlme->pscanned) { pnetwork = LIST_CONTAINOR(mlme->pscanned, struct wlan_network, list); - if (pnetwork == NULL) { + if (!pnetwork) { RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s return _FAIL:(pnetwork == NULL)\n", __func__)); ret = _FAIL; goto exit; @@ -2031,7 +1977,7 @@ int rtw_select_roaming_candidate(struct mlme_priv *mlme) } - if (candidate == NULL) { + if (!candidate) { DBG_871X("%s: return _FAIL(candidate == NULL)\n", __func__); ret = _FAIL; goto exit; @@ -2064,9 +2010,8 @@ static int rtw_check_join_candidate(struct mlme_priv *mlme int updated = false; struct adapter *adapter = container_of(mlme, struct adapter, mlmepriv); - /* check bssid, if needed */ - if (mlme->assoc_by_bssid == true) { + if (mlme->assoc_by_bssid) { if (memcmp(competitor->network.MacAddress, mlme->assoc_bssid, ETH_ALEN)) goto exit; } @@ -2115,12 +2060,8 @@ exit: /* Calling context: The caller of the sub-routine will be in critical section... - The caller must hold the following spinlock - pmlmepriv->lock - - */ int rtw_select_and_join_from_scanned_queue(struct mlme_priv *pmlmepriv) @@ -2148,7 +2089,7 @@ int rtw_select_and_join_from_scanned_queue(struct mlme_priv *pmlmepriv) while (phead != pmlmepriv->pscanned) { pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned, struct wlan_network, list); - if (pnetwork == NULL) { + if (!pnetwork) { RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s return _FAIL:(pnetwork == NULL)\n", __func__)); ret = _FAIL; goto exit; @@ -2166,7 +2107,7 @@ int rtw_select_and_join_from_scanned_queue(struct mlme_priv *pmlmepriv) } - if (candidate == NULL) { + if (!candidate) { DBG_871X("%s: return _FAIL(candidate == NULL)\n", __func__); #ifdef CONFIG_WOWLAN _clr_fwstate_(pmlmepriv, _FW_LINKED|_FW_UNDER_LINKING); @@ -2207,13 +2148,13 @@ sint rtw_set_auth(struct adapter *adapter, struct security_priv *psecuritypriv) sint res = _SUCCESS; pcmd = rtw_zmalloc(sizeof(struct cmd_obj)); - if (pcmd == NULL) { + if (!pcmd) { res = _FAIL; /* try again */ goto exit; } psetauthparm = rtw_zmalloc(sizeof(struct setauth_parm)); - if (psetauthparm == NULL) { + if (!psetauthparm) { kfree((unsigned char *)pcmd); res = _FAIL; goto exit; @@ -2227,7 +2168,6 @@ sint rtw_set_auth(struct adapter *adapter, struct security_priv *psecuritypriv) pcmd->rsp = NULL; pcmd->rspsz = 0; - INIT_LIST_HEAD(&pcmd->list); RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("after enqueue set_auth_cmd, auth_mode =%x\n", psecuritypriv->dot11AuthAlgrthm)); @@ -2247,7 +2187,7 @@ sint rtw_set_key(struct adapter *adapter, struct security_priv *psecuritypriv, s sint res = _SUCCESS; psetkeyparm = rtw_zmalloc(sizeof(struct setkey_parm)); - if (psetkeyparm == NULL) { + if (!psetkeyparm) { res = _FAIL; goto exit; } @@ -2291,14 +2231,13 @@ sint rtw_set_key(struct adapter *adapter, struct security_priv *psecuritypriv, s default: RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("\n rtw_set_key:psecuritypriv->dot11PrivacyAlgrthm = %x (must be 1 or 2 or 4 or 5)\n", psecuritypriv->dot11PrivacyAlgrthm)); res = _FAIL; - kfree((unsigned char *)psetkeyparm); + kfree(psetkeyparm); goto exit; } - if (enqueue) { pcmd = rtw_zmalloc(sizeof(struct cmd_obj)); - if (pcmd == NULL) { + if (!pcmd) { kfree((unsigned char *)psetkeyparm); res = _FAIL; /* try again */ goto exit; @@ -2315,7 +2254,7 @@ sint rtw_set_key(struct adapter *adapter, struct security_priv *psecuritypriv, s res = rtw_enqueue_cmd(pcmdpriv, pcmd); } else { setkey_hdl(adapter, (u8 *)psetkeyparm); - kfree((u8 *) psetkeyparm); + kfree(psetkeyparm); } exit: return res; @@ -2350,7 +2289,6 @@ int rtw_restruct_wmm_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie, uint in_ } - /* */ /* Ported from 8185: IsInPreAuthKeyList(). (Renamed from SecIsInPreAuthKeyList(), 2006-10-13.) */ /* Added by Annie, 2006-05-07. */ @@ -2679,7 +2617,7 @@ unsigned int rtw_restructure_ht_ie(struct adapter *padapter, u8 *in_ie, u8 *out_ ht_capie.cap_info |= cpu_to_le16(IEEE80211_HT_CAP_SGI_20); /* Get HT BW */ - if (in_ie == NULL) { + if (!in_ie) { /* TDLS: TODO 20/40 issue */ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { operation_bw = padapter->mlmeextpriv.cur_bwmode; @@ -2794,7 +2732,7 @@ unsigned int rtw_restructure_ht_ie(struct adapter *padapter, u8 *in_ie, u8 *out_ phtpriv->ht_option = true; - if (in_ie != NULL) { + if (in_ie) { p = rtw_get_ie(in_ie, _HT_ADD_INFO_IE_, &ielen, in_len); if (p && (ielen == sizeof(struct ieee80211_ht_addt_info))) { out_len = *pout_len; @@ -2824,7 +2762,6 @@ void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len, u8 channe struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); u8 cbw40_enable = 0; - if (!phtpriv->ht_option) return; @@ -2834,7 +2771,7 @@ void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len, u8 channe DBG_871X("+rtw_update_ht_cap()\n"); /* maybe needs check if ap supports rx ampdu. */ - if ((phtpriv->ampdu_enable == false) && (pregistrypriv->ampdu_enable == 1)) { + if (!(phtpriv->ampdu_enable) && pregistrypriv->ampdu_enable == 1) { if (pregistrypriv->wifi_spec == 1) { /* remove this part because testbed AP should disable RX AMPDU */ /* phtpriv->ampdu_enable = false; */ @@ -2847,7 +2784,6 @@ void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len, u8 channe /* phtpriv->ampdu_enable = true; */ } - /* check Max Rx A-MPDU Size */ len = 0; p = rtw_get_ie(pie+sizeof(struct ndis_802_11_fix_ie), _HT_CAPABILITY_IE_, &len, ie_len-sizeof(struct ndis_802_11_fix_ie)); @@ -2861,7 +2797,6 @@ void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len, u8 channe } - len = 0; p = rtw_get_ie(pie+sizeof(struct ndis_802_11_fix_ie), _HT_ADD_INFO_IE_, &len, ie_len-sizeof(struct ndis_802_11_fix_ie)); if (p && len > 0) { @@ -2961,7 +2896,7 @@ void rtw_issue_addbareq_cmd(struct adapter *padapter, struct xmit_frame *pxmitfr return; } - if (psta == NULL) { + if (!psta) { DBG_871X("%s, psta ==NUL\n", __func__); return; } @@ -2971,10 +2906,9 @@ void rtw_issue_addbareq_cmd(struct adapter *padapter, struct xmit_frame *pxmitfr return; } - phtpriv = &psta->htpriv; - if ((phtpriv->ht_option == true) && (phtpriv->ampdu_enable == true)) { + if (phtpriv->ht_option && phtpriv->ampdu_enable) { issued = (phtpriv->agg_enable_bitmap>>priority)&0x1; issued |= (phtpriv->candidate_tid_bitmap>>priority)&0x1; @@ -2994,10 +2928,8 @@ void rtw_append_exented_cap(struct adapter *padapter, u8 *out_ie, uint *pout_len u8 cap_content[8] = {0}; u8 *pframe; - - if (phtpriv->bss_coexist) { + if (phtpriv->bss_coexist) SET_EXT_CAPABILITY_ELE_BSS_COEXIST(cap_content, 1); - } pframe = rtw_set_ie(out_ie + *pout_len, EID_EXTCapability, 8, cap_content, pout_len); } diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 2128886c9924..5e687f6d2c3e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -344,7 +344,7 @@ static void init_channel_list(struct adapter *padapter, RT_CHANNEL_INFO *channel struct p2p_channels *channel_list) { - struct p2p_oper_class_map op_class[] = { + static const struct p2p_oper_class_map op_class[] = { { IEEE80211G, 81, 1, 13, 1, BW20 }, { IEEE80211G, 82, 14, 14, 1, BW20 }, { IEEE80211A, 115, 36, 48, 4, BW20 }, @@ -363,7 +363,7 @@ static void init_channel_list(struct adapter *padapter, RT_CHANNEL_INFO *channel for (op = 0; op_class[op].op_class; op++) { u8 ch; - struct p2p_oper_class_map *o = &op_class[op]; + const struct p2p_oper_class_map *o = &op_class[op]; struct p2p_reg_class *reg = NULL; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { @@ -2922,7 +2922,8 @@ int issue_probereq_ex(struct adapter *padapter, struct ndis_802_11_ssid *pssid, int i = 0; do { - ret = _issue_probereq(padapter, pssid, da, ch, append_wps, wait_ms > 0?true:false); + ret = _issue_probereq(padapter, pssid, da, ch, append_wps, + wait_ms > 0); i++; @@ -3086,8 +3087,6 @@ void issue_auth(struct adapter *padapter, struct sta_info *psta, unsigned short rtw_wep_encrypt(padapter, (u8 *)pmgntframe); DBG_871X("%s\n", __func__); dump_mgntframe(padapter, pmgntframe); - - return; } @@ -3405,8 +3404,6 @@ exit: rtw_buf_update(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len, (u8 *)pwlanhdr, pattrib->pktlen); else rtw_buf_free(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len); - - return; } /* when wait_ack is ture, this function shoule be called at process context */ @@ -3513,7 +3510,7 @@ int issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int pow } do { - ret = _issue_nulldata(padapter, da, power_mode, wait_ms > 0?true:false); + ret = _issue_nulldata(padapter, da, power_mode, wait_ms > 0); i++; @@ -3661,7 +3658,7 @@ int issue_qos_nulldata(struct adapter *padapter, unsigned char *da, u16 tid, int da = get_my_bssid(&(pmlmeinfo->network)); do { - ret = _issue_qos_nulldata(padapter, da, tid, wait_ms > 0?true:false); + ret = _issue_qos_nulldata(padapter, da, tid, wait_ms > 0); i++; @@ -3769,7 +3766,7 @@ int issue_deauth_ex(struct adapter *padapter, u8 *da, unsigned short reason, int int i = 0; do { - ret = _issue_deauth(padapter, da, reason, wait_ms > 0?true:false); + ret = _issue_deauth(padapter, da, reason, wait_ms > 0); i++; @@ -5260,8 +5257,6 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsi DBG_871X("report_del_sta_event: delete STA, mac_id =%d\n", mac_id); rtw_enqueue_cmd(pcmdpriv, pcmd_obj); - - return; } void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int cam_idx) @@ -5306,8 +5301,6 @@ void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int DBG_871X("report_add_sta_event: add STA\n"); rtw_enqueue_cmd(pcmdpriv, pcmd_obj); - - return; } /**************************************************************************** @@ -5869,8 +5862,6 @@ void link_timer_hdl(struct timer_list *t) issue_assocreq(padapter); set_link_timer(pmlmeext, REASSOC_TO); } - - return; } void addba_timer_hdl(struct timer_list *t) diff --git a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c index 4075de07e0a9..30137f0bd984 100644 --- a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c +++ b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c @@ -190,7 +190,6 @@ void rtw_ps_processor(struct adapter *padapter) } exit: pwrpriv->ps_processing = false; - return; } static void pwr_state_check_handler(struct timer_list *t) diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index 687ff3c6f09f..7fa8c84cf5f4 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -1400,10 +1400,8 @@ static sint validate_80211w_mgmt(struct adapter *adapter, union recv_frame *prec struct mlme_priv *pmlmepriv = &adapter->mlmepriv; struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; u8 *ptr = precv_frame->u.hdr.rx_data; - u8 type; u8 subtype; - type = GetFrameType(ptr); subtype = GetFrameSubType(ptr); /* bit(7)~bit(2) */ /* only support station mode */ @@ -1412,9 +1410,8 @@ static sint validate_80211w_mgmt(struct adapter *adapter, union recv_frame *prec /* unicast management frame decrypt */ if (pattrib->privacy && !(IS_MCAST(GetAddr1Ptr(ptr))) && (subtype == WIFI_DEAUTH || subtype == WIFI_DISASSOC || subtype == WIFI_ACTION)) { - u8 *ppp, *mgmt_DATA; + u8 *mgmt_DATA; u32 data_len = 0; - ppp = GetAddr2Ptr(ptr); pattrib->bdecrypted = 0; pattrib->encrypt = _AES_; @@ -1709,7 +1706,7 @@ static union recv_frame *recvframe_defrag(struct adapter *adapter, struct __queue *defrag_q) { struct list_head *plist, *phead; - u8 *data, wlanhdr_offset; + u8 wlanhdr_offset; u8 curfragnum; struct recv_frame_hdr *pfhdr, *pnfhdr; union recv_frame *prframe, *pnextrframe; @@ -1739,8 +1736,6 @@ static union recv_frame *recvframe_defrag(struct adapter *adapter, plist = get_next(plist); - data = get_recvframe_data(prframe); - while (phead != plist) { pnextrframe = (union recv_frame *)plist; pnfhdr = &pnextrframe->u.hdr; diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index 57cfe06d7d73..ed1d85499858 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -309,7 +309,6 @@ void rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe) WEP_SW_DEC_CNT_INC(psecuritypriv, prxattrib->ra); } - return; } /* 3 =====TKIP related ===== */ @@ -657,7 +656,6 @@ u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe) u8 hw_hdr_offset = 0; struct arc4context mycontext; sint curfragnum, length; - u32 prwskeylen; u8 *pframe, *payload, *iv, *prwskey; union pn48 dot11txpn; @@ -704,8 +702,6 @@ u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe) /* prwskey =&stainfo->dot118021x_UncstKey.skey[0]; */ prwskey = pattrib->dot118021x_UncstKey.skey; - prwskeylen = 16; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) { iv = pframe+pattrib->hdrlen; payload = pframe+pattrib->iv_len+pattrib->hdrlen; @@ -765,7 +761,6 @@ u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe) u8 crc[4]; struct arc4context mycontext; sint length; - u32 prwskeylen; u8 *pframe, *payload, *iv, *prwskey; union pn48 dot11txpn; @@ -820,10 +815,8 @@ u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe) /* DBG_871X("rx bc/mc packets, to perform sw rtw_tkip_decrypt\n"); */ /* prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey; */ prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey; - prwskeylen = 16; } else { prwskey = &stainfo->dot118021x_UncstKey.skey[0]; - prwskeylen = 16; } iv = pframe+prxattrib->hdrlen; @@ -1525,7 +1518,6 @@ u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe) /* Intermediate Buffers */ sint curfragnum, length; - u32 prwskeylen; u8 *pframe, *prwskey; /* *payload,*iv */ u8 hw_hdr_offset = 0; /* struct sta_info *stainfo = NULL; */ @@ -1552,8 +1544,6 @@ u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe) /* prwskey =&stainfo->dot118021x_UncstKey.skey[0]; */ prwskey = pattrib->dot118021x_UncstKey.skey; - prwskeylen = 16; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) { if ((curfragnum+1) == pattrib->nr_frags) { /* 4 the last fragment */ length = pattrib->last_txcmdsz-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len; diff --git a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c index bdc52d8d5625..09d2ca30d653 100644 --- a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c +++ b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c @@ -187,7 +187,6 @@ u32 _rtw_free_sta_priv(struct sta_priv *pstapriv) /* struct sta_info *rtw_alloc_stainfo(_queue *pfree_sta_queue, unsigned char *hwaddr) */ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) { - uint tmp_aid; s32 index; struct list_head *phash_list; struct sta_info *psta; @@ -211,8 +210,6 @@ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) /* spin_unlock_bh(&(pfree_sta_queue->lock)); */ - tmp_aid = psta->aid; - _rtw_init_stainfo(psta); psta->padapter = pstapriv->padapter; diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index ea3ea2a6b314..9590e6f351c1 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -606,19 +606,6 @@ inline void clear_cam_entry(struct adapter *adapter, u8 id) clear_cam_cache(adapter, id); } -inline void write_cam_from_cache(struct adapter *adapter, u8 id) -{ - struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); - struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl; - struct cam_entry_cache cache; - - spin_lock_bh(&cam_ctl->lock); - memcpy(&cache, &dvobj->cam_cache[id], sizeof(struct cam_entry_cache)); - spin_unlock_bh(&cam_ctl->lock); - - _write_cam(adapter, id, cache.ctrl, cache.mac, cache.key); -} - void write_cam_cache(struct adapter *adapter, u8 id, u16 ctrl, u8 *mac, u8 *key) { struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); @@ -1170,8 +1157,6 @@ void HT_info_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) pmlmeinfo->HT_info_enable = 1; memcpy(&(pmlmeinfo->HT_info), pIE->data, pIE->Length); - - return; } void HTOnAssocRsp(struct adapter *padapter) @@ -1481,11 +1466,11 @@ int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len) } } - kfree((u8 *)bssid); + kfree(bssid); return _SUCCESS; _mismatch: - kfree((u8 *)bssid); + kfree(bssid); if (pmlmepriv->NumOfBcnInfoChkFail == 0) pmlmepriv->timeBcnInfoChkStart = jiffies; diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index b5dcb78fb4f4..7011c2aa25cb 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -675,7 +675,6 @@ static void set_qos(struct pkt_file *ppktfile, struct pkt_attrib *pattrib) static s32 update_attrib(struct adapter *padapter, _pkt *pkt, struct pkt_attrib *pattrib) { - uint i; struct pkt_file pktfile; struct sta_info *psta = NULL; struct ethhdr etherhdr; @@ -689,7 +688,7 @@ static s32 update_attrib(struct adapter *padapter, _pkt *pkt, struct pkt_attrib DBG_COUNTER(padapter->tx_logs.core_tx_upd_attrib); _rtw_open_pktfile(pkt, &pktfile); - i = _rtw_pktfile_read(&pktfile, (u8 *)ðerhdr, ETH_HLEN); + _rtw_pktfile_read(&pktfile, (u8 *)ðerhdr, ETH_HLEN); pattrib->ether_type = ntohs(etherhdr.h_proto); @@ -2212,7 +2211,7 @@ void rtw_free_hwxmits(struct adapter *padapter) hwxmits = pxmitpriv->hwxmits; if (hwxmits) - kfree((u8 *)hwxmits); + kfree(hwxmits); } void rtw_init_hwxmits(struct hw_xmit *phwxmit, sint entry) @@ -2310,7 +2309,7 @@ s32 rtw_xmit(struct adapter *padapter, _pkt **ppkt) if (!pxmitframe) { drop_cnt++; - RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit: no more pxmitframe\n")); + RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("%s: no more pxmitframe\n", __func__)); DBG_COUNTER(padapter->tx_logs.core_tx_err_pxmitframe); return -1; } @@ -2318,7 +2317,7 @@ s32 rtw_xmit(struct adapter *padapter, _pkt **ppkt) res = update_attrib(padapter, *ppkt, &pxmitframe->attrib); if (res == _FAIL) { - RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit: update attrib fail\n")); + RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("%s: update attrib fail\n", __func__)); #ifdef DBG_TX_DROP_FRAME DBG_871X("DBG_TX_DROP_FRAME %s update attrib fail\n", __func__); #endif @@ -2690,14 +2689,6 @@ void wakeup_sta_to_xmit(struct adapter *padapter, struct sta_info *psta) pxmitframe->attrib.triggered = 1; -/* - spin_unlock_bh(&psta->sleep_q.lock); - if (rtw_hal_xmit(padapter, pxmitframe) == true) - { - rtw_os_xmit_complete(padapter, pxmitframe); - } - spin_lock_bh(&psta->sleep_q.lock); -*/ rtw_hal_xmitframe_enqueue(padapter, pxmitframe); @@ -2992,7 +2983,7 @@ int rtw_xmit_thread(void *context) do { err = rtw_hal_xmit_thread_handler(padapter); flush_signals_thread(); - } while (_SUCCESS == err); + } while (err == _SUCCESS); complete(&padapter->xmitpriv.terminate_xmitthread_comp); @@ -3022,9 +3013,8 @@ int rtw_sctx_wait(struct submit_ctx *sctx, const char *msg) status = sctx->status; } - if (status == RTW_SCTX_DONE_SUCCESS) { + if (status == RTW_SCTX_DONE_SUCCESS) ret = _SUCCESS; - } return ret; } @@ -3075,9 +3065,8 @@ void rtw_ack_tx_done(struct xmit_priv *pxmitpriv, int status) { struct submit_ctx *pack_tx_ops = &pxmitpriv->ack_tx_ops; - if (pxmitpriv->ack_tx) { + if (pxmitpriv->ack_tx) rtw_sctx_done_err(&pack_tx_ops, status); - } else { + else DBG_871X("%s ack_tx not set\n", __func__); - } } diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index 3239d37087c8..1ca9063a269f 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -402,8 +402,6 @@ static void GetDeltaSwingTable_8723B( *TemperatureUP_B = (u8 *)DeltaSwingTableIdx_2GA_P_8188E; *TemperatureDOWN_B = (u8 *)DeltaSwingTableIdx_2GA_N_8188E; } - - return; } diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index 6e4a1fcb8790..d5793e4614bf 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -1315,7 +1315,7 @@ void EXhalbtcoutsrc_DisplayBtCoexInfo(PBTC_COEXIST pBtCoexist) /* * Description: - *Run BT-Coexist mechansim or not + *Run BT-Coexist mechanism or not * */ void hal_btcoex_SetBTCoexist(struct adapter *padapter, u8 bBtExist) diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index eddd56abbb2d..109bd85b0cd8 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -30,7 +30,6 @@ void rtw_hal_data_deinit(struct adapter *padapter) { if (is_primary_adapter(padapter)) { /* if (padapter->isprimary) */ if (padapter->HalData) { - phy_free_filebuf(padapter); vfree(padapter->HalData); padapter->HalData = NULL; padapter->hal_data_sz = 0; diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index 6539bee9b5ba..eb7de3617d83 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -2202,1079 +2202,3 @@ void Hal_ChannelPlanToRegulation(struct adapter *Adapter, u16 ChannelPlan) break; } } - - -static char file_path_bs[PATH_MAX]; - -#define GetLineFromBuffer(buffer) strsep(&buffer, "\n") - -int phy_ConfigMACWithParaFile(struct adapter *Adapter, char *pFileName) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rlen = 0, rtStatus = _FAIL; - char *szLine, *ptmp; - u32 u4bRegOffset, u4bRegValue, u4bMove; - - if (!(Adapter->registrypriv.load_phy_file & LOAD_MAC_PARA_FILE)) - return rtStatus; - - memset(pHalData->para_file_buf, 0, MAX_PARA_FILE_BUF_LEN); - - if ((pHalData->mac_reg_len == 0) && !pHalData->mac_reg) { - rtw_merge_string(file_path_bs, PATH_MAX, rtw_phy_file_path, pFileName); - - if (rtw_is_file_readable(file_path_bs) == true) { - rlen = rtw_retrive_from_file(file_path_bs, pHalData->para_file_buf, MAX_PARA_FILE_BUF_LEN); - if (rlen > 0) { - rtStatus = _SUCCESS; - pHalData->mac_reg = vzalloc(rlen); - if (pHalData->mac_reg) { - memcpy(pHalData->mac_reg, pHalData->para_file_buf, rlen); - pHalData->mac_reg_len = rlen; - } else - DBG_871X("%s mac_reg alloc fail !\n", __func__); - } - } - } else { - if ((pHalData->mac_reg_len != 0) && (pHalData->mac_reg != NULL)) { - memcpy(pHalData->para_file_buf, pHalData->mac_reg, pHalData->mac_reg_len); - rtStatus = _SUCCESS; - } else - DBG_871X("%s(): Critical Error !!!\n", __func__); - } - - if (rtStatus == _SUCCESS) { - ptmp = pHalData->para_file_buf; - for (szLine = GetLineFromBuffer(ptmp); szLine != NULL; szLine = GetLineFromBuffer(ptmp)) { - if (!IsCommentString(szLine)) { - /* Get 1st hex value as register offset */ - if (GetHexValueFromString(szLine, &u4bRegOffset, &u4bMove)) { - if (u4bRegOffset == 0xffff) /* Ending. */ - break; - - /* Get 2nd hex value as register value. */ - szLine += u4bMove; - if (GetHexValueFromString(szLine, &u4bRegValue, &u4bMove)) - rtw_write8(Adapter, u4bRegOffset, (u8)u4bRegValue); - } - } - } - } else - DBG_871X("%s(): No File %s, Load from HWImg Array!\n", __func__, pFileName); - - return rtStatus; -} - -int phy_ConfigBBWithParaFile( - struct adapter *Adapter, char *pFileName, u32 ConfigType -) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rlen = 0, rtStatus = _FAIL; - char *szLine, *ptmp; - u32 u4bRegOffset, u4bRegValue, u4bMove; - char *pBuf = NULL; - u32 *pBufLen = NULL; - - if (!(Adapter->registrypriv.load_phy_file & LOAD_BB_PARA_FILE)) - return rtStatus; - - switch (ConfigType) { - case CONFIG_BB_PHY_REG: - pBuf = pHalData->bb_phy_reg; - pBufLen = &pHalData->bb_phy_reg_len; - break; - case CONFIG_BB_AGC_TAB: - pBuf = pHalData->bb_agc_tab; - pBufLen = &pHalData->bb_agc_tab_len; - break; - default: - DBG_871X("Unknown ConfigType!! %d\r\n", ConfigType); - break; - } - - memset(pHalData->para_file_buf, 0, MAX_PARA_FILE_BUF_LEN); - - if (pBufLen && (*pBufLen == 0) && !pBuf) { - rtw_merge_string(file_path_bs, PATH_MAX, rtw_phy_file_path, pFileName); - - if (rtw_is_file_readable(file_path_bs) == true) { - rlen = rtw_retrive_from_file(file_path_bs, pHalData->para_file_buf, MAX_PARA_FILE_BUF_LEN); - if (rlen > 0) { - rtStatus = _SUCCESS; - pBuf = vzalloc(rlen); - if (pBuf) { - memcpy(pBuf, pHalData->para_file_buf, rlen); - *pBufLen = rlen; - - switch (ConfigType) { - case CONFIG_BB_PHY_REG: - pHalData->bb_phy_reg = pBuf; - break; - case CONFIG_BB_AGC_TAB: - pHalData->bb_agc_tab = pBuf; - break; - } - } else - DBG_871X("%s(): ConfigType %d alloc fail !\n", __func__, ConfigType); - } - } - } else { - if (pBufLen && (*pBufLen == 0) && !pBuf) { - memcpy(pHalData->para_file_buf, pBuf, *pBufLen); - rtStatus = _SUCCESS; - } else - DBG_871X("%s(): Critical Error !!!\n", __func__); - } - - if (rtStatus == _SUCCESS) { - ptmp = pHalData->para_file_buf; - for (szLine = GetLineFromBuffer(ptmp); szLine != NULL; szLine = GetLineFromBuffer(ptmp)) { - if (!IsCommentString(szLine)) { - /* Get 1st hex value as register offset. */ - if (GetHexValueFromString(szLine, &u4bRegOffset, &u4bMove)) { - if (u4bRegOffset == 0xffff) /* Ending. */ - break; - else if (u4bRegOffset == 0xfe || u4bRegOffset == 0xffe) - msleep(50); - else if (u4bRegOffset == 0xfd) - mdelay(5); - else if (u4bRegOffset == 0xfc) - mdelay(1); - else if (u4bRegOffset == 0xfb) - udelay(50); - else if (u4bRegOffset == 0xfa) - udelay(5); - else if (u4bRegOffset == 0xf9) - udelay(1); - - /* Get 2nd hex value as register value. */ - szLine += u4bMove; - if (GetHexValueFromString(szLine, &u4bRegValue, &u4bMove)) { - /* DBG_871X("[BB-ADDR]%03lX =%08lX\n", u4bRegOffset, u4bRegValue); */ - PHY_SetBBReg(Adapter, u4bRegOffset, bMaskDWord, u4bRegValue); - - if (u4bRegOffset == 0xa24) - pHalData->odmpriv.RFCalibrateInfo.RegA24 = u4bRegValue; - - /* Add 1us delay between BB/RF register setting. */ - udelay(1); - } - } - } - } - } else - DBG_871X("%s(): No File %s, Load from HWImg Array!\n", __func__, pFileName); - - return rtStatus; -} - -static void phy_DecryptBBPgParaFile(struct adapter *Adapter, char *buffer) -{ - u32 i = 0, j = 0; - u8 map[95] = {0}; - u8 currentChar; - char *BufOfLines, *ptmp; - - /* DBG_871X("=====>phy_DecryptBBPgParaFile()\n"); */ - /* 32 the ascii code of the first visable char, 126 the last one */ - for (i = 0; i < 95; ++i) - map[i] = (u8) (94 - i); - - ptmp = buffer; - i = 0; - for (BufOfLines = GetLineFromBuffer(ptmp); BufOfLines != NULL; BufOfLines = GetLineFromBuffer(ptmp)) { - /* DBG_871X("Encrypted Line: %s\n", BufOfLines); */ - - for (j = 0; j < strlen(BufOfLines); ++j) { - currentChar = BufOfLines[j]; - - if (currentChar == '\0') - break; - - currentChar -= (u8) ((((i + j) * 3) % 128)); - - BufOfLines[j] = map[currentChar - 32] + 32; - } - /* DBG_871X("Decrypted Line: %s\n", BufOfLines); */ - if (strlen(BufOfLines) != 0) - i++; - BufOfLines[strlen(BufOfLines)] = '\n'; - } -} - -static int phy_ParseBBPgParaFile(struct adapter *Adapter, char *buffer) -{ - int rtStatus = _SUCCESS; - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - char *szLine, *ptmp; - u32 u4bRegOffset, u4bRegMask, u4bRegValue; - u32 u4bMove; - bool firstLine = true; - u8 tx_num = 0; - u8 band = 0, rf_path = 0; - - /* DBG_871X("=====>phy_ParseBBPgParaFile()\n"); */ - - if (Adapter->registrypriv.RegDecryptCustomFile == 1) - phy_DecryptBBPgParaFile(Adapter, buffer); - - ptmp = buffer; - for (szLine = GetLineFromBuffer(ptmp); szLine != NULL; szLine = GetLineFromBuffer(ptmp)) { - if (!IsCommentString(szLine)) { - if (isAllSpaceOrTab(szLine, sizeof(*szLine))) - continue; - - /* Get header info (relative value or exact value) */ - if (firstLine) { - if (eqNByte(szLine, (u8 *)("#[v1]"), 5)) { - - pHalData->odmpriv.PhyRegPgVersion = szLine[3] - '0'; - /* DBG_871X("This is a new format PHY_REG_PG.txt\n"); */ - } else if (eqNByte(szLine, (u8 *)("#[v0]"), 5)) { - pHalData->odmpriv.PhyRegPgVersion = szLine[3] - '0'; - /* DBG_871X("This is a old format PHY_REG_PG.txt ok\n"); */ - } else { - DBG_871X("The format in PHY_REG_PG are invalid %s\n", szLine); - return _FAIL; - } - - if (eqNByte(szLine + 5, (u8 *)("[Exact]#"), 8)) { - pHalData->odmpriv.PhyRegPgValueType = PHY_REG_PG_EXACT_VALUE; - /* DBG_871X("The values in PHY_REG_PG are exact values ok\n"); */ - firstLine = false; - continue; - } else if (eqNByte(szLine + 5, (u8 *)("[Relative]#"), 11)) { - pHalData->odmpriv.PhyRegPgValueType = PHY_REG_PG_RELATIVE_VALUE; - /* DBG_871X("The values in PHY_REG_PG are relative values ok\n"); */ - firstLine = false; - continue; - } else { - DBG_871X("The values in PHY_REG_PG are invalid %s\n", szLine); - return _FAIL; - } - } - - if (pHalData->odmpriv.PhyRegPgVersion == 0) { - /* Get 1st hex value as register offset. */ - if (GetHexValueFromString(szLine, &u4bRegOffset, &u4bMove)) { - szLine += u4bMove; - if (u4bRegOffset == 0xffff) /* Ending. */ - break; - - /* Get 2nd hex value as register mask. */ - if (GetHexValueFromString(szLine, &u4bRegMask, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - if (pHalData->odmpriv.PhyRegPgValueType == PHY_REG_PG_RELATIVE_VALUE) { - /* Get 3rd hex value as register value. */ - if (GetHexValueFromString(szLine, &u4bRegValue, &u4bMove)) { - PHY_StoreTxPowerByRate(Adapter, 0, 0, 1, u4bRegOffset, u4bRegMask, u4bRegValue); - /* DBG_871X("[ADDR] %03X =%08X Mask =%08x\n", u4bRegOffset, u4bRegValue, u4bRegMask); */ - } else - return _FAIL; - } else if (pHalData->odmpriv.PhyRegPgValueType == PHY_REG_PG_EXACT_VALUE) { - u32 combineValue = 0; - u8 integer = 0, fraction = 0; - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue <<= 8; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue <<= 8; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue <<= 8; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - PHY_StoreTxPowerByRate(Adapter, 0, 0, 1, u4bRegOffset, u4bRegMask, combineValue); - - /* DBG_871X("[ADDR] 0x%3x = 0x%4x\n", u4bRegOffset, combineValue); */ - } - } - } else if (pHalData->odmpriv.PhyRegPgVersion > 0) { - u32 index = 0; - - if (eqNByte(szLine, "0xffff", 6)) - break; - - if (!eqNByte("#[END]#", szLine, 7)) { - /* load the table label info */ - if (szLine[0] == '#') { - index = 0; - if (eqNByte(szLine, "#[2.4G]", 7)) { - band = BAND_ON_2_4G; - index += 8; - } else if (eqNByte(szLine, "#[5G]", 5)) { - band = BAND_ON_5G; - index += 6; - } else { - DBG_871X("Invalid band %s in PHY_REG_PG.txt\n", szLine); - return _FAIL; - } - - rf_path = szLine[index] - 'A'; - /* DBG_871X(" Table label Band %d, RfPath %d\n", band, rf_path); */ - } else { /* load rows of tables */ - if (szLine[1] == '1') - tx_num = RF_1TX; - else if (szLine[1] == '2') - tx_num = RF_2TX; - else if (szLine[1] == '3') - tx_num = RF_3TX; - else if (szLine[1] == '4') - tx_num = RF_4TX; - else { - DBG_871X("Invalid row in PHY_REG_PG.txt %c\n", szLine[1]); - return _FAIL; - } - - while (szLine[index] != ']') - ++index; - ++index;/* skip ] */ - - /* Get 2nd hex value as register offset. */ - szLine += index; - if (GetHexValueFromString(szLine, &u4bRegOffset, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - /* Get 2nd hex value as register mask. */ - if (GetHexValueFromString(szLine, &u4bRegMask, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - if (pHalData->odmpriv.PhyRegPgValueType == PHY_REG_PG_RELATIVE_VALUE) { - /* Get 3rd hex value as register value. */ - if (GetHexValueFromString(szLine, &u4bRegValue, &u4bMove)) { - PHY_StoreTxPowerByRate(Adapter, band, rf_path, tx_num, u4bRegOffset, u4bRegMask, u4bRegValue); - /* DBG_871X("[ADDR] %03X (tx_num %d) =%08X Mask =%08x\n", u4bRegOffset, tx_num, u4bRegValue, u4bRegMask); */ - } else - return _FAIL; - } else if (pHalData->odmpriv.PhyRegPgValueType == PHY_REG_PG_EXACT_VALUE) { - u32 combineValue = 0; - u8 integer = 0, fraction = 0; - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue <<= 8; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue <<= 8; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - - if (GetFractionValueFromString(szLine, &integer, &fraction, &u4bMove)) - szLine += u4bMove; - else - return _FAIL; - - integer *= 2; - if (fraction == 5) - integer += 1; - combineValue <<= 8; - combineValue |= (((integer / 10) << 4) + (integer % 10)); - /* DBG_871X(" %d", integer); */ - PHY_StoreTxPowerByRate(Adapter, band, rf_path, tx_num, u4bRegOffset, u4bRegMask, combineValue); - - /* DBG_871X("[ADDR] 0x%3x (tx_num %d) = 0x%4x\n", u4bRegOffset, tx_num, combineValue); */ - } - } - } - } - } - } - /* DBG_871X("<=====phy_ParseBBPgParaFile()\n"); */ - return rtStatus; -} - -int phy_ConfigBBWithPgParaFile(struct adapter *Adapter, char *pFileName) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rlen = 0, rtStatus = _FAIL; - - if (!(Adapter->registrypriv.load_phy_file & LOAD_BB_PG_PARA_FILE)) - return rtStatus; - - memset(pHalData->para_file_buf, 0, MAX_PARA_FILE_BUF_LEN); - - if ((pHalData->bb_phy_reg_pg_len == 0) && !pHalData->bb_phy_reg_pg) { - rtw_merge_string(file_path_bs, PATH_MAX, rtw_phy_file_path, pFileName); - - if (rtw_is_file_readable(file_path_bs) == true) { - rlen = rtw_retrive_from_file(file_path_bs, pHalData->para_file_buf, MAX_PARA_FILE_BUF_LEN); - if (rlen > 0) { - rtStatus = _SUCCESS; - pHalData->bb_phy_reg_pg = vzalloc(rlen); - if (pHalData->bb_phy_reg_pg) { - memcpy(pHalData->bb_phy_reg_pg, pHalData->para_file_buf, rlen); - pHalData->bb_phy_reg_pg_len = rlen; - } else - DBG_871X("%s bb_phy_reg_pg alloc fail !\n", __func__); - } - } - } else { - if ((pHalData->bb_phy_reg_pg_len != 0) && (pHalData->bb_phy_reg_pg != NULL)) { - memcpy(pHalData->para_file_buf, pHalData->bb_phy_reg_pg, pHalData->bb_phy_reg_pg_len); - rtStatus = _SUCCESS; - } else - DBG_871X("%s(): Critical Error !!!\n", __func__); - } - - if (rtStatus == _SUCCESS) { - /* DBG_871X("phy_ConfigBBWithPgParaFile(): read %s ok\n", pFileName); */ - phy_ParseBBPgParaFile(Adapter, pHalData->para_file_buf); - } else - DBG_871X("%s(): No File %s, Load from HWImg Array!\n", __func__, pFileName); - - return rtStatus; -} - -int PHY_ConfigRFWithParaFile( - struct adapter *Adapter, char *pFileName, u8 eRFPath -) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rlen = 0, rtStatus = _FAIL; - char *szLine, *ptmp; - u32 u4bRegOffset, u4bRegValue, u4bMove; - u16 i; - char *pBuf = NULL; - u32 *pBufLen = NULL; - - if (!(Adapter->registrypriv.load_phy_file & LOAD_RF_PARA_FILE)) - return rtStatus; - - switch (eRFPath) { - case ODM_RF_PATH_A: - pBuf = pHalData->rf_radio_a; - pBufLen = &pHalData->rf_radio_a_len; - break; - case ODM_RF_PATH_B: - pBuf = pHalData->rf_radio_b; - pBufLen = &pHalData->rf_radio_b_len; - break; - default: - DBG_871X("Unknown RF path!! %d\r\n", eRFPath); - break; - } - - memset(pHalData->para_file_buf, 0, MAX_PARA_FILE_BUF_LEN); - - if (pBufLen && (*pBufLen == 0) && !pBuf) { - rtw_merge_string(file_path_bs, PATH_MAX, rtw_phy_file_path, pFileName); - - if (rtw_is_file_readable(file_path_bs) == true) { - rlen = rtw_retrive_from_file(file_path_bs, pHalData->para_file_buf, MAX_PARA_FILE_BUF_LEN); - if (rlen > 0) { - rtStatus = _SUCCESS; - pBuf = vzalloc(rlen); - if (pBuf) { - memcpy(pBuf, pHalData->para_file_buf, rlen); - *pBufLen = rlen; - - switch (eRFPath) { - case ODM_RF_PATH_A: - pHalData->rf_radio_a = pBuf; - break; - case ODM_RF_PATH_B: - pHalData->rf_radio_b = pBuf; - break; - } - } else - DBG_871X("%s(): eRFPath =%d alloc fail !\n", __func__, eRFPath); - } - } - } else { - if (pBufLen && (*pBufLen == 0) && !pBuf) { - memcpy(pHalData->para_file_buf, pBuf, *pBufLen); - rtStatus = _SUCCESS; - } else - DBG_871X("%s(): Critical Error !!!\n", __func__); - } - - if (rtStatus == _SUCCESS) { - /* DBG_871X("%s(): read %s successfully\n", __func__, pFileName); */ - - ptmp = pHalData->para_file_buf; - for (szLine = GetLineFromBuffer(ptmp); szLine != NULL; szLine = GetLineFromBuffer(ptmp)) { - if (!IsCommentString(szLine)) { - /* Get 1st hex value as register offset. */ - if (GetHexValueFromString(szLine, &u4bRegOffset, &u4bMove)) { - if (u4bRegOffset == 0xfe || u4bRegOffset == 0xffe) /* Deay specific ms. Only RF configuration require delay. */ - msleep(50); - else if (u4bRegOffset == 0xfd) { - /* mdelay(5); */ - for (i = 0; i < 100; i++) - udelay(MAX_STALL_TIME); - } else if (u4bRegOffset == 0xfc) { - /* mdelay(1); */ - for (i = 0; i < 20; i++) - udelay(MAX_STALL_TIME); - } else if (u4bRegOffset == 0xfb) - udelay(50); - else if (u4bRegOffset == 0xfa) - udelay(5); - else if (u4bRegOffset == 0xf9) - udelay(1); - else if (u4bRegOffset == 0xffff) - break; - - /* Get 2nd hex value as register value. */ - szLine += u4bMove; - if (GetHexValueFromString(szLine, &u4bRegValue, &u4bMove)) { - PHY_SetRFReg(Adapter, eRFPath, u4bRegOffset, bRFRegOffsetMask, u4bRegValue); - - /* Temp add, for frequency lock, if no delay, that may cause */ - /* frequency shift, ex: 2412MHz => 2417MHz */ - /* If frequency shift, the following action may works. */ - /* Fractional-N table in radio_a.txt */ - /* 0x2a 0x00001 channel 1 */ - /* 0x2b 0x00808 frequency divider. */ - /* 0x2b 0x53333 */ - /* 0x2c 0x0000c */ - udelay(1); - } - } - } - } - } else - DBG_871X("%s(): No File %s, Load from HWImg Array!\n", __func__, pFileName); - - return rtStatus; -} - -static void initDeltaSwingIndexTables( - struct adapter *Adapter, - char *Band, - char *Path, - char *Sign, - char *Channel, - char *Rate, - char *Data -) -{ - #define STR_EQUAL_5G(_band, _path, _sign, _rate, _chnl) \ - ((strcmp(Band, _band) == 0) && (strcmp(Path, _path) == 0) && (strcmp(Sign, _sign) == 0) &&\ - (strcmp(Rate, _rate) == 0) && (strcmp(Channel, _chnl) == 0)\ - ) - #define STR_EQUAL_2G(_band, _path, _sign, _rate) \ - ((strcmp(Band, _band) == 0) && (strcmp(Path, _path) == 0) && (strcmp(Sign, _sign) == 0) &&\ - (strcmp(Rate, _rate) == 0)\ - ) - - #define STORE_SWING_TABLE(_array, _iteratedIdx) \ - for (token = strsep(&Data, delim); token != NULL; token = strsep(&Data, delim)) {\ - sscanf(token, "%d", &idx);\ - _array[_iteratedIdx++] = (u8)idx;\ - } \ - - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - PDM_ODM_T pDM_Odm = &pHalData->odmpriv; - PODM_RF_CAL_T pRFCalibrateInfo = &(pDM_Odm->RFCalibrateInfo); - u32 j = 0; - char *token; - char delim[] = ","; - u32 idx = 0; - - /* DBG_871X("===>initDeltaSwingIndexTables(): Band: %s;\nPath: %s;\nSign: %s;\nChannel: %s;\nRate: %s;\n, Data: %s;\n", */ - /* Band, Path, Sign, Channel, Rate, Data); */ - - if (STR_EQUAL_2G("2G", "A", "+", "CCK")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GCCKA_P, j); - } else if (STR_EQUAL_2G("2G", "A", "-", "CCK")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GCCKA_N, j); - } else if (STR_EQUAL_2G("2G", "B", "+", "CCK")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GCCKB_P, j); - } else if (STR_EQUAL_2G("2G", "B", "-", "CCK")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GCCKB_N, j); - } else if (STR_EQUAL_2G("2G", "A", "+", "ALL")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GA_P, j); - } else if (STR_EQUAL_2G("2G", "A", "-", "ALL")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GA_N, j); - } else if (STR_EQUAL_2G("2G", "B", "+", "ALL")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GB_P, j); - } else if (STR_EQUAL_2G("2G", "B", "-", "ALL")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_2GB_N, j); - } else if (STR_EQUAL_5G("5G", "A", "+", "ALL", "0")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_P[0], j); - } else if (STR_EQUAL_5G("5G", "A", "-", "ALL", "0")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_N[0], j); - } else if (STR_EQUAL_5G("5G", "B", "+", "ALL", "0")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_P[0], j); - } else if (STR_EQUAL_5G("5G", "B", "-", "ALL", "0")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_N[0], j); - } else if (STR_EQUAL_5G("5G", "A", "+", "ALL", "1")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_P[1], j); - } else if (STR_EQUAL_5G("5G", "A", "-", "ALL", "1")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_N[1], j); - } else if (STR_EQUAL_5G("5G", "B", "+", "ALL", "1")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_P[1], j); - } else if (STR_EQUAL_5G("5G", "B", "-", "ALL", "1")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_N[1], j); - } else if (STR_EQUAL_5G("5G", "A", "+", "ALL", "2")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_P[2], j); - } else if (STR_EQUAL_5G("5G", "A", "-", "ALL", "2")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_N[2], j); - } else if (STR_EQUAL_5G("5G", "B", "+", "ALL", "2")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_P[2], j); - } else if (STR_EQUAL_5G("5G", "B", "-", "ALL", "2")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_N[2], j); - } else if (STR_EQUAL_5G("5G", "A", "+", "ALL", "3")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_P[3], j); - } else if (STR_EQUAL_5G("5G", "A", "-", "ALL", "3")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GA_N[3], j); - } else if (STR_EQUAL_5G("5G", "B", "+", "ALL", "3")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_P[3], j); - } else if (STR_EQUAL_5G("5G", "B", "-", "ALL", "3")) { - STORE_SWING_TABLE(pRFCalibrateInfo->DeltaSwingTableIdx_5GB_N[3], j); - } else - DBG_871X("===>initDeltaSwingIndexTables(): The input is invalid!!\n"); -} - -int PHY_ConfigRFWithTxPwrTrackParaFile(struct adapter *Adapter, char *pFileName) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rlen = 0, rtStatus = _FAIL; - char *szLine, *ptmp; - - if (!(Adapter->registrypriv.load_phy_file & LOAD_RF_TXPWR_TRACK_PARA_FILE)) - return rtStatus; - - memset(pHalData->para_file_buf, 0, MAX_PARA_FILE_BUF_LEN); - - if ((pHalData->rf_tx_pwr_track_len == 0) && !pHalData->rf_tx_pwr_track) { - rtw_merge_string(file_path_bs, PATH_MAX, rtw_phy_file_path, pFileName); - - if (rtw_is_file_readable(file_path_bs) == true) { - rlen = rtw_retrive_from_file(file_path_bs, pHalData->para_file_buf, MAX_PARA_FILE_BUF_LEN); - if (rlen > 0) { - rtStatus = _SUCCESS; - pHalData->rf_tx_pwr_track = vzalloc(rlen); - if (pHalData->rf_tx_pwr_track) { - memcpy(pHalData->rf_tx_pwr_track, pHalData->para_file_buf, rlen); - pHalData->rf_tx_pwr_track_len = rlen; - } else - DBG_871X("%s rf_tx_pwr_track alloc fail !\n", __func__); - } - } - } else { - if ((pHalData->rf_tx_pwr_track_len != 0) && (pHalData->rf_tx_pwr_track != NULL)) { - memcpy(pHalData->para_file_buf, pHalData->rf_tx_pwr_track, pHalData->rf_tx_pwr_track_len); - rtStatus = _SUCCESS; - } else - DBG_871X("%s(): Critical Error !!!\n", __func__); - } - - if (rtStatus == _SUCCESS) { - /* DBG_871X("%s(): read %s successfully\n", __func__, pFileName); */ - - ptmp = pHalData->para_file_buf; - for (szLine = GetLineFromBuffer(ptmp); szLine != NULL; szLine = GetLineFromBuffer(ptmp)) { - if (!IsCommentString(szLine)) { - char band[5] = "", path[5] = "", sign[5] = ""; - char chnl[5] = "", rate[10] = ""; - char data[300] = ""; /* 100 is too small */ - const int len = strlen(szLine); - int i; - - if (len < 10 || szLine[0] != '[') - continue; - - strncpy(band, szLine+1, 2); - strncpy(path, szLine+5, 1); - strncpy(sign, szLine+8, 1); - - i = 10; /* szLine+10 */ - if (!ParseQualifiedString(szLine, &i, rate, '[', ']')) { - /* DBG_871X("Fail to parse rate!\n"); */ - } - if (!ParseQualifiedString(szLine, &i, chnl, '[', ']')) { - /* DBG_871X("Fail to parse channel group!\n"); */ - } - while (i < len && szLine[i] != '{') - i++; - if (!ParseQualifiedString(szLine, &i, data, '{', '}')) { - /* DBG_871X("Fail to parse data!\n"); */ - } - - initDeltaSwingIndexTables(Adapter, band, path, sign, chnl, rate, data); - } - } - } else - DBG_871X("%s(): No File %s, Load from HWImg Array!\n", __func__, pFileName); - - return rtStatus; -} - -static int phy_ParsePowerLimitTableFile(struct adapter *Adapter, char *buffer) -{ - u32 i = 0, forCnt = 0; - u8 loadingStage = 0, limitValue = 0, fraction = 0; - char *szLine, *ptmp; - int rtStatus = _SUCCESS; - char band[10], bandwidth[10], rateSection[10], - regulation[TXPWR_LMT_MAX_REGULATION_NUM][10], rfPath[10], colNumBuf[10]; - u8 colNum = 0; - - DBG_871X("===>phy_ParsePowerLimitTableFile()\n"); - - if (Adapter->registrypriv.RegDecryptCustomFile == 1) - phy_DecryptBBPgParaFile(Adapter, buffer); - - ptmp = buffer; - for (szLine = GetLineFromBuffer(ptmp); szLine != NULL; szLine = GetLineFromBuffer(ptmp)) { - /* skip comment */ - if (IsCommentString(szLine)) { - continue; - } - - if (loadingStage == 0) { - for (forCnt = 0; forCnt < TXPWR_LMT_MAX_REGULATION_NUM; ++forCnt) - memset((void *) regulation[forCnt], 0, 10); - - memset((void *) band, 0, 10); - memset((void *) bandwidth, 0, 10); - memset((void *) rateSection, 0, 10); - memset((void *) rfPath, 0, 10); - memset((void *) colNumBuf, 0, 10); - - if (szLine[0] != '#' || szLine[1] != '#') - continue; - - /* skip the space */ - i = 2; - while (szLine[i] == ' ' || szLine[i] == '\t') - ++i; - - szLine[--i] = ' '; /* return the space in front of the regulation info */ - - /* Parse the label of the table */ - if (!ParseQualifiedString(szLine, &i, band, ' ', ',')) { - DBG_871X("Fail to parse band!\n"); - return _FAIL; - } - if (!ParseQualifiedString(szLine, &i, bandwidth, ' ', ',')) { - DBG_871X("Fail to parse bandwidth!\n"); - return _FAIL; - } - if (!ParseQualifiedString(szLine, &i, rfPath, ' ', ',')) { - DBG_871X("Fail to parse rf path!\n"); - return _FAIL; - } - if (!ParseQualifiedString(szLine, &i, rateSection, ' ', ',')) { - DBG_871X("Fail to parse rate!\n"); - return _FAIL; - } - - loadingStage = 1; - } else if (loadingStage == 1) { - if (szLine[0] != '#' || szLine[1] != '#') - continue; - - /* skip the space */ - i = 2; - while (szLine[i] == ' ' || szLine[i] == '\t') - ++i; - - if (!eqNByte((u8 *)(szLine + i), (u8 *)("START"), 5)) { - DBG_871X("Lost \"## START\" label\n"); - return _FAIL; - } - - loadingStage = 2; - } else if (loadingStage == 2) { - if (szLine[0] != '#' || szLine[1] != '#') - continue; - - /* skip the space */ - i = 2; - while (szLine[i] == ' ' || szLine[i] == '\t') - ++i; - - if (!ParseQualifiedString(szLine, &i, colNumBuf, '#', '#')) { - DBG_871X("Fail to parse column number!\n"); - return _FAIL; - } - - if (!GetU1ByteIntegerFromStringInDecimal(colNumBuf, &colNum)) - return _FAIL; - - if (colNum > TXPWR_LMT_MAX_REGULATION_NUM) { - DBG_871X( - "invalid col number %d (greater than max %d)\n", - colNum, TXPWR_LMT_MAX_REGULATION_NUM - ); - return _FAIL; - } - - for (forCnt = 0; forCnt < colNum; ++forCnt) { - u8 regulation_name_cnt = 0; - - /* skip the space */ - while (szLine[i] == ' ' || szLine[i] == '\t') - ++i; - - while (szLine[i] != ' ' && szLine[i] != '\t' && szLine[i] != '\0') - regulation[forCnt][regulation_name_cnt++] = szLine[i++]; - /* DBG_871X("regulation %s!\n", regulation[forCnt]); */ - - if (regulation_name_cnt == 0) { - DBG_871X("invalid number of regulation!\n"); - return _FAIL; - } - } - - loadingStage = 3; - } else if (loadingStage == 3) { - char channel[10] = {0}, powerLimit[10] = {0}; - u8 cnt = 0; - - /* the table ends */ - if (szLine[0] == '#' && szLine[1] == '#') { - i = 2; - while (szLine[i] == ' ' || szLine[i] == '\t') - ++i; - - if (eqNByte((u8 *)(szLine + i), (u8 *)("END"), 3)) { - loadingStage = 0; - continue; - } else { - DBG_871X("Wrong format\n"); - DBG_871X("<===== phy_ParsePowerLimitTableFile()\n"); - return _FAIL; - } - } - - if ((szLine[0] != 'c' && szLine[0] != 'C') || - (szLine[1] != 'h' && szLine[1] != 'H')) { - DBG_871X("Meet wrong channel => power limt pair\n"); - continue; - } - i = 2;/* move to the location behind 'h' */ - - /* load the channel number */ - cnt = 0; - while (szLine[i] >= '0' && szLine[i] <= '9') { - channel[cnt] = szLine[i]; - ++cnt; - ++i; - } - /* DBG_871X("chnl %s!\n", channel); */ - - for (forCnt = 0; forCnt < colNum; ++forCnt) { - /* skip the space between channel number and the power limit value */ - while (szLine[i] == ' ' || szLine[i] == '\t') - ++i; - - /* load the power limit value */ - cnt = 0; - fraction = 0; - memset((void *) powerLimit, 0, 10); - while ((szLine[i] >= '0' && szLine[i] <= '9') || szLine[i] == '.') { - if (szLine[i] == '.') { - if ((szLine[i+1] >= '0' && szLine[i+1] <= '9')) { - fraction = szLine[i+1]; - i += 2; - } else { - DBG_871X("Wrong fraction in TXPWR_LMT.txt\n"); - return _FAIL; - } - - break; - } - - powerLimit[cnt] = szLine[i]; - ++cnt; - ++i; - } - - if (powerLimit[0] == '\0') { - powerLimit[0] = '6'; - powerLimit[1] = '3'; - i += 2; - } else { - if (!GetU1ByteIntegerFromStringInDecimal(powerLimit, &limitValue)) - return _FAIL; - - limitValue *= 2; - cnt = 0; - if (fraction == '5') - ++limitValue; - - /* the value is greater or equal to 100 */ - if (limitValue >= 100) { - powerLimit[cnt++] = limitValue/100 + '0'; - limitValue %= 100; - - if (limitValue >= 10) { - powerLimit[cnt++] = limitValue/10 + '0'; - limitValue %= 10; - } else - powerLimit[cnt++] = '0'; - - powerLimit[cnt++] = limitValue + '0'; - } else if (limitValue >= 10) { /* the value is greater or equal to 10 */ - powerLimit[cnt++] = limitValue/10 + '0'; - limitValue %= 10; - powerLimit[cnt++] = limitValue + '0'; - } - /* the value is less than 10 */ - else - powerLimit[cnt++] = limitValue + '0'; - - powerLimit[cnt] = '\0'; - } - - /* DBG_871X("ch%s => %s\n", channel, powerLimit); */ - - /* store the power limit value */ - PHY_SetTxPowerLimit(Adapter, (u8 *)regulation[forCnt], (u8 *)band, - (u8 *)bandwidth, (u8 *)rateSection, (u8 *)rfPath, (u8 *)channel, (u8 *)powerLimit); - - } - } else { - DBG_871X("Abnormal loading stage in phy_ParsePowerLimitTableFile()!\n"); - rtStatus = _FAIL; - break; - } - } - - DBG_871X("<===phy_ParsePowerLimitTableFile()\n"); - return rtStatus; -} - -int PHY_ConfigRFWithPowerLimitTableParaFile( - struct adapter *Adapter, char *pFileName -) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rlen = 0, rtStatus = _FAIL; - - if (!(Adapter->registrypriv.load_phy_file & LOAD_RF_TXPWR_LMT_PARA_FILE)) - return rtStatus; - - memset(pHalData->para_file_buf, 0, MAX_PARA_FILE_BUF_LEN); - - if ((pHalData->rf_tx_pwr_lmt_len == 0) && !pHalData->rf_tx_pwr_lmt) { - rtw_merge_string(file_path_bs, PATH_MAX, rtw_phy_file_path, pFileName); - - if (rtw_is_file_readable(file_path_bs) == true) { - rlen = rtw_retrive_from_file(file_path_bs, pHalData->para_file_buf, MAX_PARA_FILE_BUF_LEN); - if (rlen > 0) { - rtStatus = _SUCCESS; - pHalData->rf_tx_pwr_lmt = vzalloc(rlen); - if (pHalData->rf_tx_pwr_lmt) { - memcpy(pHalData->rf_tx_pwr_lmt, pHalData->para_file_buf, rlen); - pHalData->rf_tx_pwr_lmt_len = rlen; - } else - DBG_871X("%s rf_tx_pwr_lmt alloc fail !\n", __func__); - } - } - } else { - if ((pHalData->rf_tx_pwr_lmt_len != 0) && (pHalData->rf_tx_pwr_lmt != NULL)) { - memcpy(pHalData->para_file_buf, pHalData->rf_tx_pwr_lmt, pHalData->rf_tx_pwr_lmt_len); - rtStatus = _SUCCESS; - } else - DBG_871X("%s(): Critical Error !!!\n", __func__); - } - - if (rtStatus == _SUCCESS) { - /* DBG_871X("%s(): read %s ok\n", __func__, pFileName); */ - rtStatus = phy_ParsePowerLimitTableFile(Adapter, pHalData->para_file_buf); - } else - DBG_871X("%s(): No File %s, Load from HWImg Array!\n", __func__, pFileName); - - return rtStatus; -} - -void phy_free_filebuf(struct adapter *padapter) -{ - struct hal_com_data *pHalData = GET_HAL_DATA(padapter); - - if (pHalData->mac_reg) - vfree(pHalData->mac_reg); - if (pHalData->bb_phy_reg) - vfree(pHalData->bb_phy_reg); - if (pHalData->bb_agc_tab) - vfree(pHalData->bb_agc_tab); - if (pHalData->bb_phy_reg_pg) - vfree(pHalData->bb_phy_reg_pg); - if (pHalData->bb_phy_reg_mp) - vfree(pHalData->bb_phy_reg_mp); - if (pHalData->rf_radio_a) - vfree(pHalData->rf_radio_a); - if (pHalData->rf_radio_b) - vfree(pHalData->rf_radio_b); - if (pHalData->rf_tx_pwr_track) - vfree(pHalData->rf_tx_pwr_track); - if (pHalData->rf_tx_pwr_lmt) - vfree(pHalData->rf_tx_pwr_lmt); - -} diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.c b/drivers/staging/rtl8723bs/hal/odm_DIG.c index 70d98c58ca97..40fe43c62c45 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.c +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.c @@ -1074,7 +1074,6 @@ void odm_FAThresholdCheck( dm_FA_thres[1] = 4000; dm_FA_thres[2] = 5000; } - return; } u8 odm_ForbiddenIGICheck(void *pDM_VOID, u8 DIG_Dynamic_MIN, u8 CurrentIGI) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index faeaf24fa833..22931ab3a5fc 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -3773,7 +3773,6 @@ void C2HPacketHandler_8723B(struct adapter *padapter, u8 *pbuffer, u16 length) process_c2h_event(padapter, &C2hEvent, tmpBuf); /* c2h_handler_8723b(padapter,&C2hEvent); */ - return; } void SetHwReg8723B(struct adapter *padapter, u8 variable, u8 *val) @@ -4157,9 +4156,8 @@ void SetHwReg8723B(struct adapter *padapter, u8 variable, u8 *val) break; } - /* The value of ((usNavUpper + HAL_NAV_UPPER_UNIT_8723B - 1) / HAL_NAV_UPPER_UNIT_8723B) */ - /* is getting the upper integer. */ - usNavUpper = (usNavUpper + HAL_NAV_UPPER_UNIT_8723B - 1) / HAL_NAV_UPPER_UNIT_8723B; + usNavUpper = DIV_ROUND_UP(usNavUpper, + HAL_NAV_UPPER_UNIT_8723B); rtw_write8(padapter, REG_NAV_UPPER, (u8)usNavUpper); } break; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 6df2b58bdc67..cf23414d7224 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -362,24 +362,10 @@ void PHY_SetRFReg_8723B( */ s32 PHY_MACConfig8723B(struct adapter *Adapter) { - int rtStatus = _SUCCESS; struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - s8 *pszMACRegFile; - s8 sz8723MACRegFile[] = RTL8723B_PHY_MACREG; - - - pszMACRegFile = sz8723MACRegFile; - - /* */ - /* Config MAC */ - /* */ - rtStatus = phy_ConfigMACWithParaFile(Adapter, pszMACRegFile); - if (rtStatus == _FAIL) { - ODM_ReadAndConfig_MP_8723B_MAC_REG(&pHalData->odmpriv); - rtStatus = _SUCCESS; - } - return rtStatus; + ODM_ReadAndConfig_MP_8723B_MAC_REG(&pHalData->odmpriv); + return _SUCCESS; } /** @@ -427,17 +413,6 @@ static void phy_InitBBRFRegisterDefinition(struct adapter *Adapter) static int phy_BB8723b_Config_ParaFile(struct adapter *Adapter) { struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - int rtStatus = _SUCCESS; - u8 sz8723BBRegFile[] = RTL8723B_PHY_REG; - u8 sz8723AGCTableFile[] = RTL8723B_AGC_TAB; - u8 sz8723BBBRegPgFile[] = RTL8723B_PHY_REG_PG; - u8 sz8723BRFTxPwrLmtFile[] = RTL8723B_TXPWR_LMT; - u8 *pszBBRegFile = NULL, *pszAGCTableFile = NULL, *pszBBRegPgFile = NULL, *pszRFTxPwrLmtFile = NULL; - - pszBBRegFile = sz8723BBRegFile; - pszAGCTableFile = sz8723AGCTableFile; - pszBBRegPgFile = sz8723BBBRegPgFile; - pszRFTxPwrLmtFile = sz8723BRFTxPwrLmtFile; /* Read Tx Power Limit File */ PHY_InitTxPowerLimit(Adapter); @@ -445,30 +420,14 @@ static int phy_BB8723b_Config_ParaFile(struct adapter *Adapter) Adapter->registrypriv.RegEnableTxPowerLimit == 1 || (Adapter->registrypriv.RegEnableTxPowerLimit == 2 && pHalData->EEPROMRegulatory == 1) ) { - if (PHY_ConfigRFWithPowerLimitTableParaFile(Adapter, pszRFTxPwrLmtFile) == _FAIL) { - if (HAL_STATUS_SUCCESS != ODM_ConfigRFWithHeaderFile(&pHalData->odmpriv, CONFIG_RF_TXPWR_LMT, (ODM_RF_RADIO_PATH_E)0)) - rtStatus = _FAIL; - } - - if (rtStatus != _SUCCESS) { - DBG_871X("%s():Read Tx power limit fail\n", __func__); - goto phy_BB8190_Config_ParaFile_Fail; - } + ODM_ConfigRFWithHeaderFile(&pHalData->odmpriv, + CONFIG_RF_TXPWR_LMT, 0); } /* */ /* 1. Read PHY_REG.TXT BB INIT!! */ /* */ - if (phy_ConfigBBWithParaFile(Adapter, pszBBRegFile, CONFIG_BB_PHY_REG) == - _FAIL) { - if (HAL_STATUS_SUCCESS != ODM_ConfigBBWithHeaderFile(&pHalData->odmpriv, CONFIG_BB_PHY_REG)) - rtStatus = _FAIL; - } - - if (rtStatus != _SUCCESS) { - DBG_8192C("%s():Write BB Reg Fail!!", __func__); - goto phy_BB8190_Config_ParaFile_Fail; - } + ODM_ConfigBBWithHeaderFile(&pHalData->odmpriv, CONFIG_BB_PHY_REG); /* If EEPROM or EFUSE autoload OK, We must config by PHY_REG_PG.txt */ PHY_InitTxPowerByRate(Adapter); @@ -476,11 +435,8 @@ static int phy_BB8723b_Config_ParaFile(struct adapter *Adapter) Adapter->registrypriv.RegEnableTxPowerByRate == 1 || (Adapter->registrypriv.RegEnableTxPowerByRate == 2 && pHalData->EEPROMRegulatory != 2) ) { - if (phy_ConfigBBWithPgParaFile(Adapter, pszBBRegPgFile) == - _FAIL) { - if (HAL_STATUS_SUCCESS != ODM_ConfigBBWithHeaderFile(&pHalData->odmpriv, CONFIG_BB_PHY_REG_PG)) - rtStatus = _FAIL; - } + ODM_ConfigBBWithHeaderFile(&pHalData->odmpriv, + CONFIG_BB_PHY_REG_PG); if (pHalData->odmpriv.PhyRegPgValueType == PHY_REG_PG_EXACT_VALUE) PHY_TxPowerByRateConfiguration(Adapter); @@ -490,29 +446,14 @@ static int phy_BB8723b_Config_ParaFile(struct adapter *Adapter) (Adapter->registrypriv.RegEnableTxPowerLimit == 2 && pHalData->EEPROMRegulatory == 1) ) PHY_ConvertTxPowerLimitToPowerIndex(Adapter); - - if (rtStatus != _SUCCESS) { - DBG_8192C("%s():BB_PG Reg Fail!!\n", __func__); - } } /* */ /* 2. Read BB AGC table Initialization */ /* */ - if (phy_ConfigBBWithParaFile(Adapter, pszAGCTableFile, - CONFIG_BB_AGC_TAB) == _FAIL) { - if (HAL_STATUS_SUCCESS != ODM_ConfigBBWithHeaderFile(&pHalData->odmpriv, CONFIG_BB_AGC_TAB)) - rtStatus = _FAIL; - } - - if (rtStatus != _SUCCESS) { - DBG_8192C("%s():AGC Table Fail\n", __func__); - goto phy_BB8190_Config_ParaFile_Fail; - } - -phy_BB8190_Config_ParaFile_Fail: + ODM_ConfigBBWithHeaderFile(&pHalData->odmpriv, CONFIG_BB_AGC_TAB); - return rtStatus; + return _SUCCESS; } diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c index d0ffe0af5339..aafceaf9b139 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c @@ -85,19 +85,8 @@ static int phy_RF6052_Config_ParaFile(struct adapter *Adapter) u32 u4RegValue = 0; u8 eRFPath; struct bb_register_def *pPhyReg; - - int rtStatus = _SUCCESS; struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - static char sz8723RadioAFile[] = RTL8723B_PHY_RADIO_A; - static char sz8723RadioBFile[] = RTL8723B_PHY_RADIO_B; - static s8 sz8723BTxPwrTrackFile[] = RTL8723B_TXPWR_TRACK; - char *pszRadioAFile, *pszRadioBFile, *pszTxPwrTrackFile; - - pszRadioAFile = sz8723RadioAFile; - pszRadioBFile = sz8723RadioBFile; - pszTxPwrTrackFile = sz8723BTxPwrTrackFile; - /* 3----------------------------------------------------------------- */ /* 3 <2> Initialize RF */ /* 3----------------------------------------------------------------- */ @@ -136,21 +125,11 @@ static int phy_RF6052_Config_ParaFile(struct adapter *Adapter) /*----Initialize RF fom connfiguration file----*/ switch (eRFPath) { case RF_PATH_A: - if (PHY_ConfigRFWithParaFile(Adapter, pszRadioAFile, - eRFPath) == _FAIL) { - if (HAL_STATUS_FAILURE == ODM_ConfigRFWithHeaderFile(&pHalData->odmpriv, CONFIG_RF_RADIO, (ODM_RF_RADIO_PATH_E)eRFPath)) - rtStatus = _FAIL; - } - break; case RF_PATH_B: - if (PHY_ConfigRFWithParaFile(Adapter, pszRadioBFile, - eRFPath) == _FAIL) { - if (HAL_STATUS_FAILURE == ODM_ConfigRFWithHeaderFile(&pHalData->odmpriv, CONFIG_RF_RADIO, (ODM_RF_RADIO_PATH_E)eRFPath)) - rtStatus = _FAIL; - } + ODM_ConfigRFWithHeaderFile(&pHalData->odmpriv, + CONFIG_RF_RADIO, eRFPath); break; case RF_PATH_C: - break; case RF_PATH_D: break; } @@ -166,28 +145,16 @@ static int phy_RF6052_Config_ParaFile(struct adapter *Adapter) PHY_SetBBReg(Adapter, pPhyReg->rfintfs, bRFSI_RFENV << 16, u4RegValue); break; } - - if (rtStatus != _SUCCESS) { - /* RT_TRACE(COMP_FPGA, DBG_LOUD, ("phy_RF6052_Config_ParaFile():Radio[%d] Fail!!", eRFPath)); */ - goto phy_RF6052_Config_ParaFile_Fail; - } - } /* 3 ----------------------------------------------------------------- */ /* 3 Configuration of Tx Power Tracking */ /* 3 ----------------------------------------------------------------- */ - if (PHY_ConfigRFWithTxPwrTrackParaFile(Adapter, pszTxPwrTrackFile) == - _FAIL) { - ODM_ConfigRFWithTxPwrTrackHeaderFile(&pHalData->odmpriv); - } + ODM_ConfigRFWithTxPwrTrackHeaderFile(&pHalData->odmpriv); /* RT_TRACE(COMP_INIT, DBG_LOUD, ("<---phy_RF6052_Config_ParaFile()\n")); */ - return rtStatus; - -phy_RF6052_Config_ParaFile_Fail: - return rtStatus; + return _SUCCESS; } diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c index 0f3301091258..e8577c084bbd 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c @@ -179,8 +179,6 @@ static void rtl8723bs_c2h_packet_handler(struct adapter *padapter, kfree(tmp); /* DBG_871X("-%s res(%d)\n", __func__, res); */ - - return; } static inline union recv_frame *try_alloc_recvframe(struct recv_priv *precvpriv, diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 301d327d0624..5ecd1c873871 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -15,7 +15,7 @@ /* */ /* Description: */ -/* The following mapping is for SDIO host local register space. */ +/* The following mapping is for SDIO host local register space. */ /* */ /* Creadted by Roger, 2011.01.31. */ /* */ @@ -61,7 +61,6 @@ static u8 get_deviceid(u32 addr) u8 devide_id; u16 pseudo_id; - pseudo_id = (u16)(addr >> 16); switch (pseudo_id) { case 0x1025: @@ -72,10 +71,6 @@ static u8 get_deviceid(u32 addr) devide_id = WLAN_IOREG_DEVICE_ID; break; -/* case 0x1027: */ -/* devide_id = SDIO_FIRMWARE_FIFO; */ -/* break; */ - case 0x1031: devide_id = WLAN_TX_HIQ_DEVICE_ID; break; @@ -93,7 +88,6 @@ static u8 get_deviceid(u32 addr) break; default: -/* devide_id = (u8)((addr >> 13) & 0xF); */ devide_id = WLAN_IOREG_DEVICE_ID; break; } @@ -111,7 +105,6 @@ static u32 _cvrt2ftaddr(const u32 addr, u8 *pdevice_id, u16 *poffset) u16 offset; u32 ftaddr; - device_id = get_deviceid(addr); offset = 0; @@ -427,20 +420,16 @@ static u32 sdio_read_port( struct adapter *adapter; struct sdio_data *psdio; struct hal_com_data *hal; - u32 oldcnt; s32 err; - adapter = intfhdl->padapter; psdio = &adapter_to_dvobj(adapter)->intf_data; hal = GET_HAL_DATA(adapter); HalSdioGetCmdAddr8723BSdio(adapter, addr, hal->SdioRxFIFOCnt++, &addr); - oldcnt = cnt; if (cnt > psdio->block_transfer_len) cnt = _RND(cnt, psdio->block_transfer_len); -/* cnt = sdio_align_size(cnt); */ err = _sd_read(intfhdl, addr, cnt, mem); @@ -490,7 +479,6 @@ static u32 sdio_write_port( if (cnt > psdio->block_transfer_len) cnt = _RND(cnt, psdio->block_transfer_len); -/* cnt = sdio_align_size(cnt); */ err = sd_write(intfhdl, addr, cnt, xmitbuf->pdata); @@ -538,7 +526,6 @@ static s32 _sdio_local_read( u8 *tmpbuf; u32 n; - intfhdl = &adapter->iopriv.intf; HalSdioGetCmdAddr8723BSdio(adapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); @@ -711,7 +698,6 @@ static s32 ReadInterrupt8723BSdio(struct adapter *adapter, u32 *phisr) u32 hisr, himr; u8 val8, hisr_len; - if (!phisr) return false; @@ -737,73 +723,48 @@ static s32 ReadInterrupt8723BSdio(struct adapter *adapter, u32 *phisr) } /* */ -/* Description: */ -/* Initialize SDIO Host Interrupt Mask configuration variables for future use. */ +/* Description: */ +/* Initialize SDIO Host Interrupt Mask configuration variables for future use. */ /* */ -/* Assumption: */ -/* Using SDIO Local register ONLY for configuration. */ +/* Assumption: */ +/* Using SDIO Local register ONLY for configuration. */ /* */ -/* Created by Roger, 2011.02.11. */ +/* Created by Roger, 2011.02.11. */ /* */ void InitInterrupt8723BSdio(struct adapter *adapter) { struct hal_com_data *haldata; - haldata = GET_HAL_DATA(adapter); - haldata->sdio_himr = (u32)( \ - SDIO_HIMR_RX_REQUEST_MSK | - SDIO_HIMR_AVAL_MSK | -/* SDIO_HIMR_TXERR_MSK | */ -/* SDIO_HIMR_RXERR_MSK | */ -/* SDIO_HIMR_TXFOVW_MSK | */ -/* SDIO_HIMR_RXFOVW_MSK | */ -/* SDIO_HIMR_TXBCNOK_MSK | */ -/* SDIO_HIMR_TXBCNERR_MSK | */ -/* SDIO_HIMR_BCNERLY_INT_MSK | */ -/* SDIO_HIMR_C2HCMD_MSK | */ -/* SDIO_HIMR_HSISR_IND_MSK | */ -/* SDIO_HIMR_GTINT3_IND_MSK | */ -/* SDIO_HIMR_GTINT4_IND_MSK | */ -/* SDIO_HIMR_PSTIMEOUT_MSK | */ -/* SDIO_HIMR_OCPINT_MSK | */ -/* SDIO_HIMR_ATIMEND_MSK | */ -/* SDIO_HIMR_ATIMEND_E_MSK | */ -/* SDIO_HIMR_CTWEND_MSK | */ - 0); + haldata->sdio_himr = (u32)(SDIO_HIMR_RX_REQUEST_MSK | + SDIO_HIMR_AVAL_MSK | + 0); } /* */ -/* Description: */ -/* Initialize System Host Interrupt Mask configuration variables for future use. */ +/* Description: */ +/* Initialize System Host Interrupt Mask configuration variables for future use. */ /* */ -/* Created by Roger, 2011.08.03. */ +/* Created by Roger, 2011.08.03. */ /* */ void InitSysInterrupt8723BSdio(struct adapter *adapter) { struct hal_com_data *haldata; - haldata = GET_HAL_DATA(adapter); - haldata->SysIntrMask = ( \ -/* HSIMR_GPIO12_0_INT_EN | */ -/* HSIMR_SPS_OCP_INT_EN | */ -/* HSIMR_RON_INT_EN | */ -/* HSIMR_PDNINT_EN | */ -/* HSIMR_GPIO9_INT_EN | */ - 0); + haldata->SysIntrMask = (0); } /* */ -/* Description: */ -/* Enalbe SDIO Host Interrupt Mask configuration on SDIO local domain. */ +/* Description: */ +/* Enalbe SDIO Host Interrupt Mask configuration on SDIO local domain. */ /* */ -/* Assumption: */ -/* 1. Using SDIO Local register ONLY for configuration. */ -/* 2. PASSIVE LEVEL */ +/* Assumption: */ +/* 1. Using SDIO Local register ONLY for configuration. */ +/* 2. PASSIVE LEVEL */ /* */ -/* Created by Roger, 2011.02.11. */ +/* Created by Roger, 2011.02.11. */ /* */ void EnableInterrupt8723BSdio(struct adapter *adapter) { @@ -849,13 +810,13 @@ void EnableInterrupt8723BSdio(struct adapter *adapter) } /* */ -/* Description: */ -/* Disable SDIO Host IMR configuration to mask unnecessary interrupt service. */ +/* Description: */ +/* Disable SDIO Host IMR configuration to mask unnecessary interrupt service. */ /* */ -/* Assumption: */ -/* Using SDIO Local register ONLY for configuration. */ +/* Assumption: */ +/* Using SDIO Local register ONLY for configuration. */ /* */ -/* Created by Roger, 2011.02.11. */ +/* Created by Roger, 2011.02.11. */ /* */ void DisableInterrupt8723BSdio(struct adapter *adapter) { @@ -866,13 +827,13 @@ void DisableInterrupt8723BSdio(struct adapter *adapter) } /* */ -/* Description: */ -/* Using 0x100 to check the power status of FW. */ +/* Description: */ +/* Using 0x100 to check the power status of FW. */ /* */ -/* Assumption: */ -/* Using SDIO Local register ONLY for configuration. */ +/* Assumption: */ +/* Using SDIO Local register ONLY for configuration. */ /* */ -/* Created by Isaac, 2013.09.10. */ +/* Created by Isaac, 2013.09.10. */ /* */ u8 CheckIPSStatus(struct adapter *adapter) { @@ -896,7 +857,6 @@ static struct recv_buf *sd_recv_rxfifo(struct adapter *adapter, u32 size) struct recv_priv *recv_priv; struct recv_buf *recvbuf; - /* Patch for some SDIO Host 4 bytes issue */ /* ex. RK3188 */ readsize = RND4(size); @@ -938,7 +898,6 @@ static struct recv_buf *sd_recv_rxfifo(struct adapter *adapter, u32 size) return NULL; } - /* 3 4. init recvbuf */ recvbuf->len = size; recvbuf->phead = recvbuf->pskb->head; @@ -972,7 +931,6 @@ void sd_int_dpc(struct adapter *adapter) struct intf_hdl *intfhdl = &adapter->iopriv.intf; struct pwrctrl_priv *pwrctl; - hal = GET_HAL_DATA(adapter); dvobj = adapter_to_dvobj(adapter); pwrctl = dvobj_to_pwrctl(dvobj); @@ -992,7 +950,6 @@ void sd_int_dpc(struct adapter *adapter) report.state = SdioLocalCmd52Read1Byte(adapter, SDIO_REG_HCPWM1_8723B); - /* cpwm_int_hdl(adapter, &report); */ _set_workitem(&(pwrctl->cpwm_event)); } @@ -1049,13 +1006,11 @@ void sd_int_dpc(struct adapter *adapter) if (hal->sdio_hisr & SDIO_HISR_RXERR) DBG_8192C("%s: Rx Error\n", __func__); - if (hal->sdio_hisr & SDIO_HISR_RX_REQUEST) { struct recv_buf *recvbuf; int alloc_fail_time = 0; u32 hisr; -/* DBG_8192C("%s: RX Request, size =%d\n", __func__, hal->SdioRxFIFOSize); */ hal->sdio_hisr ^= SDIO_HISR_RX_REQUEST; do { hal->SdioRxFIFOSize = SdioLocalCmd52Read2Byte(adapter, SDIO_REG_RX0_REQ_LEN); @@ -1090,7 +1045,6 @@ void sd_int_hdl(struct adapter *adapter) { struct hal_com_data *hal; - if ( (adapter->bDriverStopped) || (adapter->bSurpriseRemoved) ) @@ -1120,27 +1074,24 @@ void sd_int_hdl(struct adapter *adapter) } /* */ -/* Description: */ -/* Query SDIO Local register to query current the number of Free TxPacketBuffer page. */ +/* Description: */ +/* Query SDIO Local register to query current the number of Free TxPacketBuffer page. */ /* */ -/* Assumption: */ -/* 1. Running at PASSIVE_LEVEL */ -/* 2. RT_TX_SPINLOCK is NOT acquired. */ +/* Assumption: */ +/* 1. Running at PASSIVE_LEVEL */ +/* 2. RT_TX_SPINLOCK is NOT acquired. */ /* */ -/* Created by Roger, 2011.01.28. */ +/* Created by Roger, 2011.01.28. */ /* */ u8 HalQueryTxBufferStatus8723BSdio(struct adapter *adapter) { struct hal_com_data *hal; u32 numof_free_page; - /* _irql irql; */ - hal = GET_HAL_DATA(adapter); numof_free_page = SdioLocalCmd53Read4Byte(adapter, SDIO_REG_FREE_TXPG); - /* spin_lock_bh(&phal->SdioTxFIFOFreePageLock); */ memcpy(hal->SdioTxFIFOFreePage, &numof_free_page, 4); RT_TRACE(_module_hci_ops_c_, _drv_notice_, ("%s: Free page for HIQ(%#x), MIDQ(%#x), LOWQ(%#x), PUBQ(%#x)\n", @@ -1149,14 +1100,13 @@ u8 HalQueryTxBufferStatus8723BSdio(struct adapter *adapter) hal->SdioTxFIFOFreePage[MID_QUEUE_IDX], hal->SdioTxFIFOFreePage[LOW_QUEUE_IDX], hal->SdioTxFIFOFreePage[PUBLIC_QUEUE_IDX])); - /* spin_unlock_bh(&hal->SdioTxFIFOFreePageLock); */ return true; } /* */ -/* Description: */ -/* Query SDIO Local register to get the current number of TX OQT Free Space. */ +/* Description: */ +/* Query SDIO Local register to get the current number of TX OQT Free Space. */ /* */ void HalQueryTxOQTBufferStatus8723BSdio(struct adapter *adapter) { @@ -1190,7 +1140,6 @@ u8 RecvOnePkt(struct adapter *adapter, u32 size) recvbuf = sd_recv_rxfifo(adapter, size); if (recvbuf) { - /* printk("Completed Recv One Pkt.\n"); */ sd_rxhandler(adapter, recvbuf); res = true; } else { diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h index 8d7fce1e39b7..6ec9087f2eb1 100644 --- a/drivers/staging/rtl8723bs/include/drv_types.h +++ b/drivers/staging/rtl8723bs/include/drv_types.h @@ -197,9 +197,6 @@ struct registry_priv u8 RFE_Type; u8 check_fw_ps; - u8 load_phy_file; - u8 RegDecryptCustomFile; - #ifdef CONFIG_MULTI_VIR_IFACES u8 ext_iface_num;/* primary/secondary iface is excluded */ #endif @@ -693,7 +690,6 @@ void rtw_indicate_wx_disassoc_event(struct adapter *padapter); void indicate_wx_scan_complete_event(struct adapter *padapter); int rtw_change_ifname(struct adapter *padapter, const char *ifname); -extern char *rtw_phy_file_path; extern char *rtw_initmac; extern int rtw_mc2u_disable; extern int rtw_ht_enable; diff --git a/drivers/staging/rtl8723bs/include/hal_com_phycfg.h b/drivers/staging/rtl8723bs/include/hal_com_phycfg.h index 9167f1e7827f..e9a3006a3e20 100644 --- a/drivers/staging/rtl8723bs/include/hal_com_phycfg.h +++ b/drivers/staging/rtl8723bs/include/hal_com_phycfg.h @@ -219,30 +219,4 @@ struct adapter * Adapter, u16 ChannelPlan ); -#define MAX_PARA_FILE_BUF_LEN 25600 - -#define LOAD_MAC_PARA_FILE BIT0 -#define LOAD_BB_PARA_FILE BIT1 -#define LOAD_BB_PG_PARA_FILE BIT2 -#define LOAD_BB_MP_PARA_FILE BIT3 -#define LOAD_RF_PARA_FILE BIT4 -#define LOAD_RF_TXPWR_TRACK_PARA_FILE BIT5 -#define LOAD_RF_TXPWR_LMT_PARA_FILE BIT6 - -int phy_ConfigMACWithParaFile(struct adapter *Adapter, char*pFileName); - -int phy_ConfigBBWithParaFile(struct adapter *Adapter, char*pFileName, u32 ConfigType); - -int phy_ConfigBBWithPgParaFile(struct adapter *Adapter, char*pFileName); - -int phy_ConfigBBWithMpParaFile(struct adapter *Adapter, char*pFileName); - -int PHY_ConfigRFWithParaFile(struct adapter *Adapter, char*pFileName, u8 eRFPath); - -int PHY_ConfigRFWithTxPwrTrackParaFile(struct adapter *Adapter, char*pFileName); - -int PHY_ConfigRFWithPowerLimitTableParaFile(struct adapter *Adapter, char*pFileName); - -void phy_free_filebuf(struct adapter *padapter); - #endif /* __HAL_COMMON_H__ */ diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index 7d782659a84f..e5e667df6154 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -440,27 +440,6 @@ struct hal_com_data { u32 SysIntrStatus; u32 SysIntrMask; - - char para_file_buf[MAX_PARA_FILE_BUF_LEN]; - char *mac_reg; - u32 mac_reg_len; - char *bb_phy_reg; - u32 bb_phy_reg_len; - char *bb_agc_tab; - u32 bb_agc_tab_len; - char *bb_phy_reg_pg; - u32 bb_phy_reg_pg_len; - char *bb_phy_reg_mp; - u32 bb_phy_reg_mp_len; - char *rf_radio_a; - u32 rf_radio_a_len; - char *rf_radio_b; - u32 rf_radio_b_len; - char *rf_tx_pwr_track; - u32 rf_tx_pwr_track_len; - char *rf_tx_pwr_lmt; - u32 rf_tx_pwr_lmt_len; - #ifdef CONFIG_BACKGROUND_NOISE_MONITOR s16 noise[ODM_MAX_CHANNEL_NUM]; #endif diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 81a9c19ecc6a..a40cf7b60a69 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -171,10 +171,6 @@ extern void rtw_softap_lock_suspend(void); extern void rtw_softap_unlock_suspend(void); #endif -/* File operation APIs, just for linux now */ -extern int rtw_is_file_readable(char *path); -extern int rtw_retrive_from_file(char *path, u8 *buf, u32 sz); - extern void rtw_free_netdev(struct net_device * netdev); diff --git a/drivers/staging/rtl8723bs/include/osdep_service_linux.h b/drivers/staging/rtl8723bs/include/osdep_service_linux.h index c582ede1ac12..a2d9de866c4b 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service_linux.h +++ b/drivers/staging/rtl8723bs/include/osdep_service_linux.h @@ -127,13 +127,6 @@ static inline void rtw_netif_stop_queue(struct net_device *pnetdev) netif_tx_stop_all_queues(pnetdev); } -static inline void rtw_merge_string(char *dst, int dst_len, char *src1, char *src2) -{ - int len = 0; - len += snprintf(dst+len, dst_len - len, "%s", src1); - len += snprintf(dst+len, dst_len - len, "%s", src2); -} - #define rtw_signal_process(pid, sig) kill_pid(find_vpid((pid)), (sig), 1) #define rtw_netdev_priv(netdev) (((struct rtw_netdev_priv_indicator *)netdev_priv(netdev))->priv) diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h index 8f00ced1c697..f36516fa84c7 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h @@ -21,21 +21,6 @@ #include "hal_phy_cfg.h" /* */ -/* RTL8723B From file */ -/* */ -#define RTL8723B_FW_IMG "rtl8723b/FW_NIC.bin" -#define RTL8723B_FW_WW_IMG "rtl8723b/FW_WoWLAN.bin" -#define RTL8723B_PHY_REG "rtl8723b/PHY_REG.txt" -#define RTL8723B_PHY_RADIO_A "rtl8723b/RadioA.txt" -#define RTL8723B_PHY_RADIO_B "rtl8723b/RadioB.txt" -#define RTL8723B_TXPWR_TRACK "rtl8723b/TxPowerTrack.txt" -#define RTL8723B_AGC_TAB "rtl8723b/AGC_TAB.txt" -#define RTL8723B_PHY_MACREG "rtl8723b/MAC_REG.txt" -#define RTL8723B_PHY_REG_PG "rtl8723b/PHY_REG_PG.txt" -#define RTL8723B_PHY_REG_MP "rtl8723b/PHY_REG_MP.txt" -#define RTL8723B_TXPWR_LMT "rtl8723b/TXPWR_LMT.txt" - -/* */ /* RTL8723B From header */ /* */ diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index fd3cf955c9f8..73e8ec09b6e1 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -576,7 +576,6 @@ void read_cam(struct adapter *padapter , u8 entry, u8 *get_key); /* modify HW only */ void _write_cam(struct adapter *padapter, u8 entry, u16 ctrl, u8 *mac, u8 *key); void _clear_cam_entry(struct adapter *padapter, u8 entry); -void write_cam_from_cache(struct adapter *adapter, u8 id); /* modify both HW and cache */ void write_cam(struct adapter *padapter, u8 id, u16 ctrl, u8 *mac, u8 *key); diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index f819abb756dc..8555f52ceb7c 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -165,7 +165,7 @@ static void rtw_spt_band_free(struct ieee80211_supported_band *spt_band) + sizeof(struct ieee80211_channel)*RTW_2G_CHANNELS_NUM + sizeof(struct ieee80211_rate)*RTW_G_RATES_NUM; } - kfree((u8 *)spt_band); + kfree(spt_band); } static const struct ieee80211_txrx_stypes @@ -1156,7 +1156,7 @@ static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct net_device *ndev, } addkey_end: - kfree((u8 *)param); + kfree(param); return ret; @@ -1422,7 +1422,6 @@ void rtw_cfg80211_unlink_bss(struct adapter *padapter, struct wlan_network *pnet DBG_8192C("%s(): cfg80211_unlink %s!! () ", __func__, select_network.Ssid.Ssid); cfg80211_put_bss(padapter->rtw_wdev->wiphy, bss); } - return; } void rtw_cfg80211_surveydone_event_callback(struct adapter *padapter) @@ -2194,7 +2193,7 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, ret = -EOPNOTSUPP ; } - kfree((u8 *)pwep); + kfree(pwep); if (ret < 0) goto exit; @@ -2647,7 +2646,7 @@ static int rtw_cfg80211_add_monitor_if (struct adapter *padapter, char *name, st out: if (ret && mon_wdev) { - kfree((u8 *)mon_wdev); + kfree(mon_wdev); mon_wdev = NULL; } @@ -3503,7 +3502,7 @@ void rtw_wdev_free(struct wireless_dev *wdev) wiphy_free(wdev->wiphy); - kfree((u8 *)wdev); + kfree(wdev); } void rtw_wdev_unregister(struct wireless_dev *wdev) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c index d1b199e3e5bd..db6528a01229 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c @@ -2425,19 +2425,13 @@ static int rtw_drvext_hdl(struct net_device *dev, struct iw_request_info *info, return 0; } -static int rtw_mp_ioctl_hdl(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - int ret = 0; - return ret; -} - static int rtw_get_ap_info(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { int ret = 0; - u32 cnt = 0, wpa_ielen; + int wpa_ielen; + u32 cnt = 0; struct list_head *plist, *phead; unsigned char *pbuf; u8 bssid[ETH_ALEN]; @@ -2793,7 +2787,7 @@ static int rtw_dbg_port(struct net_device *dev, DBG_871X("oper_ch =%d\n", rtw_get_oper_ch(padapter)); DBG_871X("oper_bw =%d\n", rtw_get_oper_bw(padapter)); - DBG_871X("oper_ch_offet =%d\n", rtw_get_oper_choffset(padapter)); + DBG_871X("oper_ch_offset =%d\n", rtw_get_oper_choffset(padapter)); break; case 0x05: @@ -4458,43 +4452,6 @@ static int rtw_pm_set(struct net_device *dev, return ret; } -static int rtw_mp_efuse_get(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wdata, char *extra) -{ - int err = 0; - return err; -} - -static int rtw_mp_efuse_set(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wdata, char *extra) -{ - int err = 0; - return err; -} - -static int rtw_tdls(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - int ret = 0; - return ret; -} - - -static int rtw_tdls_get(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - int ret = 0; - return ret; -} - - - - - static int rtw_test( struct net_device *dev, struct iw_request_info *info, @@ -4744,7 +4701,7 @@ static iw_handler rtw_private_handler[] = { rtw_wx_write32, /* 0x00 */ rtw_wx_read32, /* 0x01 */ rtw_drvext_hdl, /* 0x02 */ - rtw_mp_ioctl_hdl, /* 0x03 */ + NULL, /* 0x03 */ /* for MM DTV platform */ rtw_get_ap_info, /* 0x04 */ @@ -4771,15 +4728,15 @@ static iw_handler rtw_private_handler[] = { NULL, /* 0x12 */ rtw_p2p_get2, /* 0x13 */ - rtw_tdls, /* 0x14 */ - rtw_tdls_get, /* 0x15 */ + NULL, /* 0x14 */ + NULL, /* 0x15 */ rtw_pm_set, /* 0x16 */ rtw_wx_priv_null, /* 0x17 */ rtw_rereg_nd_name, /* 0x18 */ rtw_wx_priv_null, /* 0x19 */ - rtw_mp_efuse_set, /* 0x1A */ - rtw_mp_efuse_get, /* 0x1B */ + NULL, /* 0x1A */ + NULL, /* 0x1B */ NULL, /* 0x1C is reserved for hostapd */ rtw_test, /* 0x1D */ }; diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index ec3a75485233..47e984d5b7cb 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -201,24 +201,6 @@ MODULE_PARM_DESC(rtw_tx_pwr_lmt_enable, "0:Disable, 1:Enable, 2: Depend on efuse module_param(rtw_tx_pwr_by_rate, int, 0644); MODULE_PARM_DESC(rtw_tx_pwr_by_rate, "0:Disable, 1:Enable, 2: Depend on efuse"); -char *rtw_phy_file_path = ""; -module_param(rtw_phy_file_path, charp, 0644); -MODULE_PARM_DESC(rtw_phy_file_path, "The path of phy parameter"); -/* PHY FILE Bit Map */ -/* BIT0 - MAC, 0: non-support, 1: support */ -/* BIT1 - BB, 0: non-support, 1: support */ -/* BIT2 - BB_PG, 0: non-support, 1: support */ -/* BIT3 - BB_MP, 0: non-support, 1: support */ -/* BIT4 - RF, 0: non-support, 1: support */ -/* BIT5 - RF_TXPWR_TRACK, 0: non-support, 1: support */ -/* BIT6 - RF_TXPWR_LMT, 0: non-support, 1: support */ -static int rtw_load_phy_file = (BIT2 | BIT6); -module_param(rtw_load_phy_file, int, 0644); -MODULE_PARM_DESC(rtw_load_phy_file, "PHY File Bit Map"); -static int rtw_decrypt_phy_file; -module_param(rtw_decrypt_phy_file, int, 0644); -MODULE_PARM_DESC(rtw_decrypt_phy_file, "Enable Decrypt PHY File"); - int _netdev_open(struct net_device *pnetdev); int netdev_open (struct net_device *pnetdev); static int netdev_close (struct net_device *pnetdev); @@ -321,8 +303,6 @@ static void loadparam(struct adapter *padapter, _nic_hdl pnetdev) registry_par->bEn_RFE = 1; registry_par->RFE_Type = 64; - registry_par->load_phy_file = (u8)rtw_load_phy_file; - registry_par->RegDecryptCustomFile = (u8)rtw_decrypt_phy_file; registry_par->qos_opt_enable = (u8)rtw_qos_opt_enable; registry_par->hiq_filter = (u8)rtw_hiq_filter; @@ -1141,8 +1121,7 @@ void rtw_ndev_destructor(struct net_device *ndev) { DBG_871X(FUNC_NDEV_FMT "\n", FUNC_NDEV_ARG(ndev)); - if (ndev->ieee80211_ptr) - kfree((u8 *)ndev->ieee80211_ptr); + kfree(ndev->ieee80211_ptr); } void rtw_dev_unload(struct adapter *padapter) diff --git a/drivers/staging/rtl8723bs/os_dep/osdep_service.c b/drivers/staging/rtl8723bs/os_dep/osdep_service.c index 25a80041ce87..f5614e56371e 100644 --- a/drivers/staging/rtl8723bs/os_dep/osdep_service.c +++ b/drivers/staging/rtl8723bs/os_dep/osdep_service.c @@ -65,142 +65,6 @@ void _rtw_init_queue(struct __queue *pqueue) spin_lock_init(&(pqueue->lock)); } -/* -* Open a file with the specific @param path, @param flag, @param mode -* @param fpp the pointer of struct file pointer to get struct file pointer while file opening is success -* @param path the path of the file to open -* @param flag file operation flags, please refer to linux document -* @param mode please refer to linux document -* @return Linux specific error code -*/ -static int openFile(struct file **fpp, char *path, int flag, int mode) -{ - struct file *fp; - - fp = filp_open(path, flag, mode); - if (IS_ERR(fp)) { - *fpp = NULL; - return PTR_ERR(fp); - } - else { - *fpp = fp; - return 0; - } -} - -/* -* Close the file with the specific @param fp -* @param fp the pointer of struct file to close -* @return always 0 -*/ -static int closeFile(struct file *fp) -{ - filp_close(fp, NULL); - return 0; -} - -static int readFile(struct file *fp, char *buf, int len) -{ - int rlen = 0, sum = 0; - - if (!fp->f_op || !fp->f_op->read) - return -EPERM; - - while (sum < len) { - rlen = kernel_read(fp, buf + sum, len - sum, &fp->f_pos); - if (rlen > 0) - sum += rlen; - else if (0 != rlen) - return rlen; - else - break; - } - - return sum; - -} - -/* -* Test if the specifi @param path is a file and readable -* @param path the path of the file to test -* @return Linux specific error code -*/ -static int isFileReadable(char *path) -{ - struct file *fp; - int ret = 0; - char buf; - - fp = filp_open(path, O_RDONLY, 0); - if (IS_ERR(fp)) - return PTR_ERR(fp); - - if (readFile(fp, &buf, 1) != 1) - ret = -EINVAL; - - filp_close(fp, NULL); - return ret; -} - -/* -* Open the file with @param path and retrive the file content into memory starting from @param buf for @param sz at most -* @param path the path of the file to open and read -* @param buf the starting address of the buffer to store file content -* @param sz how many bytes to read at most -* @return the byte we've read, or Linux specific error code -*/ -static int retriveFromFile(char *path, u8 *buf, u32 sz) -{ - int ret = -1; - struct file *fp; - - if (path && buf) { - ret = openFile(&fp, path, O_RDONLY, 0); - - if (ret == 0) { - DBG_871X("%s openFile path:%s fp =%p\n", __func__, path , fp); - - ret = readFile(fp, buf, sz); - closeFile(fp); - - DBG_871X("%s readFile, ret:%d\n", __func__, ret); - - } else { - DBG_871X("%s openFile path:%s Fail, ret:%d\n", __func__, path, ret); - } - } else { - DBG_871X("%s NULL pointer\n", __func__); - ret = -EINVAL; - } - return ret; -} - -/* -* Test if the specifi @param path is a file and readable -* @param path the path of the file to test -* @return true or false -*/ -int rtw_is_file_readable(char *path) -{ - if (isFileReadable(path) == 0) - return true; - else - return false; -} - -/* -* Open the file with @param path and retrive the file content into memory starting from @param buf for @param sz at most -* @param path the path of the file to open and read -* @param buf the starting address of the buffer to store file content -* @param sz how many bytes to read at most -* @return the byte we've read -*/ -int rtw_retrive_from_file(char *path, u8 *buf, u32 sz) -{ - int ret = retriveFromFile(path, buf, sz); - return ret >= 0 ? ret : 0; -} - struct net_device *rtw_alloc_etherdev_with_old_priv(int sizeof_priv, void *old_priv) { struct net_device *pnetdev; diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index d3784c44f6d0..12f683e2e0e2 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -281,7 +281,6 @@ static void sdio_dvobj_deinit(struct sdio_func *func) sdio_deinit(dvobj); devobj_deinit(dvobj); } - return; } void rtw_set_hal_ops(struct adapter *padapter) diff --git a/drivers/staging/sm750fb/ddk750_chip.c b/drivers/staging/sm750fb/ddk750_chip.c index 5a317cc98a4b..02860d3ec365 100644 --- a/drivers/staging/sm750fb/ddk750_chip.c +++ b/drivers/staging/sm750fb/ddk750_chip.c @@ -56,7 +56,6 @@ static unsigned int get_mxclk_freq(void) static void set_chip_clock(unsigned int frequency) { struct pll_value pll; - unsigned int actual_mx_clk; /* Cheok_0509: For SM750LE, the chip clock is fixed. Nothing to set. */ if (sm750_get_chip_type() == SM750LE) @@ -66,8 +65,8 @@ static void set_chip_clock(unsigned int frequency) /* * Set up PLL structure to hold the value to be set in clocks. */ - pll.inputFreq = DEFAULT_INPUT_CLOCK; /* Defined in CLOCK.H */ - pll.clockType = MXCLK_PLL; + pll.input_freq = DEFAULT_INPUT_CLOCK; /* Defined in CLOCK.H */ + pll.clock_type = MXCLK_PLL; /* * Call sm750_calc_pll_value() to fill the other fields @@ -76,7 +75,7 @@ static void set_chip_clock(unsigned int frequency) * Return value of sm750_calc_pll_value gives the actual * possible clock. */ - actual_mx_clk = sm750_calc_pll_value(frequency, &pll); + sm750_calc_pll_value(frequency, &pll); /* Master Clock Control: MXCLK_PLL */ poke32(MXCLK_PLL_CTRL, sm750_format_pll_reg(&pll)); @@ -211,13 +210,13 @@ unsigned int ddk750_get_vm_size(void) return data; } -int ddk750_init_hw(struct initchip_param *pInitParam) +int ddk750_init_hw(struct initchip_param *p_init_param) { unsigned int reg; - if (pInitParam->powerMode != 0) - pInitParam->powerMode = 0; - sm750_set_power_mode(pInitParam->powerMode); + if (p_init_param->power_mode != 0) + p_init_param->power_mode = 0; + sm750_set_power_mode(p_init_param->power_mode); /* Enable display power gate & LOCALMEM power gate*/ reg = peek32(CURRENT_GATE); @@ -238,13 +237,13 @@ int ddk750_init_hw(struct initchip_param *pInitParam) } /* Set the Main Chip Clock */ - set_chip_clock(MHz((unsigned int)pInitParam->chipClock)); + set_chip_clock(MHz((unsigned int)p_init_param->chip_clock)); /* Set up memory clock. */ - set_memory_clock(MHz(pInitParam->memClock)); + set_memory_clock(MHz(p_init_param->mem_clock)); /* Set up master clock */ - set_master_clock(MHz(pInitParam->masterClock)); + set_master_clock(MHz(p_init_param->master_clock)); /* * Reset the memory controller. @@ -252,7 +251,7 @@ int ddk750_init_hw(struct initchip_param *pInitParam) * the system might hang when sw accesses the memory. * The memory should be resetted after changing the MXCLK. */ - if (pInitParam->resetMemory == 1) { + if (p_init_param->reset_memory == 1) { reg = peek32(MISC_CTRL); reg &= ~MISC_CTRL_LOCALMEM_RESET; poke32(MISC_CTRL, reg); @@ -261,7 +260,7 @@ int ddk750_init_hw(struct initchip_param *pInitParam) poke32(MISC_CTRL, reg); } - if (pInitParam->setAllEngOff == 1) { + if (p_init_param->set_all_eng_off == 1) { sm750_enable_2d_engine(0); /* Disable Overlay, if a former application left it on */ @@ -337,13 +336,13 @@ unsigned int sm750_calc_pll_value(unsigned int request_orig, ret = 0; mini_diff = ~0; request = request_orig / 1000; - input = pll->inputFreq / 1000; + input = pll->input_freq / 1000; /* * for MXCLK register, * no POD provided, so need be treated differently */ - if (pll->clockType == MXCLK_PLL) + if (pll->clock_type == MXCLK_PLL) max_d = 3; for (N = 15; N > 1; N--) { @@ -365,7 +364,7 @@ unsigned int sm750_calc_pll_value(unsigned int request_orig, if (M < 256 && M > 0) { unsigned int diff; - tmp_clock = pll->inputFreq * M / N / X; + tmp_clock = pll->input_freq * M / N / X; diff = abs(tmp_clock - request_orig); if (diff < mini_diff) { pll->M = M; @@ -383,14 +382,14 @@ unsigned int sm750_calc_pll_value(unsigned int request_orig, return ret; } -unsigned int sm750_format_pll_reg(struct pll_value *pPLL) +unsigned int sm750_format_pll_reg(struct pll_value *p_PLL) { #ifndef VALIDATION_CHIP - unsigned int POD = pPLL->POD; + unsigned int POD = p_PLL->POD; #endif - unsigned int OD = pPLL->OD; - unsigned int M = pPLL->M; - unsigned int N = pPLL->N; + unsigned int OD = p_PLL->OD; + unsigned int M = p_PLL->M; + unsigned int N = p_PLL->N; /* * Note that all PLL's have the same format. Here, we just use diff --git a/drivers/staging/sm750fb/ddk750_chip.h b/drivers/staging/sm750fb/ddk750_chip.h index 3e92b3297160..ee2e9d90f7dd 100644 --- a/drivers/staging/sm750fb/ddk750_chip.h +++ b/drivers/staging/sm750fb/ddk750_chip.h @@ -40,8 +40,8 @@ enum clock_type { }; struct pll_value { - enum clock_type clockType; - unsigned long inputFreq; /* Input clock frequency to the PLL */ + enum clock_type clock_type; + unsigned long input_freq; /* Input clock frequency to the PLL */ /* Use this when clockType = PANEL_PLL */ unsigned long M; @@ -53,41 +53,41 @@ struct pll_value { /* input struct to initChipParam() function */ struct initchip_param { /* Use power mode 0 or 1 */ - unsigned short powerMode; + unsigned short power_mode; /* * Speed of main chip clock in MHz unit * 0 = keep the current clock setting * Others = the new main chip clock */ - unsigned short chipClock; + unsigned short chip_clock; /* * Speed of memory clock in MHz unit * 0 = keep the current clock setting * Others = the new memory clock */ - unsigned short memClock; + unsigned short mem_clock; /* * Speed of master clock in MHz unit * 0 = keep the current clock setting * Others = the new master clock */ - unsigned short masterClock; + unsigned short master_clock; /* * 0 = leave all engine state untouched. * 1 = make sure they are off: 2D, Overlay, * video alpha, alpha, hardware cursors */ - unsigned short setAllEngOff; + unsigned short set_all_eng_off; /* * 0 = Do not reset the memory controller * 1 = Reset the memory controller */ - unsigned char resetMemory; + unsigned char reset_memory; /* More initialization parameter can be added if needed */ }; @@ -95,7 +95,7 @@ struct initchip_param { enum logical_chip_type sm750_get_chip_type(void); void sm750_set_chip_type(unsigned short dev_id, u8 rev_id); unsigned int sm750_calc_pll_value(unsigned int request, struct pll_value *pll); -unsigned int sm750_format_pll_reg(struct pll_value *pPLL); +unsigned int sm750_format_pll_reg(struct pll_value *p_PLL); unsigned int ddk750_get_vm_size(void); int ddk750_init_hw(struct initchip_param *pinit_param); diff --git a/drivers/staging/sm750fb/ddk750_mode.c b/drivers/staging/sm750fb/ddk750_mode.c index 4dac691ad1b1..e0230f4ffc8b 100644 --- a/drivers/staging/sm750fb/ddk750_mode.c +++ b/drivers/staging/sm750fb/ddk750_mode.c @@ -81,7 +81,7 @@ static int programModeRegisters(struct mode_parameter *pModeParam, int cnt = 0; unsigned int tmp, reg; - if (pll->clockType == SECONDARY_PLL) { + if (pll->clock_type == SECONDARY_PLL) { /* programe secondary pixel clock */ poke32(CRT_PLL_CTRL, sm750_format_pll_reg(pll)); @@ -134,7 +134,7 @@ static int programModeRegisters(struct mode_parameter *pModeParam, poke32(CRT_DISPLAY_CTRL, tmp | reg); } - } else if (pll->clockType == PRIMARY_PLL) { + } else if (pll->clock_type == PRIMARY_PLL) { unsigned int reserved; poke32(PANEL_PLL_CTRL, sm750_format_pll_reg(pll)); @@ -209,12 +209,11 @@ static int programModeRegisters(struct mode_parameter *pModeParam, int ddk750_setModeTiming(struct mode_parameter *parm, enum clock_type clock) { struct pll_value pll; - unsigned int uiActualPixelClk; - pll.inputFreq = DEFAULT_INPUT_CLOCK; - pll.clockType = clock; + pll.input_freq = DEFAULT_INPUT_CLOCK; + pll.clock_type = clock; - uiActualPixelClk = sm750_calc_pll_value(parm->pixel_clock, &pll); + sm750_calc_pll_value(parm->pixel_clock, &pll); if (sm750_get_chip_type() == SM750LE) { /* set graphic mode via IO method */ outb_p(0x88, 0x3d4); diff --git a/drivers/staging/speakup/sysfs-driver-speakup b/drivers/staging/speakup/sysfs-driver-speakup new file mode 100644 index 000000000000..be3f5d6962e9 --- /dev/null +++ b/drivers/staging/speakup/sysfs-driver-speakup @@ -0,0 +1,369 @@ +What: /sys/accessibility/speakup/attrib_bleep +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Beeps the PC speaker when there is an attribute change such as + foreground or background color when using speakup review + commands. One = on, zero = off. + +What: /sys/accessibility/speakup/bell_pos +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This works much like a typewriter bell. If for example 72 is + echoed to bell_pos, it will beep the PC speaker when typing on + a line past character 72. + +What: /sys/accessibility/speakup/bleeps +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls whether one hears beeps through the PC speaker + when using speakup's review commands. + TODO: what values does it accept? + +What: /sys/accessibility/speakup/bleep_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls the duration of the PC speaker beeps speakup + produces. + TODO: What are the units? Jiffies? + +What: /sys/accessibility/speakup/cursor_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls cursor delay when using arrow keys. When a + connection is very slow, with the default setting, when moving + with the arrows, or backspacing etc. speakup says the incorrect + characters. Set this to a higher value to adjust for the delay + and better synchronisation between cursor position and speech. + +What: /sys/accessibility/speakup/delimiters +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Delimit a word from speakup. + TODO: add more info + +What: /sys/accessibility/speakup/ex_num +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/key_echo +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls if speakup speaks keys when they are typed. One = on, + zero = off or don't echo keys. + +What: /sys/accessibility/speakup/keymap +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Speakup keymap remaps keys to Speakup functions. + It uses a binary + format. A special program called genmap is needed to compile a + textual keymap into the binary format which is then loaded into + /sys/accessibility/speakup/keymap. + +What: /sys/accessibility/speakup/no_interrupt +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls if typing interrupts output from speakup. With + no_interrupt set to zero, typing on the keyboard will interrupt + speakup if for example + the say screen command is used before the + entire screen is read. + With no_interrupt set to one, if the say + screen command is used, and one then types on the keyboard, + speakup will continue to say the whole screen regardless until + it finishes. + +What: /sys/accessibility/speakup/punc_all +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is a list of all the punctuation speakup should speak when + punc_level is set to four. + +What: /sys/accessibility/speakup/punc_level +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls the level of punctuation spoken as the screen is + displayed, not reviewed. Levels range from zero no punctuation, + to four, all punctuation. One corresponds to punc_some, two + corresponds to punc_most, and three as well as four both + correspond to punc_all. Some hardware synthesizers may have + different levels each corresponding to three and four for + punc_level. Also note that if punc_level is set to zero, and + key_echo is set to one, typed punctuation is still spoken as it + is typed. + +What: /sys/accessibility/speakup/punc_most +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is a list of all the punctuation speakup should speak when + punc_level is set to two. + +What: /sys/accessibility/speakup/punc_some +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is a list of all the punctuation speakup should speak when + punc_level is set to one. + +What: /sys/accessibility/speakup/reading_punc +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Almost the same as punc_level, the differences being that + reading_punc controls the level of punctuation when reviewing + the screen with speakup's screen review commands. The other + difference is that reading_punc set to three speaks punc_all, + and reading_punc set to four speaks all punctuation, including + spaces. + +What: /sys/accessibility/speakup/repeats +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: A list of characters speakup repeats. Normally, when there are + more than three characters in a row, speakup + just reads three of + those characters. For example, "......" would be read as dot, + dot, dot. If a . is added to the list of characters in repeats, + "......" would be read as dot, dot, dot, times six. + +What: /sys/accessibility/speakup/say_control +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: If set to one, speakup speaks shift, alt and control when those + keys are pressed. If say_control is set to zero, shift, ctrl, + and alt are not spoken when they are pressed. + +What: /sys/accessibility/speakup/say_word_ctl +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/silent +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/spell_delay +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls how fast a word is spelled + when speakup's say word + review command is pressed twice quickly to speak the current + word being reviewed. Zero just speaks the letters one after + another, while values one through four + seem to introduce more of + a pause between the spelling of each letter by speakup. + +What: /sys/accessibility/speakup/synth +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the synthesizer driver currently in use. Reading + synth returns the synthesizer driver currently in use. Writing + synth switches to the given synthesizer driver, provided it is + either built into the kernel, or already loaded as a module. + +What: /sys/accessibility/speakup/synth_direct +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Sends whatever is written to synth_direct + directly to the speech synthesizer in use, bypassing speakup. + This could be used to make the synthesizer speak + a string, or to + send control sequences to the synthesizer to change how the + synthesizer behaves. + +What: /sys/accessibility/speakup/version +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Reading version returns the version of speakup, and the version + of the synthesizer driver currently in use. + +What: /sys/accessibility/speakup/i18n/announcements +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This file contains various general announcements, most of which + cannot be categorized. You will find messages such as "You + killed Speakup", "I'm alive", "leaving help", "parked", + "unparked", and others. You will also find the names of the + screen edges and cursor tracking modes here. + +What: /sys/accessibility/speakup/i18n/chartab +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO + +What: /sys/accessibility/speakup/i18n/ctl_keys +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Here, you will find names of control keys. These are used with + Speakup's say_control feature. + +What: /sys/accessibility/speakup/i18n/function_names +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Here, you will find a list of names for Speakup functions. + These are used by the help system. For example, suppose that + you have activated help mode, and you pressed + keypad 3. Speakup + says: "keypad 3 is character, say next." + The message "character, say next" names a Speakup function, and + it comes from this function_names file. + +What: /sys/accessibility/speakup/i18n/states +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This file contains names for key states. + Again, these are part of the help system. For instance, if you + had pressed speakup + keypad 3, you would hear: + "speakup keypad 3 is go to bottom edge." + The speakup key is depressed, so the name of the key state is + speakup. + This part of the message comes from the states collection. + +What: /sys/accessibility/speakup/i18n/characters +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Through this sys entry, Speakup gives you the ability to change + how Speakup pronounces a given character. You could, for + example, change how some punctuation characters are spoken. You + can even change how Speakup will pronounce certain letters. For + further details see '12. Changing the Pronunciation of + Characters' in Speakup User's Guide (file spkguide.txt in + source). + +What: /sys/accessibility/speakup/i18n/colors +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: When you use the "say attributes" function, Speakup says the + name of the foreground and background colors. These names come + from the i18n/colors file. + +What: /sys/accessibility/speakup/i18n/formatted +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This group of messages contains embedded formatting codes, to + specify the type and width of displayed data. If you change + these, you must preserve all of the formatting codes, and they + must appear in the order used by the default messages. + +What: /sys/accessibility/speakup/i18n/key_names +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Again, key_names is used by Speakup's help system. In the + previous example, Speakup said that you pressed "keypad 3." + This name came from the key_names file. + +What: /sys/accessibility/speakup/<synth-name>/ +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: In `/sys/accessibility/speakup` is a directory corresponding to + the synthesizer driver currently in use (E.G) `soft` for the + soft driver. This directory contains files which control the + speech synthesizer itself, + as opposed to controlling the speakup + screen reader. The parameters in this directory have the same + names and functions across all + supported synthesizers. The range + of values for freq, pitch, rate, and vol is the same for all + supported synthesizers, with the given range being internally + mapped by the driver to more or less fit the range of values + supported for a given parameter by the individual synthesizer. + Below is a description of values and parameters for soft + synthesizer, which is currently the most commonly used. + +What: /sys/accessibility/speakup/soft/caps_start +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is the string that is sent to the synthesizer to cause it + to start speaking uppercase letters. For the soft synthesizer + and most others, this causes the pitch of the voice to rise + above the currently set pitch. + +What: /sys/accessibility/speakup/soft/caps_stop +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This is the string sent to the synthesizer to cause it to stop + speaking uppercase letters. In the case of the soft synthesizer + and most others, this returns the pitch of the voice + down to the + currently set pitch. + +What: /sys/accessibility/speakup/soft/delay_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/soft/direct +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Controls if punctuation is spoken by speakup, or by the + synthesizer. + For example, speakup speaks ">" as "greater", while + the espeak synthesizer used by the soft driver speaks "greater + than". Zero lets speakup speak the punctuation. One lets the + synthesizer itself speak punctuation. + +What: /sys/accessibility/speakup/soft/freq +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the frequency of the speech synthesizer. Range is + 0-9. + +What: /sys/accessibility/speakup/soft/full_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/soft/jiffy_delta +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: This controls how many jiffys the kernel gives to the + synthesizer. Setting this too high can make a system unstable, + or even crash it. + +What: /sys/accessibility/speakup/soft/pitch +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the pitch of the synthesizer. The range is 0-9. + +What: /sys/accessibility/speakup/soft/punct +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the amount of punctuation spoken by the + synthesizer. The range for the soft driver seems to be 0-2. + TODO: How is this related to speakup's punc_level, or + reading_punc. + +What: /sys/accessibility/speakup/soft/rate +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the rate of the synthesizer. Range is from zero + slowest, to nine fastest. + +What: /sys/accessibility/speakup/soft/tone +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the tone of the speech synthesizer. The range for + the soft driver seems to be 0-2. This seems to make no + difference if using espeak and the espeakup connector. + TODO: does espeakup support different tonalities? + +What: /sys/accessibility/speakup/soft/trigger_time +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: TODO: + +What: /sys/accessibility/speakup/soft/voice +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the voice used by the synthesizer if the + synthesizer can speak in more than one voice. The range for the + soft driver is 0-7. Note that while espeak supports multiple + voices, this parameter will not set the voice when the espeakup + connector is used between speakup and espeak. + +What: /sys/accessibility/speakup/soft/vol +KernelVersion: 2.6 +Contact: speakup@linux-speakup.org +Description: Gets or sets the volume of the speech synthesizer. Range is 0-9, + with zero being the softest, and nine being the loudest. + diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c index bc1eaa3a0773..826016c3431a 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c @@ -12,7 +12,7 @@ static const struct snd_pcm_hardware snd_bcm2835_playback_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_DRAIN_TRIGGER | SNDRV_PCM_INFO_SYNC_APPLPTR), + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, @@ -29,7 +29,7 @@ static const struct snd_pcm_hardware snd_bcm2835_playback_hw = { static const struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_DRAIN_TRIGGER | SNDRV_PCM_INFO_SYNC_APPLPTR), + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c index 23fba01107b9..5f6a73af57f9 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c @@ -103,6 +103,9 @@ static void audio_vchi_callback(void *param, status = vchi_msg_dequeue(instance->vchi_handle, &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE); + if (status) + return; + if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { instance->result = m.result.success; complete(&instance->msg_avail_comp); @@ -289,6 +292,7 @@ int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) VC_AUDIO_MSG_TYPE_STOP, false); } +/* FIXME: this doesn't seem working as expected for "draining" */ int bcm2835_audio_drain(struct bcm2835_alsa_stream *alsa_stream) { struct vc_audio_msg m = { diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index d4d1e44b16b2..beb6a0063bb8 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1090,8 +1090,8 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, ret = vchiq_mmal_port_set_format(dev->instance, camera_port); - if (!ret - && camera_port == + if (!ret && + camera_port == &dev->component[COMP_CAMERA]->output[CAM_PORT_VIDEO]) { bool overlay_enabled = !!dev->component[COMP_PREVIEW]->enabled; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index 8dc730cfe7a6..b86d7e436357 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -81,7 +81,6 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); struct rpi_firmware *fw = drvdata->fw; struct vchiq_slot_zero *vchiq_slot_zero; - struct resource *res; void *slot_mem; dma_addr_t slot_phys; u32 channelbase; @@ -135,8 +134,7 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) if (vchiq_init_state(state, vchiq_slot_zero) != VCHIQ_SUCCESS) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - g_regs = devm_ioremap_resource(&pdev->dev, res); + g_regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(g_regs)) return PTR_ERR(g_regs); @@ -526,11 +524,11 @@ create_pagelist(char __user *buf, size_t count, unsigned short type) return NULL; } - WARN_ON(g_free_fragments == NULL); + WARN_ON(!g_free_fragments); down(&g_free_fragments_mutex); fragments = g_free_fragments; - WARN_ON(fragments == NULL); + WARN_ON(!fragments); g_free_fragments = *(char **) g_free_fragments; up(&g_free_fragments_mutex); pagelist->type = PAGELIST_READ_WITH_FRAGMENTS + diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index b1595b13dea8..dc54f443ef3d 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -516,7 +516,7 @@ vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, ***************************************************************************/ static VCHIQ_STATUS_T -add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, +add_completion(VCHIQ_INSTANCE_T instance, enum vchiq_reason reason, struct vchiq_header *header, struct user_service *user_service, void *bulk_userdata) { @@ -583,7 +583,7 @@ add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, ***************************************************************************/ static VCHIQ_STATUS_T -service_callback(VCHIQ_REASON_T reason, struct vchiq_header *header, +service_callback(enum vchiq_reason reason, struct vchiq_header *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) { /* How do we ensure the callback goes to the right client? @@ -827,7 +827,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* Remove all services */ i = 0; while ((service = next_service_by_instance(instance->state, - instance, &i)) != NULL) { + instance, &i))) { status = vchiq_remove_service(service->handle); unlock_service(service); if (status != VCHIQ_SUCCESS) @@ -907,7 +907,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) &args.params, srvstate, instance, user_service_free); - if (service != NULL) { + if (service) { user_service->service = service; user_service->userdata = userdata; user_service->instance = instance; @@ -988,7 +988,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; service = find_service_for_instance(instance, handle); - if (service != NULL) { + if (service) { status = (cmd == VCHIQ_IOC_USE_SERVICE) ? vchiq_use_service_internal(service) : vchiq_release_service_internal(service); @@ -1021,7 +1021,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) service = find_service_for_instance(instance, args.handle); - if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { + if (service && (args.count <= MAX_ELEMENTS)) { /* Copy elements into kernel space */ struct vchiq_element elements[MAX_ELEMENTS]; @@ -1343,11 +1343,11 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) spin_unlock(&msg_queue_spinlock); complete(&user_service->remove_event); - if (header == NULL) + if (!header) ret = -ENOTCONN; else if (header->size <= args.bufsize) { /* Copy to user space if msgbuf is not NULL */ - if ((args.buf == NULL) || + if (!args.buf || (copy_to_user((void __user *)args.buf, header->data, header->size) == 0)) { @@ -1426,7 +1426,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; service = find_closed_service_for_instance(instance, handle); - if (service != NULL) { + if (service) { struct user_service *user_service = (struct user_service *)service->base.userdata; close_delivered(user_service); @@ -1666,7 +1666,7 @@ vchiq_compat_ioctl_queue_bulk(struct file *file, } struct vchiq_completion_data32 { - VCHIQ_REASON_T reason; + enum vchiq_reason reason; compat_uptr_t header; compat_uptr_t service_userdata; compat_uptr_t bulk_userdata; @@ -2223,13 +2223,13 @@ struct vchiq_state * vchiq_get_state(void) { - if (g_state.remote == NULL) + if (!g_state.remote) printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__); else if (g_state.remote->initialised != 1) printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n", __func__, g_state.remote->initialised); - return ((g_state.remote != NULL) && + return (g_state.remote && (g_state.remote->initialised == 1)) ? &g_state : NULL; } @@ -2271,7 +2271,7 @@ vchiq_videocore_wanted(struct vchiq_state *state) } static VCHIQ_STATUS_T -vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason, +vchiq_keepalive_vchiq_callback(enum vchiq_reason reason, struct vchiq_header *header, VCHIQ_SERVICE_HANDLE_T service_user, void *bulk_user) @@ -2924,7 +2924,7 @@ vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) i = 0; while ((service = next_service_by_instance(instance->state, - instance, &i)) != NULL) { + instance, &i))) { use_count += service->service_use_count; unlock_service(service); } @@ -2951,7 +2951,7 @@ vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) i = 0; while ((service = next_service_by_instance(instance->state, - instance, &i)) != NULL) { + instance, &i))) { service->trace = trace; unlock_service(service); } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 56a23a297fa4..e1898cfa5e91 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -355,7 +355,7 @@ mark_service_closing(struct vchiq_service *service) } static inline VCHIQ_STATUS_T -make_service_callback(struct vchiq_service *service, VCHIQ_REASON_T reason, +make_service_callback(struct vchiq_service *service, enum vchiq_reason reason, struct vchiq_header *header, void *bulk_userdata) { VCHIQ_STATUS_T status; @@ -542,7 +542,7 @@ reserve_space(struct vchiq_state *state, size_t space, int is_blocking) if (space > slot_space) { struct vchiq_header *header; /* Fill the remaining space with padding */ - WARN_ON(state->tx_data == NULL); + WARN_ON(!state->tx_data); header = (struct vchiq_header *) (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); header->msgid = VCHIQ_MSGID_PADDING; @@ -1230,7 +1230,7 @@ notify_bulks(struct vchiq_service *service, struct vchiq_bulk_queue *queue, spin_unlock(&bulk_waiter_spinlock); } else if (bulk->mode == VCHIQ_BULK_MODE_CALLBACK) { - VCHIQ_REASON_T reason = (bulk->dir == + enum vchiq_reason reason = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? ((bulk->actual == VCHIQ_BULK_ACTUAL_ABORTED) ? @@ -2132,9 +2132,6 @@ vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero) char threadname[16]; int i; - vchiq_log_warning(vchiq_core_log_level, - "%s: slot_zero = %pK", __func__, slot_zero); - if (vchiq_states[0]) { pr_err("%s: VCHIQ state already initialized\n", __func__); return VCHIQ_ERROR; @@ -3578,7 +3575,7 @@ void vchiq_log_dump_mem(const char *label, u32 addr, const void *void_mem, } *s++ = '\0'; - if ((label != NULL) && (*label != '\0')) + if (label && (*label != '\0')) vchiq_log_trace(VCHIQ_LOG_TRACE, "%s: %08x: %s", label, addr, line_buf); else diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 63f71b2a492f..75104986201b 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -60,8 +60,8 @@ vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE)); #define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) #define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) -#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(struct vchiq_slot_zero) + \ - VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE) +#define VCHIQ_SLOT_ZERO_SLOTS DIV_ROUND_UP(sizeof(struct vchiq_slot_zero), \ + VCHIQ_SLOT_SIZE) #define VCHIQ_MSG_PADDING 0 /* - */ #define VCHIQ_MSG_CONNECT 1 /* - */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h index c23bd105c40f..f911612a6a54 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h @@ -15,7 +15,7 @@ #define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) #define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) -typedef enum { +enum vchiq_reason { VCHIQ_SERVICE_OPENED, /* service, -, - */ VCHIQ_SERVICE_CLOSED, /* service, -, - */ VCHIQ_MESSAGE_AVAILABLE, /* service, header, - */ @@ -23,7 +23,7 @@ typedef enum { VCHIQ_BULK_RECEIVE_DONE, /* service, -, bulk_userdata */ VCHIQ_BULK_TRANSMIT_ABORTED, /* service, -, bulk_userdata */ VCHIQ_BULK_RECEIVE_ABORTED /* service, -, bulk_userdata */ -} VCHIQ_REASON_T; +}; typedef enum { VCHIQ_ERROR = -1, @@ -63,7 +63,7 @@ struct vchiq_element { typedef unsigned int VCHIQ_SERVICE_HANDLE_T; -typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, +typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(enum vchiq_reason, struct vchiq_header *, VCHIQ_SERVICE_HANDLE_T, void *); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h index 460ccea088bf..c2343a1a3a6a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h @@ -32,7 +32,7 @@ struct vchiq_queue_bulk_transfer { }; struct vchiq_completion_data { - VCHIQ_REASON_T reason; + enum vchiq_reason reason; struct vchiq_header *header; void *service_userdata; void *bulk_userdata; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c index 17a4f2c8d8b1..5e26f0abd5ee 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c @@ -509,7 +509,7 @@ EXPORT_SYMBOL(vchi_disconnect); * ***********************************************************/ -static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, +static VCHIQ_STATUS_T shim_callback(enum vchiq_reason reason, struct vchiq_header *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) @@ -628,7 +628,7 @@ int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, } } - return (service != NULL) ? 0 : -1; + return service ? 0 : -1; } EXPORT_SYMBOL(vchi_service_open); diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index c6bb4aaf9bd0..082302944c37 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -1748,8 +1748,10 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent) priv->hw->max_signal = 100; - if (vnt_init(priv)) + if (vnt_init(priv)) { + device_free_info(priv); return -ENODEV; + } device_print_info(priv); pci_set_drvdata(pcid, priv); diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 856ba97aec4f..4ac85ecb0921 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -249,10 +249,10 @@ static int vnt_init_registers(struct vnt_private *priv) } else { priv->tx_antenna_mode = ANT_B; - if (priv->tx_rx_ant_inv) - priv->rx_antenna_mode = ANT_A; - else - priv->rx_antenna_mode = ANT_B; + if (priv->tx_rx_ant_inv) + priv->rx_antenna_mode = ANT_A; + else + priv->rx_antenna_mode = ANT_B; } } @@ -362,7 +362,6 @@ static int vnt_init_registers(struct vnt_private *priv) goto end; } - ret = vnt_mac_set_led(priv, LEDSTS_TMLEN, 0x38); if (ret) goto end; diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c index 4e9cfacf75f2..f9020a4f7bbf 100644 --- a/drivers/staging/vt6656/rxtx.c +++ b/drivers/staging/vt6656/rxtx.c @@ -112,11 +112,11 @@ static u32 vnt_get_rsvtime(struct vnt_private *priv, u8 pkt_type, frame_length, rate); if (pkt_type == PK_TYPE_11B) - ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type, - 14, (u16)priv->top_cck_basic_rate); + ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type, 14, + (u16)priv->top_cck_basic_rate); else - ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type, - 14, (u16)priv->top_ofdm_basic_rate); + ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type, 14, + (u16)priv->top_ofdm_basic_rate); if (need_ack) return data_time + priv->sifs + ack_time; diff --git a/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt new file mode 100644 index 000000000000..26de6762b942 --- /dev/null +++ b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt @@ -0,0 +1,97 @@ +The WFxxx chip series can be connected via SPI or via SDIO. + +SPI +--- + +You have to declare the WFxxx chip in your device tree. + +Required properties: + - compatible: Should be "silabs,wfx-spi" + - reg: Chip select address of device + - spi-max-frequency: Maximum SPI clocking speed of device in Hz + - interrupts-extended: Should contain interrupt line (interrupt-parent + + interrupt can also been used). Trigger should be `IRQ_TYPE_EDGE_RISING`. + +Optional properties: + - reset-gpios: phandle of gpio that will be used to reset chip during probe. + Without this property, you may encounter issues with warm boot. + +Please consult Documentation/devicetree/bindings/spi/spi-bus.txt for optional +SPI connection related properties, + +Example: + +&spi1 { + wfx { + compatible = "silabs,wfx-spi"; + pinctrl-names = "default"; + pinctrl-0 = <&wfx_irq &wfx_gpios>; + interrupts-extended = <&gpio 16 IRQ_TYPE_EDGE_RISING>; + wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + reg = <0>; + spi-max-frequency = <42000000>; + }; +}; + + +SDIO +---- + +The driver is able to detect a WFxxx chip on SDIO bus by matching its Vendor ID +and Product ID. However, driver will only provide limited features in this +case. Thus declaring WFxxx chip in device tree is strongly recommended (and may +become mandatory in the future). + +Required properties: + - compatible: Should be "silabs,wfx-sdio" + - reg: Should be 1 + +In addition, it is recommended to declare a mmc-pwrseq on SDIO host above WFx. +Without it, you may encounter issues with warm boot. mmc-pwrseq should be +compatible with mmc-pwrseq-simple. Please consult +Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt for more +information. + +Example: + +/ { + wfx_pwrseq: wfx_pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wfx_reset>; + reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + }; +}; + +&mmc1 { + mmc-pwrseq = <&wfx_pwrseq>; + #address-size = <1>; + #size = <0>; + + mmc@1 { + compatible = "silabs,wfx-sdio"; + reg = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&wfx_wakeup>; + wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>; + }; +}; + +Note that #address-size and #size shoud already be defined in node mmc1, but it +is rarely the case. + +Common properties +----------------- + +Some properties are recognized either by SPI and SDIO versions: + - wakeup-gpios: phandle of gpio that will be used to wake-up chip. Without + this property, driver will disable most of power saving features. + - config-file: Use an alternative file as PDS. Default is `wf200.pds`. Only + necessary for development/debug purpose. + - slk_key: String representing hexadecimal value of secure link key to use. + Must contains 64 hexadecimal digits. Not supported in current version. + +WFx driver also supports `mac-address` and `local-mac-address` as described in +Documentation/devicetree/binding/net/ethernet.txt + diff --git a/drivers/staging/wfx/Kconfig b/drivers/staging/wfx/Kconfig new file mode 100644 index 000000000000..9b8a1c7a9e90 --- /dev/null +++ b/drivers/staging/wfx/Kconfig @@ -0,0 +1,7 @@ +config WFX + tristate "Silicon Labs wireless chips WF200 and further" + depends on MAC80211 + depends on (SPI || MMC) + help + This is a driver for Silicons Labs WFxxx series (WF200 and further) + chipsets. This chip can be found on SPI or SDIO buses. diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile new file mode 100644 index 000000000000..0d9c1ed092f6 --- /dev/null +++ b/drivers/staging/wfx/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Necessary for CREATE_TRACE_POINTS +CFLAGS_debug.o = -I$(src) + +wfx-y := \ + bh.o \ + hwio.o \ + fwio.o \ + hif_tx.o \ + hif_rx.o \ + queue.o \ + data_tx.o \ + data_rx.o \ + scan.o \ + sta.o \ + key.o \ + main.o \ + sta.o \ + debug.o +wfx-$(CONFIG_SPI) += bus_spi.o +wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o + +obj-$(CONFIG_WFX) += wfx.o diff --git a/drivers/staging/wfx/TODO b/drivers/staging/wfx/TODO new file mode 100644 index 000000000000..be990e8f18b1 --- /dev/null +++ b/drivers/staging/wfx/TODO @@ -0,0 +1,20 @@ +This is a list of things that need to be done to get this driver out of the +staging directory. + + - wfx_version.h is still there in order to ensure synchronization with github. + It can be dropped as soon as development is entirely in kernel + + - I have to take a decision about secure link support. I can: + - drop completely + - keep it in an external patch (my preferred option) + - replace call to mbedtls with kernel crypto API (necessitate a + bunch of work) + - pull mbedtls in kernel (non-realistic) + + - mac80211 interface does not (yet) have expected quality to be placed + outside of staging: + - Some processings are redundant with mac80211 ones + - Many members from wfx_dev/wfx_vif can be retrieved from mac80211 + structures + - Some functions are too complex + - ... diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c new file mode 100644 index 000000000000..3355183fc86c --- /dev/null +++ b/drivers/staging/wfx/bh.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Interrupt bottom half (BH). + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/gpio/consumer.h> +#include <net/mac80211.h> + +#include "bh.h" +#include "wfx.h" +#include "hwio.h" +#include "traces.h" +#include "secure_link.h" +#include "hif_rx.h" +#include "hif_api_cmd.h" + +static void device_wakeup(struct wfx_dev *wdev) +{ + if (!wdev->pdata.gpio_wakeup) + return; + if (gpiod_get_value(wdev->pdata.gpio_wakeup)) + return; + + gpiod_set_value(wdev->pdata.gpio_wakeup, 1); + if (wfx_api_older_than(wdev, 1, 4)) { + if (!completion_done(&wdev->hif.ctrl_ready)) + udelay(2000); + } else { + // completion.h does not provide any function to wait + // completion without consume it (a kind of + // wait_for_completion_done_timeout()). So we have to emulate + // it. + if (wait_for_completion_timeout(&wdev->hif.ctrl_ready, msecs_to_jiffies(2) + 1)) + complete(&wdev->hif.ctrl_ready); + else + dev_err(wdev->dev, "timeout while wake up chip\n"); + } +} + +static void device_release(struct wfx_dev *wdev) +{ + if (!wdev->pdata.gpio_wakeup) + return; + + gpiod_set_value(wdev->pdata.gpio_wakeup, 0); +} + +static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) +{ + struct sk_buff *skb; + struct hif_msg *hif; + size_t alloc_len; + size_t computed_len; + int release_count; + int piggyback = 0; + + WARN(read_len < 4, "corrupted read"); + WARN(read_len > round_down(0xFFF, 2) * sizeof(u16), + "%s: request exceed WFx capability", __func__); + + // Add 2 to take into account piggyback size + alloc_len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, read_len + 2); + skb = dev_alloc_skb(alloc_len); + if (!skb) + return -ENOMEM; + + if (wfx_data_read(wdev, skb->data, alloc_len)) + goto err; + + piggyback = le16_to_cpup((u16 *) (skb->data + alloc_len - 2)); + _trace_piggyback(piggyback, false); + + hif = (struct hif_msg *) skb->data; + WARN(hif->encrypted & 0x1, "unsupported encryption type"); + if (hif->encrypted == 0x2) { + if (wfx_sl_decode(wdev, (void *) hif)) { + dev_kfree_skb(skb); + // If frame was a confirmation, expect trouble in next + // exchange. However, it is harmless to fail to decode + // an indication frame, so try to continue. Anyway, + // piggyback is probably correct. + return piggyback; + } + le16_to_cpus(&hif->len); + computed_len = round_up(hif->len - sizeof(hif->len), 16) + + sizeof(struct hif_sl_msg) + + sizeof(struct hif_sl_tag); + } else { + le16_to_cpus(&hif->len); + computed_len = round_up(hif->len, 2); + } + if (computed_len != read_len) { + dev_err(wdev->dev, "inconsistent message length: %zu != %zu\n", + computed_len, read_len); + print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET, 16, 1, + hif, read_len, true); + goto err; + } + + if (!(hif->id & HIF_ID_IS_INDICATION)) { + (*is_cnf)++; + if (hif->id == HIF_CNF_ID_MULTI_TRANSMIT) + release_count = le32_to_cpu(((struct hif_cnf_multi_transmit *) hif->body)->num_tx_confs); + else + release_count = 1; + WARN(wdev->hif.tx_buffers_used < release_count, "corrupted buffer counter"); + wdev->hif.tx_buffers_used -= release_count; + if (!wdev->hif.tx_buffers_used) + wake_up(&wdev->hif.tx_buffers_empty); + } + _trace_hif_recv(hif, wdev->hif.tx_buffers_used); + + if (hif->id != HIF_IND_ID_EXCEPTION && hif->id != HIF_IND_ID_ERROR) { + if (hif->seqnum != wdev->hif.rx_seqnum) + dev_warn(wdev->dev, "wrong message sequence: %d != %d\n", + hif->seqnum, wdev->hif.rx_seqnum); + wdev->hif.rx_seqnum = (hif->seqnum + 1) % (HIF_COUNTER_MAX + 1); + } + + skb_put(skb, hif->len); + // wfx_handle_rx takes care on SKB livetime + wfx_handle_rx(wdev, skb); + + return piggyback; + +err: + if (skb) + dev_kfree_skb(skb); + return -EIO; +} + +static int bh_work_rx(struct wfx_dev *wdev, int max_msg, int *num_cnf) +{ + size_t len; + int i; + int ctrl_reg, piggyback; + + piggyback = 0; + for (i = 0; i < max_msg; i++) { + if (piggyback & CTRL_NEXT_LEN_MASK) + ctrl_reg = piggyback; + else if (try_wait_for_completion(&wdev->hif.ctrl_ready)) + ctrl_reg = atomic_xchg(&wdev->hif.ctrl_reg, 0); + else + ctrl_reg = 0; + if (!(ctrl_reg & CTRL_NEXT_LEN_MASK)) + return i; + // ctrl_reg units are 16bits words + len = (ctrl_reg & CTRL_NEXT_LEN_MASK) * 2; + piggyback = rx_helper(wdev, len, num_cnf); + if (piggyback < 0) + return i; + if (!(piggyback & CTRL_WLAN_READY)) + dev_err(wdev->dev, "unexpected piggyback value: ready bit not set: %04x\n", + piggyback); + } + if (piggyback & CTRL_NEXT_LEN_MASK) { + ctrl_reg = atomic_xchg(&wdev->hif.ctrl_reg, piggyback); + complete(&wdev->hif.ctrl_ready); + if (ctrl_reg) + dev_err(wdev->dev, "unexpected IRQ happened: %04x/%04x\n", + ctrl_reg, piggyback); + } + return i; +} + +static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif) +{ + int ret; + void *data; + bool is_encrypted = false; + size_t len = le16_to_cpu(hif->len); + + WARN(len < sizeof(*hif), "try to send corrupted data"); + + hif->seqnum = wdev->hif.tx_seqnum; + wdev->hif.tx_seqnum = (wdev->hif.tx_seqnum + 1) % (HIF_COUNTER_MAX + 1); + + if (wfx_is_secure_command(wdev, hif->id)) { + len = round_up(len - sizeof(hif->len), 16) + sizeof(hif->len) + + sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_sl_tag); + // AES support encryption in-place. However, mac80211 access to + // 802.11 header after frame was sent (to get MAC addresses). + // So, keep origin buffer clear. + data = kmalloc(len, GFP_KERNEL); + if (!data) + goto end; + is_encrypted = true; + ret = wfx_sl_encode(wdev, hif, data); + if (ret) + goto end; + } else { + data = hif; + } + WARN(len > wdev->hw_caps.size_inp_ch_buf, + "%s: request exceed WFx capability: %zu > %d\n", __func__, + len, wdev->hw_caps.size_inp_ch_buf); + len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, len); + ret = wfx_data_write(wdev, data, len); + if (ret) + goto end; + + wdev->hif.tx_buffers_used++; + _trace_hif_send(hif, wdev->hif.tx_buffers_used); +end: + if (is_encrypted) + kfree(data); +} + +static int bh_work_tx(struct wfx_dev *wdev, int max_msg) +{ + struct hif_msg *hif; + int i; + + for (i = 0; i < max_msg; i++) { + hif = NULL; + if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) { + if (try_wait_for_completion(&wdev->hif_cmd.ready)) { + WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); + hif = wdev->hif_cmd.buf_send; + } else { + hif = wfx_tx_queues_get(wdev); + } + } + if (!hif) + return i; + tx_helper(wdev, hif); + } + return i; +} + +/* In SDIO mode, it is necessary to make an access to a register to acknowledge + * last received message. It could be possible to restrict this acknowledge to + * SDIO mode and only if last operation was rx. + */ +static void ack_sdio_data(struct wfx_dev *wdev) +{ + uint32_t cfg_reg; + + config_reg_read(wdev, &cfg_reg); + if (cfg_reg & 0xFF) { + dev_warn(wdev->dev, "chip reports errors: %02x\n", cfg_reg & 0xFF); + config_reg_write_bits(wdev, 0xFF, 0x00); + } +} + +static void bh_work(struct work_struct *work) +{ + struct wfx_dev *wdev = container_of(work, struct wfx_dev, hif.bh); + int stats_req = 0, stats_cnf = 0, stats_ind = 0; + bool release_chip = false, last_op_is_rx = false; + int num_tx, num_rx; + + device_wakeup(wdev); + do { + num_tx = bh_work_tx(wdev, 32); + stats_req += num_tx; + if (num_tx) + last_op_is_rx = false; + num_rx = bh_work_rx(wdev, 32, &stats_cnf); + stats_ind += num_rx; + if (num_rx) + last_op_is_rx = true; + } while (num_rx || num_tx); + stats_ind -= stats_cnf; + + if (last_op_is_rx) + ack_sdio_data(wdev); + if (!wdev->hif.tx_buffers_used && !work_pending(work) && !atomic_read(&wdev->scan_in_progress)) { + device_release(wdev); + release_chip = true; + } + _trace_bh_stats(stats_ind, stats_req, stats_cnf, wdev->hif.tx_buffers_used, release_chip); +} + +/* + * An IRQ from chip did occur + */ +void wfx_bh_request_rx(struct wfx_dev *wdev) +{ + u32 cur, prev; + + control_reg_read(wdev, &cur); + prev = atomic_xchg(&wdev->hif.ctrl_reg, cur); + complete(&wdev->hif.ctrl_ready); + queue_work(system_highpri_wq, &wdev->hif.bh); + + if (!(cur & CTRL_NEXT_LEN_MASK)) + dev_err(wdev->dev, "unexpected control register value: length field is 0: %04x\n", + cur); + if (prev != 0) + dev_err(wdev->dev, "received IRQ but previous data was not (yet) read: %04x/%04x\n", + prev, cur); +} + +/* + * Driver want to send data + */ +void wfx_bh_request_tx(struct wfx_dev *wdev) +{ + queue_work(system_highpri_wq, &wdev->hif.bh); +} + +void wfx_bh_register(struct wfx_dev *wdev) +{ + INIT_WORK(&wdev->hif.bh, bh_work); + init_completion(&wdev->hif.ctrl_ready); + init_waitqueue_head(&wdev->hif.tx_buffers_empty); +} + +void wfx_bh_unregister(struct wfx_dev *wdev) +{ + flush_work(&wdev->hif.bh); +} diff --git a/drivers/staging/wfx/bh.h b/drivers/staging/wfx/bh.h new file mode 100644 index 000000000000..93ca98424e0b --- /dev/null +++ b/drivers/staging/wfx/bh.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Interrupt bottom half. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_BH_H +#define WFX_BH_H + +#include <linux/atomic.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +struct wfx_dev; + +struct wfx_hif { + struct work_struct bh; + struct completion ctrl_ready; + wait_queue_head_t tx_buffers_empty; + atomic_t ctrl_reg; + int rx_seqnum; + int tx_seqnum; + int tx_buffers_used; +}; + +void wfx_bh_register(struct wfx_dev *wdev); +void wfx_bh_unregister(struct wfx_dev *wdev); +void wfx_bh_request_rx(struct wfx_dev *wdev); +void wfx_bh_request_tx(struct wfx_dev *wdev); + +#endif /* WFX_BH_H */ diff --git a/drivers/staging/wfx/bus.h b/drivers/staging/wfx/bus.h new file mode 100644 index 000000000000..eb77abc09ec2 --- /dev/null +++ b/drivers/staging/wfx/bus.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common bus abstraction layer. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_BUS_H +#define WFX_BUS_H + +#include <linux/mmc/sdio_func.h> +#include <linux/spi/spi.h> + +#define WFX_REG_CONFIG 0x0 +#define WFX_REG_CONTROL 0x1 +#define WFX_REG_IN_OUT_QUEUE 0x2 +#define WFX_REG_AHB_DPORT 0x3 +#define WFX_REG_BASE_ADDR 0x4 +#define WFX_REG_SRAM_DPORT 0x5 +#define WFX_REG_SET_GEN_R_W 0x6 +#define WFX_REG_FRAME_OUT 0x7 + +struct hwbus_ops { + int (*copy_from_io)(void *bus_priv, unsigned int addr, void *dst, size_t count); + int (*copy_to_io)(void *bus_priv, unsigned int addr, const void *src, size_t count); + void (*lock)(void *bus_priv); + void (*unlock)(void *bus_priv); + size_t (*align_size)(void *bus_priv, size_t size); +}; + +extern struct sdio_driver wfx_sdio_driver; +extern struct spi_driver wfx_spi_driver; + +#endif diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c new file mode 100644 index 000000000000..f97360513150 --- /dev/null +++ b/drivers/staging/wfx/bus_sdio.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SDIO interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/module.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/card.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> + +#include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" +#include "bh.h" + +static const struct wfx_platform_data wfx_sdio_pdata = { + .file_fw = "wfm_wf200", + .file_pds = "wf200.pds", +}; + +struct wfx_sdio_priv { + struct sdio_func *func; + struct wfx_dev *core; + u8 buf_id_tx; + u8 buf_id_rx; + int of_irq; +}; + +static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id, + void *dst, size_t count) +{ + struct wfx_sdio_priv *bus = priv; + unsigned int sdio_addr = reg_id << 2; + int ret; + + WARN(reg_id > 7, "chip only has 7 registers"); + WARN(((uintptr_t) dst) & 3, "unaligned buffer size"); + WARN(count & 3, "unaligned buffer address"); + + /* Use queue mode buffers */ + if (reg_id == WFX_REG_IN_OUT_QUEUE) + sdio_addr |= (bus->buf_id_rx + 1) << 7; + ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count); + if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE) + bus->buf_id_rx = (bus->buf_id_rx + 1) % 4; + + return ret; +} + +static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id, + const void *src, size_t count) +{ + struct wfx_sdio_priv *bus = priv; + unsigned int sdio_addr = reg_id << 2; + int ret; + + WARN(reg_id > 7, "chip only has 7 registers"); + WARN(((uintptr_t) src) & 3, "unaligned buffer size"); + WARN(count & 3, "unaligned buffer address"); + + /* Use queue mode buffers */ + if (reg_id == WFX_REG_IN_OUT_QUEUE) + sdio_addr |= bus->buf_id_tx << 7; + // FIXME: discards 'const' qualifier for src + ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *) src, count); + if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE) + bus->buf_id_tx = (bus->buf_id_tx + 1) % 32; + + return ret; +} + +static void wfx_sdio_lock(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_claim_host(bus->func); +} + +static void wfx_sdio_unlock(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_release_host(bus->func); +} + +static void wfx_sdio_irq_handler(struct sdio_func *func) +{ + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + if (bus->core) + wfx_bh_request_rx(bus->core); + else + WARN(!bus->core, "race condition in driver init/deinit"); +} + +static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + if (!bus->core) { + WARN(!bus->core, "race condition in driver init/deinit"); + return IRQ_NONE; + } + sdio_claim_host(bus->func); + wfx_bh_request_rx(bus->core); + sdio_release_host(bus->func); + return IRQ_HANDLED; +} + +static int wfx_sdio_irq_subscribe(struct wfx_sdio_priv *bus) +{ + int ret; + + if (bus->of_irq) { + ret = request_irq(bus->of_irq, wfx_sdio_irq_handler_ext, + IRQF_TRIGGER_RISING, "wfx", bus); + } else { + sdio_claim_host(bus->func); + ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler); + sdio_release_host(bus->func); + } + return ret; +} + +static int wfx_sdio_irq_unsubscribe(struct wfx_sdio_priv *bus) +{ + int ret; + + if (bus->of_irq) { + free_irq(bus->of_irq, bus); + ret = 0; + } else { + sdio_claim_host(bus->func); + ret = sdio_release_irq(bus->func); + sdio_release_host(bus->func); + } + return ret; +} + +static size_t wfx_sdio_align_size(void *priv, size_t size) +{ + struct wfx_sdio_priv *bus = priv; + + return sdio_align_size(bus->func, size); +} + +static const struct hwbus_ops wfx_sdio_hwbus_ops = { + .copy_from_io = wfx_sdio_copy_from_io, + .copy_to_io = wfx_sdio_copy_to_io, + .lock = wfx_sdio_lock, + .unlock = wfx_sdio_unlock, + .align_size = wfx_sdio_align_size, +}; + +static const struct of_device_id wfx_sdio_of_match[]; +static int wfx_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct device_node *np = func->dev.of_node; + struct wfx_sdio_priv *bus; + int ret; + + if (func->num != 1) { + dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n", func->num); + return -ENODEV; + } + + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + + if (np) { + if (!of_match_node(wfx_sdio_of_match, np)) { + dev_warn(&func->dev, "no compatible device found in DT\n"); + return -ENODEV; + } + bus->of_irq = irq_of_parse_and_map(np, 0); + } else { + dev_warn(&func->dev, "device is not declared in DT, features will be limited\n"); + // FIXME: ignore VID/PID and only rely on device tree + // return -ENODEV; + } + + bus->func = func; + sdio_set_drvdata(func, bus); + func->card->quirks |= MMC_QUIRK_LENIENT_FN0 | MMC_QUIRK_BLKSZ_FOR_BYTE_MODE | MMC_QUIRK_BROKEN_BYTE_MODE_512; + + sdio_claim_host(func); + ret = sdio_enable_func(func); + // Block of 64 bytes is more efficient than 512B for frame sizes < 4k + sdio_set_block_size(func, 64); + sdio_release_host(func); + if (ret) + goto err0; + + ret = wfx_sdio_irq_subscribe(bus); + if (ret) + goto err1; + + bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata, + &wfx_sdio_hwbus_ops, bus); + if (!bus->core) { + ret = -EIO; + goto err2; + } + + ret = wfx_probe(bus->core); + if (ret) + goto err3; + + return 0; + +err3: + wfx_free_common(bus->core); +err2: + wfx_sdio_irq_unsubscribe(bus); +err1: + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +err0: + return ret; +} + +static void wfx_sdio_remove(struct sdio_func *func) +{ + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + wfx_release(bus->core); + wfx_free_common(bus->core); + wfx_sdio_irq_unsubscribe(bus); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +} + +#define SDIO_VENDOR_ID_SILABS 0x0000 +#define SDIO_DEVICE_ID_SILABS_WF200 0x1000 +static const struct sdio_device_id wfx_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_SILABS, SDIO_DEVICE_ID_SILABS_WF200) }, + // FIXME: ignore VID/PID and only rely on device tree + // { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, + { }, +}; +MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids); + +#ifdef CONFIG_OF +static const struct of_device_id wfx_sdio_of_match[] = { + { .compatible = "silabs,wfx-sdio" }, + { }, +}; +MODULE_DEVICE_TABLE(of, wfx_sdio_of_match); +#endif + +struct sdio_driver wfx_sdio_driver = { + .name = "wfx-sdio", + .id_table = wfx_sdio_ids, + .probe = wfx_sdio_probe, + .remove = wfx_sdio_remove, + .drv = { + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wfx_sdio_of_match), + } +}; diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c new file mode 100644 index 000000000000..f65f7d75e731 --- /dev/null +++ b/drivers/staging/wfx/bus_spi.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPI interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2011, Sagrad Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/spi/spi.h> +#include <linux/interrupt.h> +#include <linux/of.h> + +#include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" +#include "bh.h" + +static int gpio_reset = -2; +module_param(gpio_reset, int, 0644); +MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none."); + +#define SET_WRITE 0x7FFF /* usage: and operation */ +#define SET_READ 0x8000 /* usage: or operation */ + +static const struct wfx_platform_data wfx_spi_pdata = { + .file_fw = "wfm_wf200", + .file_pds = "wf200.pds", + .use_rising_clk = true, +}; + +struct wfx_spi_priv { + struct spi_device *func; + struct wfx_dev *core; + struct gpio_desc *gpio_reset; + struct work_struct request_rx; + bool need_swab; +}; + +/* + * WFx chip read data 16bits at time and place them directly into (little + * endian) CPU register. So, chip expect byte order like "B1 B0 B3 B2" (while + * LE is "B0 B1 B2 B3" and BE is "B3 B2 B1 B0") + * + * A little endian host with bits_per_word == 16 should do the right job + * natively. The code below to support big endian host and commonly used SPI + * 8bits. + */ +static int wfx_spi_copy_from_io(void *priv, unsigned int addr, + void *dst, size_t count) +{ + struct wfx_spi_priv *bus = priv; + u16 regaddr = (addr << 12) | (count / 2) | SET_READ; + struct spi_message m; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .rx_buf = dst, + .len = count, + }; + u16 *dst16 = dst; + int ret, i; + + WARN(count % 2, "buffer size must be a multiple of 2"); + + cpu_to_le16s(®addr); + if (bus->need_swab) + swab16s(®addr); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(bus->func, &m); + + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&dst16[i]); + return ret; +} + +static int wfx_spi_copy_to_io(void *priv, unsigned int addr, + const void *src, size_t count) +{ + struct wfx_spi_priv *bus = priv; + u16 regaddr = (addr << 12) | (count / 2); + // FIXME: use a bounce buffer + u16 *src16 = (void *) src; + int ret, i; + struct spi_message m; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .tx_buf = src, + .len = count, + }; + + WARN(count % 2, "buffer size must be a multiple of 2"); + WARN(regaddr & SET_READ, "bad addr or size overflow"); + + cpu_to_le16s(®addr); + + if (bus->need_swab) + swab16s(®addr); + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&src16[i]); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(bus->func, &m); + + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&src16[i]); + return ret; +} + +static void wfx_spi_lock(void *priv) +{ +} + +static void wfx_spi_unlock(void *priv) +{ +} + +static irqreturn_t wfx_spi_irq_handler(int irq, void *priv) +{ + struct wfx_spi_priv *bus = priv; + + if (!bus->core) { + WARN(!bus->core, "race condition in driver init/deinit"); + return IRQ_NONE; + } + queue_work(system_highpri_wq, &bus->request_rx); + return IRQ_HANDLED; +} + +static void wfx_spi_request_rx(struct work_struct *work) +{ + struct wfx_spi_priv *bus = + container_of(work, struct wfx_spi_priv, request_rx); + + wfx_bh_request_rx(bus->core); +} + +static size_t wfx_spi_align_size(void *priv, size_t size) +{ + // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned + return ALIGN(size, 4); +} + +static const struct hwbus_ops wfx_spi_hwbus_ops = { + .copy_from_io = wfx_spi_copy_from_io, + .copy_to_io = wfx_spi_copy_to_io, + .lock = wfx_spi_lock, + .unlock = wfx_spi_unlock, + .align_size = wfx_spi_align_size, +}; + +static int wfx_spi_probe(struct spi_device *func) +{ + struct wfx_spi_priv *bus; + int ret; + + if (!func->bits_per_word) + func->bits_per_word = 16; + ret = spi_setup(func); + if (ret) + return ret; + // Trace below is also displayed by spi_setup() if compiled with DEBUG + dev_dbg(&func->dev, "SPI params: CS=%d, mode=%d bits/word=%d speed=%d\n", + func->chip_select, func->mode, func->bits_per_word, func->max_speed_hz); + if (func->bits_per_word != 16 && func->bits_per_word != 8) + dev_warn(&func->dev, "unusual bits/word value: %d\n", func->bits_per_word); + if (func->max_speed_hz > 49000000) + dev_warn(&func->dev, "%dHz is a very high speed\n", func->max_speed_hz); + + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + bus->func = func; + if (func->bits_per_word == 8 || IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + bus->need_swab = true; + spi_set_drvdata(func, bus); + + bus->gpio_reset = wfx_get_gpio(&func->dev, gpio_reset, "reset"); + if (!bus->gpio_reset) { + dev_warn(&func->dev, "try to load firmware anyway\n"); + } else { + gpiod_set_value(bus->gpio_reset, 0); + udelay(100); + gpiod_set_value(bus->gpio_reset, 1); + udelay(2000); + } + + ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, + IRQF_TRIGGER_RISING, "wfx", bus); + if (ret) + return ret; + + INIT_WORK(&bus->request_rx, wfx_spi_request_rx); + bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata, + &wfx_spi_hwbus_ops, bus); + if (!bus->core) + return -EIO; + + ret = wfx_probe(bus->core); + if (ret) + wfx_free_common(bus->core); + + return ret; +} + +/* Disconnect Function to be called by SPI stack when device is disconnected */ +static int wfx_spi_disconnect(struct spi_device *func) +{ + struct wfx_spi_priv *bus = spi_get_drvdata(func); + + wfx_release(bus->core); + wfx_free_common(bus->core); + // A few IRQ will be sent during device release. Hopefully, no IRQ + // should happen after wdev/wvif are released. + devm_free_irq(&func->dev, func->irq, bus); + flush_work(&bus->request_rx); + return 0; +} + +/* + * For dynamic driver binding, kernel does not use OF to match driver. It only + * use modalias and modalias is a copy of 'compatible' DT node with vendor + * stripped. + */ +static const struct spi_device_id wfx_spi_id[] = { + { "wfx-spi", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, wfx_spi_id); + +#ifdef CONFIG_OF +static const struct of_device_id wfx_spi_of_match[] = { + { .compatible = "silabs,wfx-spi" }, + { }, +}; +MODULE_DEVICE_TABLE(of, wfx_spi_of_match); +#endif + +struct spi_driver wfx_spi_driver = { + .driver = { + .name = "wfx-spi", + .of_match_table = of_match_ptr(wfx_spi_of_match), + }, + .id_table = wfx_spi_id, + .probe = wfx_spi_probe, + .remove = wfx_spi_disconnect, +}; diff --git a/drivers/staging/wfx/data_rx.c b/drivers/staging/wfx/data_rx.c new file mode 100644 index 000000000000..3a79089c8501 --- /dev/null +++ b/drivers/staging/wfx/data_rx.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/etherdevice.h> +#include <net/mac80211.h> + +#include "data_rx.h" +#include "wfx.h" +#include "bh.h" +#include "sta.h" + +static int wfx_handle_pspoll(struct wfx_vif *wvif, struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int i; + + if (wvif->state != WFX_STATE_AP) + return 1; + if (!ether_addr_equal(wvif->vif->addr, pspoll->bssid)) + return 1; + + rcu_read_lock(); + sta = ieee80211_find_sta(wvif->vif, pspoll->ta); + if (sta) + link_id = ((struct wfx_sta_priv *) &sta->drv_priv)->link_id; + rcu_read_unlock(); + if (link_id) + pspoll_mask = BIT(link_id); + else + return 1; + + wvif->pspoll_mask |= pspoll_mask; + /* Do not report pspols if data for given link id is queued already. */ + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + if (wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[i], + pspoll_mask)) { + wfx_bh_request_tx(wvif->wdev); + return 1; + } + } + return 0; +} + +static int wfx_drop_encrypt_data(struct wfx_dev *wdev, struct hif_ind_rx *arg, struct sk_buff *skb) +{ + struct ieee80211_hdr *frame = (struct ieee80211_hdr *) skb->data; + size_t hdrlen = ieee80211_hdrlen(frame->frame_control); + size_t iv_len, icv_len; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed. + */ + switch (arg->rx_flags.encryp) { + case HIF_RI_FLAGS_WEP_ENCRYPTED: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case HIF_RI_FLAGS_TKIP_ENCRYPTED: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + break; + case HIF_RI_FLAGS_AES_ENCRYPTED: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case HIF_RI_FLAGS_WAPI_ENCRYPTED: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + dev_err(wdev->dev, "unknown encryption type %d\n", + arg->rx_flags.encryp); + return -EIO; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == HIF_STATUS_MICFAILURE) + icv_len = 0; + + if (skb->len < hdrlen + iv_len + icv_len) { + dev_warn(wdev->dev, "malformed SDU received\n"); + return -EIO; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + return 0; + +} + +void wfx_rx_cb(struct wfx_vif *wvif, struct hif_ind_rx *arg, struct sk_buff *skb) +{ + int link_id = arg->rx_flags.peer_sta_id; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *) skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data; + struct wfx_link_entry *entry = NULL; + bool early_data = false; + + memset(hdr, 0, sizeof(*hdr)); + + // FIXME: Why do we drop these frames? + if (!arg->rcpi_rssi && + (ieee80211_is_probe_resp(frame->frame_control) || + ieee80211_is_beacon(frame->frame_control))) + goto drop; + + if (link_id && link_id <= WFX_MAX_STA_IN_AP_MODE) { + entry = &wvif->link_id_db[link_id - 1]; + entry->timestamp = jiffies; + if (entry->status == WFX_LINK_SOFT && ieee80211_is_data(frame->frame_control)) + early_data = true; + } + + if (arg->status == HIF_STATUS_MICFAILURE) + hdr->flag |= RX_FLAG_MMIC_ERROR; + else if (arg->status) + goto drop; + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + dev_warn(wvif->wdev->dev, "malformed SDU received\n"); + goto drop; + } + + if (ieee80211_is_pspoll(frame->frame_control)) + if (wfx_handle_pspoll(wvif, skb)) + goto drop; + + hdr->band = NL80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency(arg->channel_number, hdr->band); + + if (arg->rxed_rate >= 14) { + hdr->encoding = RX_ENC_HT; + hdr->rate_idx = arg->rxed_rate - 14; + } else if (arg->rxed_rate >= 4) { + hdr->rate_idx = arg->rxed_rate - 2; + } else { + hdr->rate_idx = arg->rxed_rate; + } + + hdr->signal = arg->rcpi_rssi / 2 - 110; + hdr->antenna = 0; + + if (arg->rx_flags.encryp) { + if (wfx_drop_encrypt_data(wvif->wdev, arg, skb)) + goto drop; + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + if (arg->rx_flags.encryp == HIF_RI_FLAGS_TKIP_ENCRYPTED) + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + } + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (ieee80211_is_action(frame->frame_control) + && arg->rx_flags.match_uc_addr + && mgmt->u.action.category == WLAN_CATEGORY_BACK) + goto drop; + if (ieee80211_is_beacon(frame->frame_control) + && !arg->status && wvif->vif + && ether_addr_equal(ieee80211_get_SA(frame), wvif->vif->bss_conf.bssid)) { + const u8 *tim_ie; + u8 *ies = mgmt->u.beacon.variable; + size_t ies_len = skb->len - (ies - skb->data); + + tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); + if (tim_ie) { + struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) &tim_ie[2]; + + if (wvif->dtim_period != tim->dtim_period) { + wvif->dtim_period = tim->dtim_period; + schedule_work(&wvif->set_beacon_wakeup_period_work); + } + } + + /* Disable beacon filter once we're associated... */ + if (wvif->disable_beacon_filter && + (wvif->vif->bss_conf.assoc || wvif->vif->bss_conf.ibss_joined)) { + wvif->disable_beacon_filter = false; + schedule_work(&wvif->update_filtering_work); + } + } + + if (early_data) { + spin_lock_bh(&wvif->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == WFX_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(wvif->wdev->hw, skb); + spin_unlock_bh(&wvif->ps_state_lock); + } else { + ieee80211_rx_irqsafe(wvif->wdev->hw, skb); + } + + return; + +drop: + dev_kfree_skb(skb); +} diff --git a/drivers/staging/wfx/data_rx.h b/drivers/staging/wfx/data_rx.h new file mode 100644 index 000000000000..b44d15268f83 --- /dev/null +++ b/drivers/staging/wfx/data_rx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_DATA_RX_H +#define WFX_DATA_RX_H + +#include "hif_api_cmd.h" + +struct wfx_vif; +struct sk_buff; + +void wfx_rx_cb(struct wfx_vif *wvif, struct hif_ind_rx *arg, struct sk_buff *skb); + +#endif /* WFX_DATA_RX_H */ diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c new file mode 100644 index 000000000000..8ed38cac19f6 --- /dev/null +++ b/drivers/staging/wfx/data_tx.c @@ -0,0 +1,800 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <net/mac80211.h> + +#include "data_tx.h" +#include "wfx.h" +#include "bh.h" +#include "sta.h" +#include "queue.h" +#include "debug.h" +#include "traces.h" +#include "hif_tx_mib.h" + +#define WFX_INVALID_RATE_ID (0xFF) +#define WFX_LINK_ID_NO_ASSOC 15 +#define WFX_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +static int wfx_get_hw_rate(struct wfx_dev *wdev, const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return -1; + if (rate->flags & IEEE80211_TX_RC_MCS) { + if (rate->idx > 7) { + WARN(1, "wrong rate->idx value: %d", rate->idx); + return -1; + } + return rate->idx + 14; + } + // WFx only support 2GHz, else band information should be retreived + // from ieee80211_tx_info + return wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->bitrates[rate->idx].hw_value; +} + +/* TX policy cache implementation */ + +static void wfx_tx_policy_build(struct wfx_vif *wvif, struct tx_policy *policy, + struct ieee80211_tx_rate *rates) +{ + int i; + size_t count; + struct wfx_dev *wdev = wvif->wdev; + + WARN(rates[0].idx < 0, "invalid rate policy"); + memset(policy, 0, sizeof(*policy)); + for (i = 1; i < IEEE80211_TX_MAX_RATES; i++) + if (rates[i].idx < 0) + break; + count = i; + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more + */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + int rateid; + uint8_t count; + + if (rates[i].idx < 0) + break; + WARN_ON(rates[i].count > 15); + rateid = wfx_get_hw_rate(wdev, &rates[i]); + // Pack two values in each byte of policy->rates + count = rates[i].count; + if (rateid % 2) + count <<= 4; + policy->rates[rateid / 2] |= count; + } +} + +static bool tx_policy_is_equal(const struct tx_policy *a, const struct tx_policy *b) +{ + return !memcmp(a->rates, b->rates, sizeof(a->rates)); +} + +static int wfx_tx_policy_find(struct tx_policy_cache *cache, struct tx_policy *wanted) +{ + struct tx_policy *it; + + list_for_each_entry(it, &cache->used, link) + if (tx_policy_is_equal(wanted, it)) + return it - cache->cache; + list_for_each_entry(it, &cache->free, link) + if (tx_policy_is_equal(wanted, it)) + return it - cache->cache; + return -1; +} + +static void wfx_tx_policy_use(struct tx_policy_cache *cache, struct tx_policy *entry) +{ + ++entry->usage_count; + list_move(&entry->link, &cache->used); +} + +static int wfx_tx_policy_release(struct tx_policy_cache *cache, struct tx_policy *entry) +{ + int ret = --entry->usage_count; + + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +static int wfx_tx_policy_get(struct wfx_vif *wvif, struct ieee80211_tx_rate *rates, + bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + struct tx_policy wanted; + + wfx_tx_policy_build(wvif, &wanted, rates); + + spin_lock_bh(&cache->lock); + if (WARN_ON(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return WFX_INVALID_RATE_ID; + } + idx = wfx_tx_policy_find(cache, &wanted); + if (idx >= 0) { + *renew = false; + } else { + struct tx_policy *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list + */ + entry = list_entry(cache->free.prev, struct tx_policy, link); + memcpy(entry->rates, wanted.rates, sizeof(entry->rates)); + entry->uploaded = 0; + entry->usage_count = 0; + idx = entry - cache->cache; + } + wfx_tx_policy_use(cache, &cache->cache[idx]); + if (list_empty(&cache->free)) { + /* Lock TX queues. */ + wfx_tx_queues_lock(wvif->wdev); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void wfx_tx_policy_put(struct wfx_vif *wvif, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = wfx_tx_policy_release(cache, &cache->cache[idx]); + if (locked && !usage) { + /* Unlock TX queues. */ + wfx_tx_queues_unlock(wvif->wdev); + } + spin_unlock_bh(&cache->lock); +} + +static int wfx_tx_policy_upload(struct wfx_vif *wvif) +{ + int i; + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + struct hif_mib_set_tx_rate_retry_policy *arg = + kzalloc(struct_size(arg, tx_rate_retry_policy, HIF_MIB_NUM_TX_RATE_RETRY_POLICIES), GFP_KERNEL); + struct hif_mib_tx_rate_retry_policy *dst; + + spin_lock_bh(&cache->lock); + /* Upload only modified entries. */ + for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i) { + struct tx_policy *src = &cache->cache[i]; + + if (!src->uploaded && memzcmp(src->rates, sizeof(src->rates))) { + dst = arg->tx_rate_retry_policy + arg->num_tx_rate_policies; + + dst->policy_index = i; + dst->short_retry_count = 255; + dst->long_retry_count = 255; + dst->first_rate_sel = 1; + dst->terminate = 1; + dst->count_init = 1; + memcpy(&dst->rates, src->rates, sizeof(src->rates)); + src->uploaded = 1; + arg->num_tx_rate_policies++; + } + } + spin_unlock_bh(&cache->lock); + hif_set_tx_rate_retry_policy(wvif, arg); + kfree(arg); + return 0; +} + +static void wfx_tx_policy_upload_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, tx_policy_upload_work); + + wfx_tx_policy_upload(wvif); + + wfx_tx_unlock(wvif->wdev); + wfx_tx_queues_unlock(wvif->wdev); +} + +void wfx_tx_policy_init(struct wfx_vif *wvif) +{ + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + INIT_WORK(&wvif->tx_policy_upload_work, wfx_tx_policy_upload_work); + + for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +/* Link ID related functions */ + +static int wfx_alloc_link_id(struct wfx_vif *wvif, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&wvif->ps_state_lock); + for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { + if (!wvif->link_id_db[i].status) { + ret = i + 1; + break; + } else if (wvif->link_id_db[i].status != WFX_LINK_HARD && + !wvif->wdev->tx_queue_stats.link_map_cache[i + 1]) { + unsigned long inactivity = + now - wvif->link_id_db[i].timestamp; + + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + + if (ret) { + struct wfx_link_entry *entry = &wvif->link_id_db[ret - 1]; + + entry->status = WFX_LINK_RESERVE; + ether_addr_copy(entry->mac, mac); + memset(&entry->buffered, 0, WFX_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wfx_tx_lock(wvif->wdev); + + if (!schedule_work(&wvif->link_id_work)) + wfx_tx_unlock(wvif->wdev); + } else { + dev_info(wvif->wdev->dev, "no more link-id available\n"); + } + spin_unlock_bh(&wvif->ps_state_lock); + return ret; +} + +int wfx_find_link_id(struct wfx_vif *wvif, const u8 *mac) +{ + int i, ret = 0; + + spin_lock_bh(&wvif->ps_state_lock); + for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { + if (ether_addr_equal(mac, wvif->link_id_db[i].mac) && + wvif->link_id_db[i].status) { + wvif->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&wvif->ps_state_lock); + return ret; +} + +static int wfx_map_link(struct wfx_vif *wvif, struct wfx_link_entry *link_entry, int sta_id) +{ + int ret; + + ret = hif_map_link(wvif, link_entry->mac, 0, sta_id); + + if (ret == 0) + /* Save the MAC address currently associated with the peer + * for future unmap request + */ + ether_addr_copy(link_entry->old_mac, link_entry->mac); + + return ret; +} + +int wfx_unmap_link(struct wfx_vif *wvif, int sta_id) +{ + u8 *mac_addr = NULL; + + if (sta_id) + mac_addr = wvif->link_id_db[sta_id - 1].old_mac; + + return hif_map_link(wvif, mac_addr, 1, sta_id); +} + +void wfx_link_id_gc_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, link_id_gc_work.work); + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + u32 mask; + int i; + + if (wvif->state != WFX_STATE_AP) + return; + + wfx_tx_lock_flush(wvif->wdev); + spin_lock_bh(&wvif->ps_state_lock); + for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { + bool need_reset = false; + + mask = BIT(i + 1); + if (wvif->link_id_db[i].status == WFX_LINK_RESERVE || + (wvif->link_id_db[i].status == WFX_LINK_HARD && + !(wvif->link_id_map & mask))) { + if (wvif->link_id_map & mask) { + wvif->sta_asleep_mask &= ~mask; + wvif->pspoll_mask &= ~mask; + need_reset = true; + } + wvif->link_id_map |= mask; + if (wvif->link_id_db[i].status != WFX_LINK_HARD) + wvif->link_id_db[i].status = WFX_LINK_SOFT; + + spin_unlock_bh(&wvif->ps_state_lock); + if (need_reset) + wfx_unmap_link(wvif, i + 1); + wfx_map_link(wvif, &wvif->link_id_db[i], i + 1); + next_gc = min(next_gc, WFX_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&wvif->ps_state_lock); + } else if (wvif->link_id_db[i].status == WFX_LINK_SOFT) { + ttl = wvif->link_id_db[i].timestamp - now + + WFX_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + wvif->link_id_db[i].status = WFX_LINK_OFF; + wvif->link_id_map &= ~mask; + wvif->sta_asleep_mask &= ~mask; + wvif->pspoll_mask &= ~mask; + spin_unlock_bh(&wvif->ps_state_lock); + wfx_unmap_link(wvif, i + 1); + spin_lock_bh(&wvif->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } + } + if (need_reset) + skb_queue_purge(&wvif->link_id_db[i].rx_queue); + } + spin_unlock_bh(&wvif->ps_state_lock); + if (next_gc != -1) + schedule_delayed_work(&wvif->link_id_gc_work, next_gc); + wfx_tx_unlock(wvif->wdev); +} + +void wfx_link_id_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, link_id_work); + + wfx_tx_flush(wvif->wdev); + wfx_link_id_gc_work(&wvif->link_id_gc_work.work); + wfx_tx_unlock(wvif->wdev); +} + +/* Tx implementation */ + +static bool ieee80211_is_action_back(struct ieee80211_hdr *hdr) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) hdr; + + if (!ieee80211_is_action(mgmt->frame_control)) + return false; + if (mgmt->u.action.category != WLAN_CATEGORY_BACK) + return false; + return true; +} + +static void wfx_tx_manage_pm(struct wfx_vif *wvif, struct ieee80211_hdr *hdr, + struct wfx_tx_priv *tx_priv, struct ieee80211_sta *sta) +{ + u32 mask = ~BIT(tx_priv->raw_link_id); + + spin_lock_bh(&wvif->ps_state_lock); + if (ieee80211_is_auth(hdr->frame_control)) { + wvif->sta_asleep_mask &= mask; + wvif->pspoll_mask &= mask; + } + + if (tx_priv->link_id == WFX_LINK_ID_AFTER_DTIM && !wvif->mcast_buffered) { + wvif->mcast_buffered = true; + if (wvif->sta_asleep_mask) + schedule_work(&wvif->mcast_start_work); + } + + if (tx_priv->raw_link_id) { + wvif->link_id_db[tx_priv->raw_link_id - 1].timestamp = jiffies; + if (tx_priv->tid < WFX_MAX_TID) + wvif->link_id_db[tx_priv->raw_link_id - 1].buffered[tx_priv->tid]++; + } + spin_unlock_bh(&wvif->ps_state_lock); + + if (sta) + ieee80211_sta_set_buffered(sta, tx_priv->tid, true); +} + +static uint8_t wfx_tx_get_raw_link_id(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct ieee80211_hdr *hdr) +{ + struct wfx_sta_priv *sta_priv = sta ? (struct wfx_sta_priv *) &sta->drv_priv : NULL; + const u8 *da = ieee80211_get_DA(hdr); + int ret; + + if (sta_priv && sta_priv->link_id) + return sta_priv->link_id; + if (wvif->vif->type != NL80211_IFTYPE_AP) + return 0; + if (is_multicast_ether_addr(da)) + return 0; + ret = wfx_find_link_id(wvif, da); + if (!ret) + ret = wfx_alloc_link_id(wvif, da); + if (!ret) { + dev_err(wvif->wdev->dev, "no more link-id available\n"); + return WFX_LINK_ID_NO_ASSOC; + } + return ret; +} + +static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates) +{ + int i; + bool finished; + + // Firmware is not able to mix rates with differents flags + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + if (rates[0].flags & IEEE80211_TX_RC_SHORT_GI) + rates[i].flags |= IEEE80211_TX_RC_SHORT_GI; + if (!(rates[0].flags & IEEE80211_TX_RC_SHORT_GI)) + rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI; + if (!(rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)) + rates[i].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; + } + + // Sort rates and remove duplicates + do { + finished = true; + for (i = 0; i < IEEE80211_TX_MAX_RATES - 1; i++) { + if (rates[i + 1].idx == rates[i].idx && rates[i].idx != -1) { + rates[i].count = max_t(int, rates[i].count, rates[i + 1].count); + rates[i + 1].idx = -1; + rates[i + 1].count = 0; + + finished = false; + } + if (rates[i + 1].idx > rates[i].idx) { + swap(rates[i + 1], rates[i]); + finished = false; + } + } + } while (!finished); + // All retries use long GI + for (i = 1; i < IEEE80211_TX_MAX_RATES; i++) + rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI; +} + +static uint8_t wfx_tx_get_rate_id(struct wfx_vif *wvif, struct ieee80211_tx_info *tx_info) +{ + bool tx_policy_renew = false; + uint8_t rate_id; + + rate_id = wfx_tx_policy_get(wvif, tx_info->driver_rates, &tx_policy_renew); + WARN(rate_id == WFX_INVALID_RATE_ID, "unable to get a valid Tx policy"); + + if (tx_policy_renew) { + /* FIXME: It's not so optimal to stop TX queues every now and + * then. Better to reimplement task scheduling with a counter. + */ + wfx_tx_lock(wvif->wdev); + wfx_tx_queues_lock(wvif->wdev); + if (!schedule_work(&wvif->tx_policy_upload_work)) { + wfx_tx_queues_unlock(wvif->wdev); + wfx_tx_unlock(wvif->wdev); + } + } + return rate_id; +} + +static struct hif_ht_tx_parameters wfx_tx_get_tx_parms(struct wfx_dev *wdev, struct ieee80211_tx_info *tx_info) +{ + struct ieee80211_tx_rate *rate = &tx_info->driver_rates[0]; + struct hif_ht_tx_parameters ret = { }; + + if (!(rate->flags & IEEE80211_TX_RC_MCS)) + ret.frame_format = HIF_FRAME_FORMAT_NON_HT; + else if (!(rate->flags & IEEE80211_TX_RC_GREEN_FIELD)) + ret.frame_format = HIF_FRAME_FORMAT_MIXED_FORMAT_HT; + else + ret.frame_format = HIF_FRAME_FORMAT_GF_HT_11N; + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + ret.short_gi = 1; + if (tx_info->flags & IEEE80211_TX_CTL_STBC) + ret.stbc = 0; // FIXME: Not yet supported by firmware? + return ret; +} + +static uint8_t wfx_tx_get_tid(struct ieee80211_hdr *hdr) +{ + // FIXME: ieee80211_get_tid(hdr) should be sufficient for all cases. + if (!ieee80211_is_data(hdr->frame_control)) + return WFX_MAX_TID; + if (ieee80211_is_data_qos(hdr->frame_control)) + return ieee80211_get_tid(hdr); + else + return 0; +} + +static int wfx_tx_get_icv_len(struct ieee80211_key_conf *hw_key) +{ + int mic_space; + + if (!hw_key) + return 0; + mic_space = (hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) ? 8 : 0; + return hw_key->icv_len + mic_space; +} + +static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct sk_buff *skb) +{ + struct hif_msg *hif_msg; + struct hif_req_tx *req; + struct wfx_tx_priv *tx_priv; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int queue_id = tx_info->hw_queue; + size_t offset = (size_t) skb->data & 3; + int wmsg_len = sizeof(struct hif_msg) + sizeof(struct hif_req_tx) + offset; + + WARN(queue_id >= IEEE80211_NUM_ACS, "unsupported queue_id"); + wfx_tx_fixup_rates(tx_info->driver_rates); + + // From now tx_info->control is unusable + memset(tx_info->rate_driver_data, 0, sizeof(struct wfx_tx_priv)); + // Fill tx_priv + tx_priv = (struct wfx_tx_priv *) tx_info->rate_driver_data; + tx_priv->tid = wfx_tx_get_tid(hdr); + tx_priv->raw_link_id = wfx_tx_get_raw_link_id(wvif, sta, hdr); + tx_priv->link_id = tx_priv->raw_link_id; + if (ieee80211_has_protected(hdr->frame_control)) + tx_priv->hw_key = hw_key; + if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) + tx_priv->link_id = WFX_LINK_ID_AFTER_DTIM; + if (sta && (sta->uapsd_queues & BIT(queue_id))) + tx_priv->link_id = WFX_LINK_ID_UAPSD; + + // Fill hif_msg + WARN(skb_headroom(skb) < wmsg_len, "not enough space in skb"); + WARN(offset & 1, "attempt to transmit an unaligned frame"); + skb_put(skb, wfx_tx_get_icv_len(tx_priv->hw_key)); + skb_push(skb, wmsg_len); + memset(skb->data, 0, wmsg_len); + hif_msg = (struct hif_msg *) skb->data; + hif_msg->len = cpu_to_le16(skb->len); + hif_msg->id = HIF_REQ_ID_TX; + hif_msg->interface = wvif->id; + if (skb->len > wvif->wdev->hw_caps.size_inp_ch_buf) { + dev_warn(wvif->wdev->dev, "requested frame size (%d) is larger than maximum supported (%d)\n", + skb->len, wvif->wdev->hw_caps.size_inp_ch_buf); + skb_pull(skb, wmsg_len); + return -EIO; + } + + // Fill tx request + req = (struct hif_req_tx *) hif_msg->body; + req->packet_id = queue_id << 16 | IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + req->data_flags.fc_offset = offset; + req->queue_id.peer_sta_id = tx_priv->raw_link_id; + // Queue index are inverted between firmware and Linux + req->queue_id.queue_id = 3 - queue_id; + req->ht_tx_parameters = wfx_tx_get_tx_parms(wvif->wdev, tx_info); + req->tx_flags.retry_policy_index = wfx_tx_get_rate_id(wvif, tx_info); + + // Auxilliary operations + wfx_tx_manage_pm(wvif, hdr, tx_priv, sta); + wfx_tx_queue_put(wvif->wdev, &wvif->wdev->tx_queue[queue_id], skb); + wfx_bh_request_tx(wvif->wdev); + return 0; +} + +void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif; + struct ieee80211_sta *sta = control ? control->sta : NULL; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + size_t driver_data_room = FIELD_SIZEOF(struct ieee80211_tx_info, rate_driver_data); + + compiletime_assert(sizeof(struct wfx_tx_priv) <= driver_data_room, + "struct tx_priv is too large"); + WARN(skb->next || skb->prev, "skb is already member of a list"); + // control.vif can be NULL for injected frames + if (tx_info->control.vif) + wvif = (struct wfx_vif *) tx_info->control.vif->drv_priv; + else + wvif = wvif_iterate(wdev, NULL); + if (WARN_ON(!wvif)) + goto drop; + // FIXME: why? + if (ieee80211_is_action_back(hdr)) { + dev_info(wdev->dev, "drop BA action\n"); + goto drop; + } + if (wfx_tx_inner(wvif, sta, skb)) + goto drop; + + return; + +drop: + ieee80211_tx_status_irqsafe(wdev->hw, skb); +} + +void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg) +{ + int i; + int tx_count; + struct sk_buff *skb; + struct ieee80211_tx_rate *rate; + struct ieee80211_tx_info *tx_info; + const struct wfx_tx_priv *tx_priv; + + skb = wfx_pending_get(wvif->wdev, arg->packet_id); + if (!skb) { + dev_warn(wvif->wdev->dev, "received unknown packet_id (%#.8x) from chip\n", arg->packet_id); + return; + } + tx_info = IEEE80211_SKB_CB(skb); + tx_priv = wfx_skb_tx_priv(skb); + _trace_tx_stats(arg, skb, wfx_pending_get_pkt_us_delay(wvif->wdev, skb)); + + // You can touch to tx_priv, but don't touch to tx_info->status. + tx_count = arg->ack_failures; + if (!arg->status || arg->ack_failures) + tx_count += 1; // Also report success + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + rate = &tx_info->status.rates[i]; + if (rate->idx < 0) + break; + if (tx_count < rate->count && arg->status && arg->ack_failures) + dev_dbg(wvif->wdev->dev, "all retries were not consumed: %d != %d\n", + rate->count, tx_count); + if (tx_count <= rate->count && tx_count && arg->txed_rate != wfx_get_hw_rate(wvif->wdev, rate)) + dev_dbg(wvif->wdev->dev, "inconsistent tx_info rates: %d != %d\n", + arg->txed_rate, wfx_get_hw_rate(wvif->wdev, rate)); + if (tx_count > rate->count) { + tx_count -= rate->count; + } else if (!tx_count) { + rate->count = 0; + rate->idx = -1; + } else { + rate->count = tx_count; + tx_count = 0; + } + } + if (tx_count) + dev_dbg(wvif->wdev->dev, "%d more retries than expected\n", tx_count); + skb_trim(skb, skb->len - wfx_tx_get_icv_len(tx_priv->hw_key)); + + // From now, you can touch to tx_info->status, but do not touch to + // tx_priv anymore + // FIXME: use ieee80211_tx_info_clear_status() + memset(tx_info->rate_driver_data, 0, sizeof(tx_info->rate_driver_data)); + memset(tx_info->pad, 0, sizeof(tx_info->pad)); + + if (!arg->status) { + if (wvif->bss_loss_state && arg->packet_id == wvif->bss_loss_confirm_id) + wfx_cqm_bssloss_sm(wvif, 0, 1, 0); + tx_info->status.tx_time = arg->media_delay - arg->tx_queue_delay; + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + tx_info->flags |= IEEE80211_TX_STAT_ACK; + } else if (arg->status == HIF_REQUEUE) { + /* "REQUEUE" means "implicit suspend" */ + struct hif_ind_suspend_resume_tx suspend = { + .suspend_resume_flags.resume = 0, + .suspend_resume_flags.bc_mc_only = 1, + }; + + WARN(!arg->tx_result_flags.requeue, "incoherent status and result_flags"); + wfx_suspend_resume(wvif, &suspend); + tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + } else { + if (wvif->bss_loss_state && arg->packet_id == wvif->bss_loss_confirm_id) + wfx_cqm_bssloss_sm(wvif, 0, 0, 1); + } + wfx_pending_remove(wvif->wdev, skb); +} + +static void wfx_notify_buffered_tx(struct wfx_vif *wvif, struct sk_buff *skb, + struct hif_req_tx *req) +{ + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int tid = wfx_tx_get_tid(hdr); + int raw_link_id = req->queue_id.peer_sta_id; + u8 *buffered; + + if (raw_link_id && tid < WFX_MAX_TID) { + buffered = wvif->link_id_db[raw_link_id - 1].buffered; + + spin_lock_bh(&wvif->ps_state_lock); + WARN(!buffered[tid], "inconsistent notification"); + buffered[tid]--; + spin_unlock_bh(&wvif->ps_state_lock); + + if (!buffered[tid]) { + rcu_read_lock(); + sta = ieee80211_find_sta(wvif->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +} + +void wfx_skb_dtor(struct wfx_dev *wdev, struct sk_buff *skb) +{ + struct hif_msg *hif = (struct hif_msg *) skb->data; + struct hif_req_tx *req = (struct hif_req_tx *) hif->body; + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + unsigned int offset = sizeof(struct hif_req_tx) + sizeof(struct hif_msg) + req->data_flags.fc_offset; + + WARN_ON(!wvif); + skb_pull(skb, offset); + wfx_notify_buffered_tx(wvif, skb, req); + wfx_tx_policy_put(wvif, req->tx_flags.retry_policy_index); + ieee80211_tx_status_irqsafe(wdev->hw, skb); +} diff --git a/drivers/staging/wfx/data_tx.h b/drivers/staging/wfx/data_tx.h new file mode 100644 index 000000000000..0a19ef10a4ab --- /dev/null +++ b/drivers/staging/wfx/data_tx.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_DATA_TX_H +#define WFX_DATA_TX_H + +#include <linux/list.h> +#include <net/mac80211.h> + +#include "hif_api_cmd.h" +#include "hif_api_mib.h" + +// FIXME: use IEEE80211_NUM_TIDS +#define WFX_MAX_TID 8 + +struct wfx_tx_priv; +struct wfx_dev; +struct wfx_vif; + +enum wfx_link_status { + WFX_LINK_OFF, + WFX_LINK_RESERVE, + WFX_LINK_SOFT, + WFX_LINK_HARD, +}; + +struct wfx_link_entry { + unsigned long timestamp; + enum wfx_link_status status; + uint8_t mac[ETH_ALEN]; + uint8_t old_mac[ETH_ALEN]; + uint8_t buffered[WFX_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +struct tx_policy { + struct list_head link; + uint8_t rates[12]; + uint8_t usage_count; + uint8_t uploaded; +}; + +struct tx_policy_cache { + struct tx_policy cache[HIF_MIB_NUM_TX_RATE_RETRY_POLICIES]; + // FIXME: use a trees and drop hash from tx_policy + struct list_head used; + struct list_head free; + spinlock_t lock; +}; + +struct wfx_tx_priv { + ktime_t xmit_timestamp; + struct ieee80211_key_conf *hw_key; + uint8_t link_id; + uint8_t raw_link_id; + uint8_t tid; +} __packed; + +void wfx_tx_policy_init(struct wfx_vif *wvif); + +void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb); +void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg); +void wfx_skb_dtor(struct wfx_dev *wdev, struct sk_buff *skb); + +int wfx_unmap_link(struct wfx_vif *wvif, int link_id); +void wfx_link_id_work(struct work_struct *work); +void wfx_link_id_gc_work(struct work_struct *work); +int wfx_find_link_id(struct wfx_vif *wvif, const u8 *mac); + +static inline struct wfx_tx_priv *wfx_skb_tx_priv(struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info; + + if (!skb) + return NULL; + tx_info = IEEE80211_SKB_CB(skb); + return (struct wfx_tx_priv *) tx_info->rate_driver_data; +} + +static inline struct hif_req_tx *wfx_skb_txreq(struct sk_buff *skb) +{ + struct hif_msg *hif = (struct hif_msg *) skb->data; + struct hif_req_tx *req = (struct hif_req_tx *) hif->body; + + return req; +} + +#endif /* WFX_DATA_TX_H */ diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c new file mode 100644 index 000000000000..761ad9b4f27e --- /dev/null +++ b/drivers/staging/wfx/debug.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Debugfs interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/crc32.h> + +#include "debug.h" +#include "wfx.h" +#include "sta.h" +#include "main.h" +#include "hif_tx.h" +#include "hif_tx_mib.h" + +#define CREATE_TRACE_POINTS +#include "traces.h" + +static const struct trace_print_flags hif_msg_print_map[] = { + hif_msg_list, +}; + +static const struct trace_print_flags hif_mib_print_map[] = { + hif_mib_list, +}; + +static const struct trace_print_flags wfx_reg_print_map[] = { + wfx_reg_list, +}; + +static const char *get_symbol(unsigned long val, + const struct trace_print_flags *symbol_array) +{ + int i; + + for (i = 0; symbol_array[i].mask != -1; i++) { + if (val == symbol_array[i].mask) + return symbol_array[i].name; + } + + return "unknown"; +} + +const char *get_hif_name(unsigned long id) +{ + return get_symbol(id, hif_msg_print_map); +} + +const char *get_mib_name(unsigned long id) +{ + return get_symbol(id, hif_mib_print_map); +} + +const char *get_reg_name(unsigned long id) +{ + return get_symbol(id, wfx_reg_print_map); +} + +static int wfx_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct wfx_dev *wdev = seq->private; + struct hif_mib_extended_count_table counters; + + ret = hif_get_counters_table(wdev, &counters); + if (ret < 0) + return ret; + if (ret > 0) + return -EIO; + +#define PUT_COUNTER(name) \ + seq_printf(seq, "%24s %d\n", #name ":", le32_to_cpu(counters.count_##name)) + + PUT_COUNTER(tx_packets); + PUT_COUNTER(tx_multicast_frames); + PUT_COUNTER(tx_frames_success); + PUT_COUNTER(tx_frame_failures); + PUT_COUNTER(tx_frames_retried); + PUT_COUNTER(tx_frames_multi_retried); + + PUT_COUNTER(rts_success); + PUT_COUNTER(rts_failures); + PUT_COUNTER(ack_failures); + + PUT_COUNTER(rx_packets); + PUT_COUNTER(rx_frames_success); + PUT_COUNTER(rx_packet_errors); + PUT_COUNTER(plcp_errors); + PUT_COUNTER(fcs_errors); + PUT_COUNTER(rx_decryption_failures); + PUT_COUNTER(rx_mic_failures); + PUT_COUNTER(rx_no_key_failures); + PUT_COUNTER(rx_frame_duplicates); + PUT_COUNTER(rx_multicast_frames); + PUT_COUNTER(rx_cmacicv_errors); + PUT_COUNTER(rx_cmac_replays); + PUT_COUNTER(rx_mgmt_ccmp_replays); + + PUT_COUNTER(rx_beacon); + PUT_COUNTER(miss_beacon); + +#undef PUT_COUNTER + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(wfx_counters); + +static const char * const channel_names[] = { + [0] = "1M", + [1] = "2M", + [2] = "5.5M", + [3] = "11M", + /* Entries 4 and 5 does not exist */ + [6] = "6M", + [7] = "9M", + [8] = "12M", + [9] = "18M", + [10] = "24M", + [11] = "36M", + [12] = "48M", + [13] = "54M", + [14] = "MCS0", + [15] = "MCS1", + [16] = "MCS2", + [17] = "MCS3", + [18] = "MCS4", + [19] = "MCS5", + [20] = "MCS6", + [21] = "MCS7", +}; + +static int wfx_rx_stats_show(struct seq_file *seq, void *v) +{ + struct wfx_dev *wdev = seq->private; + struct hif_rx_stats *st = &wdev->rx_stats; + int i; + + mutex_lock(&wdev->rx_stats_lock); + seq_printf(seq, "Timestamp: %dus\n", st->date); + seq_printf(seq, "Low power clock: frequency %uHz, external %s\n", + st->pwr_clk_freq, + st->is_ext_pwr_clk ? "yes" : "no"); + seq_printf(seq, "Num. of frames: %d, PER (x10e4): %d, Throughput: %dKbps/s\n", + st->nb_rx_frame, st->per_total, st->throughput); + seq_puts(seq, " Num. of PER RSSI SNR CFO\n"); + seq_puts(seq, " frames (x10e4) (dBm) (dB) (kHz)\n"); + for (i = 0; i < ARRAY_SIZE(channel_names); i++) { + if (channel_names[i]) + seq_printf(seq, "%5s %8d %8d %8d %8d %8d\n", + channel_names[i], st->nb_rx_by_rate[i], + st->per[i], st->rssi[i] / 100, + st->snr[i] / 100, st->cfo[i]); + } + mutex_unlock(&wdev->rx_stats_lock); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(wfx_rx_stats); + +static ssize_t wfx_send_pds_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wfx_dev *wdev = file->private_data; + char *buf; + int ret; + + if (*ppos != 0) { + dev_dbg(wdev->dev, "PDS data must be written in one transaction"); + return -EBUSY; + } + buf = memdup_user(user_buf, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + *ppos = *ppos + count; + ret = wfx_send_pds(wdev, buf, count); + kfree(buf); + if (ret < 0) + return ret; + return count; +} + +static const struct file_operations wfx_send_pds_fops = { + .open = simple_open, + .write = wfx_send_pds_write, +}; + +static ssize_t wfx_burn_slk_key_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wfx_dev *wdev = file->private_data; + + dev_info(wdev->dev, "this driver does not support secure link\n"); + return -EINVAL; +} + +static const struct file_operations wfx_burn_slk_key_fops = { + .open = simple_open, + .write = wfx_burn_slk_key_write, +}; + +struct dbgfs_hif_msg { + struct wfx_dev *wdev; + struct completion complete; + u8 reply[1024]; + int ret; +}; + +static ssize_t wfx_send_hif_msg_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dbgfs_hif_msg *context = file->private_data; + struct wfx_dev *wdev = context->wdev; + struct hif_msg *request; + + if (completion_done(&context->complete)) { + dev_dbg(wdev->dev, "read previous result before start a new one\n"); + return -EBUSY; + } + if (count < sizeof(struct hif_msg)) + return -EINVAL; + + // wfx_cmd_send() chekc that reply buffer is wide enough, but do not + // return precise length read. User have to know how many bytes should + // be read. Filling reply buffer with a memory pattern may help user. + memset(context->reply, 0xFF, sizeof(context->reply)); + request = memdup_user(user_buf, count); + if (IS_ERR(request)) + return PTR_ERR(request); + if (request->len != count) { + kfree(request); + return -EINVAL; + } + context->ret = wfx_cmd_send(wdev, request, context->reply, sizeof(context->reply), false); + + kfree(request); + complete(&context->complete); + return count; +} + +static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dbgfs_hif_msg *context = file->private_data; + int ret; + + if (count > sizeof(context->reply)) + return -EINVAL; + ret = wait_for_completion_interruptible(&context->complete); + if (ret) + return ret; + if (context->ret < 0) + return context->ret; + // Be carefull, write() is waiting for a full message while read() + // only return a payload + if (copy_to_user(user_buf, context->reply, count)) + return -EFAULT; + + return count; +} + +static int wfx_send_hif_msg_open(struct inode *inode, struct file *file) +{ + struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL); + + if (!context) + return -ENOMEM; + context->wdev = inode->i_private; + init_completion(&context->complete); + file->private_data = context; + return 0; +} + +static int wfx_send_hif_msg_release(struct inode *inode, struct file *file) +{ + struct dbgfs_hif_msg *context = file->private_data; + + kfree(context); + return 0; +} + +static const struct file_operations wfx_send_hif_msg_fops = { + .open = wfx_send_hif_msg_open, + .release = wfx_send_hif_msg_release, + .write = wfx_send_hif_msg_write, + .read = wfx_send_hif_msg_read, +}; + +int wfx_debug_init(struct wfx_dev *wdev) +{ + struct dentry *d; + + d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + debugfs_create_file("counters", 0444, d, wdev, &wfx_counters_fops); + debugfs_create_file("rx_stats", 0444, d, wdev, &wfx_rx_stats_fops); + debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops); + debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops); + debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); + + return 0; +} diff --git a/drivers/staging/wfx/debug.h b/drivers/staging/wfx/debug.h new file mode 100644 index 000000000000..6f2f84d64c9e --- /dev/null +++ b/drivers/staging/wfx/debug.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Debugfs interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2011, ST-Ericsson + */ +#ifndef WFX_DEBUG_H +#define WFX_DEBUG_H + +struct wfx_dev; + +int wfx_debug_init(struct wfx_dev *wdev); + +const char *get_hif_name(unsigned long id); +const char *get_mib_name(unsigned long id); +const char *get_reg_name(unsigned long id); + +#endif /* WFX_DEBUG_H */ diff --git a/drivers/staging/wfx/fwio.c b/drivers/staging/wfx/fwio.c new file mode 100644 index 000000000000..6d82c6244c7f --- /dev/null +++ b/drivers/staging/wfx/fwio.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Firmware loading. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/firmware.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/bitfield.h> + +#include "fwio.h" +#include "wfx.h" +#include "hwio.h" + +// Addresses below are in SRAM area +#define WFX_DNLD_FIFO 0x09004000 +#define DNLD_BLOCK_SIZE 0x0400 +#define DNLD_FIFO_SIZE 0x8000 // (32 * DNLD_BLOCK_SIZE) +// Download Control Area (DCA) +#define WFX_DCA_IMAGE_SIZE 0x0900C000 +#define WFX_DCA_PUT 0x0900C004 +#define WFX_DCA_GET 0x0900C008 +#define WFX_DCA_HOST_STATUS 0x0900C00C +#define HOST_READY 0x87654321 +#define HOST_INFO_READ 0xA753BD99 +#define HOST_UPLOAD_PENDING 0xABCDDCBA +#define HOST_UPLOAD_COMPLETE 0xD4C64A99 +#define HOST_OK_TO_JUMP 0x174FC882 +#define WFX_DCA_NCP_STATUS 0x0900C010 +#define NCP_NOT_READY 0x12345678 +#define NCP_READY 0x87654321 +#define NCP_INFO_READY 0xBD53EF99 +#define NCP_DOWNLOAD_PENDING 0xABCDDCBA +#define NCP_DOWNLOAD_COMPLETE 0xCAFEFECA +#define NCP_AUTH_OK 0xD4C64A99 +#define NCP_AUTH_FAIL 0x174FC882 +#define NCP_PUB_KEY_RDY 0x7AB41D19 +#define WFX_DCA_FW_SIGNATURE 0x0900C014 +#define FW_SIGNATURE_SIZE 0x40 +#define WFX_DCA_FW_HASH 0x0900C054 +#define FW_HASH_SIZE 0x08 +#define WFX_DCA_FW_VERSION 0x0900C05C +#define FW_VERSION_SIZE 0x04 +#define WFX_DCA_RESERVED 0x0900C060 +#define DCA_RESERVED_SIZE 0x20 +#define WFX_STATUS_INFO 0x0900C080 +#define WFX_BOOTLOADER_LABEL 0x0900C084 +#define BOOTLOADER_LABEL_SIZE 0x3C +#define WFX_PTE_INFO 0x0900C0C0 +#define PTE_INFO_KEYSET_IDX 0x0D +#define PTE_INFO_SIZE 0x10 +#define WFX_ERR_INFO 0x0900C0D0 +#define ERR_INVALID_SEC_TYPE 0x05 +#define ERR_SIG_VERIF_FAILED 0x0F +#define ERR_AES_CTRL_KEY 0x10 +#define ERR_ECC_PUB_KEY 0x11 +#define ERR_MAC_KEY 0x18 + +#define DCA_TIMEOUT 50 // milliseconds +#define WAKEUP_TIMEOUT 200 // milliseconds + +static const char * const fwio_error_strings[] = { + [ERR_INVALID_SEC_TYPE] = "Invalid section type or wrong encryption", + [ERR_SIG_VERIF_FAILED] = "Signature verification failed", + [ERR_AES_CTRL_KEY] = "AES control key not initialized", + [ERR_ECC_PUB_KEY] = "ECC public key not initialized", + [ERR_MAC_KEY] = "MAC key not initialized", +}; + +/* + * request_firmware() allocate data using vmalloc(). It is not compatible with + * underlying hardware that use DMA. Function below detect this case and + * allocate a bounce buffer if necessary. + * + * Notice that, in doubt, you can enable CONFIG_DEBUG_SG to ask kernel to + * detect this problem at runtime (else, kernel silently fail). + * + * NOTE: it may also be possible to use 'pages' from struct firmware and avoid + * bounce buffer + */ +static int sram_write_dma_safe(struct wfx_dev *wdev, u32 addr, const u8 *buf, + size_t len) +{ + int ret; + const u8 *tmp; + + if (!virt_addr_valid(buf)) { + tmp = kmemdup(buf, len, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + } else { + tmp = buf; + } + ret = sram_buf_write(wdev, addr, tmp, len); + if (!virt_addr_valid(buf)) + kfree(tmp); + return ret; +} + +int get_firmware(struct wfx_dev *wdev, u32 keyset_chip, + const struct firmware **fw, int *file_offset) +{ + int keyset_file; + char filename[256]; + const char *data; + int ret; + + snprintf(filename, sizeof(filename), "%s_%02X.sec", wdev->pdata.file_fw, keyset_chip); + ret = firmware_request_nowarn(fw, filename, wdev->dev); + if (ret) { + dev_info(wdev->dev, "can't load %s, falling back to %s.sec\n", filename, wdev->pdata.file_fw); + snprintf(filename, sizeof(filename), "%s.sec", wdev->pdata.file_fw); + ret = request_firmware(fw, filename, wdev->dev); + if (ret) { + dev_err(wdev->dev, "can't load %s\n", filename); + *fw = NULL; + return ret; + } + } + + data = (*fw)->data; + if (memcmp(data, "KEYSET", 6) != 0) { + // Legacy firmware format + *file_offset = 0; + keyset_file = 0x90; + } else { + *file_offset = 8; + keyset_file = (hex_to_bin(data[6]) * 16) | hex_to_bin(data[7]); + if (keyset_file < 0) { + dev_err(wdev->dev, "%s corrupted\n", filename); + release_firmware(*fw); + *fw = NULL; + return -EINVAL; + } + } + if (keyset_file != keyset_chip) { + dev_err(wdev->dev, "firmware keyset is incompatible with chip (file: 0x%02X, chip: 0x%02X)\n", + keyset_file, keyset_chip); + release_firmware(*fw); + *fw = NULL; + return -ENODEV; + } + wdev->keyset = keyset_file; + return 0; +} + +static int wait_ncp_status(struct wfx_dev *wdev, u32 status) +{ + ktime_t now, start; + u32 reg; + int ret; + + start = ktime_get(); + for (;;) { + ret = sram_reg_read(wdev, WFX_DCA_NCP_STATUS, ®); + if (ret < 0) + return -EIO; + now = ktime_get(); + if (reg == status) + break; + if (ktime_after(now, ktime_add_ms(start, DCA_TIMEOUT))) + return -ETIMEDOUT; + } + if (ktime_compare(now, start)) + dev_dbg(wdev->dev, "chip answer after %lldus\n", ktime_us_delta(now, start)); + else + dev_dbg(wdev->dev, "chip answer immediately\n"); + return 0; +} + +static int upload_firmware(struct wfx_dev *wdev, const u8 *data, size_t len) +{ + int ret; + u32 offs, bytes_done; + ktime_t now, start; + + if (len % DNLD_BLOCK_SIZE) { + dev_err(wdev->dev, "firmware size is not aligned. Buffer overrun will occur\n"); + return -EIO; + } + offs = 0; + while (offs < len) { + start = ktime_get(); + for (;;) { + ret = sram_reg_read(wdev, WFX_DCA_GET, &bytes_done); + if (ret < 0) + return ret; + now = ktime_get(); + if (offs + DNLD_BLOCK_SIZE - bytes_done < DNLD_FIFO_SIZE) + break; + if (ktime_after(now, ktime_add_ms(start, DCA_TIMEOUT))) + return -ETIMEDOUT; + } + if (ktime_compare(now, start)) + dev_dbg(wdev->dev, "answer after %lldus\n", ktime_us_delta(now, start)); + + ret = sram_write_dma_safe(wdev, WFX_DNLD_FIFO + (offs % DNLD_FIFO_SIZE), + data + offs, DNLD_BLOCK_SIZE); + if (ret < 0) + return ret; + + // WFx seems to not support writing 0 in this register during + // first loop + offs += DNLD_BLOCK_SIZE; + ret = sram_reg_write(wdev, WFX_DCA_PUT, offs); + if (ret < 0) + return ret; + } + return 0; +} + +static void print_boot_status(struct wfx_dev *wdev) +{ + u32 val32; + + sram_reg_read(wdev, WFX_STATUS_INFO, &val32); + if (val32 == 0x12345678) { + dev_info(wdev->dev, "no error reported by secure boot\n"); + } else { + sram_reg_read(wdev, WFX_ERR_INFO, &val32); + if (val32 < ARRAY_SIZE(fwio_error_strings) && fwio_error_strings[val32]) + dev_info(wdev->dev, "secure boot error: %s\n", fwio_error_strings[val32]); + else + dev_info(wdev->dev, "secure boot error: Unknown (0x%02x)\n", val32); + } +} + +static int load_firmware_secure(struct wfx_dev *wdev) +{ + const struct firmware *fw = NULL; + int header_size; + int fw_offset; + ktime_t start; + u8 *buf; + int ret; + + BUILD_BUG_ON(PTE_INFO_SIZE > BOOTLOADER_LABEL_SIZE); + buf = kmalloc(BOOTLOADER_LABEL_SIZE + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_READY); + ret = wait_ncp_status(wdev, NCP_INFO_READY); + if (ret) + goto error; + + sram_buf_read(wdev, WFX_BOOTLOADER_LABEL, buf, BOOTLOADER_LABEL_SIZE); + buf[BOOTLOADER_LABEL_SIZE] = 0; + dev_dbg(wdev->dev, "bootloader: \"%s\"\n", buf); + + sram_buf_read(wdev, WFX_PTE_INFO, buf, PTE_INFO_SIZE); + ret = get_firmware(wdev, buf[PTE_INFO_KEYSET_IDX], &fw, &fw_offset); + if (ret) + goto error; + header_size = fw_offset + FW_SIGNATURE_SIZE + FW_HASH_SIZE; + + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_INFO_READ); + ret = wait_ncp_status(wdev, NCP_READY); + if (ret) + goto error; + + sram_reg_write(wdev, WFX_DNLD_FIFO, 0xFFFFFFFF); // Fifo init + sram_write_dma_safe(wdev, WFX_DCA_FW_VERSION, "\x01\x00\x00\x00", FW_VERSION_SIZE); + sram_write_dma_safe(wdev, WFX_DCA_FW_SIGNATURE, fw->data + fw_offset, FW_SIGNATURE_SIZE); + sram_write_dma_safe(wdev, WFX_DCA_FW_HASH, fw->data + fw_offset + FW_SIGNATURE_SIZE, FW_HASH_SIZE); + sram_reg_write(wdev, WFX_DCA_IMAGE_SIZE, fw->size - header_size); + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_UPLOAD_PENDING); + ret = wait_ncp_status(wdev, NCP_DOWNLOAD_PENDING); + if (ret) + goto error; + + start = ktime_get(); + ret = upload_firmware(wdev, fw->data + header_size, fw->size - header_size); + if (ret) + goto error; + dev_dbg(wdev->dev, "firmware load after %lldus\n", ktime_us_delta(ktime_get(), start)); + + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_UPLOAD_COMPLETE); + ret = wait_ncp_status(wdev, NCP_AUTH_OK); + // Legacy ROM support + if (ret < 0) + ret = wait_ncp_status(wdev, NCP_PUB_KEY_RDY); + if (ret < 0) + goto error; + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_OK_TO_JUMP); + +error: + kfree(buf); + if (fw) + release_firmware(fw); + if (ret) + print_boot_status(wdev); + return ret; +} + +static int init_gpr(struct wfx_dev *wdev) +{ + int ret, i; + static const struct { + int index; + u32 value; + } gpr_init[] = { + { 0x07, 0x208775 }, + { 0x08, 0x2EC020 }, + { 0x09, 0x3C3C3C }, + { 0x0B, 0x322C44 }, + { 0x0C, 0xA06497 }, + }; + + for (i = 0; i < ARRAY_SIZE(gpr_init); i++) { + ret = igpr_reg_write(wdev, gpr_init[i].index, gpr_init[i].value); + if (ret < 0) + return ret; + dev_dbg(wdev->dev, " index %02x: %08x\n", gpr_init[i].index, gpr_init[i].value); + } + return 0; +} + +int wfx_init_device(struct wfx_dev *wdev) +{ + int ret; + int hw_revision, hw_type; + int wakeup_timeout = 50; // ms + ktime_t now, start; + u32 reg; + + reg = CFG_DIRECT_ACCESS_MODE | CFG_CPU_RESET | CFG_WORD_MODE2; + if (wdev->pdata.use_rising_clk) + reg |= CFG_CLK_RISE_EDGE; + ret = config_reg_write(wdev, reg); + if (ret < 0) { + dev_err(wdev->dev, "bus returned an error during first write access. Host configuration error?\n"); + return -EIO; + } + + ret = config_reg_read(wdev, ®); + if (ret < 0) { + dev_err(wdev->dev, "bus returned an error during first read access. Bus configuration error?\n"); + return -EIO; + } + if (reg == 0 || reg == ~0) { + dev_err(wdev->dev, "chip mute. Bus configuration error or chip wasn't reset?\n"); + return -EIO; + } + dev_dbg(wdev->dev, "initial config register value: %08x\n", reg); + + hw_revision = FIELD_GET(CFG_DEVICE_ID_MAJOR, reg); + if (hw_revision == 0 || hw_revision > 2) { + dev_err(wdev->dev, "bad hardware revision number: %d\n", hw_revision); + return -ENODEV; + } + hw_type = FIELD_GET(CFG_DEVICE_ID_TYPE, reg); + if (hw_type == 1) { + dev_notice(wdev->dev, "development hardware detected\n"); + wakeup_timeout = 2000; + } + + ret = init_gpr(wdev); + if (ret < 0) + return ret; + + ret = control_reg_write(wdev, CTRL_WLAN_WAKEUP); + if (ret < 0) + return -EIO; + start = ktime_get(); + for (;;) { + ret = control_reg_read(wdev, ®); + now = ktime_get(); + if (reg & CTRL_WLAN_READY) + break; + if (ktime_after(now, ktime_add_ms(start, wakeup_timeout))) { + dev_err(wdev->dev, "chip didn't wake up. Chip wasn't reset?\n"); + return -ETIMEDOUT; + } + } + dev_dbg(wdev->dev, "chip wake up after %lldus\n", ktime_us_delta(now, start)); + + ret = config_reg_write_bits(wdev, CFG_CPU_RESET, 0); + if (ret < 0) + return ret; + ret = load_firmware_secure(wdev); + if (ret < 0) + return ret; + ret = config_reg_write_bits(wdev, CFG_DIRECT_ACCESS_MODE | CFG_IRQ_ENABLE_DATA | CFG_IRQ_ENABLE_WRDY, CFG_IRQ_ENABLE_DATA); + return ret; +} diff --git a/drivers/staging/wfx/fwio.h b/drivers/staging/wfx/fwio.h new file mode 100644 index 000000000000..6028f92503fe --- /dev/null +++ b/drivers/staging/wfx/fwio.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Firmware loading. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_FWIO_H +#define WFX_FWIO_H + +struct wfx_dev; + +int wfx_init_device(struct wfx_dev *wdev); + +#endif /* WFX_FWIO_H */ diff --git a/drivers/staging/wfx/hif_api_cmd.h b/drivers/staging/wfx/hif_api_cmd.h new file mode 100644 index 000000000000..7c5d1ea6098d --- /dev/null +++ b/drivers/staging/wfx/hif_api_cmd.h @@ -0,0 +1,681 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * WFx hardware interface definitions + * + * Copyright (c) 2018-2019, Silicon Laboratories Inc. + */ + +#ifndef WFX_HIF_API_CMD_H +#define WFX_HIF_API_CMD_H + +#include "hif_api_general.h" + +#define HIF_NUM_AC 4 + +#define HIF_API_SSID_SIZE API_SSID_SIZE + +enum hif_requests_ids { + HIF_REQ_ID_RESET = 0x0a, + HIF_REQ_ID_READ_MIB = 0x05, + HIF_REQ_ID_WRITE_MIB = 0x06, + HIF_REQ_ID_START_SCAN = 0x07, + HIF_REQ_ID_STOP_SCAN = 0x08, + HIF_REQ_ID_TX = 0x04, + HIF_REQ_ID_JOIN = 0x0b, + HIF_REQ_ID_SET_PM_MODE = 0x10, + HIF_REQ_ID_SET_BSS_PARAMS = 0x11, + HIF_REQ_ID_ADD_KEY = 0x0c, + HIF_REQ_ID_REMOVE_KEY = 0x0d, + HIF_REQ_ID_EDCA_QUEUE_PARAMS = 0x13, + HIF_REQ_ID_START = 0x17, + HIF_REQ_ID_BEACON_TRANSMIT = 0x18, + HIF_REQ_ID_UPDATE_IE = 0x1b, + HIF_REQ_ID_MAP_LINK = 0x1c, +}; + +enum hif_confirmations_ids { + HIF_CNF_ID_RESET = 0x0a, + HIF_CNF_ID_READ_MIB = 0x05, + HIF_CNF_ID_WRITE_MIB = 0x06, + HIF_CNF_ID_START_SCAN = 0x07, + HIF_CNF_ID_STOP_SCAN = 0x08, + HIF_CNF_ID_TX = 0x04, + HIF_CNF_ID_MULTI_TRANSMIT = 0x1e, + HIF_CNF_ID_JOIN = 0x0b, + HIF_CNF_ID_SET_PM_MODE = 0x10, + HIF_CNF_ID_SET_BSS_PARAMS = 0x11, + HIF_CNF_ID_ADD_KEY = 0x0c, + HIF_CNF_ID_REMOVE_KEY = 0x0d, + HIF_CNF_ID_EDCA_QUEUE_PARAMS = 0x13, + HIF_CNF_ID_START = 0x17, + HIF_CNF_ID_BEACON_TRANSMIT = 0x18, + HIF_CNF_ID_UPDATE_IE = 0x1b, + HIF_CNF_ID_MAP_LINK = 0x1c, +}; + +enum hif_indications_ids { + HIF_IND_ID_RX = 0x84, + HIF_IND_ID_SCAN_CMPL = 0x86, + HIF_IND_ID_JOIN_COMPLETE = 0x8f, + HIF_IND_ID_SET_PM_MODE_CMPL = 0x89, + HIF_IND_ID_SUSPEND_RESUME_TX = 0x8c, + HIF_IND_ID_EVENT = 0x85 +}; + +union hif_commands_ids { + enum hif_requests_ids request; + enum hif_confirmations_ids confirmation; + enum hif_indications_ids indication; +}; + +enum hif_status { + HIF_STATUS_SUCCESS = 0x0, + HIF_STATUS_FAILURE = 0x1, + HIF_INVALID_PARAMETER = 0x2, + HIF_STATUS_WARNING = 0x3, + HIF_ERROR_UNSUPPORTED_MSG_ID = 0x4, + HIF_STATUS_DECRYPTFAILURE = 0x10, + HIF_STATUS_MICFAILURE = 0x11, + HIF_STATUS_NO_KEY_FOUND = 0x12, + HIF_STATUS_RETRY_EXCEEDED = 0x13, + HIF_STATUS_TX_LIFETIME_EXCEEDED = 0x14, + HIF_REQUEUE = 0x15, + HIF_STATUS_REFUSED = 0x16, + HIF_STATUS_BUSY = 0x17 +}; + +struct hif_reset_flags { + uint8_t reset_stat:1; + uint8_t reset_all_int:1; + uint8_t reserved1:6; + uint8_t reserved2[3]; +} __packed; + +struct hif_req_reset { + struct hif_reset_flags reset_flags; +} __packed; + +struct hif_cnf_reset { + uint32_t status; +} __packed; + +struct hif_req_read_mib { + uint16_t mib_id; + uint16_t reserved; +} __packed; + +struct hif_cnf_read_mib { + uint32_t status; + uint16_t mib_id; + uint16_t length; + uint8_t mib_data[]; +} __packed; + +struct hif_req_write_mib { + uint16_t mib_id; + uint16_t length; + uint8_t mib_data[]; +} __packed; + +struct hif_cnf_write_mib { + uint32_t status; +} __packed; + +struct hif_ie_flags { + uint8_t beacon:1; + uint8_t probe_resp:1; + uint8_t probe_req:1; + uint8_t reserved1:5; + uint8_t reserved2; +} __packed; + +struct hif_ie_tlv { + uint8_t type; + uint8_t length; + uint8_t data[]; +} __packed; + +struct hif_req_update_ie { + struct hif_ie_flags ie_flags; + uint16_t num_i_es; + struct hif_ie_tlv ie[]; +} __packed; + +struct hif_cnf_update_ie { + uint32_t status; +} __packed; + +struct hif_scan_type { + uint8_t type:1; + uint8_t mode:1; + uint8_t reserved:6; +} __packed; + +struct hif_scan_flags { + uint8_t fbg:1; + uint8_t reserved1:1; + uint8_t pre:1; + uint8_t reserved2:5; +} __packed; + +struct hif_auto_scan_param { + uint16_t interval; + uint8_t reserved; + int8_t rssi_thr; +} __packed; + +struct hif_ssid_def { + uint32_t ssid_length; + uint8_t ssid[HIF_API_SSID_SIZE]; +} __packed; + +#define HIF_API_MAX_NB_SSIDS 2 +#define HIF_API_MAX_NB_CHANNELS 14 + +struct hif_req_start_scan { + uint8_t band; + struct hif_scan_type scan_type; + struct hif_scan_flags scan_flags; + uint8_t max_transmit_rate; + struct hif_auto_scan_param auto_scan_param; + uint8_t num_of_probe_requests; + uint8_t probe_delay; + uint8_t num_of_ssi_ds; + uint8_t num_of_channels; + uint32_t min_channel_time; + uint32_t max_channel_time; + int32_t tx_power_level; + uint8_t ssid_and_channel_lists[]; +} __packed; + +struct hif_start_scan_req_cstnbssid_body { + uint8_t band; + struct hif_scan_type scan_type; + struct hif_scan_flags scan_flags; + uint8_t max_transmit_rate; + struct hif_auto_scan_param auto_scan_param; + uint8_t num_of_probe_requests; + uint8_t probe_delay; + uint8_t num_of_ssi_ds; + uint8_t num_of_channels; + uint32_t min_channel_time; + uint32_t max_channel_time; + int32_t tx_power_level; + struct hif_ssid_def ssid_def[HIF_API_MAX_NB_SSIDS]; + uint8_t channel_list[]; +} __packed; + +struct hif_cnf_start_scan { + uint32_t status; +} __packed; + +struct hif_cnf_stop_scan { + uint32_t status; +} __packed; + +enum hif_pm_mode_status { + HIF_PM_MODE_ACTIVE = 0x0, + HIF_PM_MODE_PS = 0x1, + HIF_PM_MODE_UNDETERMINED = 0x2 +}; + +struct hif_ind_scan_cmpl { + uint32_t status; + uint8_t pm_mode; + uint8_t num_channels_completed; + uint16_t reserved; +} __packed; + +enum hif_queue_id { + HIF_QUEUE_ID_BACKGROUND = 0x0, + HIF_QUEUE_ID_BESTEFFORT = 0x1, + HIF_QUEUE_ID_VIDEO = 0x2, + HIF_QUEUE_ID_VOICE = 0x3 +}; + +enum hif_frame_format { + HIF_FRAME_FORMAT_NON_HT = 0x0, + HIF_FRAME_FORMAT_MIXED_FORMAT_HT = 0x1, + HIF_FRAME_FORMAT_GF_HT_11N = 0x2 +}; + +enum hif_stbc { + HIF_STBC_NOT_ALLOWED = 0x0, + HIF_STBC_ALLOWED = 0x1 +}; + +struct hif_queue { + uint8_t queue_id:2; + uint8_t peer_sta_id:4; + uint8_t reserved:2; +} __packed; + +struct hif_data_flags { + uint8_t more:1; + uint8_t fc_offset:3; + uint8_t reserved:4; +} __packed; + +struct hif_tx_flags { + uint8_t start_exp:1; + uint8_t reserved:3; + uint8_t retry_policy_index:4; +} __packed; + +struct hif_ht_tx_parameters { + uint8_t frame_format:4; + uint8_t fec_coding:1; + uint8_t short_gi:1; + uint8_t reserved1:1; + uint8_t stbc:1; + uint8_t reserved2; + uint8_t aggregation:1; + uint8_t reserved3:7; + uint8_t reserved4; +} __packed; + +struct hif_req_tx { + uint32_t packet_id; + uint8_t max_tx_rate; + struct hif_queue queue_id; + struct hif_data_flags data_flags; + struct hif_tx_flags tx_flags; + uint32_t reserved; + uint32_t expire_time; + struct hif_ht_tx_parameters ht_tx_parameters; + uint8_t frame[]; +} __packed; + +enum hif_qos_ackplcy { + HIF_QOS_ACKPLCY_NORMAL = 0x0, + HIF_QOS_ACKPLCY_TXNOACK = 0x1, + HIF_QOS_ACKPLCY_NOEXPACK = 0x2, + HIF_QOS_ACKPLCY_BLCKACK = 0x3 +}; + +struct hif_tx_result_flags { + uint8_t aggr:1; + uint8_t requeue:1; + uint8_t ack_policy:2; + uint8_t txop_limit:1; + uint8_t reserved1:3; + uint8_t reserved2; +} __packed; + +struct hif_cnf_tx { + uint32_t status; + uint32_t packet_id; + uint8_t txed_rate; + uint8_t ack_failures; + struct hif_tx_result_flags tx_result_flags; + uint32_t media_delay; + uint32_t tx_queue_delay; +} __packed; + +struct hif_cnf_multi_transmit { + uint32_t num_tx_confs; + struct hif_cnf_tx tx_conf_payload[]; +} __packed; + +enum hif_ri_flags_encrypt { + HIF_RI_FLAGS_UNENCRYPTED = 0x0, + HIF_RI_FLAGS_WEP_ENCRYPTED = 0x1, + HIF_RI_FLAGS_TKIP_ENCRYPTED = 0x2, + HIF_RI_FLAGS_AES_ENCRYPTED = 0x3, + HIF_RI_FLAGS_WAPI_ENCRYPTED = 0x4 +}; + +struct hif_rx_flags { + uint8_t encryp:3; + uint8_t in_aggr:1; + uint8_t first_aggr:1; + uint8_t last_aggr:1; + uint8_t defrag:1; + uint8_t beacon:1; + uint8_t tim:1; + uint8_t bitmap:1; + uint8_t match_ssid:1; + uint8_t match_bssid:1; + uint8_t more:1; + uint8_t reserved1:1; + uint8_t ht:1; + uint8_t stbc:1; + uint8_t match_uc_addr:1; + uint8_t match_mc_addr:1; + uint8_t match_bc_addr:1; + uint8_t key_type:1; + uint8_t key_index:4; + uint8_t reserved2:1; + uint8_t peer_sta_id:4; + uint8_t reserved3:2; + uint8_t reserved4:1; +} __packed; + +struct hif_ind_rx { + uint32_t status; + uint16_t channel_number; + uint8_t rxed_rate; + uint8_t rcpi_rssi; + struct hif_rx_flags rx_flags; + uint8_t frame[]; +} __packed; + + +struct hif_req_edca_queue_params { + uint8_t queue_id; + uint8_t reserved1; + uint8_t aifsn; + uint8_t reserved2; + uint16_t cw_min; + uint16_t cw_max; + uint16_t tx_op_limit; + uint16_t allowed_medium_time; + uint32_t reserved3; +} __packed; + +struct hif_cnf_edca_queue_params { + uint32_t status; +} __packed; + +enum hif_ap_mode { + HIF_MODE_IBSS = 0x0, + HIF_MODE_BSS = 0x1 +}; + +enum hif_preamble { + HIF_PREAMBLE_LONG = 0x0, + HIF_PREAMBLE_SHORT = 0x1, + HIF_PREAMBLE_SHORT_LONG12 = 0x2 +}; + +struct hif_join_flags { + uint8_t reserved1:2; + uint8_t force_no_beacon:1; + uint8_t force_with_ind:1; + uint8_t reserved2:4; +} __packed; + +struct hif_req_join { + uint8_t mode; + uint8_t band; + uint16_t channel_number; + uint8_t bssid[ETH_ALEN]; + uint16_t atim_window; + uint8_t preamble_type; + uint8_t probe_for_join; + uint8_t reserved; + struct hif_join_flags join_flags; + uint32_t ssid_length; + uint8_t ssid[HIF_API_SSID_SIZE]; + uint32_t beacon_interval; + uint32_t basic_rate_set; +} __packed; + +struct hif_cnf_join { + uint32_t status; +} __packed; + +struct hif_ind_join_complete { + uint32_t status; +} __packed; + +struct hif_bss_flags { + uint8_t lost_count_only:1; + uint8_t reserved:7; +} __packed; + +struct hif_req_set_bss_params { + struct hif_bss_flags bss_flags; + uint8_t beacon_lost_count; + uint16_t aid; + uint32_t operational_rate_set; +} __packed; + +struct hif_cnf_set_bss_params { + uint32_t status; +} __packed; + +struct hif_pm_mode { + uint8_t enter_psm:1; + uint8_t reserved:6; + uint8_t fast_psm:1; +} __packed; + +struct hif_req_set_pm_mode { + struct hif_pm_mode pm_mode; + uint8_t fast_psm_idle_period; + uint8_t ap_psm_change_period; + uint8_t min_auto_ps_poll_period; +} __packed; + +struct hif_cnf_set_pm_mode { + uint32_t status; +} __packed; + +struct hif_ind_set_pm_mode_cmpl { + uint32_t status; + uint8_t pm_mode; + uint8_t reserved[3]; +} __packed; + + +struct hif_req_start { + uint8_t mode; + uint8_t band; + uint16_t channel_number; + uint32_t reserved1; + uint32_t beacon_interval; + uint8_t dtim_period; + uint8_t preamble_type; + uint8_t reserved2; + uint8_t ssid_length; + uint8_t ssid[HIF_API_SSID_SIZE]; + uint32_t basic_rate_set; +} __packed; + +struct hif_cnf_start { + uint32_t status; +} __packed; + +enum hif_beacon { + HIF_BEACON_STOP = 0x0, + HIF_BEACON_START = 0x1 +}; + +struct hif_req_beacon_transmit { + uint8_t enable_beaconing; + uint8_t reserved[3]; +} __packed; + +struct hif_cnf_beacon_transmit { + uint32_t status; +} __packed; + +enum hif_sta_map_direction { + HIF_STA_MAP = 0x0, + HIF_STA_UNMAP = 0x1 +}; + +struct hif_map_link_flags { + uint8_t map_direction:1; + uint8_t mfpc:1; + uint8_t reserved:6; +} __packed; + +struct hif_req_map_link { + uint8_t mac_addr[ETH_ALEN]; + struct hif_map_link_flags map_link_flags; + uint8_t peer_sta_id; +} __packed; + +struct hif_cnf_map_link { + uint32_t status; +} __packed; + +struct hif_suspend_resume_flags { + uint8_t resume:1; + uint8_t reserved1:2; + uint8_t bc_mc_only:1; + uint8_t reserved2:4; + uint8_t reserved3; +} __packed; + +struct hif_ind_suspend_resume_tx { + struct hif_suspend_resume_flags suspend_resume_flags; + uint16_t peer_sta_set; +} __packed; + + +#define MAX_KEY_ENTRIES 24 +#define HIF_API_WEP_KEY_DATA_SIZE 16 +#define HIF_API_TKIP_KEY_DATA_SIZE 16 +#define HIF_API_RX_MIC_KEY_SIZE 8 +#define HIF_API_TX_MIC_KEY_SIZE 8 +#define HIF_API_AES_KEY_DATA_SIZE 16 +#define HIF_API_WAPI_KEY_DATA_SIZE 16 +#define HIF_API_MIC_KEY_DATA_SIZE 16 +#define HIF_API_IGTK_KEY_DATA_SIZE 16 +#define HIF_API_RX_SEQUENCE_COUNTER_SIZE 8 +#define HIF_API_IPN_SIZE 8 + +enum hif_key_type { + HIF_KEY_TYPE_WEP_DEFAULT = 0x0, + HIF_KEY_TYPE_WEP_PAIRWISE = 0x1, + HIF_KEY_TYPE_TKIP_GROUP = 0x2, + HIF_KEY_TYPE_TKIP_PAIRWISE = 0x3, + HIF_KEY_TYPE_AES_GROUP = 0x4, + HIF_KEY_TYPE_AES_PAIRWISE = 0x5, + HIF_KEY_TYPE_WAPI_GROUP = 0x6, + HIF_KEY_TYPE_WAPI_PAIRWISE = 0x7, + HIF_KEY_TYPE_IGTK_GROUP = 0x8, + HIF_KEY_TYPE_NONE = 0x9 +}; + +struct hif_wep_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t reserved; + uint8_t key_length; + uint8_t key_data[HIF_API_WEP_KEY_DATA_SIZE]; +} __packed; + +struct hif_wep_group_key { + uint8_t key_id; + uint8_t key_length; + uint8_t reserved[2]; + uint8_t key_data[HIF_API_WEP_KEY_DATA_SIZE]; +} __packed; + +struct hif_tkip_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t reserved[2]; + uint8_t tkip_key_data[HIF_API_TKIP_KEY_DATA_SIZE]; + uint8_t rx_mic_key[HIF_API_RX_MIC_KEY_SIZE]; + uint8_t tx_mic_key[HIF_API_TX_MIC_KEY_SIZE]; +} __packed; + +struct hif_tkip_group_key { + uint8_t tkip_key_data[HIF_API_TKIP_KEY_DATA_SIZE]; + uint8_t rx_mic_key[HIF_API_RX_MIC_KEY_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; + uint8_t rx_sequence_counter[HIF_API_RX_SEQUENCE_COUNTER_SIZE]; +} __packed; + +struct hif_aes_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t reserved[2]; + uint8_t aes_key_data[HIF_API_AES_KEY_DATA_SIZE]; +} __packed; + +struct hif_aes_group_key { + uint8_t aes_key_data[HIF_API_AES_KEY_DATA_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; + uint8_t rx_sequence_counter[HIF_API_RX_SEQUENCE_COUNTER_SIZE]; +} __packed; + +struct hif_wapi_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t key_id; + uint8_t reserved; + uint8_t wapi_key_data[HIF_API_WAPI_KEY_DATA_SIZE]; + uint8_t mic_key_data[HIF_API_MIC_KEY_DATA_SIZE]; +} __packed; + +struct hif_wapi_group_key { + uint8_t wapi_key_data[HIF_API_WAPI_KEY_DATA_SIZE]; + uint8_t mic_key_data[HIF_API_MIC_KEY_DATA_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; +} __packed; + +struct hif_igtk_group_key { + uint8_t igtk_key_data[HIF_API_IGTK_KEY_DATA_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; + uint8_t ipn[HIF_API_IPN_SIZE]; +} __packed; + +union hif_privacy_key_data { + struct hif_wep_pairwise_key wep_pairwise_key; + struct hif_wep_group_key wep_group_key; + struct hif_tkip_pairwise_key tkip_pairwise_key; + struct hif_tkip_group_key tkip_group_key; + struct hif_aes_pairwise_key aes_pairwise_key; + struct hif_aes_group_key aes_group_key; + struct hif_wapi_pairwise_key wapi_pairwise_key; + struct hif_wapi_group_key wapi_group_key; + struct hif_igtk_group_key igtk_group_key; +}; + +struct hif_req_add_key { + uint8_t type; + uint8_t entry_index; + uint8_t int_id:2; + uint8_t reserved1:6; + uint8_t reserved2; + union hif_privacy_key_data key; +} __packed; + +struct hif_cnf_add_key { + uint32_t status; +} __packed; + +struct hif_req_remove_key { + uint8_t entry_index; + uint8_t reserved[3]; +} __packed; + +struct hif_cnf_remove_key { + uint32_t status; +} __packed; + +enum hif_event_ind { + HIF_EVENT_IND_BSSLOST = 0x1, + HIF_EVENT_IND_BSSREGAINED = 0x2, + HIF_EVENT_IND_RCPI_RSSI = 0x3, + HIF_EVENT_IND_PS_MODE_ERROR = 0x4, + HIF_EVENT_IND_INACTIVITY = 0x5 +}; + +enum hif_ps_mode_error { + HIF_PS_ERROR_NO_ERROR = 0, + HIF_PS_ERROR_AP_NOT_RESP_TO_POLL = 1, + HIF_PS_ERROR_AP_NOT_RESP_TO_UAPSD_TRIGGER = 2, + HIF_PS_ERROR_AP_SENT_UNICAST_IN_DOZE = 3, + HIF_PS_ERROR_AP_NO_DATA_AFTER_TIM = 4 +}; + +union hif_event_data { + uint8_t rcpi_rssi; + uint32_t ps_mode_error; + uint32_t peer_sta_set; +}; + +struct hif_ind_event { + uint32_t event_id; + union hif_event_data event_data; +} __packed; + + +#endif diff --git a/drivers/staging/wfx/hif_api_general.h b/drivers/staging/wfx/hif_api_general.h new file mode 100644 index 000000000000..d885b55d2882 --- /dev/null +++ b/drivers/staging/wfx/hif_api_general.h @@ -0,0 +1,437 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * WFx hardware interface definitions + * + * Copyright (c) 2018-2019, Silicon Laboratories Inc. + */ + +#ifndef WFX_HIF_API_GENERAL_H +#define WFX_HIF_API_GENERAL_H + +#ifdef __KERNEL__ +#include <linux/types.h> +#include <linux/if_ether.h> +#else +#include <net/ethernet.h> +#include <stdint.h> +#define __packed __attribute__((__packed__)) +#endif + +#define API_SSID_SIZE 32 + +#define HIF_ID_IS_INDICATION 0x80 +#define HIF_COUNTER_MAX 7 + +struct hif_msg { + uint16_t len; + uint8_t id; + uint8_t reserved:1; + uint8_t interface:2; + uint8_t seqnum:3; + uint8_t encrypted:2; + uint8_t body[]; +} __packed; + +enum hif_general_requests_ids { + HIF_REQ_ID_CONFIGURATION = 0x09, + HIF_REQ_ID_CONTROL_GPIO = 0x26, + HIF_REQ_ID_SET_SL_MAC_KEY = 0x27, + HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS = 0x28, + HIF_REQ_ID_SL_CONFIGURE = 0x29, + HIF_REQ_ID_PREVENT_ROLLBACK = 0x2a, + HIF_REQ_ID_PTA_SETTINGS = 0x2b, + HIF_REQ_ID_PTA_PRIORITY = 0x2c, + HIF_REQ_ID_PTA_STATE = 0x2d, + HIF_REQ_ID_SHUT_DOWN = 0x32, +}; + +enum hif_general_confirmations_ids { + HIF_CNF_ID_CONFIGURATION = 0x09, + HIF_CNF_ID_CONTROL_GPIO = 0x26, + HIF_CNF_ID_SET_SL_MAC_KEY = 0x27, + HIF_CNF_ID_SL_EXCHANGE_PUB_KEYS = 0x28, + HIF_CNF_ID_SL_CONFIGURE = 0x29, + HIF_CNF_ID_PREVENT_ROLLBACK = 0x2a, + HIF_CNF_ID_PTA_SETTINGS = 0x2b, + HIF_CNF_ID_PTA_PRIORITY = 0x2c, + HIF_CNF_ID_PTA_STATE = 0x2d, + HIF_CNF_ID_SHUT_DOWN = 0x32, +}; + +enum hif_general_indications_ids { + HIF_IND_ID_EXCEPTION = 0xe0, + HIF_IND_ID_STARTUP = 0xe1, + HIF_IND_ID_WAKEUP = 0xe2, + HIF_IND_ID_GENERIC = 0xe3, + HIF_IND_ID_ERROR = 0xe4, + HIF_IND_ID_SL_EXCHANGE_PUB_KEYS = 0xe5 +}; + +enum hif_hi_status { + HI_STATUS_SUCCESS = 0x0000, + HI_STATUS_FAILURE = 0x0001, + HI_INVALID_PARAMETER = 0x0002, + HI_STATUS_GPIO_WARNING = 0x0003, + HI_ERROR_UNSUPPORTED_MSG_ID = 0x0004, + SL_MAC_KEY_STATUS_SUCCESS = 0x005A, + SL_MAC_KEY_STATUS_FAILED_KEY_ALREADY_BURNED = 0x006B, + SL_MAC_KEY_STATUS_FAILED_RAM_MODE_NOT_ALLOWED = 0x007C, + SL_MAC_KEY_STATUS_FAILED_UNKNOWN_MODE = 0x008D, + SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS = 0x009E, + SL_PUB_KEY_EXCHANGE_STATUS_FAILED = 0x00AF, + PREVENT_ROLLBACK_CNF_SUCCESS = 0x1234, + PREVENT_ROLLBACK_CNF_WRONG_MAGIC_WORD = 0x1256 +}; + +enum hif_api_rate_index { + API_RATE_INDEX_B_1MBPS = 0, + API_RATE_INDEX_B_2MBPS = 1, + API_RATE_INDEX_B_5P5MBPS = 2, + API_RATE_INDEX_B_11MBPS = 3, + API_RATE_INDEX_PBCC_22MBPS = 4, + API_RATE_INDEX_PBCC_33MBPS = 5, + API_RATE_INDEX_G_6MBPS = 6, + API_RATE_INDEX_G_9MBPS = 7, + API_RATE_INDEX_G_12MBPS = 8, + API_RATE_INDEX_G_18MBPS = 9, + API_RATE_INDEX_G_24MBPS = 10, + API_RATE_INDEX_G_36MBPS = 11, + API_RATE_INDEX_G_48MBPS = 12, + API_RATE_INDEX_G_54MBPS = 13, + API_RATE_INDEX_N_6P5MBPS = 14, + API_RATE_INDEX_N_13MBPS = 15, + API_RATE_INDEX_N_19P5MBPS = 16, + API_RATE_INDEX_N_26MBPS = 17, + API_RATE_INDEX_N_39MBPS = 18, + API_RATE_INDEX_N_52MBPS = 19, + API_RATE_INDEX_N_58P5MBPS = 20, + API_RATE_INDEX_N_65MBPS = 21, + API_RATE_NUM_ENTRIES = 22 +}; + + +enum hif_fw_type { + HIF_FW_TYPE_ETF = 0x0, + HIF_FW_TYPE_WFM = 0x1, + HIF_FW_TYPE_WSM = 0x2 +}; + +struct hif_capabilities { + uint8_t link_mode:2; + uint8_t reserved1:6; + uint8_t reserved2; + uint8_t reserved3; + uint8_t reserved4; +} __packed; + +struct hif_otp_regul_sel_mode_info { + uint8_t region_sel_mode:4; + uint8_t reserved:4; +} __packed; + +struct hif_otp_phy_info { + uint8_t phy1_region:3; + uint8_t phy0_region:3; + uint8_t otp_phy_ver:2; +} __packed; + +#define API_OPN_SIZE 14 +#define API_UID_SIZE 8 +#define API_DISABLED_CHANNEL_LIST_SIZE 2 +#define API_FIRMWARE_LABEL_SIZE 128 + +struct hif_ind_startup { + uint32_t status; + uint16_t hardware_id; + uint8_t opn[API_OPN_SIZE]; + uint8_t uid[API_UID_SIZE]; + uint16_t num_inp_ch_bufs; + uint16_t size_inp_ch_buf; + uint8_t num_links_ap; + uint8_t num_interfaces; + uint8_t mac_addr[2][ETH_ALEN]; + uint8_t api_version_minor; + uint8_t api_version_major; + struct hif_capabilities capabilities; + uint8_t firmware_build; + uint8_t firmware_minor; + uint8_t firmware_major; + uint8_t firmware_type; + uint8_t disabled_channel_list[API_DISABLED_CHANNEL_LIST_SIZE]; + struct hif_otp_regul_sel_mode_info regul_sel_mode_info; + struct hif_otp_phy_info otp_phy_info; + uint32_t supported_rate_mask; + uint8_t firmware_label[API_FIRMWARE_LABEL_SIZE]; +} __packed; + +struct hif_ind_wakeup { +} __packed; + +struct hif_req_configuration { + uint16_t length; + uint8_t pds_data[]; +} __packed; + +struct hif_cnf_configuration { + uint32_t status; +} __packed; + +enum hif_gpio_mode { + HIF_GPIO_MODE_D0 = 0x0, + HIF_GPIO_MODE_D1 = 0x1, + HIF_GPIO_MODE_OD0 = 0x2, + HIF_GPIO_MODE_OD1 = 0x3, + HIF_GPIO_MODE_TRISTATE = 0x4, + HIF_GPIO_MODE_TOGGLE = 0x5, + HIF_GPIO_MODE_READ = 0x6 +}; + +struct hif_req_control_gpio { + uint8_t gpio_label; + uint8_t gpio_mode; +} __packed; + +enum hif_gpio_error { + HIF_GPIO_ERROR_0 = 0x0, + HIF_GPIO_ERROR_1 = 0x1, + HIF_GPIO_ERROR_2 = 0x2 +}; + +struct hif_cnf_control_gpio { + uint32_t status; + uint32_t value; +} __packed; + +enum hif_generic_indication_type { + HIF_GENERIC_INDICATION_TYPE_RAW = 0x0, + HIF_GENERIC_INDICATION_TYPE_STRING = 0x1, + HIF_GENERIC_INDICATION_TYPE_RX_STATS = 0x2 +}; + +struct hif_rx_stats { + uint32_t nb_rx_frame; + uint32_t nb_crc_frame; + uint32_t per_total; + uint32_t throughput; + uint32_t nb_rx_by_rate[API_RATE_NUM_ENTRIES]; + uint16_t per[API_RATE_NUM_ENTRIES]; + int16_t snr[API_RATE_NUM_ENTRIES]; + int16_t rssi[API_RATE_NUM_ENTRIES]; + int16_t cfo[API_RATE_NUM_ENTRIES]; + uint32_t date; + uint32_t pwr_clk_freq; + uint8_t is_ext_pwr_clk; + int8_t current_temp; +} __packed; + +union hif_indication_data { + struct hif_rx_stats rx_stats; + uint8_t raw_data[1]; +}; + +struct hif_ind_generic { + uint32_t indication_type; + union hif_indication_data indication_data; +} __packed; + + +#define HIF_EXCEPTION_DATA_SIZE 124 + +struct hif_ind_exception { + uint8_t data[HIF_EXCEPTION_DATA_SIZE]; +} __packed; + + +enum hif_error { + HIF_ERROR_FIRMWARE_ROLLBACK = 0x0, + HIF_ERROR_FIRMWARE_DEBUG_ENABLED = 0x1, + HIF_ERROR_OUTDATED_SESSION_KEY = 0x2, + HIF_ERROR_INVALID_SESSION_KEY = 0x3, + HIF_ERROR_OOR_VOLTAGE = 0x4, + HIF_ERROR_PDS_VERSION = 0x5, + HIF_ERROR_OOR_TEMPERATURE = 0x6, + HIF_ERROR_REQ_DURING_KEY_EXCHANGE = 0x7, + HIF_ERROR_MULTI_TX_CNF_SECURELINK = 0x8, + HIF_ERROR_SECURELINK_OVERFLOW = 0x9, + HIF_ERROR_SECURELINK_DECRYPTION = 0xa +}; + +struct hif_ind_error { + uint32_t type; + uint8_t data[]; +} __packed; + +enum hif_secure_link_state { + SEC_LINK_UNAVAILABLE = 0x0, + SEC_LINK_RESERVED = 0x1, + SEC_LINK_EVAL = 0x2, + SEC_LINK_ENFORCED = 0x3 +}; + +enum hif_sl_encryption_type { + NO_ENCRYPTION = 0, + TX_ENCRYPTION = 1, + RX_ENCRYPTION = 2, + HP_ENCRYPTION = 3 +}; + +struct hif_sl_msg_hdr { + uint32_t seqnum:30; + uint32_t encrypted:2; +} __packed; + +struct hif_sl_msg { + struct hif_sl_msg_hdr hdr; + uint16_t len; + uint8_t payload[]; +} __packed; + +#define AES_CCM_TAG_SIZE 16 + +struct hif_sl_tag { + uint8_t tag[16]; +} __packed; + +enum hif_sl_mac_key_dest { + SL_MAC_KEY_DEST_OTP = 0x78, + SL_MAC_KEY_DEST_RAM = 0x87 +}; + +#define API_KEY_VALUE_SIZE 32 + +struct hif_req_set_sl_mac_key { + uint8_t otp_or_ram; + uint8_t key_value[API_KEY_VALUE_SIZE]; +} __packed; + +struct hif_cnf_set_sl_mac_key { + uint32_t status; +} __packed; + +#define API_HOST_PUB_KEY_SIZE 32 +#define API_HOST_PUB_KEY_MAC_SIZE 64 + +enum hif_sl_session_key_alg { + HIF_SL_CURVE25519 = 0x01, + HIF_SL_KDF = 0x02 +}; + +struct hif_req_sl_exchange_pub_keys { + uint8_t algorithm:2; + uint8_t reserved1:6; + uint8_t reserved2[3]; + uint8_t host_pub_key[API_HOST_PUB_KEY_SIZE]; + uint8_t host_pub_key_mac[API_HOST_PUB_KEY_MAC_SIZE]; +} __packed; + +struct hif_cnf_sl_exchange_pub_keys { + uint32_t status; +} __packed; + +#define API_NCP_PUB_KEY_SIZE 32 +#define API_NCP_PUB_KEY_MAC_SIZE 64 + +struct hif_ind_sl_exchange_pub_keys { + uint32_t status; + uint8_t ncp_pub_key[API_NCP_PUB_KEY_SIZE]; + uint8_t ncp_pub_key_mac[API_NCP_PUB_KEY_MAC_SIZE]; +} __packed; + +#define API_ENCR_BMP_SIZE 32 + +struct hif_req_sl_configure { + uint8_t encr_bmp[API_ENCR_BMP_SIZE]; + uint8_t disable_session_key_protection:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +struct hif_cnf_sl_configure { + uint32_t status; +} __packed; + +struct hif_req_prevent_rollback { + uint32_t magic_word; +} __packed; + +struct hif_cnf_prevent_rollback { + uint32_t status; +} __packed; + +enum hif_pta_mode { + PTA_1W_WLAN_MASTER = 0, + PTA_1W_COEX_MASTER = 1, + PTA_2W = 2, + PTA_3W = 3, + PTA_4W = 4 +}; + +enum hif_signal_level { + SIGNAL_LOW = 0, + SIGNAL_HIGH = 1 +}; + +enum hif_coex_type { + COEX_TYPE_GENERIC = 0, + COEX_TYPE_BLE = 1 +}; + +enum hif_grant_state { + NO_GRANT = 0, + GRANT = 1 +}; + +struct hif_req_pta_settings { + uint8_t pta_mode; + uint8_t request_signal_active_level; + uint8_t priority_signal_active_level; + uint8_t freq_signal_active_level; + uint8_t grant_signal_active_level; + uint8_t coex_type; + uint8_t default_grant_state; + uint8_t simultaneous_rx_accesses; + uint8_t priority_sampling_time; + uint8_t tx_rx_sampling_time; + uint8_t freq_sampling_time; + uint8_t grant_valid_time; + uint8_t fem_control_time; + uint8_t first_slot_time; + uint16_t periodic_tx_rx_sampling_time; + uint16_t coex_quota; + uint16_t wlan_quota; +} __packed; + +struct hif_cnf_pta_settings { + uint32_t status; +} __packed; + +enum hif_pta_priority { + HIF_PTA_PRIORITY_COEX_MAXIMIZED = 0x00000562, + HIF_PTA_PRIORITY_COEX_HIGH = 0x00000462, + HIF_PTA_PRIORITY_BALANCED = 0x00001461, + HIF_PTA_PRIORITY_WLAN_HIGH = 0x00001851, + HIF_PTA_PRIORITY_WLAN_MAXIMIZED = 0x00001A51 +}; + +struct hif_req_pta_priority { + uint32_t priority; +} __packed; + +struct hif_cnf_pta_priority { + uint32_t status; +} __packed; + +enum hif_pta_state { + PTA_OFF = 0, + PTA_ON = 1 +}; + +struct hif_req_pta_state { + uint32_t pta_state; +} __packed; + +struct hif_cnf_pta_state { + uint32_t status; +} __packed; + +#endif diff --git a/drivers/staging/wfx/hif_api_mib.h b/drivers/staging/wfx/hif_api_mib.h new file mode 100644 index 000000000000..3c56ef2978a2 --- /dev/null +++ b/drivers/staging/wfx/hif_api_mib.h @@ -0,0 +1,558 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * WFx hardware interface definitions + * + * Copyright (c) 2018-2019, Silicon Laboratories Inc. + */ + +#ifndef WFX_HIF_API_MIB_H +#define WFX_HIF_API_MIB_H + +#include "hif_api_general.h" + +#define HIF_API_IPV4_ADDRESS_SIZE 4 +#define HIF_API_IPV6_ADDRESS_SIZE 16 + +enum hif_mib_ids { + HIF_MIB_ID_GL_OPERATIONAL_POWER_MODE = 0x2000, + HIF_MIB_ID_GL_BLOCK_ACK_INFO = 0x2001, + HIF_MIB_ID_GL_SET_MULTI_MSG = 0x2002, + HIF_MIB_ID_CCA_CONFIG = 0x2003, + HIF_MIB_ID_ETHERTYPE_DATAFRAME_CONDITION = 0x2010, + HIF_MIB_ID_PORT_DATAFRAME_CONDITION = 0x2011, + HIF_MIB_ID_MAGIC_DATAFRAME_CONDITION = 0x2012, + HIF_MIB_ID_MAC_ADDR_DATAFRAME_CONDITION = 0x2013, + HIF_MIB_ID_IPV4_ADDR_DATAFRAME_CONDITION = 0x2014, + HIF_MIB_ID_IPV6_ADDR_DATAFRAME_CONDITION = 0x2015, + HIF_MIB_ID_UC_MC_BC_DATAFRAME_CONDITION = 0x2016, + HIF_MIB_ID_CONFIG_DATA_FILTER = 0x2017, + HIF_MIB_ID_SET_DATA_FILTERING = 0x2018, + HIF_MIB_ID_ARP_IP_ADDRESSES_TABLE = 0x2019, + HIF_MIB_ID_NS_IP_ADDRESSES_TABLE = 0x201A, + HIF_MIB_ID_RX_FILTER = 0x201B, + HIF_MIB_ID_BEACON_FILTER_TABLE = 0x201C, + HIF_MIB_ID_BEACON_FILTER_ENABLE = 0x201D, + HIF_MIB_ID_GRP_SEQ_COUNTER = 0x2030, + HIF_MIB_ID_TSF_COUNTER = 0x2031, + HIF_MIB_ID_STATISTICS_TABLE = 0x2032, + HIF_MIB_ID_COUNTERS_TABLE = 0x2033, + HIF_MIB_ID_MAX_TX_POWER_LEVEL = 0x2034, + HIF_MIB_ID_EXTENDED_COUNTERS_TABLE = 0x2035, + HIF_MIB_ID_DOT11_MAC_ADDRESS = 0x2040, + HIF_MIB_ID_DOT11_MAX_TRANSMIT_MSDU_LIFETIME = 0x2041, + HIF_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME = 0x2042, + HIF_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID = 0x2043, + HIF_MIB_ID_DOT11_RTS_THRESHOLD = 0x2044, + HIF_MIB_ID_SLOT_TIME = 0x2045, + HIF_MIB_ID_CURRENT_TX_POWER_LEVEL = 0x2046, + HIF_MIB_ID_NON_ERP_PROTECTION = 0x2047, + HIF_MIB_ID_TEMPLATE_FRAME = 0x2048, + HIF_MIB_ID_BEACON_WAKEUP_PERIOD = 0x2049, + HIF_MIB_ID_RCPI_RSSI_THRESHOLD = 0x204A, + HIF_MIB_ID_BLOCK_ACK_POLICY = 0x204B, + HIF_MIB_ID_OVERRIDE_INTERNAL_TX_RATE = 0x204C, + HIF_MIB_ID_SET_ASSOCIATION_MODE = 0x204D, + HIF_MIB_ID_SET_UAPSD_INFORMATION = 0x204E, + HIF_MIB_ID_SET_TX_RATE_RETRY_POLICY = 0x204F, + HIF_MIB_ID_PROTECTED_MGMT_POLICY = 0x2050, + HIF_MIB_ID_SET_HT_PROTECTION = 0x2051, + HIF_MIB_ID_KEEP_ALIVE_PERIOD = 0x2052, + HIF_MIB_ID_ARP_KEEP_ALIVE_PERIOD = 0x2053, + HIF_MIB_ID_INACTIVITY_TIMER = 0x2054, + HIF_MIB_ID_INTERFACE_PROTECTION = 0x2055, + HIF_MIB_ID_BEACON_STATS = 0x2056, +}; + +#define HIF_OP_POWER_MODE_MASK 0xf + +enum hif_op_power_mode { + HIF_OP_POWER_MODE_ACTIVE = 0x0, + HIF_OP_POWER_MODE_DOZE = 0x1, + HIF_OP_POWER_MODE_QUIESCENT = 0x2 +}; + +struct hif_mib_gl_operational_power_mode { + uint8_t power_mode:4; + uint8_t reserved1:3; + uint8_t wup_ind_activation:1; + uint8_t reserved2[3]; +} __packed; + +struct hif_mib_gl_block_ack_info { + uint8_t rx_buffer_size; + uint8_t rx_max_num_agreements; + uint8_t tx_buffer_size; + uint8_t tx_max_num_agreements; +} __packed; + +struct hif_mib_gl_set_multi_msg { + uint8_t enable_multi_tx_conf:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +enum hif_cca_thr_mode { + HIF_CCA_THR_MODE_RELATIVE = 0x0, + HIF_CCA_THR_MODE_ABSOLUTE = 0x1 +}; + +struct hif_mib_gl_cca_config { + uint8_t cca_thr_mode; + uint8_t reserved[3]; +} __packed; + +#define MAX_NUMBER_DATA_FILTERS 0xA + +#define MAX_NUMBER_IPV4_ADDR_CONDITIONS 0x4 +#define MAX_NUMBER_IPV6_ADDR_CONDITIONS 0x4 +#define MAX_NUMBER_MAC_ADDR_CONDITIONS 0x4 +#define MAX_NUMBER_UC_MC_BC_CONDITIONS 0x4 +#define MAX_NUMBER_ETHER_TYPE_CONDITIONS 0x4 +#define MAX_NUMBER_PORT_CONDITIONS 0x4 +#define MAX_NUMBER_MAGIC_CONDITIONS 0x4 +#define MAX_NUMBER_ARP_CONDITIONS 0x2 +#define MAX_NUMBER_NS_CONDITIONS 0x2 + +struct hif_mib_ethertype_data_frame_condition { + uint8_t condition_idx; + uint8_t reserved; + uint16_t ether_type; +} __packed; + +enum hif_udp_tcp_protocol { + HIF_PROTOCOL_UDP = 0x0, + HIF_PROTOCOL_TCP = 0x1, + HIF_PROTOCOL_BOTH_UDP_TCP = 0x2 +}; + +enum hif_which_port { + HIF_PORT_DST = 0x0, + HIF_PORT_SRC = 0x1, + HIF_PORT_SRC_OR_DST = 0x2 +}; + +struct hif_mib_ports_data_frame_condition { + uint8_t condition_idx; + uint8_t protocol; + uint8_t which_port; + uint8_t reserved1; + uint16_t port_number; + uint8_t reserved2[2]; +} __packed; + +#define HIF_API_MAGIC_PATTERN_SIZE 32 + +struct hif_mib_magic_data_frame_condition { + uint8_t condition_idx; + uint8_t offset; + uint8_t magic_pattern_length; + uint8_t reserved; + uint8_t magic_pattern[HIF_API_MAGIC_PATTERN_SIZE]; +} __packed; + +enum hif_mac_addr_type { + HIF_MAC_ADDR_A1 = 0x0, + HIF_MAC_ADDR_A2 = 0x1, + HIF_MAC_ADDR_A3 = 0x2 +}; + +struct hif_mib_mac_addr_data_frame_condition { + uint8_t condition_idx; + uint8_t address_type; + uint8_t mac_address[ETH_ALEN]; +} __packed; + +enum hif_ip_addr_mode { + HIF_IP_ADDR_SRC = 0x0, + HIF_IP_ADDR_DST = 0x1 +}; + +struct hif_mib_ipv4_addr_data_frame_condition { + uint8_t condition_idx; + uint8_t address_mode; + uint8_t reserved[2]; + uint8_t i_pv4_address[HIF_API_IPV4_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_ipv6_addr_data_frame_condition { + uint8_t condition_idx; + uint8_t address_mode; + uint8_t reserved[2]; + uint8_t i_pv6_address[HIF_API_IPV6_ADDRESS_SIZE]; +} __packed; + +union hif_addr_type { + uint8_t value; + struct { + uint8_t type_unicast:1; + uint8_t type_multicast:1; + uint8_t type_broadcast:1; + uint8_t reserved:5; + } bits; +}; + +struct hif_mib_uc_mc_bc_data_frame_condition { + uint8_t condition_idx; + union hif_addr_type param; + uint8_t reserved[2]; +} __packed; + +struct hif_mib_config_data_filter { + uint8_t filter_idx; + uint8_t enable; + uint8_t reserved1[2]; + uint8_t eth_type_cond; + uint8_t port_cond; + uint8_t magic_cond; + uint8_t mac_cond; + uint8_t ipv4_cond; + uint8_t ipv6_cond; + uint8_t uc_mc_bc_cond; + uint8_t reserved2; +} __packed; + +struct hif_mib_set_data_filtering { + uint8_t default_filter; + uint8_t enable; + uint8_t reserved[2]; +} __packed; + +enum hif_arp_ns_frame_treatment { + HIF_ARP_NS_FILTERING_DISABLE = 0x0, + HIF_ARP_NS_FILTERING_ENABLE = 0x1, + HIF_ARP_NS_REPLY_ENABLE = 0x2 +}; + +struct hif_mib_arp_ip_addr_table { + uint8_t condition_idx; + uint8_t arp_enable; + uint8_t reserved[2]; + uint8_t ipv4_address[HIF_API_IPV4_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_ns_ip_addr_table { + uint8_t condition_idx; + uint8_t ns_enable; + uint8_t reserved[2]; + uint8_t ipv6_address[HIF_API_IPV6_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_rx_filter { + uint8_t reserved1:1; + uint8_t bssid_filter:1; + uint8_t reserved2:1; + uint8_t fwd_probe_req:1; + uint8_t keep_alive_filter:1; + uint8_t reserved3:3; + uint8_t reserved4[3]; +} __packed; + +#define HIF_API_OUI_SIZE 3 +#define HIF_API_MATCH_DATA_SIZE 3 + +struct hif_ie_table_entry { + uint8_t ie_id; + uint8_t has_changed:1; + uint8_t no_longer:1; + uint8_t has_appeared:1; + uint8_t reserved:1; + uint8_t num_match_data:4; + uint8_t oui[HIF_API_OUI_SIZE]; + uint8_t match_data[HIF_API_MATCH_DATA_SIZE]; +} __packed; + +struct hif_mib_bcn_filter_table { + uint32_t num_of_info_elmts; + struct hif_ie_table_entry ie_table[]; +} __packed; + +enum hif_beacon_filter { + HIF_BEACON_FILTER_DISABLE = 0x0, + HIF_BEACON_FILTER_ENABLE = 0x1, + HIF_BEACON_FILTER_AUTO_ERP = 0x2 +}; + +struct hif_mib_bcn_filter_enable { + uint32_t enable; + uint32_t bcn_count; +} __packed; + +struct hif_mib_group_seq_counter { + uint32_t bits4716; + uint16_t bits1500; + uint16_t reserved; +} __packed; + +struct hif_mib_tsf_counter { + uint32_t tsf_counterlo; + uint32_t tsf_counterhi; +} __packed; + +struct hif_mib_stats_table { + int16_t latest_snr; + uint8_t latest_rcpi; + int8_t latest_rssi; +} __packed; + +struct hif_mib_extended_count_table { + uint32_t count_plcp_errors; + uint32_t count_fcs_errors; + uint32_t count_tx_packets; + uint32_t count_rx_packets; + uint32_t count_rx_packet_errors; + uint32_t count_rx_decryption_failures; + uint32_t count_rx_mic_failures; + uint32_t count_rx_no_key_failures; + uint32_t count_tx_multicast_frames; + uint32_t count_tx_frames_success; + uint32_t count_tx_frame_failures; + uint32_t count_tx_frames_retried; + uint32_t count_tx_frames_multi_retried; + uint32_t count_rx_frame_duplicates; + uint32_t count_rts_success; + uint32_t count_rts_failures; + uint32_t count_ack_failures; + uint32_t count_rx_multicast_frames; + uint32_t count_rx_frames_success; + uint32_t count_rx_cmacicv_errors; + uint32_t count_rx_cmac_replays; + uint32_t count_rx_mgmt_ccmp_replays; + uint32_t count_rx_bipmic_errors; + uint32_t count_rx_beacon; + uint32_t count_miss_beacon; + uint32_t reserved[15]; +} __packed; + +struct hif_mib_count_table { + uint32_t count_plcp_errors; + uint32_t count_fcs_errors; + uint32_t count_tx_packets; + uint32_t count_rx_packets; + uint32_t count_rx_packet_errors; + uint32_t count_rx_decryption_failures; + uint32_t count_rx_mic_failures; + uint32_t count_rx_no_key_failures; + uint32_t count_tx_multicast_frames; + uint32_t count_tx_frames_success; + uint32_t count_tx_frame_failures; + uint32_t count_tx_frames_retried; + uint32_t count_tx_frames_multi_retried; + uint32_t count_rx_frame_duplicates; + uint32_t count_rts_success; + uint32_t count_rts_failures; + uint32_t count_ack_failures; + uint32_t count_rx_multicast_frames; + uint32_t count_rx_frames_success; + uint32_t count_rx_cmacicv_errors; + uint32_t count_rx_cmac_replays; + uint32_t count_rx_mgmt_ccmp_replays; + uint32_t count_rx_bipmic_errors; +} __packed; + +struct hif_mib_max_tx_power_level { + int32_t max_tx_power_level_rf_port1; + int32_t max_tx_power_level_rf_port2; +} __packed; + +struct hif_mib_beacon_stats { + int32_t latest_tbtt_diff; + uint32_t reserved[4]; +} __packed; + +struct hif_mib_mac_address { + uint8_t mac_addr[ETH_ALEN]; + uint16_t reserved; +} __packed; + +struct hif_mib_dot11_max_transmit_msdu_lifetime { + uint32_t max_life_time; +} __packed; + +struct hif_mib_dot11_max_receive_lifetime { + uint32_t max_life_time; +} __packed; + +struct hif_mib_wep_default_key_id { + uint8_t wep_default_key_id; + uint8_t reserved[3]; +} __packed; + +struct hif_mib_dot11_rts_threshold { + uint32_t threshold; +} __packed; + +struct hif_mib_slot_time { + uint32_t slot_time; +} __packed; + +struct hif_mib_current_tx_power_level { + int32_t power_level; +} __packed; + +struct hif_mib_non_erp_protection { + uint8_t use_cts_to_self:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +enum hif_tx_mode { + HIF_TX_MODE_MIXED = 0x0, + HIF_TX_MODE_GREENFIELD = 0x1 +}; + +enum hif_tmplt { + HIF_TMPLT_PRBREQ = 0x0, + HIF_TMPLT_BCN = 0x1, + HIF_TMPLT_NULL = 0x2, + HIF_TMPLT_QOSNUL = 0x3, + HIF_TMPLT_PSPOLL = 0x4, + HIF_TMPLT_PRBRES = 0x5, + HIF_TMPLT_ARP = 0x6, + HIF_TMPLT_NA = 0x7 +}; + +#define HIF_API_MAX_TEMPLATE_FRAME_SIZE 700 + +struct hif_mib_template_frame { + uint8_t frame_type; + uint8_t init_rate:7; + uint8_t mode:1; + uint16_t frame_length; + uint8_t frame[HIF_API_MAX_TEMPLATE_FRAME_SIZE]; +} __packed; + +struct hif_mib_beacon_wake_up_period { + uint8_t wakeup_period_min; + uint8_t receive_dtim:1; + uint8_t reserved1:7; + uint8_t wakeup_period_max; + uint8_t reserved2; +} __packed; + +struct hif_mib_rcpi_rssi_threshold { + uint8_t detection:1; + uint8_t rcpi_rssi:1; + uint8_t upperthresh:1; + uint8_t lowerthresh:1; + uint8_t reserved:4; + uint8_t lower_threshold; + uint8_t upper_threshold; + uint8_t rolling_average_count; +} __packed; + +#define DEFAULT_BA_MAX_RX_BUFFER_SIZE 16 + +struct hif_mib_block_ack_policy { + uint8_t block_ack_tx_tid_policy; + uint8_t reserved1; + uint8_t block_ack_rx_tid_policy; + uint8_t block_ack_rx_max_buffer_size; +} __packed; + +struct hif_mib_override_int_rate { + uint8_t internal_tx_rate; + uint8_t non_erp_internal_tx_rate; + uint8_t reserved[2]; +} __packed; + +enum hif_mpdu_start_spacing { + HIF_MPDU_START_SPACING_NO_RESTRIC = 0x0, + HIF_MPDU_START_SPACING_QUARTER = 0x1, + HIF_MPDU_START_SPACING_HALF = 0x2, + HIF_MPDU_START_SPACING_ONE = 0x3, + HIF_MPDU_START_SPACING_TWO = 0x4, + HIF_MPDU_START_SPACING_FOUR = 0x5, + HIF_MPDU_START_SPACING_EIGHT = 0x6, + HIF_MPDU_START_SPACING_SIXTEEN = 0x7 +}; + +struct hif_mib_set_association_mode { + uint8_t preambtype_use:1; + uint8_t mode:1; + uint8_t rateset:1; + uint8_t spacing:1; + uint8_t reserved:4; + uint8_t preamble_type; + uint8_t mixed_or_greenfield_type; + uint8_t mpdu_start_spacing; + uint32_t basic_rate_set; +} __packed; + +struct hif_mib_set_uapsd_information { + uint8_t trig_bckgrnd:1; + uint8_t trig_be:1; + uint8_t trig_video:1; + uint8_t trig_voice:1; + uint8_t reserved1:4; + uint8_t deliv_bckgrnd:1; + uint8_t deliv_be:1; + uint8_t deliv_video:1; + uint8_t deliv_voice:1; + uint8_t reserved2:4; + uint16_t min_auto_trigger_interval; + uint16_t max_auto_trigger_interval; + uint16_t auto_trigger_step; +} __packed; + +struct hif_mib_tx_rate_retry_policy { + uint8_t policy_index; + uint8_t short_retry_count; + uint8_t long_retry_count; + uint8_t first_rate_sel:2; + uint8_t terminate:1; + uint8_t count_init:1; + uint8_t reserved1:4; + uint8_t rate_recovery_count; + uint8_t reserved2[3]; + uint8_t rates[12]; +} __packed; + +#define HIF_MIB_NUM_TX_RATE_RETRY_POLICIES 16 + +struct hif_mib_set_tx_rate_retry_policy { + uint8_t num_tx_rate_policies; + uint8_t reserved[3]; + struct hif_mib_tx_rate_retry_policy tx_rate_retry_policy[]; +} __packed; + +struct hif_mib_protected_mgmt_policy { + uint8_t pmf_enable:1; + uint8_t unpmf_allowed:1; + uint8_t host_enc_auth_frames:1; + uint8_t reserved1:5; + uint8_t reserved2[3]; +} __packed; + +struct hif_mib_set_ht_protection { + uint8_t dual_cts_prot:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +struct hif_mib_keep_alive_period { + uint16_t keep_alive_period; + uint8_t reserved[2]; +} __packed; + +struct hif_mib_arp_keep_alive_period { + uint16_t arp_keep_alive_period; + uint8_t encr_type; + uint8_t reserved; + uint8_t sender_ipv4_address[HIF_API_IPV4_ADDRESS_SIZE]; + uint8_t target_ipv4_address[HIF_API_IPV4_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_inactivity_timer { + uint8_t min_active_time; + uint8_t max_active_time; + uint16_t reserved; +} __packed; + +struct hif_mib_interface_protection { + uint8_t use_cts_prot:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + + +#endif diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c new file mode 100644 index 000000000000..36e171b27ae2 --- /dev/null +++ b/drivers/staging/wfx/hif_rx.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of chip-to-host event (aka indications) of WFxxx Split Mac + * (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/skbuff.h> +#include <linux/etherdevice.h> + +#include "hif_rx.h" +#include "wfx.h" +#include "scan.h" +#include "bh.h" +#include "sta.h" +#include "data_rx.h" +#include "secure_link.h" +#include "hif_api_cmd.h" + +static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + // All confirm messages start with status + int status = le32_to_cpu(*((__le32 *) buf)); + int cmd = hif->id; + int len = hif->len - 4; // drop header + + WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); + + if (!wdev->hif_cmd.buf_send) { + dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd); + return -EINVAL; + } + + if (cmd != wdev->hif_cmd.buf_send->id) { + dev_warn(wdev->dev, "chip response mismatch request: 0x%.2x vs 0x%.2x\n", + cmd, wdev->hif_cmd.buf_send->id); + return -EINVAL; + } + + if (wdev->hif_cmd.buf_recv) { + if (wdev->hif_cmd.len_recv >= len) + memcpy(wdev->hif_cmd.buf_recv, buf, len); + else + status = -ENOMEM; + } + wdev->hif_cmd.ret = status; + + if (!wdev->hif_cmd.async) { + complete(&wdev->hif_cmd.done); + } else { + wdev->hif_cmd.buf_send = NULL; + mutex_unlock(&wdev->hif_cmd.lock); + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_unlock(&wdev->hif_cmd.key_renew_lock); + } + return status; +} + +static int hif_tx_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_cnf_tx *body = buf; + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + + WARN_ON(!wvif); + if (!wvif) + return -EFAULT; + + wfx_tx_confirm_cb(wvif, body); + return 0; +} + +static int hif_multi_tx_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_cnf_multi_transmit *body = buf; + struct hif_cnf_tx *buf_loc = (struct hif_cnf_tx *) &body->tx_conf_payload; + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + int count = body->num_tx_confs; + int i; + + WARN(count <= 0, "corrupted message"); + WARN_ON(!wvif); + if (!wvif) + return -EFAULT; + + for (i = 0; i < count; ++i) { + wfx_tx_confirm_cb(wvif, buf_loc); + buf_loc++; + } + return 0; +} + +static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_startup *body = buf; + + if (body->status || body->firmware_type > 4) { + dev_err(wdev->dev, "received invalid startup indication"); + return -EINVAL; + } + memcpy(&wdev->hw_caps, body, sizeof(struct hif_ind_startup)); + le32_to_cpus(&wdev->hw_caps.status); + le16_to_cpus(&wdev->hw_caps.hardware_id); + le16_to_cpus(&wdev->hw_caps.num_inp_ch_bufs); + le16_to_cpus(&wdev->hw_caps.size_inp_ch_buf); + + complete(&wdev->firmware_ready); + return 0; +} + +static int hif_wakeup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + if (!wdev->pdata.gpio_wakeup + || !gpiod_get_value(wdev->pdata.gpio_wakeup)) { + dev_warn(wdev->dev, "unexpected wake-up indication\n"); + return -EIO; + } + return 0; +} + +static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_sl_exchange_pub_keys *body = buf; + + // Compatibility with legacy secure link + if (body->status == SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS) + body->status = 0; + if (body->status) + dev_warn(wdev->dev, "secure link negociation error\n"); + wfx_sl_check_pubkey(wdev, body->ncp_pub_key, body->ncp_pub_key_mac); + return 0; +} + +static int hif_receive_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf, struct sk_buff *skb) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_rx *body = buf; + + if (!wvif) { + dev_warn(wdev->dev, "ignore rx data for non-existent vif %d\n", hif->interface); + return 0; + } + skb_pull(skb, sizeof(struct hif_msg) + sizeof(struct hif_ind_rx)); + wfx_rx_cb(wvif, body, skb); + + return 0; +} + +static int hif_event_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_event *body = buf; + struct wfx_hif_event *event; + int first; + + WARN_ON(!wvif); + if (!wvif) + return 0; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + memcpy(&event->evt, body, sizeof(struct hif_ind_event)); + spin_lock(&wvif->event_queue_lock); + first = list_empty(&wvif->event_queue); + list_add_tail(&event->link, &wvif->event_queue); + spin_unlock(&wvif->event_queue_lock); + + if (first) + schedule_work(&wvif->event_handler_work); + + return 0; +} + +static int hif_pm_mode_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + + WARN_ON(!wvif); + complete(&wvif->set_pm_mode_complete); + + return 0; +} + +static int hif_scan_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_scan_cmpl *body = buf; + + WARN_ON(!wvif); + wfx_scan_complete_cb(wvif, body); + + return 0; +} + +static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + + WARN_ON(!wvif); + dev_warn(wdev->dev, "unattended JoinCompleteInd\n"); + + return 0; +} + +static int hif_suspend_resume_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_suspend_resume_tx *body = buf; + + WARN_ON(!wvif); + wfx_suspend_resume(wvif, body); + + return 0; +} + +static int hif_error_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_error *body = buf; + u8 *pRollback = (u8 *) body->data; + u32 *pStatus = (u32 *) body->data; + + switch (body->type) { + case HIF_ERROR_FIRMWARE_ROLLBACK: + dev_err(wdev->dev, "asynchronous error: firmware rollback error %d\n", *pRollback); + break; + case HIF_ERROR_FIRMWARE_DEBUG_ENABLED: + dev_err(wdev->dev, "asynchronous error: firmware debug feature enabled\n"); + break; + case HIF_ERROR_OUTDATED_SESSION_KEY: + dev_err(wdev->dev, "asynchronous error: secure link outdated key: %#.8x\n", *pStatus); + break; + case HIF_ERROR_INVALID_SESSION_KEY: + dev_err(wdev->dev, "asynchronous error: invalid session key\n"); + break; + case HIF_ERROR_OOR_VOLTAGE: + dev_err(wdev->dev, "asynchronous error: out-of-range overvoltage: %#.8x\n", *pStatus); + break; + case HIF_ERROR_PDS_VERSION: + dev_err(wdev->dev, "asynchronous error: wrong PDS payload or version: %#.8x\n", *pStatus); + break; + default: + dev_err(wdev->dev, "asynchronous error: unknown (%d)\n", body->type); + break; + } + return 0; +} + +static int hif_generic_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_generic *body = buf; + + switch (body->indication_type) { + case HIF_GENERIC_INDICATION_TYPE_RAW: + return 0; + case HIF_GENERIC_INDICATION_TYPE_STRING: + dev_info(wdev->dev, "firmware says: %s\n", (char *) body->indication_data.raw_data); + return 0; + case HIF_GENERIC_INDICATION_TYPE_RX_STATS: + mutex_lock(&wdev->rx_stats_lock); + // Older firmware send a generic indication beside RxStats + if (!wfx_api_older_than(wdev, 1, 4)) + dev_info(wdev->dev, "Rx test ongoing. Temperature: %d°C\n", body->indication_data.rx_stats.current_temp); + memcpy(&wdev->rx_stats, &body->indication_data.rx_stats, sizeof(wdev->rx_stats)); + mutex_unlock(&wdev->rx_stats_lock); + return 0; + default: + dev_err(wdev->dev, "generic_indication: unknown indication type: %#.8x\n", body->indication_type); + return -EIO; + } +} + +static int hif_exception_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + size_t len = hif->len - 4; // drop header + dev_err(wdev->dev, "firmware exception\n"); + print_hex_dump_bytes("Dump: ", DUMP_PREFIX_NONE, buf, len); + wdev->chip_frozen = 1; + + return -1; +} + +static const struct { + int msg_id; + int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); +} hif_handlers[] = { + /* Confirmations */ + { HIF_CNF_ID_TX, hif_tx_confirm }, + { HIF_CNF_ID_MULTI_TRANSMIT, hif_multi_tx_confirm }, + /* Indications */ + { HIF_IND_ID_STARTUP, hif_startup_indication }, + { HIF_IND_ID_WAKEUP, hif_wakeup_indication }, + { HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication }, + { HIF_IND_ID_SET_PM_MODE_CMPL, hif_pm_mode_complete_indication }, + { HIF_IND_ID_SCAN_CMPL, hif_scan_complete_indication }, + { HIF_IND_ID_SUSPEND_RESUME_TX, hif_suspend_resume_indication }, + { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, + { HIF_IND_ID_EVENT, hif_event_indication }, + { HIF_IND_ID_GENERIC, hif_generic_indication }, + { HIF_IND_ID_ERROR, hif_error_indication }, + { HIF_IND_ID_EXCEPTION, hif_exception_indication }, + // FIXME: allocate skb_p from hif_receive_indication and make it generic + //{ HIF_IND_ID_RX, hif_receive_indication }, +}; + +void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) +{ + int i; + struct hif_msg *hif = (struct hif_msg *) skb->data; + int hif_id = hif->id; + + if (hif_id == HIF_IND_ID_RX) { + // hif_receive_indication take care of skb lifetime + hif_receive_indication(wdev, hif, hif->body, skb); + return; + } + // Note: mutex_is_lock cause an implicit memory barrier that protect + // buf_send + if (mutex_is_locked(&wdev->hif_cmd.lock) + && wdev->hif_cmd.buf_send + && wdev->hif_cmd.buf_send->id == hif_id) { + hif_generic_confirm(wdev, hif, hif->body); + goto free; + } + for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) { + if (hif_handlers[i].msg_id == hif_id) { + if (hif_handlers[i].handler) + hif_handlers[i].handler(wdev, hif, hif->body); + goto free; + } + } + dev_err(wdev->dev, "unsupported HIF ID %02x\n", hif_id); +free: + dev_kfree_skb(skb); +} diff --git a/drivers/staging/wfx/hif_rx.h b/drivers/staging/wfx/hif_rx.h new file mode 100644 index 000000000000..f07c10c8c6bd --- /dev/null +++ b/drivers/staging/wfx/hif_rx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of chip-to-host event (aka indications) of WFxxx Split Mac + * (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (C) 2010, ST-Ericsson SA + */ +#ifndef WFX_HIF_RX_H +#define WFX_HIF_RX_H + +struct wfx_dev; +struct sk_buff; + +void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb); + +#endif diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c new file mode 100644 index 000000000000..2d40225a0fce --- /dev/null +++ b/drivers/staging/wfx/hif_tx.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx + * Split Mac (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/skbuff.h> +#include <linux/etherdevice.h> + +#include "hif_tx.h" +#include "wfx.h" +#include "bh.h" +#include "hwio.h" +#include "debug.h" +#include "sta.h" + +void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) +{ + init_completion(&hif_cmd->ready); + init_completion(&hif_cmd->done); + mutex_init(&hif_cmd->lock); + mutex_init(&hif_cmd->key_renew_lock); +} + +static void wfx_fill_header(struct hif_msg *hif, int if_id, unsigned int cmd, size_t size) +{ + if (if_id == -1) + if_id = 2; + + WARN(cmd > 0x3f, "invalid WSM command %#.2x", cmd); + WARN(size > 0xFFF, "requested buffer is too large: %zu bytes", size); + WARN(if_id > 0x3, "invalid interface ID %d", if_id); + + hif->len = cpu_to_le16(size + 4); + hif->id = cmd; + hif->interface = if_id; +} + +static void *wfx_alloc_hif(size_t body_len, struct hif_msg **hif) +{ + *hif = kzalloc(sizeof(struct hif_msg) + body_len, GFP_KERNEL); + if (*hif) + return (*hif)->body; + else + return NULL; +} + +int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async) +{ + const char *mib_name = ""; + const char *mib_sep = ""; + int cmd = request->id; + int vif = request->interface; + int ret; + + WARN(wdev->hif_cmd.buf_recv && wdev->hif_cmd.async, "API usage error"); + + // Do not wait for any reply if chip is frozen + if (wdev->chip_frozen) + return -ETIMEDOUT; + + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_lock(&wdev->hif_cmd.key_renew_lock); + + mutex_lock(&wdev->hif_cmd.lock); + WARN(wdev->hif_cmd.buf_send, "data locking error"); + + // Note: call to complete() below has an implicit memory barrier that + // hopefully protect buf_send + wdev->hif_cmd.buf_send = request; + wdev->hif_cmd.buf_recv = reply; + wdev->hif_cmd.len_recv = reply_len; + wdev->hif_cmd.async = async; + complete(&wdev->hif_cmd.ready); + + wfx_bh_request_tx(wdev); + + // NOTE: no timeout is catched async is enabled + if (async) + return 0; + + ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ); + if (!ret) { + dev_err(wdev->dev, "chip is abnormally long to answer\n"); + reinit_completion(&wdev->hif_cmd.ready); + ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ); + } + if (!ret) { + dev_err(wdev->dev, "chip did not answer\n"); + wfx_pending_dump_old_frames(wdev, 3000); + wdev->chip_frozen = 1; + reinit_completion(&wdev->hif_cmd.done); + ret = -ETIMEDOUT; + } else { + ret = wdev->hif_cmd.ret; + } + + wdev->hif_cmd.buf_send = NULL; + mutex_unlock(&wdev->hif_cmd.lock); + + if (ret && (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) { + mib_name = get_mib_name(((u16 *) request)[2]); + mib_sep = "/"; + } + if (ret < 0) + dev_err(wdev->dev, + "WSM request %s%s%s (%#.2x) on vif %d returned error %d\n", + get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); + if (ret > 0) + dev_warn(wdev->dev, + "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n", + get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); + + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_unlock(&wdev->hif_cmd.key_renew_lock); + return ret; +} + +// This function is special. After HIF_REQ_ID_SHUT_DOWN, chip won't reply to any +// request anymore. We need to slightly hack struct wfx_hif_cmd for that job. Be +// carefull to only call this funcion during device unregister. +int hif_shutdown(struct wfx_dev *wdev) +{ + int ret; + struct hif_msg *hif; + + wfx_alloc_hif(0, &hif); + wfx_fill_header(hif, -1, HIF_REQ_ID_SHUT_DOWN, 0); + ret = wfx_cmd_send(wdev, hif, NULL, 0, true); + // After this command, chip won't reply. Be sure to give enough time to + // bh to send buffer: + msleep(100); + wdev->hif_cmd.buf_send = NULL; + if (wdev->pdata.gpio_wakeup) + gpiod_set_value(wdev->pdata.gpio_wakeup, 0); + else + control_reg_write(wdev, 0); + mutex_unlock(&wdev->hif_cmd.lock); + kfree(hif); + return ret; +} + +int hif_configuration(struct wfx_dev *wdev, const u8 *conf, size_t len) +{ + int ret; + size_t buf_len = sizeof(struct hif_req_configuration) + len; + struct hif_msg *hif; + struct hif_req_configuration *body = wfx_alloc_hif(buf_len, &hif); + + body->length = cpu_to_le16(len); + memcpy(body->pds_data, conf, len); + wfx_fill_header(hif, -1, HIF_REQ_ID_CONFIGURATION, buf_len); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_reset(struct wfx_vif *wvif, bool reset_stat) +{ + int ret; + struct hif_msg *hif; + struct hif_req_reset *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->reset_flags.reset_stat = reset_stat; + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_RESET, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, size_t val_len) +{ + int ret; + struct hif_msg *hif; + int buf_len = sizeof(struct hif_cnf_read_mib) + val_len; + struct hif_req_read_mib *body = wfx_alloc_hif(sizeof(*body), &hif); + struct hif_cnf_read_mib *reply = kmalloc(buf_len, GFP_KERNEL); + + body->mib_id = cpu_to_le16(mib_id); + wfx_fill_header(hif, vif_id, HIF_REQ_ID_READ_MIB, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, reply, buf_len, false); + + if (!ret && mib_id != reply->mib_id) { + dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__); + ret = -EIO; + } + if (ret == -ENOMEM) + dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n", + get_mib_name(mib_id), val_len, reply->length); + if (!ret) + memcpy(val, &reply->mib_data, reply->length); + else + memset(val, 0xFF, val_len); + kfree(hif); + kfree(reply); + return ret; +} + +int hif_write_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, size_t val_len) +{ + int ret; + struct hif_msg *hif; + int buf_len = sizeof(struct hif_req_write_mib) + val_len; + struct hif_req_write_mib *body = wfx_alloc_hif(buf_len, &hif); + + body->mib_id = cpu_to_le16(mib_id); + body->length = cpu_to_le16(val_len); + memcpy(&body->mib_data, val, val_len); + wfx_fill_header(hif, vif_id, HIF_REQ_ID_WRITE_MIB, buf_len); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_scan(struct wfx_vif *wvif, const struct wfx_scan_params *arg) +{ + int ret, i; + struct hif_msg *hif; + struct hif_ssid_def *ssids; + size_t buf_len = sizeof(struct hif_req_start_scan) + + arg->scan_req.num_of_channels * sizeof(u8) + + arg->scan_req.num_of_ssi_ds * sizeof(struct hif_ssid_def); + struct hif_req_start_scan *body = wfx_alloc_hif(buf_len, &hif); + u8 *ptr = (u8 *) body + sizeof(*body); + + WARN(arg->scan_req.num_of_channels > HIF_API_MAX_NB_CHANNELS, "invalid params"); + WARN(arg->scan_req.num_of_ssi_ds > 2, "invalid params"); + WARN(arg->scan_req.band > 1, "invalid params"); + + // FIXME: This API is unnecessary complex, fixing NumOfChannels and + // adding a member SsidDef at end of struct hif_req_start_scan would + // simplify that a lot. + memcpy(body, &arg->scan_req, sizeof(*body)); + cpu_to_le32s(&body->min_channel_time); + cpu_to_le32s(&body->max_channel_time); + cpu_to_le32s(&body->tx_power_level); + memcpy(ptr, arg->ssids, arg->scan_req.num_of_ssi_ds * sizeof(struct hif_ssid_def)); + ssids = (struct hif_ssid_def *) ptr; + for (i = 0; i < body->num_of_ssi_ds; ++i) + cpu_to_le32s(&ssids[i].ssid_length); + ptr += arg->scan_req.num_of_ssi_ds * sizeof(struct hif_ssid_def); + memcpy(ptr, arg->ch, arg->scan_req.num_of_channels * sizeof(u8)); + ptr += arg->scan_req.num_of_channels * sizeof(u8); + WARN(buf_len != ptr - (u8 *) body, "allocation size mismatch"); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START_SCAN, buf_len); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_stop_scan(struct wfx_vif *wvif) +{ + int ret; + struct hif_msg *hif; + // body associated to HIF_REQ_ID_STOP_SCAN is empty + wfx_alloc_hif(0, &hif); + + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_STOP_SCAN, 0); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_join(struct wfx_vif *wvif, const struct hif_req_join *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_join *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(struct hif_req_join)); + cpu_to_le16s(&body->channel_number); + cpu_to_le16s(&body->atim_window); + cpu_to_le32s(&body->ssid_length); + cpu_to_le32s(&body->beacon_interval); + cpu_to_le32s(&body->basic_rate_set); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_JOIN, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_set_bss_params(struct wfx_vif *wvif, const struct hif_req_set_bss_params *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_set_bss_params *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(*body)); + cpu_to_le16s(&body->aid); + cpu_to_le32s(&body->operational_rate_set); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_BSS_PARAMS, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_add_key(struct wfx_dev *wdev, const struct hif_req_add_key *arg) +{ + int ret; + struct hif_msg *hif; + // FIXME: only send necessary bits + struct hif_req_add_key *body = wfx_alloc_hif(sizeof(*body), &hif); + + // FIXME: swap bytes as necessary in body + memcpy(body, arg, sizeof(*body)); + if (wfx_api_older_than(wdev, 1, 5)) + // Legacy firmwares expect that add_key to be sent on right + // interface. + wfx_fill_header(hif, arg->int_id, HIF_REQ_ID_ADD_KEY, sizeof(*body)); + else + wfx_fill_header(hif, -1, HIF_REQ_ID_ADD_KEY, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_remove_key(struct wfx_dev *wdev, int idx) +{ + int ret; + struct hif_msg *hif; + struct hif_req_remove_key *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->entry_index = idx; + wfx_fill_header(hif, -1, HIF_REQ_ID_REMOVE_KEY, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_set_edca_queue_params(struct wfx_vif *wvif, const struct hif_req_edca_queue_params *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_edca_queue_params *body = wfx_alloc_hif(sizeof(*body), &hif); + + // NOTE: queues numerotation are not the same between WFx and Linux + memcpy(body, arg, sizeof(*body)); + cpu_to_le16s(&body->cw_min); + cpu_to_le16s(&body->cw_max); + cpu_to_le16s(&body->tx_op_limit); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_EDCA_QUEUE_PARAMS, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_set_pm_mode *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(*body)); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_PM_MODE, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_start(struct wfx_vif *wvif, const struct hif_req_start *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_start *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(*body)); + cpu_to_le16s(&body->channel_number); + cpu_to_le32s(&body->beacon_interval); + cpu_to_le32s(&body->basic_rate_set); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_beacon_transmit(struct wfx_vif *wvif, bool enable_beaconing) +{ + int ret; + struct hif_msg *hif; + struct hif_req_beacon_transmit *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->enable_beaconing = enable_beaconing ? 1 : 0; + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_BEACON_TRANSMIT, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_map_link(struct wfx_vif *wvif, u8 *mac_addr, int flags, int sta_id) +{ + int ret; + struct hif_msg *hif; + struct hif_req_map_link *body = wfx_alloc_hif(sizeof(*body), &hif); + + if (mac_addr) + ether_addr_copy(body->mac_addr, mac_addr); + body->map_link_flags = *(struct hif_map_link_flags *) &flags; + body->peer_sta_id = sta_id; + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_MAP_LINK, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_update_ie(struct wfx_vif *wvif, const struct hif_ie_flags *target_frame, + const u8 *ies, size_t ies_len) +{ + int ret; + struct hif_msg *hif; + int buf_len = sizeof(struct hif_req_update_ie) + ies_len; + struct hif_req_update_ie *body = wfx_alloc_hif(buf_len, &hif); + + memcpy(&body->ie_flags, target_frame, sizeof(struct hif_ie_flags)); + body->num_i_es = cpu_to_le16(1); + memcpy(body->ie, ies, ies_len); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_UPDATE_IE, buf_len); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_sl_send_pub_keys(struct wfx_dev *wdev, const uint8_t *pubkey, const uint8_t *pubkey_hmac) +{ + int ret; + struct hif_msg *hif; + struct hif_req_sl_exchange_pub_keys *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->algorithm = HIF_SL_CURVE25519; + memcpy(body->host_pub_key, pubkey, sizeof(body->host_pub_key)); + memcpy(body->host_pub_key_mac, pubkey_hmac, sizeof(body->host_pub_key_mac)); + wfx_fill_header(hif, -1, HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + // Compatibility with legacy secure link + if (ret == SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS) + ret = 0; + return ret; +} + +int hif_sl_config(struct wfx_dev *wdev, const unsigned long *bitmap) +{ + int ret; + struct hif_msg *hif; + struct hif_req_sl_configure *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body->encr_bmp, bitmap, sizeof(body->encr_bmp)); + wfx_fill_header(hif, -1, HIF_REQ_ID_SL_CONFIGURE, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_sl_set_mac_key(struct wfx_dev *wdev, const uint8_t *slk_key, int destination) +{ + int ret; + struct hif_msg *hif; + struct hif_req_set_sl_mac_key *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body->key_value, slk_key, sizeof(body->key_value)); + body->otp_or_ram = destination; + wfx_fill_header(hif, -1, HIF_REQ_ID_SET_SL_MAC_KEY, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + // Compatibility with legacy secure link + if (ret == SL_MAC_KEY_STATUS_SUCCESS) + ret = 0; + return ret; +} diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h new file mode 100644 index 000000000000..6f2ea2f3a77d --- /dev/null +++ b/drivers/staging/wfx/hif_tx.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx + * Split Mac (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (C) 2010, ST-Ericsson SA + */ +#ifndef WFX_HIF_TX_H +#define WFX_HIF_TX_H + +#include "hif_api_cmd.h" + +struct wfx_dev; +struct wfx_vif; + +struct wfx_scan_params { + struct hif_req_start_scan scan_req; + struct hif_ssid_def *ssids; + uint8_t *ch; +}; + +struct wfx_hif_cmd { + struct mutex lock; + struct mutex key_renew_lock; + struct completion ready; + struct completion done; + bool async; + struct hif_msg *buf_send; + void *buf_recv; + size_t len_recv; + int ret; +}; + +void wfx_init_hif_cmd(struct wfx_hif_cmd *wfx_hif_cmd); +int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, + void *reply, size_t reply_len, bool async); + +int hif_shutdown(struct wfx_dev *wdev); +int hif_configuration(struct wfx_dev *wdev, const u8 *conf, size_t len); +int hif_reset(struct wfx_vif *wvif, bool reset_stat); +int hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, + void *buf, size_t buf_size); +int hif_write_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, + void *buf, size_t buf_size); +int hif_scan(struct wfx_vif *wvif, const struct wfx_scan_params *arg); +int hif_stop_scan(struct wfx_vif *wvif); +int hif_join(struct wfx_vif *wvif, const struct hif_req_join *arg); +int hif_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg); +int hif_set_bss_params(struct wfx_vif *wvif, + const struct hif_req_set_bss_params *arg); +int hif_add_key(struct wfx_dev *wdev, const struct hif_req_add_key *arg); +int hif_remove_key(struct wfx_dev *wdev, int idx); +int hif_set_edca_queue_params(struct wfx_vif *wvif, + const struct hif_req_edca_queue_params *arg); +int hif_start(struct wfx_vif *wvif, const struct hif_req_start *arg); +int hif_beacon_transmit(struct wfx_vif *wvif, bool enable); +int hif_map_link(struct wfx_vif *wvif, u8 *mac_addr, int flags, int sta_id); +int hif_update_ie(struct wfx_vif *wvif, const struct hif_ie_flags *target_frame, + const u8 *ies, size_t ies_len); +int hif_sl_set_mac_key(struct wfx_dev *wdev, const uint8_t *slk_key, int destination); +int hif_sl_config(struct wfx_dev *wdev, const unsigned long *bitmap); +int hif_sl_send_pub_keys(struct wfx_dev *wdev, + const uint8_t *pubkey, const uint8_t *pubkey_hmac); + +#endif diff --git a/drivers/staging/wfx/hif_tx_mib.h b/drivers/staging/wfx/hif_tx_mib.h new file mode 100644 index 000000000000..3339ad95f732 --- /dev/null +++ b/drivers/staging/wfx/hif_tx_mib.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of host-to-chip MIBs of WFxxx Split Mac (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (C) 2010, ST-Ericsson SA + */ +#ifndef WFX_HIF_TX_MIB_H +#define WFX_HIF_TX_MIB_H + +#include <linux/etherdevice.h> + +#include "wfx.h" +#include "hif_tx.h" +#include "hif_api_mib.h" + +static inline int hif_set_output_power(struct wfx_vif *wvif, int power_level) +{ + __le32 val = cpu_to_le32(power_level); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int hif_set_beacon_wakeup_period(struct wfx_vif *wvif, + unsigned int dtim_interval, + unsigned int listen_interval) +{ + struct hif_mib_beacon_wake_up_period val = { + .wakeup_period_min = dtim_interval, + .receive_dtim = 0, + .wakeup_period_max = cpu_to_le16(listen_interval), + }; + + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +static inline int hif_set_rcpi_rssi_threshold(struct wfx_vif *wvif, + struct hif_mib_rcpi_rssi_threshold *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_RCPI_RSSI_THRESHOLD, arg, sizeof(*arg)); +} + +static inline int hif_get_counters_table(struct wfx_dev *wdev, + struct hif_mib_extended_count_table *arg) +{ + if (wfx_api_older_than(wdev, 1, 3)) { + // extended_count_table is wider than count_table + memset(arg, 0xFF, sizeof(*arg)); + return hif_read_mib(wdev, 0, HIF_MIB_ID_COUNTERS_TABLE, + arg, sizeof(struct hif_mib_count_table)); + } else { + return hif_read_mib(wdev, 0, HIF_MIB_ID_EXTENDED_COUNTERS_TABLE, + arg, sizeof(struct hif_mib_extended_count_table)); + } +} + +static inline int hif_set_macaddr(struct wfx_vif *wvif, u8 *mac) +{ + struct hif_mib_mac_address msg = { }; + + if (mac) + ether_addr_copy(msg.mac_addr, mac); + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_DOT11_MAC_ADDRESS, + &msg, sizeof(msg)); +} + +static inline int hif_set_rx_filter(struct wfx_vif *wvif, bool filter_bssid, + bool fwd_probe_req) +{ + struct hif_mib_rx_filter val = { }; + + if (filter_bssid) + val.bssid_filter = 1; + if (fwd_probe_req) + val.fwd_probe_req = 1; + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_RX_FILTER, + &val, sizeof(val)); +} + +static inline int hif_set_beacon_filter_table(struct wfx_vif *wvif, + int tbl_len, + struct hif_ie_table_entry *tbl) +{ + int ret; + struct hif_mib_bcn_filter_table *val; + int buf_len = struct_size(val, ie_table, tbl_len); + + val = kzalloc(buf_len, GFP_KERNEL); + if (!val) + return -ENOMEM; + val->num_of_info_elmts = cpu_to_le32(tbl_len); + memcpy(val->ie_table, tbl, tbl_len * sizeof(*tbl)); + ret = hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_BEACON_FILTER_TABLE, val, buf_len); + kfree(val); + return ret; +} + +static inline int hif_beacon_filter_control(struct wfx_vif *wvif, + int enable, int beacon_count) +{ + struct hif_mib_bcn_filter_enable arg = { + .enable = cpu_to_le32(enable), + .bcn_count = cpu_to_le32(beacon_count), + }; + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_BEACON_FILTER_ENABLE, &arg, sizeof(arg)); +} + +static inline int hif_set_operational_mode(struct wfx_dev *wdev, + enum hif_op_power_mode mode) +{ + struct hif_mib_gl_operational_power_mode val = { + .power_mode = mode, + .wup_ind_activation = 1, + }; + + return hif_write_mib(wdev, -1, HIF_MIB_ID_GL_OPERATIONAL_POWER_MODE, + &val, sizeof(val)); +} + +static inline int hif_set_template_frame(struct wfx_vif *wvif, + struct hif_mib_template_frame *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_TEMPLATE_FRAME, + arg, sizeof(*arg)); +} + +static inline int hif_set_mfp(struct wfx_vif *wvif, bool capable, bool required) +{ + struct hif_mib_protected_mgmt_policy val = { }; + + WARN(required && !capable, "incoherent arguments"); + if (capable) { + val.pmf_enable = 1; + val.host_enc_auth_frames = 1; + } + if (!required) + val.unpmf_allowed = 1; + cpu_to_le32s((uint32_t *) &val); + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_PROTECTED_MGMT_POLICY, + &val, sizeof(val)); +} + +static inline int hif_set_block_ack_policy(struct wfx_vif *wvif, + u8 tx_tid_policy, u8 rx_tid_policy) +{ + struct hif_mib_block_ack_policy val = { + .block_ack_tx_tid_policy = tx_tid_policy, + .block_ack_rx_tid_policy = rx_tid_policy, + }; + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_BLOCK_ACK_POLICY, + &val, sizeof(val)); +} + +static inline int hif_set_association_mode(struct wfx_vif *wvif, + struct hif_mib_set_association_mode *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_ASSOCIATION_MODE, arg, sizeof(*arg)); +} + +static inline int hif_set_tx_rate_retry_policy(struct wfx_vif *wvif, + struct hif_mib_set_tx_rate_retry_policy *arg) +{ + size_t size = struct_size(arg, tx_rate_retry_policy, arg->num_tx_rate_policies); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, size); +} + +static inline int hif_set_mac_addr_condition(struct wfx_vif *wvif, + struct hif_mib_mac_addr_data_frame_condition *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_MAC_ADDR_DATAFRAME_CONDITION, + arg, sizeof(*arg)); +} + +static inline int hif_set_uc_mc_bc_condition(struct wfx_vif *wvif, + struct hif_mib_uc_mc_bc_data_frame_condition *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_UC_MC_BC_DATAFRAME_CONDITION, + arg, sizeof(*arg)); +} + +static inline int hif_set_config_data_filter(struct wfx_vif *wvif, + struct hif_mib_config_data_filter *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_CONFIG_DATA_FILTER, arg, sizeof(*arg)); +} + +static inline int hif_set_data_filtering(struct wfx_vif *wvif, + struct hif_mib_set_data_filtering *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_DATA_FILTERING, arg, sizeof(*arg)); +} + +static inline int hif_keep_alive_period(struct wfx_vif *wvif, int period) +{ + struct hif_mib_keep_alive_period arg = { + .keep_alive_period = cpu_to_le16(period), + }; + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +static inline int hif_set_arp_ipv4_filter(struct wfx_vif *wvif, + struct hif_mib_arp_ip_addr_table *fp) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +static inline int hif_use_multi_tx_conf(struct wfx_dev *wdev, + bool enabled) +{ + __le32 arg = enabled ? cpu_to_le32(1) : 0; + + return hif_write_mib(wdev, -1, HIF_MIB_ID_GL_SET_MULTI_MSG, + &arg, sizeof(arg)); +} + +static inline int hif_set_uapsd_info(struct wfx_vif *wvif, + struct hif_mib_set_uapsd_information *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + +static inline int hif_erp_use_protection(struct wfx_vif *wvif, bool enable) +{ + __le32 arg = enable ? cpu_to_le32(1) : 0; + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_NON_ERP_PROTECTION, &arg, sizeof(arg)); +} + +static inline int hif_slot_time(struct wfx_vif *wvif, int val) +{ + __le32 arg = cpu_to_le32(val); + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_SLOT_TIME, + &arg, sizeof(arg)); +} + +static inline int hif_dual_cts_protection(struct wfx_vif *wvif, bool val) +{ + struct hif_mib_set_ht_protection arg = { + .dual_cts_prot = val, + }; + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_SET_HT_PROTECTION, + &arg, sizeof(arg)); +} + +static inline int hif_wep_default_key_id(struct wfx_vif *wvif, int val) +{ + __le32 arg = cpu_to_le32(val); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &arg, sizeof(arg)); +} + +static inline int hif_rts_threshold(struct wfx_vif *wvif, int val) +{ + __le32 arg = cpu_to_le32(val > 0 ? val : 0xFFFF); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_DOT11_RTS_THRESHOLD, &arg, sizeof(arg)); +} + +#endif diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c new file mode 100644 index 000000000000..0cf52aee10e7 --- /dev/null +++ b/drivers/staging/wfx/hwio.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Low-level I/O functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "hwio.h" +#include "wfx.h" +#include "bus.h" +#include "traces.h" + +/* + * Internal helpers. + * + * About CONFIG_VMAP_STACK: + * When CONFIG_VMAP_STACK is enabled, it is not possible to run DMA on stack + * allocated data. Functions below that work with registers (aka functions + * ending with "32") automatically reallocate buffers with kmalloc. However, + * functions that work with arbitrary length buffers let's caller to handle + * memory location. In doubt, enable CONFIG_DEBUG_SG to detect badly located + * buffer. + */ + +static int read32(struct wfx_dev *wdev, int reg, u32 *val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + *val = ~0; // Never return undefined value + if (!tmp) + return -ENOMEM; + ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, tmp, sizeof(u32)); + if (ret >= 0) + *val = le32_to_cpu(*tmp); + kfree(tmp); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +static int write32(struct wfx_dev *wdev, int reg, u32 val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + *tmp = cpu_to_le32(val); + ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, tmp, sizeof(u32)); + kfree(tmp); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +static int read32_locked(struct wfx_dev *wdev, int reg, u32 *val) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = read32(wdev, reg, val); + _trace_io_read32(reg, *val); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int write32_locked(struct wfx_dev *wdev, int reg, u32 val) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = write32(wdev, reg, val); + _trace_io_write32(reg, val); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int write32_bits_locked(struct wfx_dev *wdev, int reg, u32 mask, u32 val) +{ + int ret; + u32 val_r, val_w; + + WARN_ON(~mask & val); + val &= mask; + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = read32(wdev, reg, &val_r); + _trace_io_read32(reg, val_r); + if (ret < 0) + goto err; + val_w = (val_r & ~mask) | val; + if (val_w != val_r) { + ret = write32(wdev, reg, val_w); + _trace_io_write32(reg, val_w); + } +err: + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int indirect_read(struct wfx_dev *wdev, int reg, u32 addr, void *buf, size_t len) +{ + int ret; + int i; + u32 cfg; + u32 prefetch; + + WARN_ON(len >= 0x2000); + WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT); + + if (reg == WFX_REG_AHB_DPORT) + prefetch = CFG_PREFETCH_AHB; + else if (reg == WFX_REG_SRAM_DPORT) + prefetch = CFG_PREFETCH_SRAM; + else + return -ENODEV; + + ret = write32(wdev, WFX_REG_BASE_ADDR, addr); + if (ret < 0) + goto err; + + ret = read32(wdev, WFX_REG_CONFIG, &cfg); + if (ret < 0) + goto err; + + ret = write32(wdev, WFX_REG_CONFIG, cfg | prefetch); + if (ret < 0) + goto err; + + for (i = 0; i < 20; i++) { + ret = read32(wdev, WFX_REG_CONFIG, &cfg); + if (ret < 0) + goto err; + if (!(cfg & prefetch)) + break; + udelay(200); + } + if (i == 20) { + ret = -ETIMEDOUT; + goto err; + } + + ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, buf, len); + +err: + if (ret < 0) + memset(buf, 0xFF, len); // Never return undefined value + return ret; +} + +static int indirect_write(struct wfx_dev *wdev, int reg, u32 addr, const void *buf, size_t len) +{ + int ret; + + WARN_ON(len >= 0x2000); + WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT); + ret = write32(wdev, WFX_REG_BASE_ADDR, addr); + if (ret < 0) + return ret; + + return wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, buf, len); +} + +static int indirect_read_locked(struct wfx_dev *wdev, int reg, u32 addr, void *buf, size_t len) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_read(wdev, reg, addr, buf, len); + _trace_io_ind_read(reg, addr, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int indirect_write_locked(struct wfx_dev *wdev, int reg, u32 addr, const void *buf, size_t len) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_write(wdev, reg, addr, buf, len); + _trace_io_ind_write(reg, addr, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int indirect_read32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 *val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_read(wdev, reg, addr, tmp, sizeof(u32)); + *val = cpu_to_le32(*tmp); + _trace_io_ind_read32(reg, addr, *val); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + kfree(tmp); + return ret; +} + +static int indirect_write32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + *tmp = cpu_to_le32(val); + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_write(wdev, reg, addr, tmp, sizeof(u32)); + _trace_io_ind_write32(reg, addr, val); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + kfree(tmp); + return ret; +} + +int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len) +{ + int ret; + + WARN((long) buf & 3, "%s: unaligned buffer", __func__); + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len); + _trace_io_read(WFX_REG_IN_OUT_QUEUE, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t len) +{ + int ret; + + WARN((long) buf & 3, "%s: unaligned buffer", __func__); + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len); + _trace_io_write(WFX_REG_IN_OUT_QUEUE, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len) +{ + return indirect_read_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len); +} + +int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len) +{ + return indirect_read_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len); +} + +int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len) +{ + return indirect_write_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len); +} + +int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len) +{ + return indirect_write_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len); +} + +int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val) +{ + return indirect_read32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val); +} + +int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val) +{ + return indirect_read32_locked(wdev, WFX_REG_AHB_DPORT, addr, val); +} + +int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val) +{ + return indirect_write32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val); +} + +int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val) +{ + return indirect_write32_locked(wdev, WFX_REG_AHB_DPORT, addr, val); +} + +int config_reg_read(struct wfx_dev *wdev, u32 *val) +{ + return read32_locked(wdev, WFX_REG_CONFIG, val); +} + +int config_reg_write(struct wfx_dev *wdev, u32 val) +{ + return write32_locked(wdev, WFX_REG_CONFIG, val); +} + +int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val) +{ + return write32_bits_locked(wdev, WFX_REG_CONFIG, mask, val); +} + +int control_reg_read(struct wfx_dev *wdev, u32 *val) +{ + return read32_locked(wdev, WFX_REG_CONTROL, val); +} + +int control_reg_write(struct wfx_dev *wdev, u32 val) +{ + return write32_locked(wdev, WFX_REG_CONTROL, val); +} + +int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val) +{ + return write32_bits_locked(wdev, WFX_REG_CONTROL, mask, val); +} + +int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val) +{ + int ret; + + *val = ~0; // Never return undefined value + ret = write32_locked(wdev, WFX_REG_SET_GEN_R_W, IGPR_RW | index << 24); + if (ret) + return ret; + ret = read32_locked(wdev, WFX_REG_SET_GEN_R_W, val); + if (ret) + return ret; + *val &= IGPR_VALUE; + return ret; +} + +int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val) +{ + return write32_locked(wdev, WFX_REG_SET_GEN_R_W, index << 24 | val); +} diff --git a/drivers/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h new file mode 100644 index 000000000000..906524f71fd1 --- /dev/null +++ b/drivers/staging/wfx/hwio.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Low-level API. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_HWIO_H +#define WFX_HWIO_H + +#include <linux/types.h> + +struct wfx_dev; + +int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t buf_len); +int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t buf_len); + +int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len); +int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len); + +int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len); +int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len); + +int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val); +int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val); + +int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val); +int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val); + +#define CFG_ERR_SPI_FRAME 0x00000001 // only with SPI +#define CFG_ERR_SDIO_BUF_MISMATCH 0x00000001 // only with SDIO +#define CFG_ERR_BUF_UNDERRUN 0x00000002 +#define CFG_ERR_DATA_IN_TOO_LARGE 0x00000004 +#define CFG_ERR_HOST_NO_OUT_QUEUE 0x00000008 +#define CFG_ERR_BUF_OVERRUN 0x00000010 +#define CFG_ERR_DATA_OUT_TOO_LARGE 0x00000020 +#define CFG_ERR_HOST_NO_IN_QUEUE 0x00000040 +#define CFG_ERR_HOST_CRC_MISS 0x00000080 // only with SDIO +#define CFG_SPI_IGNORE_CS 0x00000080 // only with SPI +#define CFG_WORD_MODE_MASK 0x00000300 // Bytes ordering (only writable in SPI): +#define CFG_WORD_MODE0 0x00000000 // B1,B0,B3,B2 (In SPI, register address and CONFIG data always use this mode) +#define CFG_WORD_MODE1 0x00000100 // B3,B2,B1,B0 +#define CFG_WORD_MODE2 0x00000200 // B0,B1,B2,B3 (SDIO) +#define CFG_DIRECT_ACCESS_MODE 0x00000400 // Direct or queue access mode +#define CFG_PREFETCH_AHB 0x00000800 +#define CFG_DISABLE_CPU_CLK 0x00001000 +#define CFG_PREFETCH_SRAM 0x00002000 +#define CFG_CPU_RESET 0x00004000 +#define CFG_SDIO_DISABLE_IRQ 0x00008000 // only with SDIO +#define CFG_IRQ_ENABLE_DATA 0x00010000 +#define CFG_IRQ_ENABLE_WRDY 0x00020000 +#define CFG_CLK_RISE_EDGE 0x00040000 +#define CFG_SDIO_DISABLE_CRC_CHK 0x00080000 // only with SDIO +#define CFG_RESERVED 0x00F00000 +#define CFG_DEVICE_ID_MAJOR 0x07000000 +#define CFG_DEVICE_ID_RESERVED 0x78000000 +#define CFG_DEVICE_ID_TYPE 0x80000000 +int config_reg_read(struct wfx_dev *wdev, u32 *val); +int config_reg_write(struct wfx_dev *wdev, u32 val); +int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val); + +#define CTRL_NEXT_LEN_MASK 0x00000FFF +#define CTRL_WLAN_WAKEUP 0x00001000 +#define CTRL_WLAN_READY 0x00002000 +int control_reg_read(struct wfx_dev *wdev, u32 *val); +int control_reg_write(struct wfx_dev *wdev, u32 val); +int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val); + +#define IGPR_RW 0x80000000 +#define IGPR_INDEX 0x7F000000 +#define IGPR_VALUE 0x00FFFFFF +int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val); +int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val); + +#endif /* WFX_HWIO_H */ diff --git a/drivers/staging/wfx/key.c b/drivers/staging/wfx/key.c new file mode 100644 index 000000000000..6d03abec20e4 --- /dev/null +++ b/drivers/staging/wfx/key.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Key management related functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <net/mac80211.h> + +#include "key.h" +#include "wfx.h" +#include "hif_tx_mib.h" + +static int wfx_alloc_key(struct wfx_dev *wdev) +{ + int idx; + + idx = ffs(~wdev->key_map) - 1; + if (idx < 0 || idx >= MAX_KEY_ENTRIES) + return -1; + + wdev->key_map |= BIT(idx); + wdev->keys[idx].entry_index = idx; + return idx; +} + +static void wfx_free_key(struct wfx_dev *wdev, int idx) +{ + WARN(!(wdev->key_map & BIT(idx)), "inconsistent key allocation"); + memset(&wdev->keys[idx], 0, sizeof(wdev->keys[idx])); + wdev->key_map &= ~BIT(idx); +} + +static uint8_t fill_wep_pair(struct hif_wep_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + WARN(key->keylen > sizeof(msg->key_data), "inconsistent data"); + msg->key_length = key->keylen; + memcpy(msg->key_data, key->key, key->keylen); + ether_addr_copy(msg->peer_address, peer_addr); + return HIF_KEY_TYPE_WEP_PAIRWISE; +} + +static uint8_t fill_wep_group(struct hif_wep_group_key *msg, + struct ieee80211_key_conf *key) +{ + WARN(key->keylen > sizeof(msg->key_data), "inconsistent data"); + msg->key_id = key->keyidx; + msg->key_length = key->keylen; + memcpy(msg->key_data, key->key, key->keylen); + return HIF_KEY_TYPE_WEP_DEFAULT; +} + +static uint8_t fill_tkip_pair(struct hif_tkip_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + uint8_t *keybuf = key->key; + + WARN(key->keylen != sizeof(msg->tkip_key_data) + + sizeof(msg->tx_mic_key) + + sizeof(msg->rx_mic_key), "inconsistent data"); + memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data)); + keybuf += sizeof(msg->tkip_key_data); + memcpy(msg->tx_mic_key, keybuf, sizeof(msg->tx_mic_key)); + keybuf += sizeof(msg->tx_mic_key); + memcpy(msg->rx_mic_key, keybuf, sizeof(msg->rx_mic_key)); + ether_addr_copy(msg->peer_address, peer_addr); + return HIF_KEY_TYPE_TKIP_PAIRWISE; +} + +static uint8_t fill_tkip_group(struct hif_tkip_group_key *msg, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq, + enum nl80211_iftype iftype) +{ + uint8_t *keybuf = key->key; + + WARN(key->keylen != sizeof(msg->tkip_key_data) + + 2 * sizeof(msg->rx_mic_key), "inconsistent data"); + msg->key_id = key->keyidx; + memcpy(msg->rx_sequence_counter, &seq->tkip.iv16, sizeof(seq->tkip.iv16)); + memcpy(msg->rx_sequence_counter + sizeof(uint16_t), &seq->tkip.iv32, sizeof(seq->tkip.iv32)); + memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data)); + keybuf += sizeof(msg->tkip_key_data); + if (iftype == NL80211_IFTYPE_AP) + // Use Tx MIC Key + memcpy(msg->rx_mic_key, keybuf + 0, sizeof(msg->rx_mic_key)); + else + // Use Rx MIC Key + memcpy(msg->rx_mic_key, keybuf + 8, sizeof(msg->rx_mic_key)); + return HIF_KEY_TYPE_TKIP_GROUP; +} + +static uint8_t fill_ccmp_pair(struct hif_aes_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + WARN(key->keylen != sizeof(msg->aes_key_data), "inconsistent data"); + ether_addr_copy(msg->peer_address, peer_addr); + memcpy(msg->aes_key_data, key->key, key->keylen); + return HIF_KEY_TYPE_AES_PAIRWISE; +} + +static uint8_t fill_ccmp_group(struct hif_aes_group_key *msg, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + WARN(key->keylen != sizeof(msg->aes_key_data), "inconsistent data"); + memcpy(msg->aes_key_data, key->key, key->keylen); + memcpy(msg->rx_sequence_counter, seq->ccmp.pn, sizeof(seq->ccmp.pn)); + memreverse(msg->rx_sequence_counter, sizeof(seq->ccmp.pn)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_AES_GROUP; +} + +static uint8_t fill_sms4_pair(struct hif_wapi_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + uint8_t *keybuf = key->key; + + WARN(key->keylen != sizeof(msg->wapi_key_data) + + sizeof(msg->mic_key_data), "inconsistent data"); + ether_addr_copy(msg->peer_address, peer_addr); + memcpy(msg->wapi_key_data, keybuf, sizeof(msg->wapi_key_data)); + keybuf += sizeof(msg->wapi_key_data); + memcpy(msg->mic_key_data, keybuf, sizeof(msg->mic_key_data)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_WAPI_PAIRWISE; +} + +static uint8_t fill_sms4_group(struct hif_wapi_group_key *msg, + struct ieee80211_key_conf *key) +{ + uint8_t *keybuf = key->key; + + WARN(key->keylen != sizeof(msg->wapi_key_data) + + sizeof(msg->mic_key_data), "inconsistent data"); + memcpy(msg->wapi_key_data, keybuf, sizeof(msg->wapi_key_data)); + keybuf += sizeof(msg->wapi_key_data); + memcpy(msg->mic_key_data, keybuf, sizeof(msg->mic_key_data)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_WAPI_GROUP; +} + +static uint8_t fill_aes_cmac_group(struct hif_igtk_group_key *msg, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + WARN(key->keylen != sizeof(msg->igtk_key_data), "inconsistent data"); + memcpy(msg->igtk_key_data, key->key, key->keylen); + memcpy(msg->ipn, seq->aes_cmac.pn, sizeof(seq->aes_cmac.pn)); + memreverse(msg->ipn, sizeof(seq->aes_cmac.pn)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_IGTK_GROUP; +} + +static int wfx_add_key(struct wfx_vif *wvif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret; + struct hif_req_add_key *k; + struct ieee80211_key_seq seq; + struct wfx_dev *wdev = wvif->wdev; + int idx = wfx_alloc_key(wvif->wdev); + bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE; + + WARN(key->flags & IEEE80211_KEY_FLAG_PAIRWISE && !sta, "inconsistent data"); + ieee80211_get_key_rx_seq(key, 0, &seq); + if (idx < 0) + return -EINVAL; + k = &wdev->keys[idx]; + k->int_id = wvif->id; + if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) { + if (pairwise) + k->type = fill_wep_pair(&k->key.wep_pairwise_key, key, sta->addr); + else + k->type = fill_wep_group(&k->key.wep_group_key, key); + } else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { + if (pairwise) + k->type = fill_tkip_pair(&k->key.tkip_pairwise_key, key, sta->addr); + else + k->type = fill_tkip_group(&k->key.tkip_group_key, key, &seq, wvif->vif->type); + } else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) { + if (pairwise) + k->type = fill_ccmp_pair(&k->key.aes_pairwise_key, key, sta->addr); + else + k->type = fill_ccmp_group(&k->key.aes_group_key, key, &seq); + } else if (key->cipher == WLAN_CIPHER_SUITE_SMS4) { + if (pairwise) + k->type = fill_sms4_pair(&k->key.wapi_pairwise_key, key, sta->addr); + else + k->type = fill_sms4_group(&k->key.wapi_group_key, key); + } else if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + k->type = fill_aes_cmac_group(&k->key.igtk_group_key, key, &seq); + } else { + dev_warn(wdev->dev, "unsupported key type %d\n", key->cipher); + wfx_free_key(wdev, idx); + return -EOPNOTSUPP; + } + ret = hif_add_key(wdev, k); + if (ret) { + wfx_free_key(wdev, idx); + return -EOPNOTSUPP; + } + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; + key->hw_key_idx = idx; + return 0; +} + +static int wfx_remove_key(struct wfx_vif *wvif, struct ieee80211_key_conf *key) +{ + WARN(key->hw_key_idx >= MAX_KEY_ENTRIES, "corrupted hw_key_idx"); + wfx_free_key(wvif->wdev, key->hw_key_idx); + return hif_remove_key(wvif->wdev, key->hw_key_idx); +} + +int wfx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + + mutex_lock(&wvif->wdev->conf_mutex); + if (cmd == SET_KEY) + ret = wfx_add_key(wvif, sta, key); + if (cmd == DISABLE_KEY) + ret = wfx_remove_key(wvif, key); + mutex_unlock(&wvif->wdev->conf_mutex); + return ret; +} + +int wfx_upload_keys(struct wfx_vif *wvif) +{ + int i; + struct hif_req_add_key *key; + struct wfx_dev *wdev = wvif->wdev; + + for (i = 0; i < ARRAY_SIZE(wdev->keys); i++) { + if (wdev->key_map & BIT(i)) { + key = &wdev->keys[i]; + if (key->int_id == wvif->id) + hif_add_key(wdev, key); + } + } + return 0; +} + +void wfx_wep_key_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, wep_key_work); + + wfx_tx_flush(wvif->wdev); + hif_wep_default_key_id(wvif, wvif->wep_default_key_id); + wfx_pending_requeue(wvif->wdev, wvif->wep_pending_skb); + wvif->wep_pending_skb = NULL; + wfx_tx_unlock(wvif->wdev); +} diff --git a/drivers/staging/wfx/key.h b/drivers/staging/wfx/key.h new file mode 100644 index 000000000000..9436ccdf4d3b --- /dev/null +++ b/drivers/staging/wfx/key.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of mac80211 API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_KEY_H +#define WFX_KEY_H + +#include <net/mac80211.h> + +struct wfx_dev; +struct wfx_vif; + +int wfx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int wfx_upload_keys(struct wfx_vif *wvif); +void wfx_wep_key_work(struct work_struct *work); + +#endif /* WFX_STA_H */ diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c new file mode 100644 index 000000000000..d2508bc950fa --- /dev/null +++ b/drivers/staging/wfx/main.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Device probe and register. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2008, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + */ +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_net.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/mmc/sdio_func.h> +#include <linux/spi/spi.h> +#include <linux/etherdevice.h> +#include <linux/firmware.h> + +#include "main.h" +#include "wfx.h" +#include "fwio.h" +#include "hwio.h" +#include "bus.h" +#include "bh.h" +#include "sta.h" +#include "key.h" +#include "debug.h" +#include "data_tx.h" +#include "secure_link.h" +#include "hif_tx_mib.h" +#include "hif_api_cmd.h" +#include "wfx_version.h" + +#define WFX_PDS_MAX_SIZE 1500 + +MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx"); +MODULE_AUTHOR("Jérôme Pouiller <jerome.pouiller@silabs.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(WFX_LABEL); + +static int gpio_wakeup = -2; +module_param(gpio_wakeup, int, 0644); +MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none."); + +static char *slk_key; +module_param(slk_key, charp, 0600); +MODULE_PARM_DESC(slk_key, "secret key for secure link (expect 64 hexadecimal digits)."); + +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ +} + +static struct ieee80211_rate wfx_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(55, 2, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(110, 3, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel wfx_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_supported_band wfx_band_2ghz = { + .channels = wfx_2ghz_chantable, + .n_channels = ARRAY_SIZE(wfx_2ghz_chantable), + .bitrates = wfx_rates, + .n_bitrates = ARRAY_SIZE(wfx_rates), + .ht_cap = { + // Receive caps + .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_MAX_AMSDU | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask = { 0xFF }, // MCS0 to MCS7 + .rx_highest = 65, + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const struct ieee80211_iface_limit wdev_iface_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination wfx_iface_combinations[] = { + { + .num_different_channels = 2, + .max_interfaces = 2, + .limits = wdev_iface_limits, + .n_limits = ARRAY_SIZE(wdev_iface_limits), + } +}; + +static const struct ieee80211_ops wfx_ops = { + .start = wfx_start, + .stop = wfx_stop, + .add_interface = wfx_add_interface, + .remove_interface = wfx_remove_interface, + .config = wfx_config, + .tx = wfx_tx, + .conf_tx = wfx_conf_tx, + .hw_scan = wfx_hw_scan, + .sta_add = wfx_sta_add, + .sta_remove = wfx_sta_remove, + .sta_notify = wfx_sta_notify, + .set_tim = wfx_set_tim, + .set_key = wfx_set_key, + .set_rts_threshold = wfx_set_rts_threshold, + .bss_info_changed = wfx_bss_info_changed, + .prepare_multicast = wfx_prepare_multicast, + .configure_filter = wfx_configure_filter, + .ampdu_action = wfx_ampdu_action, + .flush = wfx_flush, + .add_chanctx = wfx_add_chanctx, + .remove_chanctx = wfx_remove_chanctx, + .change_chanctx = wfx_change_chanctx, + .assign_vif_chanctx = wfx_assign_vif_chanctx, + .unassign_vif_chanctx = wfx_unassign_vif_chanctx, +}; + +bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) +{ + if (wdev->hw_caps.api_version_major < major) + return true; + if (wdev->hw_caps.api_version_major > major) + return false; + if (wdev->hw_caps.api_version_minor < minor) + return true; + return false; +} + +struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label) +{ + struct gpio_desc *ret; + char label_buf[256]; + + if (override >= 0) { + snprintf(label_buf, sizeof(label_buf), "wfx_%s", label); + ret = ERR_PTR(devm_gpio_request_one(dev, override, GPIOF_OUT_INIT_LOW, label_buf)); + if (!ret) + ret = gpio_to_desc(override); + } else if (override == -1) { + ret = NULL; + } else { + ret = devm_gpiod_get(dev, label, GPIOD_OUT_LOW); + } + if (IS_ERR(ret) || !ret) { + if (!ret || PTR_ERR(ret) == -ENOENT) + dev_warn(dev, "gpio %s is not defined\n", label); + else + dev_warn(dev, "error while requesting gpio %s\n", label); + ret = NULL; + } else { + dev_dbg(dev, "using gpio %d for %s\n", desc_to_gpio(ret), label); + } + return ret; +} + +static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata) +{ + const char *ascii_key = NULL; + int ret = 0; + + if (slk_key) + ascii_key = slk_key; + if (!ascii_key) + ret = of_property_read_string(dev->of_node, "slk_key", &ascii_key); + if (ret == -EILSEQ || ret == -ENODATA) + dev_err(dev, "ignoring malformatted key from DT\n"); + if (!ascii_key) + return; + + ret = hex2bin(pdata->slk_key, ascii_key, sizeof(pdata->slk_key)); + if (ret) { + dev_err(dev, "ignoring malformatted key: %s\n", ascii_key); + memset(pdata->slk_key, 0, sizeof(pdata->slk_key)); + return; + } + dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n"); +} + +/* NOTE: wfx_send_pds() destroy buf */ +int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len) +{ + int ret; + int start, brace_level, i; + + start = 0; + brace_level = 0; + if (buf[0] != '{') { + dev_err(wdev->dev, "valid PDS start with '{'. Did you forget to compress it?\n"); + return -EINVAL; + } + for (i = 1; i < len - 1; i++) { + if (buf[i] == '{') + brace_level++; + if (buf[i] == '}') + brace_level--; + if (buf[i] == '}' && !brace_level) { + i++; + if (i - start + 1 > WFX_PDS_MAX_SIZE) + return -EFBIG; + buf[start] = '{'; + buf[i] = 0; + dev_dbg(wdev->dev, "send PDS '%s}'\n", buf + start); + buf[i] = '}'; + ret = hif_configuration(wdev, buf + start, i - start + 1); + if (ret == HIF_STATUS_FAILURE) { + dev_err(wdev->dev, "PDS bytes %d to %d: invalid data (unsupported options?)\n", start, i); + return -EINVAL; + } + if (ret == -ETIMEDOUT) { + dev_err(wdev->dev, "PDS bytes %d to %d: chip didn't reply (corrupted file?)\n", start, i); + return ret; + } + if (ret) { + dev_err(wdev->dev, "PDS bytes %d to %d: chip returned an unknown error\n", start, i); + return -EIO; + } + buf[i] = ','; + start = i; + } + } + return 0; +} + +static int wfx_send_pdata_pds(struct wfx_dev *wdev) +{ + int ret = 0; + const struct firmware *pds; + unsigned char *tmp_buf; + + ret = request_firmware(&pds, wdev->pdata.file_pds, wdev->dev); + if (ret) { + dev_err(wdev->dev, "can't load PDS file %s\n", wdev->pdata.file_pds); + return ret; + } + tmp_buf = kmemdup(pds->data, pds->size, GFP_KERNEL); + ret = wfx_send_pds(wdev, tmp_buf, pds->size); + kfree(tmp_buf); + release_firmware(pds); + return ret; +} + +struct wfx_dev *wfx_init_common(struct device *dev, + const struct wfx_platform_data *pdata, + const struct hwbus_ops *hwbus_ops, + void *hwbus_priv) +{ + struct ieee80211_hw *hw; + struct wfx_dev *wdev; + + hw = ieee80211_alloc_hw(sizeof(struct wfx_dev), &wfx_ops); + if (!hw) + return NULL; + + SET_IEEE80211_DEV(hw, dev); + + ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, MFP_CAPABLE); + + hw->vif_data_size = sizeof(struct wfx_vif); + hw->sta_data_size = sizeof(struct wfx_sta_priv); + hw->queues = 4; + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_msg) + + sizeof(struct hif_req_tx) + + 4 /* alignment */ + 8 /* TKIP IV */; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + hw->wiphy->max_ap_assoc_sta = WFX_MAX_STA_IN_AP_MODE; + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(wfx_iface_combinations); + hw->wiphy->iface_combinations = wfx_iface_combinations; + hw->wiphy->bands[NL80211_BAND_2GHZ] = devm_kmalloc(dev, sizeof(wfx_band_2ghz), GFP_KERNEL); + // FIXME: also copy wfx_rates and wfx_2ghz_chantable + memcpy(hw->wiphy->bands[NL80211_BAND_2GHZ], &wfx_band_2ghz, sizeof(wfx_band_2ghz)); + + wdev = hw->priv; + wdev->hw = hw; + wdev->dev = dev; + wdev->hwbus_ops = hwbus_ops; + wdev->hwbus_priv = hwbus_priv; + memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + of_property_read_string(dev->of_node, "config-file", &wdev->pdata.file_pds); + wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup"); + wfx_fill_sl_key(dev, &wdev->pdata); + + mutex_init(&wdev->conf_mutex); + mutex_init(&wdev->rx_stats_lock); + init_completion(&wdev->firmware_ready); + wfx_init_hif_cmd(&wdev->hif_cmd); + wfx_tx_queues_init(wdev); + + return wdev; +} + +void wfx_free_common(struct wfx_dev *wdev) +{ + mutex_destroy(&wdev->rx_stats_lock); + mutex_destroy(&wdev->conf_mutex); + wfx_tx_queues_deinit(wdev); + ieee80211_free_hw(wdev->hw); +} + +int wfx_probe(struct wfx_dev *wdev) +{ + int i; + int err; + const void *macaddr; + struct gpio_desc *gpio_saved; + + // During first part of boot, gpio_wakeup cannot yet been used. So + // prevent bh() to touch it. + gpio_saved = wdev->pdata.gpio_wakeup; + wdev->pdata.gpio_wakeup = NULL; + + wfx_bh_register(wdev); + + err = wfx_init_device(wdev); + if (err) + goto err1; + + err = wait_for_completion_interruptible_timeout(&wdev->firmware_ready, 10 * HZ); + if (err <= 0) { + if (err == 0) { + dev_err(wdev->dev, "timeout while waiting for startup indication. IRQ configuration error?\n"); + err = -ETIMEDOUT; + } else if (err == -ERESTARTSYS) { + dev_info(wdev->dev, "probe interrupted by user\n"); + } + goto err1; + } + + // FIXME: fill wiphy::hw_version + dev_info(wdev->dev, "started firmware %d.%d.%d \"%s\" (API: %d.%d, keyset: %02X, caps: 0x%.8X)\n", + wdev->hw_caps.firmware_major, wdev->hw_caps.firmware_minor, + wdev->hw_caps.firmware_build, wdev->hw_caps.firmware_label, + wdev->hw_caps.api_version_major, wdev->hw_caps.api_version_minor, + wdev->keyset, *((u32 *) &wdev->hw_caps.capabilities)); + snprintf(wdev->hw->wiphy->fw_version, sizeof(wdev->hw->wiphy->fw_version), + "%d.%d.%d", + wdev->hw_caps.firmware_major, + wdev->hw_caps.firmware_minor, + wdev->hw_caps.firmware_build); + + if (wfx_api_older_than(wdev, 1, 0)) { + dev_err(wdev->dev, "unsupported firmware API version (expect 1 while firmware returns %d)\n", + wdev->hw_caps.api_version_major); + err = -ENOTSUPP; + goto err1; + } + + err = wfx_sl_init(wdev); + if (err && wdev->hw_caps.capabilities.link_mode == SEC_LINK_ENFORCED) { + dev_err(wdev->dev, "chip require secure_link, but can't negociate it\n"); + goto err1; + } + + if (wdev->hw_caps.regul_sel_mode_info.region_sel_mode) { + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[11].flags |= IEEE80211_CHAN_NO_IR; + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[12].flags |= IEEE80211_CHAN_NO_IR; + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[13].flags |= IEEE80211_CHAN_DISABLED; + } + + dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds); + err = wfx_send_pdata_pds(wdev); + if (err < 0) + goto err1; + + wdev->pdata.gpio_wakeup = gpio_saved; + if (wdev->pdata.gpio_wakeup) { + dev_dbg(wdev->dev, "enable 'quiescent' power mode with gpio %d and PDS file %s\n", + desc_to_gpio(wdev->pdata.gpio_wakeup), wdev->pdata.file_pds); + gpiod_set_value(wdev->pdata.gpio_wakeup, 1); + control_reg_write(wdev, 0); + hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT); + } else { + hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_DOZE); + } + + hif_use_multi_tx_conf(wdev, true); + + for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) { + eth_zero_addr(wdev->addresses[i].addr); + macaddr = of_get_mac_address(wdev->dev->of_node); + if (macaddr) { + ether_addr_copy(wdev->addresses[i].addr, macaddr); + wdev->addresses[i].addr[ETH_ALEN - 1] += i; + } + ether_addr_copy(wdev->addresses[i].addr, wdev->hw_caps.mac_addr[i]); + if (!is_valid_ether_addr(wdev->addresses[i].addr)) { + dev_warn(wdev->dev, "using random MAC address\n"); + eth_random_addr(wdev->addresses[i].addr); + } + dev_info(wdev->dev, "MAC address %d: %pM\n", i, wdev->addresses[i].addr); + } + wdev->hw->wiphy->n_addresses = ARRAY_SIZE(wdev->addresses); + wdev->hw->wiphy->addresses = wdev->addresses; + + err = ieee80211_register_hw(wdev->hw); + if (err) + goto err1; + + err = wfx_debug_init(wdev); + if (err) + goto err2; + + return 0; + +err2: + ieee80211_unregister_hw(wdev->hw); + ieee80211_free_hw(wdev->hw); +err1: + wfx_bh_unregister(wdev); + return err; +} + +void wfx_release(struct wfx_dev *wdev) +{ + ieee80211_unregister_hw(wdev->hw); + hif_shutdown(wdev); + wfx_bh_unregister(wdev); + wfx_sl_deinit(wdev); +} + +static int __init wfx_core_init(void) +{ + int ret = 0; + + pr_info("wfx: Silicon Labs " WFX_LABEL "\n"); + + if (IS_ENABLED(CONFIG_SPI)) + ret = spi_register_driver(&wfx_spi_driver); + if (IS_ENABLED(CONFIG_MMC) && !ret) + ret = sdio_register_driver(&wfx_sdio_driver); + return ret; +} +module_init(wfx_core_init); + +static void __exit wfx_core_exit(void) +{ + if (IS_ENABLED(CONFIG_MMC)) + sdio_unregister_driver(&wfx_sdio_driver); + if (IS_ENABLED(CONFIG_SPI)) + spi_unregister_driver(&wfx_spi_driver); +} +module_exit(wfx_core_exit); diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h new file mode 100644 index 000000000000..f2b07ed1627c --- /dev/null +++ b/drivers/staging/wfx/main.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Device probe and register. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + */ +#ifndef WFX_MAIN_H +#define WFX_MAIN_H + +#include <linux/device.h> +#include <linux/gpio/consumer.h> + +#include "bus.h" +#include "hif_api_general.h" + +struct wfx_dev; + +struct wfx_platform_data { + /* Keyset and ".sec" extention will appended to this string */ + const char *file_fw; + const char *file_pds; + unsigned char slk_key[API_KEY_VALUE_SIZE]; + struct gpio_desc *gpio_wakeup; + /* + * if true HIF D_out is sampled on the rising edge of the clock + * (intended to be used in 50Mhz SDIO) + */ + bool use_rising_clk; +}; + +struct wfx_dev *wfx_init_common(struct device *dev, + const struct wfx_platform_data *pdata, + const struct hwbus_ops *hwbus_ops, + void *hwbus_priv); +void wfx_free_common(struct wfx_dev *wdev); + +int wfx_probe(struct wfx_dev *wdev); +void wfx_release(struct wfx_dev *wdev); + +struct gpio_desc *wfx_get_gpio(struct device *dev, int override, + const char *label); +bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor); +int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len); + +#endif diff --git a/drivers/staging/wfx/queue.c b/drivers/staging/wfx/queue.c new file mode 100644 index 000000000000..ef3ee55cf621 --- /dev/null +++ b/drivers/staging/wfx/queue.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * O(1) TX queue with built-in allocator. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <linux/sched.h> +#include <net/mac80211.h> + +#include "queue.h" +#include "wfx.h" +#include "sta.h" +#include "data_tx.h" + +void wfx_tx_lock(struct wfx_dev *wdev) +{ + atomic_inc(&wdev->tx_lock); +} + +void wfx_tx_unlock(struct wfx_dev *wdev) +{ + int tx_lock = atomic_dec_return(&wdev->tx_lock); + + WARN(tx_lock < 0, "inconsistent tx_lock value"); + if (!tx_lock) + wfx_bh_request_tx(wdev); +} + +void wfx_tx_flush(struct wfx_dev *wdev) +{ + int ret; + + WARN(!atomic_read(&wdev->tx_lock), "tx_lock is not locked"); + + // Do not wait for any reply if chip is frozen + if (wdev->chip_frozen) + return; + + mutex_lock(&wdev->hif_cmd.lock); + ret = wait_event_timeout(wdev->hif.tx_buffers_empty, + !wdev->hif.tx_buffers_used, + msecs_to_jiffies(3000)); + if (!ret) { + dev_warn(wdev->dev, "cannot flush tx buffers (%d still busy)\n", wdev->hif.tx_buffers_used); + wfx_pending_dump_old_frames(wdev, 3000); + // FIXME: drop pending frames here + wdev->chip_frozen = 1; + } + mutex_unlock(&wdev->hif_cmd.lock); +} + +void wfx_tx_lock_flush(struct wfx_dev *wdev) +{ + wfx_tx_lock(wdev); + wfx_tx_flush(wdev); +} + +void wfx_tx_queues_lock(struct wfx_dev *wdev) +{ + int i; + struct wfx_queue *queue; + + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + queue = &wdev->tx_queue[i]; + spin_lock_bh(&queue->queue.lock); + if (queue->tx_locked_cnt++ == 0) + ieee80211_stop_queue(wdev->hw, queue->queue_id); + spin_unlock_bh(&queue->queue.lock); + } +} + +void wfx_tx_queues_unlock(struct wfx_dev *wdev) +{ + int i; + struct wfx_queue *queue; + + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + queue = &wdev->tx_queue[i]; + spin_lock_bh(&queue->queue.lock); + WARN(!queue->tx_locked_cnt, "queue already unlocked"); + if (--queue->tx_locked_cnt == 0) + ieee80211_wake_queue(wdev->hw, queue->queue_id); + spin_unlock_bh(&queue->queue.lock); + } +} + +/* If successful, LOCKS the TX queue! */ +void wfx_tx_queues_wait_empty_vif(struct wfx_vif *wvif) +{ + int i; + bool done; + struct wfx_queue *queue; + struct sk_buff *item; + struct wfx_dev *wdev = wvif->wdev; + struct hif_msg *hif; + + if (wvif->wdev->chip_frozen) { + wfx_tx_lock_flush(wdev); + wfx_tx_queues_clear(wdev); + return; + } + + do { + done = true; + wfx_tx_lock_flush(wdev); + for (i = 0; i < IEEE80211_NUM_ACS && done; ++i) { + queue = &wdev->tx_queue[i]; + spin_lock_bh(&queue->queue.lock); + skb_queue_walk(&queue->queue, item) { + hif = (struct hif_msg *) item->data; + if (hif->interface == wvif->id) + done = false; + } + spin_unlock_bh(&queue->queue.lock); + } + if (!done) { + wfx_tx_unlock(wdev); + msleep(20); + } + } while (!done); +} + +static void wfx_tx_queue_clear(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff_head *gc_list) +{ + int i; + struct sk_buff *item; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + spin_lock_bh(&queue->queue.lock); + while ((item = __skb_dequeue(&queue->queue)) != NULL) + skb_queue_head(gc_list, item); + spin_lock_bh(&stats->pending.lock); + for (i = 0; i < ARRAY_SIZE(stats->link_map_cache); ++i) { + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->pending.lock); + spin_unlock_bh(&queue->queue.lock); +} + +void wfx_tx_queues_clear(struct wfx_dev *wdev) +{ + int i; + struct sk_buff *item; + struct sk_buff_head gc_list; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + skb_queue_head_init(&gc_list); + for (i = 0; i < IEEE80211_NUM_ACS; ++i) + wfx_tx_queue_clear(wdev, &wdev->tx_queue[i], &gc_list); + wake_up(&stats->wait_link_id_empty); + while ((item = skb_dequeue(&gc_list)) != NULL) + wfx_skb_dtor(wdev, item); +} + +void wfx_tx_queues_init(struct wfx_dev *wdev) +{ + int i; + + memset(&wdev->tx_queue_stats, 0, sizeof(wdev->tx_queue_stats)); + memset(wdev->tx_queue, 0, sizeof(wdev->tx_queue)); + skb_queue_head_init(&wdev->tx_queue_stats.pending); + init_waitqueue_head(&wdev->tx_queue_stats.wait_link_id_empty); + + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + wdev->tx_queue[i].queue_id = i; + skb_queue_head_init(&wdev->tx_queue[i].queue); + } +} + +void wfx_tx_queues_deinit(struct wfx_dev *wdev) +{ + WARN_ON(!skb_queue_empty(&wdev->tx_queue_stats.pending)); + wfx_tx_queues_clear(wdev); +} + +size_t wfx_tx_queue_get_num_queued(struct wfx_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->queue.lock); + if (link_id_map == (u32)-1) { + ret = skb_queue_len(&queue->queue); + } else { + ret = 0; + for (i = 0, bit = 1; i < ARRAY_SIZE(queue->link_map_cache); ++i, bit <<= 1) { + if (link_id_map & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_bh(&queue->queue.lock); + return ret; +} + +void wfx_tx_queue_put(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff *skb) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + + WARN(tx_priv->link_id >= ARRAY_SIZE(stats->link_map_cache), "invalid link-id value"); + spin_lock_bh(&queue->queue.lock); + __skb_queue_tail(&queue->queue, skb); + + ++queue->link_map_cache[tx_priv->link_id]; + + spin_lock_bh(&stats->pending.lock); + ++stats->link_map_cache[tx_priv->link_id]; + spin_unlock_bh(&stats->pending.lock); + spin_unlock_bh(&queue->queue.lock); +} + +static struct sk_buff *wfx_tx_queue_get(struct wfx_dev *wdev, + struct wfx_queue *queue, + u32 link_id_map) +{ + struct sk_buff *skb = NULL; + struct sk_buff *item; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + struct wfx_tx_priv *tx_priv; + bool wakeup_stats = false; + + spin_lock_bh(&queue->queue.lock); + skb_queue_walk(&queue->queue, item) { + tx_priv = wfx_skb_tx_priv(item); + if (link_id_map & BIT(tx_priv->link_id)) { + skb = item; + break; + } + } + WARN_ON(!skb); + if (skb) { + tx_priv = wfx_skb_tx_priv(skb); + tx_priv->xmit_timestamp = ktime_get(); + __skb_unlink(skb, &queue->queue); + --queue->link_map_cache[tx_priv->link_id]; + + spin_lock_bh(&stats->pending.lock); + __skb_queue_tail(&stats->pending, skb); + if (!--stats->link_map_cache[tx_priv->link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->pending.lock); + } + spin_unlock_bh(&queue->queue.lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return skb; +} + +int wfx_pending_requeue(struct wfx_dev *wdev, struct sk_buff *skb) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + struct wfx_queue *queue = &wdev->tx_queue[skb_get_queue_mapping(skb)]; + + WARN_ON(skb_get_queue_mapping(skb) > 3); + spin_lock_bh(&queue->queue.lock); + ++queue->link_map_cache[tx_priv->link_id]; + + spin_lock_bh(&stats->pending.lock); + ++stats->link_map_cache[tx_priv->link_id]; + __skb_unlink(skb, &stats->pending); + spin_unlock_bh(&stats->pending.lock); + __skb_queue_tail(&queue->queue, skb); + spin_unlock_bh(&queue->queue.lock); + return 0; +} + +int wfx_pending_remove(struct wfx_dev *wdev, struct sk_buff *skb) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + spin_lock_bh(&stats->pending.lock); + __skb_unlink(skb, &stats->pending); + spin_unlock_bh(&stats->pending.lock); + wfx_skb_dtor(wdev, skb); + + return 0; +} + +struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id) +{ + struct sk_buff *skb; + struct hif_req_tx *req; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + spin_lock_bh(&stats->pending.lock); + skb_queue_walk(&stats->pending, skb) { + req = wfx_skb_txreq(skb); + if (req->packet_id == packet_id) { + spin_unlock_bh(&stats->pending.lock); + return skb; + } + } + spin_unlock_bh(&stats->pending.lock); + WARN(1, "cannot find packet in pending queue"); + return NULL; +} + +void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + ktime_t now = ktime_get(); + struct wfx_tx_priv *tx_priv; + struct hif_req_tx *req; + struct sk_buff *skb; + bool first = true; + + spin_lock_bh(&stats->pending.lock); + skb_queue_walk(&stats->pending, skb) { + tx_priv = wfx_skb_tx_priv(skb); + req = wfx_skb_txreq(skb); + if (ktime_after(now, ktime_add_ms(tx_priv->xmit_timestamp, limit_ms))) { + if (first) { + dev_info(wdev->dev, "frames stuck in firmware since %dms or more:\n", + limit_ms); + first = false; + } + dev_info(wdev->dev, " id %08x sent %lldms ago\n", + req->packet_id, + ktime_ms_delta(now, tx_priv->xmit_timestamp)); + } + } + spin_unlock_bh(&stats->pending.lock); +} + +unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb) +{ + ktime_t now = ktime_get(); + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + + return ktime_us_delta(now, tx_priv->xmit_timestamp); +} + +bool wfx_tx_queues_is_empty(struct wfx_dev *wdev) +{ + int i; + struct sk_buff_head *queue; + bool ret = true; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + queue = &wdev->tx_queue[i].queue; + spin_lock_bh(&queue->lock); + if (!skb_queue_empty(queue)) + ret = false; + spin_unlock_bh(&queue->lock); + } + return ret; +} + +static bool hif_handle_tx_data(struct wfx_vif *wvif, struct sk_buff *skb, + struct wfx_queue *queue) +{ + bool handled = false; + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + struct hif_req_tx *req = wfx_skb_txreq(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *) (req->frame + req->data_flags.fc_offset); + + enum { + do_probe, + do_drop, + do_wep, + do_tx, + } action = do_tx; + + switch (wvif->vif->type) { + case NL80211_IFTYPE_STATION: + if (wvif->state < WFX_STATE_PRE_STA) + action = do_drop; + break; + case NL80211_IFTYPE_AP: + if (!wvif->state) { + action = do_drop; + } else if (!(BIT(tx_priv->raw_link_id) & (BIT(0) | wvif->link_id_map))) { + dev_warn(wvif->wdev->dev, "a frame with expired link-id is dropped\n"); + action = do_drop; + } + break; + case NL80211_IFTYPE_ADHOC: + if (wvif->state != WFX_STATE_IBSS) + action = do_drop; + break; + case NL80211_IFTYPE_MONITOR: + default: + action = do_drop; + break; + } + + if (action == do_tx) { + if (ieee80211_is_nullfunc(frame->frame_control)) { + mutex_lock(&wvif->bss_loss_lock); + if (wvif->bss_loss_state) { + wvif->bss_loss_confirm_id = req->packet_id; + req->queue_id.queue_id = HIF_QUEUE_ID_VOICE; + } + mutex_unlock(&wvif->bss_loss_lock); + } else if (ieee80211_has_protected(frame->frame_control) && + tx_priv->hw_key && + tx_priv->hw_key->keyidx != wvif->wep_default_key_id && + (tx_priv->hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || + tx_priv->hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { + action = do_wep; + } + } + + switch (action) { + case do_drop: + wfx_pending_remove(wvif->wdev, skb); + handled = true; + break; + case do_wep: + wfx_tx_lock(wvif->wdev); + wvif->wep_default_key_id = tx_priv->hw_key->keyidx; + wvif->wep_pending_skb = skb; + if (!schedule_work(&wvif->wep_key_work)) + wfx_tx_unlock(wvif->wdev); + handled = true; + break; + case do_tx: + break; + default: + /* Do nothing */ + break; + } + return handled; +} + +static int wfx_get_prio_queue(struct wfx_vif *wvif, + u32 tx_allowed_mask, int *total) +{ + static const int urgent = BIT(WFX_LINK_ID_AFTER_DTIM) | + BIT(WFX_LINK_ID_UAPSD); + struct hif_req_edca_queue_params *edca; + unsigned int score, best = -1; + int winner = -1; + int i; + + /* search for a winner using edca params */ + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + int queued; + + edca = &wvif->edca.params[i]; + queued = wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[i], + tx_allowed_mask); + if (!queued) + continue; + *total += queued; + score = ((edca->aifsn + edca->cw_min) << 16) + + ((edca->cw_max - edca->cw_min) * + (get_random_int() & 0xFFFF)); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && wvif->wdev->tx_burst_idx >= 0 && + winner != wvif->wdev->tx_burst_idx && + !wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[winner], tx_allowed_mask & urgent) && + wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[wvif->wdev->tx_burst_idx], tx_allowed_mask)) + winner = wvif->wdev->tx_burst_idx; + + return winner; +} + +static int wfx_tx_queue_mask_get(struct wfx_vif *wvif, + struct wfx_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (wvif->mcast_tx) { + tx_allowed_mask = BIT(WFX_LINK_ID_AFTER_DTIM); + idx = wfx_get_prio_queue(wvif, tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~wvif->sta_asleep_mask; + tx_allowed_mask |= BIT(WFX_LINK_ID_UAPSD); + if (wvif->sta_asleep_mask) { + tx_allowed_mask |= wvif->pspoll_mask; + tx_allowed_mask &= ~BIT(WFX_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(WFX_LINK_ID_AFTER_DTIM); + } + idx = wfx_get_prio_queue(wvif, tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &wvif->wdev->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev) +{ + struct sk_buff *skb; + struct hif_msg *hif = NULL; + struct hif_req_tx *req = NULL; + struct wfx_queue *queue = NULL; + struct wfx_queue *vif_queue = NULL; + u32 tx_allowed_mask = 0; + u32 vif_tx_allowed_mask = 0; + const struct wfx_tx_priv *tx_priv = NULL; + struct wfx_vif *wvif; + /* More is used only for broadcasts. */ + bool more = false; + bool vif_more = false; + int not_found; + int burst; + + for (;;) { + int ret = -ENOENT; + int queue_num; + struct ieee80211_hdr *hdr; + + if (atomic_read(&wdev->tx_lock)) + return NULL; + + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + spin_lock_bh(&wvif->ps_state_lock); + + not_found = wfx_tx_queue_mask_get(wvif, &vif_queue, &vif_tx_allowed_mask, &vif_more); + + if (wvif->mcast_buffered && (not_found || !vif_more) && + (wvif->mcast_tx || !wvif->sta_asleep_mask)) { + wvif->mcast_buffered = false; + if (wvif->mcast_tx) { + wvif->mcast_tx = false; + schedule_work(&wvif->mcast_stop_work); + } + } + + spin_unlock_bh(&wvif->ps_state_lock); + + if (vif_more) { + more = 1; + tx_allowed_mask = vif_tx_allowed_mask; + queue = vif_queue; + ret = 0; + break; + } else if (!not_found) { + if (queue && queue != vif_queue) + dev_info(wdev->dev, "vifs disagree about queue priority\n"); + tx_allowed_mask |= vif_tx_allowed_mask; + queue = vif_queue; + ret = 0; + } + } + + if (ret) + return 0; + + queue_num = queue - wdev->tx_queue; + + skb = wfx_tx_queue_get(wdev, queue, tx_allowed_mask); + if (!skb) + continue; + tx_priv = wfx_skb_tx_priv(skb); + hif = (struct hif_msg *) skb->data; + wvif = wdev_to_wvif(wdev, hif->interface); + WARN_ON(!wvif); + + if (hif_handle_tx_data(wvif, skb, queue)) + continue; /* Handled by WSM */ + + wvif->pspoll_mask &= ~BIT(tx_priv->raw_link_id); + + /* allow bursting if txop is set */ + if (wvif->edca.params[queue_num].tx_op_limit) + burst = (int)wfx_tx_queue_get_num_queued(queue, tx_allowed_mask) + 1; + else + burst = 1; + + /* store index of bursting queue */ + if (burst > 1) + wdev->tx_burst_idx = queue_num; + else + wdev->tx_burst_idx = -1; + + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs + */ + if (more) { + req = (struct hif_req_tx *) hif->body; + hdr = (struct ieee80211_hdr *) (req->frame + req->data_flags.fc_offset); + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + return hif; + } +} diff --git a/drivers/staging/wfx/queue.h b/drivers/staging/wfx/queue.h new file mode 100644 index 000000000000..938dbf3469e7 --- /dev/null +++ b/drivers/staging/wfx/queue.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * O(1) TX queue with built-in allocator. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_QUEUE_H +#define WFX_QUEUE_H + +#include <linux/skbuff.h> + +#include "hif_api_cmd.h" + +#define WFX_MAX_STA_IN_AP_MODE 14 +#define WFX_LINK_ID_AFTER_DTIM (WFX_MAX_STA_IN_AP_MODE + 1) +#define WFX_LINK_ID_UAPSD (WFX_MAX_STA_IN_AP_MODE + 2) +#define WFX_LINK_ID_MAX (WFX_MAX_STA_IN_AP_MODE + 3) + +struct wfx_dev; +struct wfx_vif; + +struct wfx_queue { + struct sk_buff_head queue; + int tx_locked_cnt; + int link_map_cache[WFX_LINK_ID_MAX]; + u8 queue_id; +}; + +struct wfx_queue_stats { + int link_map_cache[WFX_LINK_ID_MAX]; + struct sk_buff_head pending; + wait_queue_head_t wait_link_id_empty; +}; + +void wfx_tx_lock(struct wfx_dev *wdev); +void wfx_tx_unlock(struct wfx_dev *wdev); +void wfx_tx_flush(struct wfx_dev *wdev); +void wfx_tx_lock_flush(struct wfx_dev *wdev); + +void wfx_tx_queues_init(struct wfx_dev *wdev); +void wfx_tx_queues_deinit(struct wfx_dev *wdev); +void wfx_tx_queues_lock(struct wfx_dev *wdev); +void wfx_tx_queues_unlock(struct wfx_dev *wdev); +void wfx_tx_queues_clear(struct wfx_dev *wdev); +bool wfx_tx_queues_is_empty(struct wfx_dev *wdev); +void wfx_tx_queues_wait_empty_vif(struct wfx_vif *wvif); +struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev); + +void wfx_tx_queue_put(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff *skb); +size_t wfx_tx_queue_get_num_queued(struct wfx_queue *queue, u32 link_id_map); + +struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id); +int wfx_pending_remove(struct wfx_dev *wdev, struct sk_buff *skb); +int wfx_pending_requeue(struct wfx_dev *wdev, struct sk_buff *skb); +unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb); +void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms); + +#endif /* WFX_QUEUE_H */ diff --git a/drivers/staging/wfx/scan.c b/drivers/staging/wfx/scan.c new file mode 100644 index 000000000000..cba735c1e73c --- /dev/null +++ b/drivers/staging/wfx/scan.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Scan related functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <net/mac80211.h> + +#include "scan.h" +#include "wfx.h" +#include "sta.h" +#include "hif_tx_mib.h" + +static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw, bool aborted) +{ + struct cfg80211_scan_info info = { + .aborted = aborted ? 1 : 0, + }; + + ieee80211_scan_completed(hw, &info); +} + +static void wfx_scan_restart_delayed(struct wfx_vif *wvif) +{ + if (wvif->delayed_unjoin) { + wvif->delayed_unjoin = false; + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wvif->wdev); + } else if (wvif->delayed_link_loss) { + wvif->delayed_link_loss = 0; + wfx_cqm_bssloss_sm(wvif, 1, 0, 0); + } +} + +static int wfx_scan_start(struct wfx_vif *wvif, struct wfx_scan_params *scan) +{ + int ret; + int tmo = 500; + + if (wvif->state == WFX_STATE_PRE_STA) + return -EBUSY; + + tmo += scan->scan_req.num_of_channels * + ((20 * (scan->scan_req.max_channel_time)) + 10); + atomic_set(&wvif->scan.in_progress, 1); + atomic_set(&wvif->wdev->scan_in_progress, 1); + + schedule_delayed_work(&wvif->scan.timeout, msecs_to_jiffies(tmo)); + ret = hif_scan(wvif, scan); + if (ret) { + wfx_scan_failed_cb(wvif); + atomic_set(&wvif->scan.in_progress, 0); + atomic_set(&wvif->wdev->scan_in_progress, 0); + cancel_delayed_work_sync(&wvif->scan.timeout); + wfx_scan_restart_delayed(wvif); + } + return ret; +} + +int wfx_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct cfg80211_scan_request *req = &hw_req->req; + struct sk_buff *skb; + int i, ret; + struct hif_mib_template_frame *p; + + if (!wvif) + return -EINVAL; + + if (wvif->state == WFX_STATE_AP) + return -EOPNOTSUPP; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + if (req->n_ssids > HIF_API_MAX_NB_SSIDS) + return -EINVAL; + + skb = ieee80211_probereq_get(hw, wvif->vif->addr, NULL, 0, req->ie_len); + if (!skb) + return -ENOMEM; + + if (req->ie_len) + memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); + + mutex_lock(&wdev->conf_mutex); + + p = (struct hif_mib_template_frame *)skb_push(skb, 4); + p->frame_type = HIF_TMPLT_PRBREQ; + p->frame_length = cpu_to_le16(skb->len - 4); + ret = hif_set_template_frame(wvif, p); + skb_pull(skb, 4); + + if (!ret) + /* Host want to be the probe responder. */ + ret = wfx_fwd_probe_req(wvif, true); + if (ret) { + mutex_unlock(&wdev->conf_mutex); + dev_kfree_skb(skb); + return ret; + } + + wfx_tx_lock_flush(wdev); + + WARN(wvif->scan.req, "unexpected concurrent scan"); + wvif->scan.req = req; + wvif->scan.n_ssids = 0; + wvif->scan.status = 0; + wvif->scan.begin = &req->channels[0]; + wvif->scan.curr = wvif->scan.begin; + wvif->scan.end = &req->channels[req->n_channels]; + wvif->scan.output_power = wdev->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + struct hif_ssid_def *dst = &wvif->scan.ssids[wvif->scan.n_ssids]; + + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->ssid_length = req->ssids[i].ssid_len; + ++wvif->scan.n_ssids; + } + + mutex_unlock(&wdev->conf_mutex); + + if (skb) + dev_kfree_skb(skb); + schedule_work(&wvif->scan.work); + return 0; +} + +void wfx_scan_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.work); + struct ieee80211_channel **it; + struct wfx_scan_params scan = { + .scan_req.scan_type.type = 0, /* Foreground */ + }; + struct ieee80211_channel *first; + bool first_run = (wvif->scan.begin == wvif->scan.curr && + wvif->scan.begin != wvif->scan.end); + int i; + + down(&wvif->scan.lock); + mutex_lock(&wvif->wdev->conf_mutex); + + if (first_run) { + if (wvif->state == WFX_STATE_STA && + !(wvif->powersave_mode.pm_mode.enter_psm)) { + struct hif_req_set_pm_mode pm = wvif->powersave_mode; + + pm.pm_mode.enter_psm = 1; + wfx_set_pm(wvif, &pm); + } + } + + if (!wvif->scan.req || wvif->scan.curr == wvif->scan.end) { + if (wvif->scan.output_power != wvif->wdev->output_power) + hif_set_output_power(wvif, wvif->wdev->output_power * 10); + + if (wvif->scan.status < 0) + dev_warn(wvif->wdev->dev, "scan failed\n"); + else if (wvif->scan.req) + dev_dbg(wvif->wdev->dev, "scan completed\n"); + else + dev_dbg(wvif->wdev->dev, "scan canceled\n"); + + wvif->scan.req = NULL; + wfx_scan_restart_delayed(wvif); + wfx_tx_unlock(wvif->wdev); + mutex_unlock(&wvif->wdev->conf_mutex); + __ieee80211_scan_completed_compat(wvif->wdev->hw, wvif->scan.status ? 1 : 0); + up(&wvif->scan.lock); + if (wvif->state == WFX_STATE_STA && + !(wvif->powersave_mode.pm_mode.enter_psm)) + wfx_set_pm(wvif, &wvif->powersave_mode); + return; + } + first = *wvif->scan.curr; + + for (it = wvif->scan.curr + 1, i = 1; + it != wvif->scan.end && i < HIF_API_MAX_NB_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_NO_IR) + break; + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + (*it)->max_power != first->max_power) + break; + } + scan.scan_req.band = first->band; + + if (wvif->scan.req->no_cck) + scan.scan_req.max_transmit_rate = API_RATE_INDEX_G_6MBPS; + else + scan.scan_req.max_transmit_rate = API_RATE_INDEX_B_1MBPS; + scan.scan_req.num_of_probe_requests = + (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; + scan.scan_req.num_of_ssi_ds = wvif->scan.n_ssids; + scan.ssids = &wvif->scan.ssids[0]; + scan.scan_req.num_of_channels = it - wvif->scan.curr; + scan.scan_req.probe_delay = 100; + // FIXME: Check if FW can do active scan while joined. + if (wvif->state == WFX_STATE_STA) { + scan.scan_req.scan_type.type = 1; + scan.scan_req.scan_flags.fbg = 1; + } + + scan.ch = kcalloc(scan.scan_req.num_of_channels, sizeof(u8), GFP_KERNEL); + + if (!scan.ch) { + wvif->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.scan_req.num_of_channels; ++i) + scan.ch[i] = wvif->scan.curr[i]->hw_value; + + if (wvif->scan.curr[0]->flags & IEEE80211_CHAN_NO_IR) { + scan.scan_req.min_channel_time = 50; + scan.scan_req.max_channel_time = 150; + } else { + scan.scan_req.min_channel_time = 10; + scan.scan_req.max_channel_time = 50; + } + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + wvif->scan.output_power != first->max_power) { + wvif->scan.output_power = first->max_power; + hif_set_output_power(wvif, wvif->scan.output_power * 10); + } + wvif->scan.status = wfx_scan_start(wvif, &scan); + kfree(scan.ch); + if (wvif->scan.status) + goto fail; + wvif->scan.curr = it; + mutex_unlock(&wvif->wdev->conf_mutex); + return; + +fail: + wvif->scan.curr = wvif->scan.end; + mutex_unlock(&wvif->wdev->conf_mutex); + up(&wvif->scan.lock); + schedule_work(&wvif->scan.work); +} + +static void wfx_scan_complete(struct wfx_vif *wvif) +{ + up(&wvif->scan.lock); + atomic_set(&wvif->wdev->scan_in_progress, 0); + + wfx_scan_work(&wvif->scan.work); +} + +void wfx_scan_failed_cb(struct wfx_vif *wvif) +{ + if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) { + wvif->scan.status = -EIO; + schedule_work(&wvif->scan.timeout.work); + } +} + +void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg) +{ + if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) { + wvif->scan.status = 1; + schedule_work(&wvif->scan.timeout.work); + } +} + +void wfx_scan_timeout(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.timeout.work); + + if (atomic_xchg(&wvif->scan.in_progress, 0)) { + if (wvif->scan.status > 0) { + wvif->scan.status = 0; + } else if (!wvif->scan.status) { + dev_warn(wvif->wdev->dev, "timeout waiting for scan complete notification\n"); + wvif->scan.status = -ETIMEDOUT; + wvif->scan.curr = wvif->scan.end; + hif_stop_scan(wvif); + } + wfx_scan_complete(wvif); + } +} diff --git a/drivers/staging/wfx/scan.h b/drivers/staging/wfx/scan.h new file mode 100644 index 000000000000..b4ddd0771a9b --- /dev/null +++ b/drivers/staging/wfx/scan.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Scan related functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_SCAN_H +#define WFX_SCAN_H + +#include <linux/semaphore.h> +#include <linux/workqueue.h> +#include <net/mac80211.h> + +#include "hif_api_cmd.h" + +struct wfx_dev; +struct wfx_vif; + +struct wfx_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct hif_ssid_def ssids[HIF_API_MAX_NB_SSIDS]; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; +}; + +int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req); +void wfx_scan_work(struct work_struct *work); +void wfx_scan_timeout(struct work_struct *work); +void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg); +void wfx_scan_failed_cb(struct wfx_vif *wvif); + +#endif /* WFX_SCAN_H */ diff --git a/drivers/staging/wfx/secure_link.h b/drivers/staging/wfx/secure_link.h new file mode 100644 index 000000000000..e2da1c73c760 --- /dev/null +++ b/drivers/staging/wfx/secure_link.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, Silicon Laboratories, Inc. + */ +#ifndef WFX_SECURE_LINK_H +#define WFX_SECURE_LINK_H + +#include "hif_api_general.h" + +struct wfx_dev; + + +struct sl_context { +}; + +static inline bool wfx_is_secure_command(struct wfx_dev *wdev, int cmd_id) +{ + return false; +} + +static inline int wfx_sl_decode(struct wfx_dev *wdev, struct hif_sl_msg *m) +{ + return -EIO; +} + +static inline int wfx_sl_encode(struct wfx_dev *wdev, struct hif_msg *input, struct hif_sl_msg *output) +{ + return -EIO; +} + +static inline int wfx_sl_check_pubkey(struct wfx_dev *wdev, uint8_t *ncp_pubkey, uint8_t *ncp_pubmac) +{ + return -EIO; +} + +static inline int wfx_sl_init(struct wfx_dev *wdev) +{ + return -EIO; +} + +static inline void wfx_sl_deinit(struct wfx_dev *wdev) +{ +} + + +#endif diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c new file mode 100644 index 000000000000..688586e823c0 --- /dev/null +++ b/drivers/staging/wfx/sta.c @@ -0,0 +1,1640 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of mac80211 API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include <net/mac80211.h> + +#include "sta.h" +#include "wfx.h" +#include "fwio.h" +#include "bh.h" +#include "key.h" +#include "scan.h" +#include "debug.h" +#include "hif_tx.h" +#include "hif_tx_mib.h" + +#define TXOP_UNIT 32 +#define HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES 2 + +static u32 wfx_rate_mask_to_hw(struct wfx_dev *wdev, u32 rates) +{ + int i; + u32 ret = 0; + // WFx only support 2GHz + struct ieee80211_supported_band *sband = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; + + for (i = 0; i < sband->n_bitrates; i++) { + if (rates & BIT(i)) { + if (i >= sband->n_bitrates) + dev_warn(wdev->dev, "unsupported basic rate\n"); + else + ret |= BIT(sband->bitrates[i].hw_value); + } + } + return ret; +} + +static void __wfx_free_event_queue(struct list_head *list) +{ + struct wfx_hif_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, list, link) { + list_del(&event->link); + kfree(event); + } +} + +static void wfx_free_event_queue(struct wfx_vif *wvif) +{ + LIST_HEAD(list); + + spin_lock(&wvif->event_queue_lock); + list_splice_init(&wvif->event_queue, &list); + spin_unlock(&wvif->event_queue_lock); + + __wfx_free_event_queue(&list); +} + +void wfx_cqm_bssloss_sm(struct wfx_vif *wvif, int init, int good, int bad) +{ + int tx = 0; + + mutex_lock(&wvif->bss_loss_lock); + wvif->delayed_link_loss = 0; + cancel_work_sync(&wvif->bss_params_work); + + /* If we have a pending unjoin */ + if (wvif->delayed_unjoin) + goto end; + + if (init) { + schedule_delayed_work(&wvif->bss_loss_work, HZ); + wvif->bss_loss_state = 0; + + if (!atomic_read(&wvif->wdev->tx_lock)) + tx = 1; + } else if (good) { + cancel_delayed_work_sync(&wvif->bss_loss_work); + wvif->bss_loss_state = 0; + schedule_work(&wvif->bss_params_work); + } else if (bad) { + /* FIXME Should we just keep going until we time out? */ + if (wvif->bss_loss_state < 3) + tx = 1; + } else { + cancel_delayed_work_sync(&wvif->bss_loss_work); + wvif->bss_loss_state = 0; + } + + /* Spit out a NULL packet to our AP if necessary */ + // FIXME: call ieee80211_beacon_loss/ieee80211_connection_loss instead + if (tx) { + struct sk_buff *skb; + + wvif->bss_loss_state++; + + skb = ieee80211_nullfunc_get(wvif->wdev->hw, wvif->vif, false); + if (!skb) + goto end; + memset(IEEE80211_SKB_CB(skb), 0, sizeof(*IEEE80211_SKB_CB(skb))); + IEEE80211_SKB_CB(skb)->control.vif = wvif->vif; + IEEE80211_SKB_CB(skb)->driver_rates[0].idx = 0; + IEEE80211_SKB_CB(skb)->driver_rates[0].count = 1; + IEEE80211_SKB_CB(skb)->driver_rates[1].idx = -1; + wfx_tx(wvif->wdev->hw, NULL, skb); + } +end: + mutex_unlock(&wvif->bss_loss_lock); +} + +static int wfx_set_uapsd_param(struct wfx_vif *wvif, + const struct wfx_edca_params *arg) +{ + int ret; + + /* Here's the mapping AC [queue, bit] + * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] + */ + + if (arg->uapsd_enable[IEEE80211_AC_VO]) + wvif->uapsd_info.trig_voice = 1; + else + wvif->uapsd_info.trig_voice = 0; + + if (arg->uapsd_enable[IEEE80211_AC_VI]) + wvif->uapsd_info.trig_video = 1; + else + wvif->uapsd_info.trig_video = 0; + + if (arg->uapsd_enable[IEEE80211_AC_BE]) + wvif->uapsd_info.trig_be = 1; + else + wvif->uapsd_info.trig_be = 0; + + if (arg->uapsd_enable[IEEE80211_AC_BK]) + wvif->uapsd_info.trig_bckgrnd = 1; + else + wvif->uapsd_info.trig_bckgrnd = 0; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 + */ + wvif->uapsd_info.min_auto_trigger_interval = 0; + wvif->uapsd_info.max_auto_trigger_interval = 0; + wvif->uapsd_info.auto_trigger_step = 0; + + ret = hif_set_uapsd_info(wvif, &wvif->uapsd_info); + return ret; +} + +int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable) +{ + wvif->fwd_probe_req = enable; + return hif_set_rx_filter(wvif, wvif->filter_bssid, + wvif->fwd_probe_req); +} + +static int wfx_set_mcast_filter(struct wfx_vif *wvif, + struct wfx_grp_addr_table *fp) +{ + int i, ret; + struct hif_mib_config_data_filter config = { }; + struct hif_mib_set_data_filtering filter_data = { }; + struct hif_mib_mac_addr_data_frame_condition filter_addr_val = { }; + struct hif_mib_uc_mc_bc_data_frame_condition filter_addr_type = { }; + + // Temporary workaround for filters + return hif_set_data_filtering(wvif, &filter_data); + + if (!fp->enable) { + filter_data.enable = 0; + return hif_set_data_filtering(wvif, &filter_data); + } + + // A1 Address match on list + for (i = 0; i < fp->num_addresses; i++) { + filter_addr_val.condition_idx = i; + filter_addr_val.address_type = HIF_MAC_ADDR_A1; + ether_addr_copy(filter_addr_val.mac_address, fp->address_list[i]); + ret = hif_set_mac_addr_condition(wvif, &filter_addr_val); + if (ret) + return ret; + config.mac_cond |= 1 << i; + } + + // Accept unicast and broadcast + filter_addr_type.condition_idx = 0; + filter_addr_type.param.bits.type_unicast = 1; + filter_addr_type.param.bits.type_broadcast = 1; + ret = hif_set_uc_mc_bc_condition(wvif, &filter_addr_type); + if (ret) + return ret; + + config.uc_mc_bc_cond = 1; + config.filter_idx = 0; // TODO #define MULTICAST_FILTERING 0 + config.enable = 1; + ret = hif_set_config_data_filter(wvif, &config); + if (ret) + return ret; + + // discard all data frames except match filter + filter_data.enable = 1; + filter_data.default_filter = 1; // discard all + ret = hif_set_data_filtering(wvif, &filter_data); + + return ret; +} + +void wfx_update_filtering(struct wfx_vif *wvif) +{ + int ret; + bool is_sta = wvif->vif && NL80211_IFTYPE_STATION == wvif->vif->type; + bool filter_bssid = wvif->filter_bssid; + bool fwd_probe_req = wvif->fwd_probe_req; + struct hif_mib_bcn_filter_enable bf_ctrl; + struct hif_ie_table_entry filter_ies[] = { + { + .ie_id = WLAN_EID_VENDOR_SPECIFIC, + .has_changed = 1, + .no_longer = 1, + .has_appeared = 1, + .oui = { 0x50, 0x6F, 0x9A }, + }, { + .ie_id = WLAN_EID_HT_OPERATION, + .has_changed = 1, + .no_longer = 1, + .has_appeared = 1, + }, { + .ie_id = WLAN_EID_ERP_INFO, + .has_changed = 1, + .no_longer = 1, + .has_appeared = 1, + } + }; + int n_filter_ies; + + if (wvif->state == WFX_STATE_PASSIVE) + return; + + if (wvif->disable_beacon_filter) { + bf_ctrl.enable = 0; + bf_ctrl.bcn_count = 1; + n_filter_ies = 0; + } else if (!is_sta) { + bf_ctrl.enable = HIF_BEACON_FILTER_ENABLE | HIF_BEACON_FILTER_AUTO_ERP; + bf_ctrl.bcn_count = 0; + n_filter_ies = 2; + } else { + bf_ctrl.enable = HIF_BEACON_FILTER_ENABLE; + bf_ctrl.bcn_count = 0; + n_filter_ies = 3; + } + + ret = hif_set_rx_filter(wvif, filter_bssid, fwd_probe_req); + if (!ret) + ret = hif_set_beacon_filter_table(wvif, n_filter_ies, filter_ies); + if (!ret) + ret = hif_beacon_filter_control(wvif, bf_ctrl.enable, bf_ctrl.bcn_count); + if (!ret) + ret = wfx_set_mcast_filter(wvif, &wvif->mcast_filter); + if (ret) + dev_err(wvif->wdev->dev, "update filtering failed: %d\n", ret); +} + +static void wfx_update_filtering_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, update_filtering_work); + + wfx_update_filtering(wvif); +} + +u64 wfx_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) +{ + int i; + struct netdev_hw_addr *ha; + struct wfx_vif *wvif = NULL; + struct wfx_dev *wdev = hw->priv; + int count = netdev_hw_addr_list_count(mc_list); + + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + memset(&wvif->mcast_filter, 0x00, sizeof(wvif->mcast_filter)); + if (!count || count > ARRAY_SIZE(wvif->mcast_filter.address_list)) + continue; + + i = 0; + netdev_hw_addr_list_for_each(ha, mc_list) { + ether_addr_copy(wvif->mcast_filter.address_list[i], ha->addr); + i++; + } + wvif->mcast_filter.enable = 1; + wvif->mcast_filter.num_addresses = count; + } + + return 0; +} + +void wfx_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 unused) +{ + struct wfx_vif *wvif = NULL; + struct wfx_dev *wdev = hw->priv; + + *total_flags &= FIF_OTHER_BSS | FIF_FCSFAIL | FIF_PROBE_REQ; + + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + down(&wvif->scan.lock); + wvif->filter_bssid = (*total_flags & (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 0 : 1; + wvif->disable_beacon_filter = !(*total_flags & FIF_PROBE_REQ); + wfx_fwd_probe_req(wvif, true); + wfx_update_filtering(wvif); + up(&wvif->scan.lock); + } +} + +int wfx_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + u16 old_uapsd_flags, new_uapsd_flags; + struct hif_req_edca_queue_params *edca; + + mutex_lock(&wdev->conf_mutex); + + if (queue < hw->queues) { + old_uapsd_flags = *((u16 *) &wvif->uapsd_info); + edca = &wvif->edca.params[queue]; + + wvif->edca.uapsd_enable[queue] = params->uapsd; + edca->aifsn = params->aifs; + edca->cw_min = params->cw_min; + edca->cw_max = params->cw_max; + edca->tx_op_limit = params->txop * TXOP_UNIT; + edca->allowed_medium_time = 0; + ret = hif_set_edca_queue_params(wvif, edca); + if (ret) { + ret = -EINVAL; + goto out; + } + + if (wvif->vif->type == NL80211_IFTYPE_STATION) { + ret = wfx_set_uapsd_param(wvif, &wvif->edca); + new_uapsd_flags = *((u16 *) &wvif->uapsd_info); + if (!ret && wvif->setbssparams_done && + wvif->state == WFX_STATE_STA && + old_uapsd_flags != new_uapsd_flags) + ret = wfx_set_pm(wvif, &wvif->powersave_mode); + } + } else { + ret = -EINVAL; + } + +out: + mutex_unlock(&wdev->conf_mutex); + return ret; +} + +int wfx_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg) +{ + struct hif_req_set_pm_mode pm = *arg; + u16 uapsd_flags; + int ret; + + if (wvif->state != WFX_STATE_STA || !wvif->bss_params.aid) + return 0; + + memcpy(&uapsd_flags, &wvif->uapsd_info, sizeof(uapsd_flags)); + + if (uapsd_flags != 0) + pm.pm_mode.fast_psm = 0; + + // Kernel disable PowerSave when multiple vifs are in use. In contrary, + // it is absolutly necessary to enable PowerSave for WF200 + if (wvif_count(wvif->wdev) > 1) { + pm.pm_mode.enter_psm = 1; + pm.pm_mode.fast_psm = 0; + } + + if (!wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300))) + dev_warn(wvif->wdev->dev, "timeout while waiting of set_pm_mode_complete\n"); + ret = hif_set_pm(wvif, &pm); + // FIXME: why ? + if (-ETIMEDOUT == wvif->scan.status) + wvif->scan.status = 1; + return ret; +} + +int wfx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = NULL; + + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) + hif_rts_threshold(wvif, value); + return 0; +} + +/* If successful, LOCKS the TX queue! */ +static int __wfx_flush(struct wfx_dev *wdev, bool drop) +{ + int ret; + + for (;;) { + if (drop) { + wfx_tx_queues_clear(wdev); + } else { + ret = wait_event_timeout( + wdev->tx_queue_stats.wait_link_id_empty, + wfx_tx_queues_is_empty(wdev), + 2 * HZ); + } + + if (!drop && ret <= 0) { + ret = -ETIMEDOUT; + break; + } + ret = 0; + + wfx_tx_lock_flush(wdev); + if (!wfx_tx_queues_is_empty(wdev)) { + /* Highly unlikely: WSM requeued frames. */ + wfx_tx_unlock(wdev); + continue; + } + break; + } + return ret; +} + +void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif; + + if (vif) { + wvif = (struct wfx_vif *) vif->drv_priv; + if (wvif->vif->type == NL80211_IFTYPE_MONITOR) + drop = true; + if (wvif->vif->type == NL80211_IFTYPE_AP && !wvif->enable_beacon) + drop = true; + } + + // FIXME: only flush requested vif + if (!__wfx_flush(wdev, drop)) + wfx_tx_unlock(wdev); +} + +/* WSM callbacks */ + +static void wfx_event_report_rssi(struct wfx_vif *wvif, uint8_t raw_rcpi_rssi) +{ + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + int rcpi_rssi; + int cqm_evt; + + rcpi_rssi = raw_rcpi_rssi / 2 - 110; + if (rcpi_rssi <= wvif->cqm_rssi_thold) + cqm_evt = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + else + cqm_evt = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + ieee80211_cqm_rssi_notify(wvif->vif, cqm_evt, rcpi_rssi, GFP_KERNEL); +} + +static void wfx_event_handler_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, event_handler_work); + struct wfx_hif_event *event; + + LIST_HEAD(list); + + spin_lock(&wvif->event_queue_lock); + list_splice_init(&wvif->event_queue, &list); + spin_unlock(&wvif->event_queue_lock); + + list_for_each_entry(event, &list, link) { + switch (event->evt.event_id) { + case HIF_EVENT_IND_BSSLOST: + cancel_work_sync(&wvif->unjoin_work); + if (!down_trylock(&wvif->scan.lock)) { + wfx_cqm_bssloss_sm(wvif, 1, 0, 0); + up(&wvif->scan.lock); + } else { + /* Scan is in progress. Delay reporting. + * Scan complete will trigger bss_loss_work + */ + wvif->delayed_link_loss = 1; + /* Also start a watchdog. */ + schedule_delayed_work(&wvif->bss_loss_work, 5 * HZ); + } + break; + case HIF_EVENT_IND_BSSREGAINED: + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + cancel_work_sync(&wvif->unjoin_work); + break; + case HIF_EVENT_IND_RCPI_RSSI: + wfx_event_report_rssi(wvif, event->evt.event_data.rcpi_rssi); + break; + case HIF_EVENT_IND_PS_MODE_ERROR: + dev_warn(wvif->wdev->dev, "error while processing power save request\n"); + break; + default: + dev_warn(wvif->wdev->dev, "unhandled event indication: %.2x\n", event->evt.event_id); + break; + } + } + __wfx_free_event_queue(&list); +} + +static void wfx_bss_loss_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, bss_loss_work.work); + + ieee80211_connection_loss(wvif->vif); +} + +static void wfx_bss_params_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, bss_params_work); + + mutex_lock(&wvif->wdev->conf_mutex); + wvif->bss_params.bss_flags.lost_count_only = 1; + hif_set_bss_params(wvif, &wvif->bss_params); + wvif->bss_params.bss_flags.lost_count_only = 0; + mutex_unlock(&wvif->wdev->conf_mutex); +} + +static void wfx_set_beacon_wakeup_period_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, set_beacon_wakeup_period_work); + + hif_set_beacon_wakeup_period(wvif, wvif->dtim_period, wvif->dtim_period); +} + +static void wfx_do_unjoin(struct wfx_vif *wvif) +{ + mutex_lock(&wvif->wdev->conf_mutex); + + if (atomic_read(&wvif->scan.in_progress)) { + if (wvif->delayed_unjoin) + dev_dbg(wvif->wdev->dev, "delayed unjoin is already scheduled\n"); + else + wvif->delayed_unjoin = true; + goto done; + } + + wvif->delayed_link_loss = false; + + if (!wvif->state) + goto done; + + if (wvif->state == WFX_STATE_AP) + goto done; + + cancel_work_sync(&wvif->update_filtering_work); + cancel_work_sync(&wvif->set_beacon_wakeup_period_work); + wvif->state = WFX_STATE_PASSIVE; + + /* Unjoin is a reset. */ + wfx_tx_flush(wvif->wdev); + hif_keep_alive_period(wvif, 0); + hif_reset(wvif, false); + hif_set_output_power(wvif, wvif->wdev->output_power * 10); + wvif->dtim_period = 0; + hif_set_macaddr(wvif, wvif->vif->addr); + wfx_free_event_queue(wvif); + cancel_work_sync(&wvif->event_handler_work); + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + + /* Disable Block ACKs */ + hif_set_block_ack_policy(wvif, 0, 0); + + wvif->disable_beacon_filter = false; + wfx_update_filtering(wvif); + memset(&wvif->bss_params, 0, sizeof(wvif->bss_params)); + wvif->setbssparams_done = false; + memset(&wvif->ht_info, 0, sizeof(wvif->ht_info)); + +done: + mutex_unlock(&wvif->wdev->conf_mutex); +} + +static void wfx_set_mfp(struct wfx_vif *wvif, struct cfg80211_bss *bss) +{ + const int pairwise_cipher_suite_count_offset = 8 / sizeof(uint16_t); + const int pairwise_cipher_suite_size = 4 / sizeof(uint16_t); + const int akm_suite_size = 4 / sizeof(uint16_t); + const uint16_t *ptr = NULL; + bool mfpc = false; + bool mfpr = false; + + /* 802.11w protected mgmt frames */ + + /* retrieve MFPC and MFPR flags from beacon or PBRSP */ + + rcu_read_lock(); + if (bss) + ptr = (const uint16_t *) ieee80211_bss_get_ie(bss, WLAN_EID_RSN); + + if (ptr) { + ptr += pairwise_cipher_suite_count_offset; + ptr += 1 + pairwise_cipher_suite_size * *ptr; + ptr += 1 + akm_suite_size * *ptr; + mfpr = *ptr & BIT(6); + mfpc = *ptr & BIT(7); + } + rcu_read_unlock(); + + hif_set_mfp(wvif, mfpc, mfpr); +} + +/* MUST be called with tx_lock held! It will be unlocked for us. */ +static void wfx_do_join(struct wfx_vif *wvif) +{ + const u8 *bssid; + struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; + struct cfg80211_bss *bss = NULL; + struct hif_req_join join = { + .mode = conf->ibss_joined ? HIF_MODE_IBSS : HIF_MODE_BSS, + .preamble_type = conf->use_short_preamble ? HIF_PREAMBLE_SHORT : HIF_PREAMBLE_LONG, + .probe_for_join = 1, + .atim_window = 0, + .basic_rate_set = wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates), + }; + + if (wvif->channel->flags & IEEE80211_CHAN_NO_IR) + join.probe_for_join = 0; + + if (wvif->state) + wfx_do_unjoin(wvif); + + bssid = wvif->vif->bss_conf.bssid; + + bss = cfg80211_get_bss(wvif->wdev->hw->wiphy, wvif->channel, bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + + if (!bss && !conf->ibss_joined) { + wfx_tx_unlock(wvif->wdev); + return; + } + + mutex_lock(&wvif->wdev->conf_mutex); + + /* Under the conf lock: check scan status and + * bail out if it is in progress. + */ + if (atomic_read(&wvif->scan.in_progress)) { + wfx_tx_unlock(wvif->wdev); + goto done_put; + } + + /* Sanity check basic rates */ + if (!join.basic_rate_set) + join.basic_rate_set = 7; + + /* Sanity check beacon interval */ + if (!wvif->beacon_int) + wvif->beacon_int = 1; + + join.beacon_interval = wvif->beacon_int; + + // DTIM period will be set on first Beacon + wvif->dtim_period = 0; + + join.channel_number = wvif->channel->hw_value; + memcpy(join.bssid, bssid, sizeof(join.bssid)); + + if (!conf->ibss_joined) { + const u8 *ssidie; + + rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssidie) { + join.ssid_length = ssidie[1]; + memcpy(join.ssid, &ssidie[2], join.ssid_length); + } + rcu_read_unlock(); + } + + wfx_tx_flush(wvif->wdev); + + if (wvif_count(wvif->wdev) <= 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + + wfx_set_mfp(wvif, bss); + + /* Perform actual join */ + wvif->wdev->tx_burst_idx = -1; + if (hif_join(wvif, &join)) { + ieee80211_connection_loss(wvif->vif); + wvif->join_complete_status = -1; + /* Tx lock still held, unjoin will clear it. */ + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wvif->wdev); + } else { + wvif->join_complete_status = 0; + if (wvif->vif->type == NL80211_IFTYPE_ADHOC) + wvif->state = WFX_STATE_IBSS; + else + wvif->state = WFX_STATE_PRE_STA; + wfx_tx_unlock(wvif->wdev); + + /* Upload keys */ + wfx_upload_keys(wvif); + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one + */ + wvif->disable_beacon_filter = true; + } + wfx_update_filtering(wvif); + +done_put: + mutex_unlock(&wvif->wdev->conf_mutex); + if (bss) + cfg80211_put_bss(wvif->wdev->hw->wiphy, bss); +} + +static void wfx_unjoin_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, unjoin_work); + + wfx_do_unjoin(wvif); + wfx_tx_unlock(wvif->wdev); +} + +int wfx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; + struct wfx_link_entry *entry; + struct sk_buff *skb; + + if (wvif->vif->type != NL80211_IFTYPE_AP) + return 0; + + sta_priv->vif_id = wvif->id; + sta_priv->link_id = wfx_find_link_id(wvif, sta->addr); + if (!sta_priv->link_id) { + dev_warn(wdev->dev, "mo more link-id available\n"); + return -ENOENT; + } + + entry = &wvif->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&wvif->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + wvif->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = WFX_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(wdev->hw, skb); + spin_unlock_bh(&wvif->ps_state_lock); + return 0; +} + +int wfx_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; + struct wfx_link_entry *entry; + + if (wvif->vif->type != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &wvif->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&wvif->ps_state_lock); + entry->status = WFX_LINK_RESERVE; + entry->timestamp = jiffies; + wfx_tx_lock(wdev); + if (!schedule_work(&wvif->link_id_work)) + wfx_tx_unlock(wdev); + spin_unlock_bh(&wvif->ps_state_lock); + flush_work(&wvif->link_id_work); + return 0; +} + +static void wfx_set_cts_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, set_cts_work); + u8 erp_ie[3] = { WLAN_EID_ERP_INFO, 1, 0 }; + struct hif_ie_flags target_frame = { + .beacon = 1, + }; + + mutex_lock(&wvif->wdev->conf_mutex); + erp_ie[2] = wvif->erp_info; + mutex_unlock(&wvif->wdev->conf_mutex); + + hif_erp_use_protection(wvif, erp_ie[2] & WLAN_ERP_USE_PROTECTION); + + if (wvif->vif->type != NL80211_IFTYPE_STATION) + hif_update_ie(wvif, &target_frame, erp_ie, sizeof(erp_ie)); +} + +static int wfx_start_ap(struct wfx_vif *wvif) +{ + int ret; + struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; + struct hif_req_start start = { + .channel_number = wvif->channel->hw_value, + .beacon_interval = conf->beacon_int, + .dtim_period = conf->dtim_period, + .preamble_type = conf->use_short_preamble ? HIF_PREAMBLE_SHORT : HIF_PREAMBLE_LONG, + .basic_rate_set = wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates), + }; + + memset(start.ssid, 0, sizeof(start.ssid)); + if (!conf->hidden_ssid) { + start.ssid_length = conf->ssid_len; + memcpy(start.ssid, conf->ssid, start.ssid_length); + } + + wvif->beacon_int = conf->beacon_int; + wvif->dtim_period = conf->dtim_period; + + memset(&wvif->link_id_db, 0, sizeof(wvif->link_id_db)); + + wvif->wdev->tx_burst_idx = -1; + ret = hif_start(wvif, &start); + if (!ret) + ret = wfx_upload_keys(wvif); + if (!ret) { + if (wvif_count(wvif->wdev) <= 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + wvif->state = WFX_STATE_AP; + wfx_update_filtering(wvif); + } + return ret; +} + +static int wfx_update_beaconing(struct wfx_vif *wvif) +{ + struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; + + if (wvif->vif->type == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (wvif->state != WFX_STATE_AP || + wvif->beacon_int != conf->beacon_int) { + wfx_tx_lock_flush(wvif->wdev); + if (wvif->state != WFX_STATE_PASSIVE) + hif_reset(wvif, false); + wvif->state = WFX_STATE_PASSIVE; + wfx_start_ap(wvif); + wfx_tx_unlock(wvif->wdev); + } else { + } + } + return 0; +} + +static int wfx_upload_beacon(struct wfx_vif *wvif) +{ + int ret = 0; + struct sk_buff *skb = NULL; + struct ieee80211_mgmt *mgmt; + struct hif_mib_template_frame *p; + + if (wvif->vif->type == NL80211_IFTYPE_STATION || + wvif->vif->type == NL80211_IFTYPE_MONITOR || + wvif->vif->type == NL80211_IFTYPE_UNSPECIFIED) + goto done; + + skb = ieee80211_beacon_get(wvif->wdev->hw, wvif->vif); + + if (!skb) + return -ENOMEM; + + p = (struct hif_mib_template_frame *) skb_push(skb, 4); + p->frame_type = HIF_TMPLT_BCN; + p->init_rate = API_RATE_INDEX_B_1MBPS; /* 1Mbps DSSS */ + p->frame_length = cpu_to_le16(skb->len - 4); + + ret = hif_set_template_frame(wvif, p); + + skb_pull(skb, 4); + + if (ret) + goto done; + /* TODO: Distill probe resp; remove TIM and any other beacon-specific + * IEs + */ + mgmt = (void *)skb->data; + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); + + p->frame_type = HIF_TMPLT_PRBRES; + + ret = hif_set_template_frame(wvif, p); + wfx_fwd_probe_req(wvif, false); + +done: + if (!skb) + dev_kfree_skb(skb); + return ret; +} + +static int wfx_is_ht(const struct wfx_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static int wfx_ht_greenfield(const struct wfx_ht_info *ht_info) +{ + return wfx_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static int wfx_ht_ampdu_density(const struct wfx_ht_info *ht_info) +{ + if (!wfx_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +static void wfx_join_finalize(struct wfx_vif *wvif, struct ieee80211_bss_conf *info) +{ + struct ieee80211_sta *sta = NULL; + struct hif_mib_set_association_mode association_mode = { }; + + if (info->dtim_period) + wvif->dtim_period = info->dtim_period; + wvif->beacon_int = info->beacon_int; + + rcu_read_lock(); + if (info->bssid && !info->ibss_joined) + sta = ieee80211_find_sta(wvif->vif, info->bssid); + if (sta) { + wvif->ht_info.ht_cap = sta->ht_cap; + wvif->bss_params.operational_rate_set = + wfx_rate_mask_to_hw(wvif->wdev, sta->supp_rates[wvif->channel->band]); + wvif->ht_info.operation_mode = info->ht_operation_mode; + } else { + memset(&wvif->ht_info, 0, sizeof(wvif->ht_info)); + wvif->bss_params.operational_rate_set = -1; + } + rcu_read_unlock(); + + /* Non Greenfield stations present */ + if (wvif->ht_info.operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + hif_dual_cts_protection(wvif, true); + else + hif_dual_cts_protection(wvif, false); + + association_mode.preambtype_use = 1; + association_mode.mode = 1; + association_mode.rateset = 1; + association_mode.spacing = 1; + association_mode.preamble_type = info->use_short_preamble ? HIF_PREAMBLE_SHORT : HIF_PREAMBLE_LONG; + association_mode.basic_rate_set = cpu_to_le32(wfx_rate_mask_to_hw(wvif->wdev, info->basic_rates)); + association_mode.mixed_or_greenfield_type = wfx_ht_greenfield(&wvif->ht_info); + association_mode.mpdu_start_spacing = wfx_ht_ampdu_density(&wvif->ht_info); + + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + cancel_work_sync(&wvif->unjoin_work); + + wvif->bss_params.beacon_lost_count = 20; + wvif->bss_params.aid = info->aid; + + if (wvif->dtim_period < 1) + wvif->dtim_period = 1; + + hif_set_association_mode(wvif, &association_mode); + + if (!info->ibss_joined) { + hif_keep_alive_period(wvif, 30 /* sec */); + hif_set_bss_params(wvif, &wvif->bss_params); + wvif->setbssparams_done = true; + wfx_set_beacon_wakeup_period_work(&wvif->set_beacon_wakeup_period_work); + wfx_set_pm(wvif, &wvif->powersave_mode); + } +} + +void wfx_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + bool do_join = false; + int i; + int nb_arp_addr; + + mutex_lock(&wdev->conf_mutex); + + /* TODO: BSS_CHANGED_QOS */ + if (changed & BSS_CHANGED_ARP_FILTER) { + struct hif_mib_arp_ip_addr_table filter = { }; + + nb_arp_addr = info->arp_addr_cnt; + if (nb_arp_addr <= 0 || nb_arp_addr > HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES) + nb_arp_addr = 0; + + for (i = 0; i < HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES; i++) { + filter.condition_idx = i; + if (i < nb_arp_addr) { + // Caution: type of arp_addr_list[i] is __be32 + memcpy(filter.ipv4_address, &info->arp_addr_list[i], sizeof(filter.ipv4_address)); + filter.arp_enable = HIF_ARP_NS_FILTERING_ENABLE; + } else { + filter.arp_enable = HIF_ARP_NS_FILTERING_DISABLE; + } + hif_set_arp_ipv4_filter(wvif, &filter); + } + } + + if (changed & + (BSS_CHANGED_BEACON | BSS_CHANGED_AP_PROBE_RESP | + BSS_CHANGED_BSSID | BSS_CHANGED_SSID | BSS_CHANGED_IBSS)) { + wvif->beacon_int = info->beacon_int; + wfx_update_beaconing(wvif); + wfx_upload_beacon(wvif); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED && wvif->state != WFX_STATE_IBSS) { + if (wvif->enable_beacon != info->enable_beacon) { + hif_beacon_transmit(wvif, info->enable_beacon); + wvif->enable_beacon = info->enable_beacon; + } + } + + /* assoc/disassoc, or maybe AID changed */ + if (changed & BSS_CHANGED_ASSOC) { + wfx_tx_lock_flush(wdev); + wvif->wep_default_key_id = -1; + wfx_tx_unlock(wdev); + } + + if (changed & BSS_CHANGED_ASSOC && !info->assoc && + (wvif->state == WFX_STATE_STA || wvif->state == WFX_STATE_IBSS)) { + /* Shedule unjoin work */ + wfx_tx_lock(wdev); + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wdev); + } else { + if (changed & BSS_CHANGED_BEACON_INT) { + if (info->ibss_joined) + do_join = true; + else if (wvif->state == WFX_STATE_AP) + wfx_update_beaconing(wvif); + } + + if (changed & BSS_CHANGED_BSSID) + do_join = true; + + if (changed & + (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID | + BSS_CHANGED_IBSS | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_HT)) { + if (info->assoc) { + if (wvif->state < WFX_STATE_PRE_STA) { + ieee80211_connection_loss(vif); + mutex_unlock(&wdev->conf_mutex); + return; + } else if (wvif->state == WFX_STATE_PRE_STA) { + wvif->state = WFX_STATE_STA; + } + } else { + do_join = true; + } + + if (info->assoc || info->ibss_joined) + wfx_join_finalize(wvif, info); + else + memset(&wvif->bss_params, 0, sizeof(wvif->bss_params)); + } + } + + /* ERP Protection */ + if (changed & (BSS_CHANGED_ASSOC | + BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_ERP_PREAMBLE)) { + u32 prev_erp_info = wvif->erp_info; + + if (info->use_cts_prot) + wvif->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + wvif->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (info->use_short_preamble) + wvif->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + wvif->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + if (prev_erp_info != wvif->erp_info) + schedule_work(&wvif->set_cts_work); + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) + hif_slot_time(wvif, info->use_short_slot ? 9 : 20); + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct hif_mib_rcpi_rssi_threshold th = { + .rolling_average_count = 8, + .detection = 1, + }; + + wvif->cqm_rssi_thold = info->cqm_rssi_thold; + + if (!info->cqm_rssi_thold && !info->cqm_rssi_hyst) { + th.upperthresh = 1; + th.lowerthresh = 1; + } else { + /* FIXME It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more reliable and stable. + */ + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + th.upper_threshold = info->cqm_rssi_thold + info->cqm_rssi_hyst; + th.upper_threshold = (th.upper_threshold + 110) * 2; + th.lower_threshold = info->cqm_rssi_thold; + th.lower_threshold = (th.lower_threshold + 110) * 2; + } + hif_set_rcpi_rssi_threshold(wvif, &th); + } + + if (changed & BSS_CHANGED_TXPOWER && info->txpower != wdev->output_power) { + wdev->output_power = info->txpower; + hif_set_output_power(wvif, wdev->output_power * 10); + } + mutex_unlock(&wdev->conf_mutex); + + if (do_join) { + wfx_tx_lock_flush(wdev); + wfx_do_join(wvif); /* Will unlock it for us */ + } +} + +static void wfx_ps_notify(struct wfx_vif *wvif, enum sta_notify_cmd notify_cmd, + int link_id) +{ + u32 bit, prev; + + spin_lock_bh(&wvif->ps_state_lock); + /* Zero link id means "for all link IDs" */ + if (link_id) { + bit = BIT(link_id); + } else if (notify_cmd != STA_NOTIFY_AWAKE) { + dev_warn(wvif->wdev->dev, "unsupported notify command\n"); + bit = 0; + } else { + bit = wvif->link_id_map; + } + prev = wvif->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (wvif->mcast_buffered && !wvif->sta_asleep_mask) + schedule_work(&wvif->mcast_start_work); + wvif->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + wvif->sta_asleep_mask &= ~bit; + wvif->pspoll_mask &= ~bit; + if (link_id && !wvif->sta_asleep_mask) + schedule_work(&wvif->mcast_stop_work); + wfx_bh_request_tx(wvif->wdev); + } + break; + } + spin_unlock_bh(&wvif->ps_state_lock); +} + +void wfx_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) +{ + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; + + wfx_ps_notify(wvif, notify_cmd, sta_priv->link_id); +} + +static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct hif_ie_flags target_frame = { + .beacon = 1, + }; + u16 tim_offset, tim_length; + u8 *tim_ptr; + + skb = ieee80211_beacon_get_tim(wvif->wdev->hw, wvif->vif, + &tim_offset, &tim_length); + if (!skb) { + if (!__wfx_flush(wvif->wdev, true)) + wfx_tx_unlock(wvif->wdev); + return -ENOENT; + } + tim_ptr = skb->data + tim_offset; + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. + */ + tim_ptr[2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + tim_ptr[4] |= 1; + else + tim_ptr[4] &= ~1; + } + + hif_update_ie(wvif, &target_frame, tim_ptr, tim_length); + dev_kfree_skb(skb); + + return 0; +} + +static void wfx_set_tim_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, set_tim_work); + + wfx_set_tim_impl(wvif, wvif->aid0_bit_set); +} + +int wfx_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_sta_priv *sta_dev = (struct wfx_sta_priv *) &sta->drv_priv; + struct wfx_vif *wvif = wdev_to_wvif(wdev, sta_dev->vif_id); + + schedule_work(&wvif->set_tim_work); + return 0; +} + +static void wfx_mcast_start_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_start_work); + long tmo = wvif->dtim_period * TU_TO_JIFFIES(wvif->beacon_int + 20); + + cancel_work_sync(&wvif->mcast_stop_work); + if (!wvif->aid0_bit_set) { + wfx_tx_lock_flush(wvif->wdev); + wfx_set_tim_impl(wvif, true); + wvif->aid0_bit_set = true; + mod_timer(&wvif->mcast_timeout, jiffies + tmo); + wfx_tx_unlock(wvif->wdev); + } +} + +static void wfx_mcast_stop_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_stop_work); + + if (wvif->aid0_bit_set) { + del_timer_sync(&wvif->mcast_timeout); + wfx_tx_lock_flush(wvif->wdev); + wvif->aid0_bit_set = false; + wfx_set_tim_impl(wvif, false); + wfx_tx_unlock(wvif->wdev); + } +} + +static void wfx_mcast_timeout(struct timer_list *t) +{ + struct wfx_vif *wvif = from_timer(wvif, t, mcast_timeout); + + dev_warn(wvif->wdev->dev, "multicast delivery timeout\n"); + spin_lock_bh(&wvif->ps_state_lock); + wvif->mcast_tx = wvif->aid0_bit_set && wvif->mcast_buffered; + if (wvif->mcast_tx) + wfx_bh_request_tx(wvif->wdev); + spin_unlock_bh(&wvif->ps_state_lock); +} + +int wfx_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. + */ + + /* Note that we still need this function stubbed. */ + + return -ENOTSUPP; +} + +void wfx_suspend_resume(struct wfx_vif *wvif, + struct hif_ind_suspend_resume_tx *arg) +{ + if (arg->suspend_resume_flags.bc_mc_only) { + bool cancel_tmo = false; + + spin_lock_bh(&wvif->ps_state_lock); + if (!arg->suspend_resume_flags.resume) + wvif->mcast_tx = false; + else + wvif->mcast_tx = wvif->aid0_bit_set && wvif->mcast_buffered; + if (wvif->mcast_tx) { + cancel_tmo = true; + wfx_bh_request_tx(wvif->wdev); + } + spin_unlock_bh(&wvif->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&wvif->mcast_timeout); + } else if (arg->suspend_resume_flags.resume) { + // FIXME: should change each station status independently + wfx_ps_notify(wvif, STA_NOTIFY_AWAKE, 0); + wfx_bh_request_tx(wvif->wdev); + } else { + // FIXME: should change each station status independently + wfx_ps_notify(wvif, STA_NOTIFY_SLEEP, 0); + } +} + +int wfx_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + return 0; +} + +void wfx_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ +} + +void wfx_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + u32 changed) +{ +} + +int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct ieee80211_channel *ch = conf->def.chan; + + WARN(wvif->channel, "channel overwrite"); + wvif->channel = ch; + wvif->ht_info.channel_type = cfg80211_get_chandef_type(&conf->def); + + return 0; +} + +void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct ieee80211_channel *ch = conf->def.chan; + + WARN(wvif->channel != ch, "channel mismatch"); + wvif->channel = NULL; +} + +int wfx_config(struct ieee80211_hw *hw, u32 changed) +{ + int ret = 0; + struct wfx_dev *wdev = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + struct wfx_vif *wvif; + + // FIXME: Interface id should not been hardcoded + wvif = wdev_to_wvif(wdev, 0); + if (!wvif) { + WARN(1, "interface 0 does not exist anymore"); + return 0; + } + + down(&wvif->scan.lock); + mutex_lock(&wdev->conf_mutex); + if (changed & IEEE80211_CONF_CHANGE_POWER) { + wdev->output_power = conf->power_level; + hif_set_output_power(wvif, wdev->output_power * 10); + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + memset(&wvif->powersave_mode, 0, sizeof(wvif->powersave_mode)); + if (conf->flags & IEEE80211_CONF_PS) { + wvif->powersave_mode.pm_mode.enter_psm = 1; + if (conf->dynamic_ps_timeout > 0) { + wvif->powersave_mode.pm_mode.fast_psm = 1; + // Firmware does not support more than 128ms + wvif->powersave_mode.fast_psm_idle_period = + min(conf->dynamic_ps_timeout * 2, 255); + } + } + if (wvif->state == WFX_STATE_STA && wvif->bss_params.aid) + wfx_set_pm(wvif, &wvif->powersave_mode); + } + wvif = wdev_to_wvif(wdev, 0); + } + + mutex_unlock(&wdev->conf_mutex); + up(&wvif->scan.lock); + return ret; +} + +int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + int i; + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + // FIXME: parameters are set by kernel juste after interface_add. + // Keep struct hif_req_edca_queue_params blank? + struct hif_req_edca_queue_params default_edca_params[] = { + [IEEE80211_AC_VO] = { + .queue_id = HIF_QUEUE_ID_VOICE, + .aifsn = 2, + .cw_min = 3, + .cw_max = 7, + .tx_op_limit = TXOP_UNIT * 47, + }, + [IEEE80211_AC_VI] = { + .queue_id = HIF_QUEUE_ID_VIDEO, + .aifsn = 2, + .cw_min = 7, + .cw_max = 15, + .tx_op_limit = TXOP_UNIT * 94, + }, + [IEEE80211_AC_BE] = { + .queue_id = HIF_QUEUE_ID_BESTEFFORT, + .aifsn = 3, + .cw_min = 15, + .cw_max = 1023, + .tx_op_limit = TXOP_UNIT * 0, + }, + [IEEE80211_AC_BK] = { + .queue_id = HIF_QUEUE_ID_BACKGROUND, + .aifsn = 7, + .cw_min = 15, + .cw_max = 1023, + .tx_op_limit = TXOP_UNIT * 0, + }, + }; + + BUILD_BUG_ON(ARRAY_SIZE(default_edca_params) != ARRAY_SIZE(wvif->edca.params)); + if (wfx_api_older_than(wdev, 2, 0)) { + default_edca_params[IEEE80211_AC_BE].queue_id = HIF_QUEUE_ID_BACKGROUND; + default_edca_params[IEEE80211_AC_BK].queue_id = HIF_QUEUE_ID_BESTEFFORT; + } + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + + mutex_lock(&wdev->conf_mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + break; + default: + mutex_unlock(&wdev->conf_mutex); + return -EOPNOTSUPP; + } + + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { + if (!wdev->vif[i]) { + wdev->vif[i] = vif; + wvif->id = i; + break; + } + } + if (i == ARRAY_SIZE(wdev->vif)) { + mutex_unlock(&wdev->conf_mutex); + return -EOPNOTSUPP; + } + // FIXME: prefer use of container_of() to get vif + wvif->vif = vif; + wvif->wdev = wdev; + + INIT_WORK(&wvif->link_id_work, wfx_link_id_work); + INIT_DELAYED_WORK(&wvif->link_id_gc_work, wfx_link_id_gc_work); + + spin_lock_init(&wvif->ps_state_lock); + INIT_WORK(&wvif->set_tim_work, wfx_set_tim_work); + + INIT_WORK(&wvif->mcast_start_work, wfx_mcast_start_work); + INIT_WORK(&wvif->mcast_stop_work, wfx_mcast_stop_work); + timer_setup(&wvif->mcast_timeout, wfx_mcast_timeout, 0); + + wvif->setbssparams_done = false; + mutex_init(&wvif->bss_loss_lock); + INIT_DELAYED_WORK(&wvif->bss_loss_work, wfx_bss_loss_work); + + wvif->wep_default_key_id = -1; + INIT_WORK(&wvif->wep_key_work, wfx_wep_key_work); + + sema_init(&wvif->scan.lock, 1); + INIT_WORK(&wvif->scan.work, wfx_scan_work); + INIT_DELAYED_WORK(&wvif->scan.timeout, wfx_scan_timeout); + + spin_lock_init(&wvif->event_queue_lock); + INIT_LIST_HEAD(&wvif->event_queue); + INIT_WORK(&wvif->event_handler_work, wfx_event_handler_work); + + init_completion(&wvif->set_pm_mode_complete); + complete(&wvif->set_pm_mode_complete); + INIT_WORK(&wvif->set_beacon_wakeup_period_work, wfx_set_beacon_wakeup_period_work); + INIT_WORK(&wvif->update_filtering_work, wfx_update_filtering_work); + INIT_WORK(&wvif->bss_params_work, wfx_bss_params_work); + INIT_WORK(&wvif->set_cts_work, wfx_set_cts_work); + INIT_WORK(&wvif->unjoin_work, wfx_unjoin_work); + + mutex_unlock(&wdev->conf_mutex); + + hif_set_macaddr(wvif, vif->addr); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + memcpy(&wvif->edca.params[i], &default_edca_params[i], sizeof(default_edca_params[i])); + wvif->edca.uapsd_enable[i] = false; + hif_set_edca_queue_params(wvif, &wvif->edca.params[i]); + } + wfx_set_uapsd_param(wvif, &wvif->edca); + + wfx_tx_policy_init(wvif); + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + // Combo mode does not support Block Acks. We can re-enable them + if (wvif_count(wdev) == 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + else + hif_set_block_ack_policy(wvif, 0x00, 0x00); + // Combo force powersave mode. We can re-enable it now + wfx_set_pm(wvif, &wvif->powersave_mode); + } + return 0; +} + +void wfx_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + int i; + + // If scan is in progress, stop it + while (down_trylock(&wvif->scan.lock)) + schedule(); + up(&wvif->scan.lock); + wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300)); + + mutex_lock(&wdev->conf_mutex); + switch (wvif->state) { + case WFX_STATE_PRE_STA: + case WFX_STATE_STA: + case WFX_STATE_IBSS: + wfx_tx_lock_flush(wdev); + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wdev); + break; + case WFX_STATE_AP: + for (i = 0; wvif->link_id_map; ++i) { + if (wvif->link_id_map & BIT(i)) { + wfx_unmap_link(wvif, i); + wvif->link_id_map &= ~BIT(i); + } + } + memset(wvif->link_id_db, 0, sizeof(wvif->link_id_db)); + wvif->sta_asleep_mask = 0; + wvif->enable_beacon = false; + wvif->mcast_tx = false; + wvif->aid0_bit_set = false; + wvif->mcast_buffered = false; + wvif->pspoll_mask = 0; + /* reset.link_id = 0; */ + hif_reset(wvif, false); + break; + default: + break; + } + + wvif->state = WFX_STATE_PASSIVE; + wfx_tx_queues_wait_empty_vif(wvif); + wfx_tx_unlock(wdev); + + /* FIXME: In add to reset MAC address, try to reset interface */ + hif_set_macaddr(wvif, NULL); + + cancel_delayed_work_sync(&wvif->scan.timeout); + + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + cancel_work_sync(&wvif->unjoin_work); + cancel_delayed_work_sync(&wvif->link_id_gc_work); + del_timer_sync(&wvif->mcast_timeout); + wfx_free_event_queue(wvif); + + wdev->vif[wvif->id] = NULL; + wvif->vif = NULL; + + mutex_unlock(&wdev->conf_mutex); + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + // Combo mode does not support Block Acks. We can re-enable them + if (wvif_count(wdev) == 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + else + hif_set_block_ack_policy(wvif, 0x00, 0x00); + // Combo force powersave mode. We can re-enable it now + wfx_set_pm(wvif, &wvif->powersave_mode); + } +} + +int wfx_start(struct ieee80211_hw *hw) +{ + return 0; +} + +void wfx_stop(struct ieee80211_hw *hw) +{ + struct wfx_dev *wdev = hw->priv; + + wfx_tx_lock_flush(wdev); + mutex_lock(&wdev->conf_mutex); + wfx_tx_queues_clear(wdev); + mutex_unlock(&wdev->conf_mutex); + wfx_tx_unlock(wdev); + WARN(atomic_read(&wdev->tx_lock), "tx_lock is locked"); +} diff --git a/drivers/staging/wfx/sta.h b/drivers/staging/wfx/sta.h new file mode 100644 index 000000000000..307ed0196110 --- /dev/null +++ b/drivers/staging/wfx/sta.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of mac80211 API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_STA_H +#define WFX_STA_H + +#include <net/mac80211.h> + +#include "hif_api_cmd.h" + +struct wfx_dev; +struct wfx_vif; + +enum wfx_state { + WFX_STATE_PASSIVE = 0, + WFX_STATE_PRE_STA, + WFX_STATE_STA, + WFX_STATE_IBSS, + WFX_STATE_AP, +}; + +struct wfx_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + uint16_t operation_mode; +}; + +struct wfx_hif_event { + struct list_head link; + struct hif_ind_event evt; +}; + +struct wfx_edca_params { + /* NOTE: index is a linux queue id. */ + struct hif_req_edca_queue_params params[IEEE80211_NUM_ACS]; + bool uapsd_enable[IEEE80211_NUM_ACS]; +}; + +struct wfx_grp_addr_table { + bool enable; + int num_addresses; + u8 address_list[8][ETH_ALEN]; +}; + +struct wfx_sta_priv { + int link_id; + int vif_id; +}; + +// mac80211 interface +int wfx_start(struct ieee80211_hw *hw); +void wfx_stop(struct ieee80211_hw *hw); +int wfx_config(struct ieee80211_hw *hw, u32 changed); +int wfx_set_rts_threshold(struct ieee80211_hw *hw, u32 value); +u64 wfx_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); +void wfx_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 unused); + +int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); +int wfx_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +void wfx_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed); +int wfx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int wfx_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void wfx_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, struct ieee80211_sta *sta); +int wfx_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); +int wfx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params); +int wfx_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); +void wfx_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); +void wfx_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, u32 changed); +int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf); +void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf); + +// WSM Callbacks +void wfx_suspend_resume(struct wfx_vif *wvif, struct hif_ind_suspend_resume_tx *arg); + +// Other Helpers +void wfx_cqm_bssloss_sm(struct wfx_vif *wvif, int init, int good, int bad); +void wfx_update_filtering(struct wfx_vif *wvif); +int wfx_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg); +int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable); + +#endif /* WFX_STA_H */ diff --git a/drivers/staging/wfx/traces.h b/drivers/staging/wfx/traces.h new file mode 100644 index 000000000000..67457cda133b --- /dev/null +++ b/drivers/staging/wfx/traces.h @@ -0,0 +1,434 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Tracepoints definitions. + * + * Copyright (c) 2018-2019, Silicon Laboratories, Inc. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM wfx + +#if !defined(_WFX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _WFX_TRACE_H + +#include <linux/tracepoint.h> +#include <net/mac80211.h> + +#include "bus.h" +#include "hif_api_cmd.h" +#include "hif_api_mib.h" + +/* The hell below need some explanations. For each symbolic number, we need to + * define it with TRACE_DEFINE_ENUM() and in a list for __print_symbolic. + * + * 1. Define a new macro that call TRACE_DEFINE_ENUM(): + * + * #define xxx_name(sym) TRACE_DEFINE_ENUM(sym); + * + * 2. Define list of all symbols: + * + * #define list_names \ + * ... \ + * xxx_name(XXX) \ + * ... + * + * 3. Instanciate that list_names: + * + * list_names + * + * 4. Redefine xxx_name() as a entry of array for __print_symbolic() + * + * #undef xxx_name + * #define xxx_name(msg) { msg, #msg }, + * + * 5. list_name can now nearlu be used with __print_symbolic() but, + * __print_symbolic() dislike last comma of list. So we define a new list + * with a dummy element: + * + * #define list_for_print_symbolic list_names { -1, NULL } + */ + +#define _hif_msg_list \ + hif_cnf_name(ADD_KEY) \ + hif_cnf_name(BEACON_TRANSMIT) \ + hif_cnf_name(EDCA_QUEUE_PARAMS) \ + hif_cnf_name(JOIN) \ + hif_cnf_name(MAP_LINK) \ + hif_cnf_name(READ_MIB) \ + hif_cnf_name(REMOVE_KEY) \ + hif_cnf_name(RESET) \ + hif_cnf_name(SET_BSS_PARAMS) \ + hif_cnf_name(SET_PM_MODE) \ + hif_cnf_name(START) \ + hif_cnf_name(START_SCAN) \ + hif_cnf_name(STOP_SCAN) \ + hif_cnf_name(TX) \ + hif_cnf_name(MULTI_TRANSMIT) \ + hif_cnf_name(UPDATE_IE) \ + hif_cnf_name(WRITE_MIB) \ + hif_cnf_name(CONFIGURATION) \ + hif_cnf_name(CONTROL_GPIO) \ + hif_cnf_name(PREVENT_ROLLBACK) \ + hif_cnf_name(SET_SL_MAC_KEY) \ + hif_cnf_name(SL_CONFIGURE) \ + hif_cnf_name(SL_EXCHANGE_PUB_KEYS) \ + hif_cnf_name(SHUT_DOWN) \ + hif_ind_name(EVENT) \ + hif_ind_name(JOIN_COMPLETE) \ + hif_ind_name(RX) \ + hif_ind_name(SCAN_CMPL) \ + hif_ind_name(SET_PM_MODE_CMPL) \ + hif_ind_name(SUSPEND_RESUME_TX) \ + hif_ind_name(SL_EXCHANGE_PUB_KEYS) \ + hif_ind_name(ERROR) \ + hif_ind_name(EXCEPTION) \ + hif_ind_name(GENERIC) \ + hif_ind_name(WAKEUP) \ + hif_ind_name(STARTUP) + +#define hif_msg_list_enum _hif_msg_list + +#undef hif_cnf_name +#undef hif_ind_name +#define hif_cnf_name(msg) TRACE_DEFINE_ENUM(HIF_CNF_ID_##msg); +#define hif_ind_name(msg) TRACE_DEFINE_ENUM(HIF_IND_ID_##msg); +hif_msg_list_enum +#undef hif_cnf_name +#undef hif_ind_name +#define hif_cnf_name(msg) { HIF_CNF_ID_##msg, #msg }, +#define hif_ind_name(msg) { HIF_IND_ID_##msg, #msg }, +#define hif_msg_list hif_msg_list_enum { -1, NULL } + +#define _hif_mib_list \ + hif_mib_name(ARP_IP_ADDRESSES_TABLE) \ + hif_mib_name(ARP_KEEP_ALIVE_PERIOD) \ + hif_mib_name(BEACON_FILTER_ENABLE) \ + hif_mib_name(BEACON_FILTER_TABLE) \ + hif_mib_name(BEACON_WAKEUP_PERIOD) \ + hif_mib_name(BLOCK_ACK_POLICY) \ + hif_mib_name(CONFIG_DATA_FILTER) \ + hif_mib_name(COUNTERS_TABLE) \ + hif_mib_name(CURRENT_TX_POWER_LEVEL) \ + hif_mib_name(DOT11_MAC_ADDRESS) \ + hif_mib_name(DOT11_MAX_RECEIVE_LIFETIME) \ + hif_mib_name(DOT11_MAX_TRANSMIT_MSDU_LIFETIME) \ + hif_mib_name(DOT11_RTS_THRESHOLD) \ + hif_mib_name(DOT11_WEP_DEFAULT_KEY_ID) \ + hif_mib_name(GL_BLOCK_ACK_INFO) \ + hif_mib_name(GL_OPERATIONAL_POWER_MODE) \ + hif_mib_name(GL_SET_MULTI_MSG) \ + hif_mib_name(INACTIVITY_TIMER) \ + hif_mib_name(INTERFACE_PROTECTION) \ + hif_mib_name(IPV4_ADDR_DATAFRAME_CONDITION) \ + hif_mib_name(IPV6_ADDR_DATAFRAME_CONDITION) \ + hif_mib_name(KEEP_ALIVE_PERIOD) \ + hif_mib_name(MAC_ADDR_DATAFRAME_CONDITION) \ + hif_mib_name(NON_ERP_PROTECTION) \ + hif_mib_name(NS_IP_ADDRESSES_TABLE) \ + hif_mib_name(OVERRIDE_INTERNAL_TX_RATE) \ + hif_mib_name(PROTECTED_MGMT_POLICY) \ + hif_mib_name(RX_FILTER) \ + hif_mib_name(RCPI_RSSI_THRESHOLD) \ + hif_mib_name(SET_ASSOCIATION_MODE) \ + hif_mib_name(SET_DATA_FILTERING) \ + hif_mib_name(ETHERTYPE_DATAFRAME_CONDITION) \ + hif_mib_name(SET_HT_PROTECTION) \ + hif_mib_name(MAGIC_DATAFRAME_CONDITION) \ + hif_mib_name(SET_TX_RATE_RETRY_POLICY) \ + hif_mib_name(SET_UAPSD_INFORMATION) \ + hif_mib_name(PORT_DATAFRAME_CONDITION) \ + hif_mib_name(SLOT_TIME) \ + hif_mib_name(STATISTICS_TABLE) \ + hif_mib_name(TEMPLATE_FRAME) \ + hif_mib_name(TSF_COUNTER) \ + hif_mib_name(UC_MC_BC_DATAFRAME_CONDITION) + +#define hif_mib_list_enum _hif_mib_list + +#undef hif_mib_name +#define hif_mib_name(mib) TRACE_DEFINE_ENUM(HIF_MIB_ID_##mib); +hif_mib_list_enum +#undef hif_mib_name +#define hif_mib_name(mib) { HIF_MIB_ID_##mib, #mib }, +#define hif_mib_list hif_mib_list_enum { -1, NULL } + +DECLARE_EVENT_CLASS(hif_data, + TP_PROTO(struct hif_msg *hif, int tx_fill_level, bool is_recv), + TP_ARGS(hif, tx_fill_level, is_recv), + TP_STRUCT__entry( + __field(int, tx_fill_level) + __field(int, msg_id) + __field(const char *, msg_type) + __field(int, msg_len) + __field(int, buf_len) + __field(int, if_id) + __field(int, mib) + __array(u8, buf, 128) + ), + TP_fast_assign( + int header_len; + + __entry->tx_fill_level = tx_fill_level; + __entry->msg_len = hif->len; + __entry->msg_id = hif->id; + __entry->if_id = hif->interface; + if (is_recv) + __entry->msg_type = __entry->msg_id & 0x80 ? "IND" : "CNF"; + else + __entry->msg_type = "REQ"; + if (!is_recv && + (__entry->msg_id == HIF_REQ_ID_READ_MIB || __entry->msg_id == HIF_REQ_ID_WRITE_MIB)) { + __entry->mib = le16_to_cpup((u16 *) hif->body); + header_len = 4; + } else { + __entry->mib = -1; + header_len = 0; + } + __entry->buf_len = min_t(int, __entry->msg_len, sizeof(__entry->buf)) + - sizeof(struct hif_msg) - header_len; + memcpy(__entry->buf, hif->body + header_len, __entry->buf_len); + ), + TP_printk("%d:%d:%s_%s%s%s: %s%s (%d bytes)", + __entry->tx_fill_level, + __entry->if_id, + __print_symbolic(__entry->msg_id, hif_msg_list), + __entry->msg_type, + __entry->mib != -1 ? "/" : "", + __entry->mib != -1 ? __print_symbolic(__entry->mib, hif_mib_list) : "", + __print_hex(__entry->buf, __entry->buf_len), + __entry->msg_len > sizeof(__entry->buf) ? " ..." : "", + __entry->msg_len + ) +); +DEFINE_EVENT(hif_data, hif_send, + TP_PROTO(struct hif_msg *hif, int tx_fill_level, bool is_recv), + TP_ARGS(hif, tx_fill_level, is_recv)); +#define _trace_hif_send(hif, tx_fill_level) trace_hif_send(hif, tx_fill_level, false) +DEFINE_EVENT(hif_data, hif_recv, + TP_PROTO(struct hif_msg *hif, int tx_fill_level, bool is_recv), + TP_ARGS(hif, tx_fill_level, is_recv)); +#define _trace_hif_recv(hif, tx_fill_level) trace_hif_recv(hif, tx_fill_level, true) + +#define wfx_reg_list_enum \ + wfx_reg_name(WFX_REG_CONFIG, "CONFIG") \ + wfx_reg_name(WFX_REG_CONTROL, "CONTROL") \ + wfx_reg_name(WFX_REG_IN_OUT_QUEUE, "QUEUE") \ + wfx_reg_name(WFX_REG_AHB_DPORT, "AHB") \ + wfx_reg_name(WFX_REG_BASE_ADDR, "BASE_ADDR") \ + wfx_reg_name(WFX_REG_SRAM_DPORT, "SRAM") \ + wfx_reg_name(WFX_REG_SET_GEN_R_W, "SET_GEN_R_W") \ + wfx_reg_name(WFX_REG_FRAME_OUT, "FRAME_OUT") + +#undef wfx_reg_name +#define wfx_reg_name(sym, name) TRACE_DEFINE_ENUM(sym); +wfx_reg_list_enum +#undef wfx_reg_name +#define wfx_reg_name(sym, name) { sym, name }, +#define wfx_reg_list wfx_reg_list_enum { -1, NULL } + +DECLARE_EVENT_CLASS(io_data, + TP_PROTO(int reg, int addr, const void *io_buf, size_t len), + TP_ARGS(reg, addr, io_buf, len), + TP_STRUCT__entry( + __field(int, reg) + __field(int, addr) + __field(int, msg_len) + __field(int, buf_len) + __array(u8, buf, 32) + __array(u8, addr_str, 10) + ), + TP_fast_assign( + __entry->reg = reg; + __entry->addr = addr; + __entry->msg_len = len; + __entry->buf_len = min_t(int, sizeof(__entry->buf), __entry->msg_len); + memcpy(__entry->buf, io_buf, __entry->buf_len); + if (addr >= 0) + snprintf(__entry->addr_str, 10, "/%08x", addr); + else + __entry->addr_str[0] = 0; + ), + TP_printk("%s%s: %s%s (%d bytes)", + __print_symbolic(__entry->reg, wfx_reg_list), + __entry->addr_str, + __print_hex(__entry->buf, __entry->buf_len), + __entry->msg_len > sizeof(__entry->buf) ? " ..." : "", + __entry->msg_len + ) +); +DEFINE_EVENT(io_data, io_write, + TP_PROTO(int reg, int addr, const void *io_buf, size_t len), + TP_ARGS(reg, addr, io_buf, len)); +#define _trace_io_ind_write(reg, addr, io_buf, len) trace_io_write(reg, addr, io_buf, len) +#define _trace_io_write(reg, io_buf, len) trace_io_write(reg, -1, io_buf, len) +DEFINE_EVENT(io_data, io_read, + TP_PROTO(int reg, int addr, const void *io_buf, size_t len), + TP_ARGS(reg, addr, io_buf, len)); +#define _trace_io_ind_read(reg, addr, io_buf, len) trace_io_read(reg, addr, io_buf, len) +#define _trace_io_read(reg, io_buf, len) trace_io_read(reg, -1, io_buf, len) + +DECLARE_EVENT_CLASS(io_data32, + TP_PROTO(int reg, int addr, u32 val), + TP_ARGS(reg, addr, val), + TP_STRUCT__entry( + __field(int, reg) + __field(int, addr) + __field(int, val) + __array(u8, addr_str, 10) + ), + TP_fast_assign( + __entry->reg = reg; + __entry->addr = addr; + __entry->val = val; + if (addr >= 0) + snprintf(__entry->addr_str, 10, "/%08x", addr); + else + __entry->addr_str[0] = 0; + ), + TP_printk("%s%s: %08x", + __print_symbolic(__entry->reg, wfx_reg_list), + __entry->addr_str, + __entry->val + ) +); +DEFINE_EVENT(io_data32, io_write32, + TP_PROTO(int reg, int addr, u32 val), + TP_ARGS(reg, addr, val)); +#define _trace_io_ind_write32(reg, addr, val) trace_io_write32(reg, addr, val) +#define _trace_io_write32(reg, val) trace_io_write32(reg, -1, val) +DEFINE_EVENT(io_data32, io_read32, + TP_PROTO(int reg, int addr, u32 val), + TP_ARGS(reg, addr, val)); +#define _trace_io_ind_read32(reg, addr, val) trace_io_read32(reg, addr, val) +#define _trace_io_read32(reg, val) trace_io_read32(reg, -1, val) + +DECLARE_EVENT_CLASS(piggyback, + TP_PROTO(u32 val, bool ignored), + TP_ARGS(val, ignored), + TP_STRUCT__entry( + __field(int, val) + __field(bool, ignored) + ), + TP_fast_assign( + __entry->val = val; + __entry->ignored = ignored; + ), + TP_printk("CONTROL: %08x%s", + __entry->val, + __entry->ignored ? " (ignored)" : "" + ) +); +DEFINE_EVENT(piggyback, piggyback, + TP_PROTO(u32 val, bool ignored), + TP_ARGS(val, ignored)); +#define _trace_piggyback(val, ignored) trace_piggyback(val, ignored) + +TRACE_EVENT(bh_stats, + TP_PROTO(int ind, int req, int cnf, int busy, bool release), + TP_ARGS(ind, req, cnf, busy, release), + TP_STRUCT__entry( + __field(int, ind) + __field(int, req) + __field(int, cnf) + __field(int, busy) + __field(bool, release) + ), + TP_fast_assign( + __entry->ind = ind; + __entry->req = req; + __entry->cnf = cnf; + __entry->busy = busy; + __entry->release = release; + ), + TP_printk("IND/REQ/CNF:%3d/%3d/%3d, REQ in progress:%3d, WUP: %s", + __entry->ind, + __entry->req, + __entry->cnf, + __entry->busy, + __entry->release ? "release" : "keep" + ) +); +#define _trace_bh_stats(ind, req, cnf, busy, release) trace_bh_stats(ind, req, cnf, busy, release) + +TRACE_EVENT(tx_stats, + TP_PROTO(struct hif_cnf_tx *tx_cnf, struct sk_buff *skb, int delay), + TP_ARGS(tx_cnf, skb, delay), + TP_STRUCT__entry( + __field(int, pkt_id) + __field(int, delay_media) + __field(int, delay_queue) + __field(int, delay_fw) + __field(int, ack_failures) + __field(int, flags) + __array(int, rate, 4) + __array(int, tx_count, 4) + ), + TP_fast_assign( + // Keep sync with wfx_rates definition in main.c + static const int hw_rate[] = { 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13 }; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rates = tx_info->driver_rates; + int i; + + __entry->pkt_id = tx_cnf->packet_id; + __entry->delay_media = tx_cnf->media_delay; + __entry->delay_queue = tx_cnf->tx_queue_delay; + __entry->delay_fw = delay; + __entry->ack_failures = tx_cnf->ack_failures; + if (!tx_cnf->status || __entry->ack_failures) + __entry->ack_failures += 1; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + if (rates[0].flags & IEEE80211_TX_RC_MCS) + __entry->rate[i] = rates[i].idx; + else + __entry->rate[i] = hw_rate[rates[i].idx]; + __entry->tx_count[i] = rates[i].count; + } + __entry->flags = 0; + if (rates[0].flags & IEEE80211_TX_RC_MCS) + __entry->flags |= 0x01; + if (rates[0].flags & IEEE80211_TX_RC_SHORT_GI) + __entry->flags |= 0x02; + if (rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD) + __entry->flags |= 0x04; + if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) + __entry->flags |= 0x08; + if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) + __entry->flags |= 0x10; + if (tx_cnf->status) + __entry->flags |= 0x20; + if (tx_cnf->status == HIF_REQUEUE) + __entry->flags |= 0x40; + ), + TP_printk("packet ID: %08x, rate policy: %s %d|%d %d|%d %d|%d %d|%d -> %d attempt, Delays media/queue/total: %4dus/%4dus/%4dus", + __entry->pkt_id, + __print_flags(__entry->flags, NULL, + { 0x01, "M" }, { 0x02, "S" }, { 0x04, "G" }, + { 0x08, "R" }, { 0x10, "D" }, { 0x20, "F" }, + { 0x40, "Q" }), + __entry->rate[0], + __entry->tx_count[0], + __entry->rate[1], + __entry->tx_count[1], + __entry->rate[2], + __entry->tx_count[2], + __entry->rate[3], + __entry->tx_count[3], + __entry->ack_failures, + __entry->delay_media, + __entry->delay_queue, + __entry->delay_fw + ) +); +#define _trace_tx_stats(tx_cnf, skb, delay) trace_tx_stats(tx_cnf, skb, delay) + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE traces + +#include <trace/define_trace.h> diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h new file mode 100644 index 000000000000..d678b5a08873 --- /dev/null +++ b/drivers/staging/wfx/wfx.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common private data for Silicon Labs WFx chips. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + */ +#ifndef WFX_H +#define WFX_H + +#include <linux/completion.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/nospec.h> +#include <net/mac80211.h> + +#include "bh.h" +#include "data_tx.h" +#include "main.h" +#include "queue.h" +#include "secure_link.h" +#include "sta.h" +#include "scan.h" +#include "hif_tx.h" +#include "hif_api_general.h" + +struct hwbus_ops; + +struct wfx_dev { + struct wfx_platform_data pdata; + struct device *dev; + struct ieee80211_hw *hw; + struct ieee80211_vif *vif[2]; + struct mac_address addresses[2]; + const struct hwbus_ops *hwbus_ops; + void *hwbus_priv; + + u8 keyset; + struct completion firmware_ready; + struct hif_ind_startup hw_caps; + struct wfx_hif hif; + struct sl_context sl; + int chip_frozen; + struct mutex conf_mutex; + + struct wfx_hif_cmd hif_cmd; + struct wfx_queue tx_queue[4]; + struct wfx_queue_stats tx_queue_stats; + int tx_burst_idx; + atomic_t tx_lock; + + u32 key_map; + struct hif_req_add_key keys[MAX_KEY_ENTRIES]; + + struct hif_rx_stats rx_stats; + struct mutex rx_stats_lock; + + int output_power; + atomic_t scan_in_progress; +}; + +struct wfx_vif { + struct wfx_dev *wdev; + struct ieee80211_vif *vif; + struct ieee80211_channel *channel; + int id; + enum wfx_state state; + + int delayed_link_loss; + int bss_loss_state; + u32 bss_loss_confirm_id; + struct mutex bss_loss_lock; + struct delayed_work bss_loss_work; + + u32 link_id_map; + struct wfx_link_entry link_id_db[WFX_MAX_STA_IN_AP_MODE]; + struct delayed_work link_id_gc_work; + struct work_struct link_id_work; + + bool aid0_bit_set; + bool mcast_tx; + bool mcast_buffered; + struct wfx_grp_addr_table mcast_filter; + struct timer_list mcast_timeout; + struct work_struct mcast_start_work; + struct work_struct mcast_stop_work; + + s8 wep_default_key_id; + struct sk_buff *wep_pending_skb; + struct work_struct wep_key_work; + + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + u32 sta_asleep_mask; + u32 pspoll_mask; + spinlock_t ps_state_lock; + struct work_struct set_tim_work; + + int dtim_period; + int beacon_int; + bool enable_beacon; + struct work_struct set_beacon_wakeup_period_work; + + bool filter_bssid; + bool fwd_probe_req; + bool disable_beacon_filter; + struct work_struct update_filtering_work; + + u32 erp_info; + int cqm_rssi_thold; + bool setbssparams_done; + struct wfx_ht_info ht_info; + struct wfx_edca_params edca; + struct hif_mib_set_uapsd_information uapsd_info; + struct hif_req_set_bss_params bss_params; + struct work_struct bss_params_work; + struct work_struct set_cts_work; + + int join_complete_status; + bool delayed_unjoin; + struct work_struct unjoin_work; + + struct wfx_scan scan; + + struct hif_req_set_pm_mode powersave_mode; + struct completion set_pm_mode_complete; + + struct list_head event_queue; + spinlock_t event_queue_lock; + struct work_struct event_handler_work; +}; + +static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id) +{ + if (vif_id >= ARRAY_SIZE(wdev->vif)) { + dev_dbg(wdev->dev, "requesting non-existent vif: %d\n", vif_id); + return NULL; + } + vif_id = array_index_nospec(vif_id, ARRAY_SIZE(wdev->vif)); + if (!wdev->vif[vif_id]) { + dev_dbg(wdev->dev, "requesting non-allocated vif: %d\n", vif_id); + return NULL; + } + return (struct wfx_vif *) wdev->vif[vif_id]->drv_priv; +} + +static inline struct wfx_vif *wvif_iterate(struct wfx_dev *wdev, struct wfx_vif *cur) +{ + int i; + int mark = 0; + struct wfx_vif *tmp; + + if (!cur) + mark = 1; + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { + tmp = wdev_to_wvif(wdev, i); + if (mark && tmp) + return tmp; + if (tmp == cur) + mark = 1; + } + return NULL; +} + +static inline int wvif_count(struct wfx_dev *wdev) +{ + int i; + int ret = 0; + struct wfx_vif *wvif; + + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { + wvif = wdev_to_wvif(wdev, i); + if (wvif) + ret++; + } + return ret; +} + +static inline void memreverse(uint8_t *src, uint8_t length) +{ + uint8_t *lo = src; + uint8_t *hi = src + length - 1; + uint8_t swap; + + while (lo < hi) { + swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } +} + +static inline int memzcmp(void *src, unsigned int size) +{ + uint8_t *buf = src; + + if (!size) + return 0; + if (*buf) + return 1; + return memcmp(buf, buf + 1, size - 1); +} + +#endif /* WFX_H */ diff --git a/drivers/staging/wfx/wfx_version.h b/drivers/staging/wfx/wfx_version.h new file mode 100644 index 000000000000..6e7f30207c73 --- /dev/null +++ b/drivers/staging/wfx/wfx_version.h @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! */ +#define WFX_LABEL "2.3.1" diff --git a/drivers/staging/wilc1000/wilc_hif.c b/drivers/staging/wilc1000/wilc_hif.c index f2b7d5a1be17..0ac2b6ac50b0 100644 --- a/drivers/staging/wilc1000/wilc_hif.c +++ b/drivers/staging/wilc1000/wilc_hif.c @@ -183,11 +183,17 @@ int wilc_get_vif_idx(struct wilc_vif *vif) static struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx) { int index = idx - 1; + struct wilc_vif *vif; if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC) return NULL; - return wilc->vif[index]; + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + if (vif->idx == index) + return vif; + } + + return NULL; } static int handle_scan_done(struct wilc_vif *vif, enum scan_event evt) diff --git a/drivers/staging/wilc1000/wilc_netdev.c b/drivers/staging/wilc1000/wilc_netdev.c index 508acb8bb089..93c0d6e78813 100644 --- a/drivers/staging/wilc1000/wilc_netdev.c +++ b/drivers/staging/wilc1000/wilc_netdev.c @@ -97,29 +97,25 @@ void wilc_mac_indicate(struct wilc *wilc) static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) { u8 *bssid, *bssid1; - int i = 0; struct net_device *ndev = NULL; + struct wilc_vif *vif; bssid = mac_header + 10; bssid1 = mac_header + 4; - mutex_lock(&wilc->vif_mutex); - for (i = 0; i < wilc->vif_num; i++) { - if (wilc->vif[i]->mode == WILC_STATION_MODE) - if (ether_addr_equal_unaligned(bssid, - wilc->vif[i]->bssid)) { - ndev = wilc->vif[i]->ndev; + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + if (vif->mode == WILC_STATION_MODE) + if (ether_addr_equal_unaligned(bssid, vif->bssid)) { + ndev = vif->ndev; goto out; } - if (wilc->vif[i]->mode == WILC_AP_MODE) - if (ether_addr_equal_unaligned(bssid1, - wilc->vif[i]->bssid)) { - ndev = wilc->vif[i]->ndev; + if (vif->mode == WILC_AP_MODE) + if (ether_addr_equal_unaligned(bssid1, vif->bssid)) { + ndev = vif->ndev; goto out; } } out: - mutex_unlock(&wilc->vif_mutex); return ndev; } @@ -137,13 +133,16 @@ void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode) int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) { - u8 i = 0; + int srcu_idx; u8 ret_val = 0; + struct wilc_vif *vif; - for (i = 0; i < wilc->vif_num; i++) - if (!is_zero_ether_addr(wilc->vif[i]->bssid)) + srcu_idx = srcu_read_lock(&wilc->srcu); + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + if (!is_zero_ether_addr(vif->bssid)) ret_val++; - + } + srcu_read_unlock(&wilc->srcu, srcu_idx); return ret_val; } @@ -167,16 +166,16 @@ static int wilc_txq_task(void *vp) do { ret = wilc_wlan_handle_txq(wl, &txq_count); if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) { - int i; + int srcu_idx; struct wilc_vif *ifc; - mutex_lock(&wl->vif_mutex); - for (i = 0; i < wl->vif_num; i++) { - ifc = wl->vif[i]; + srcu_idx = srcu_read_lock(&wl->srcu); + list_for_each_entry_rcu(ifc, &wl->vif_list, + list) { if (ifc->mac_opened && ifc->ndev) netif_wake_queue(ifc->ndev); } - mutex_unlock(&wl->vif_mutex); + srcu_read_unlock(&wl->srcu, srcu_idx); } } while (ret == -ENOBUFS && !wl->close); } @@ -424,18 +423,6 @@ fail: return -1; } -static void wlan_deinit_locks(struct net_device *dev) -{ - struct wilc_vif *vif = netdev_priv(dev); - struct wilc *wilc = vif->wilc; - - mutex_destroy(&wilc->hif_cs); - mutex_destroy(&wilc->rxq_cs); - mutex_destroy(&wilc->cfg_cmd_lock); - mutex_destroy(&wilc->txq_add_to_head_cs); - mutex_destroy(&wilc->vif_mutex); -} - static void wlan_deinitialize_threads(struct net_device *dev) { struct wilc_vif *vif = netdev_priv(dev); @@ -477,7 +464,6 @@ static void wilc_wlan_deinitialize(struct net_device *dev) wilc_wlan_stop(wl, vif); wilc_wlan_cleanup(dev); - wlan_deinit_locks(dev); wl->initialized = false; @@ -738,14 +724,15 @@ netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) wilc_tx_complete); if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { - int i; + int srcu_idx; + struct wilc_vif *vif; - mutex_lock(&wilc->vif_mutex); - for (i = 0; i < wilc->vif_num; i++) { - if (wilc->vif[i]->mac_opened) - netif_stop_queue(wilc->vif[i]->ndev); + srcu_idx = srcu_read_lock(&wilc->srcu); + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + if (vif->mac_opened) + netif_stop_queue(vif->ndev); } - mutex_unlock(&wilc->vif_mutex); + srcu_read_unlock(&wilc->srcu, srcu_idx); } return 0; @@ -823,14 +810,13 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size) { - int i = 0; + int srcu_idx; struct wilc_vif *vif; - mutex_lock(&wilc->vif_mutex); - for (i = 0; i < wilc->vif_num; i++) { + srcu_idx = srcu_read_lock(&wilc->srcu); + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { u16 type = le16_to_cpup((__le16 *)buff); - vif = netdev_priv(wilc->vif[i]->ndev); if ((type == vif->frame_reg[0].type && vif->frame_reg[0].reg) || (type == vif->frame_reg[1].type && vif->frame_reg[1].reg)) { wilc_wfi_p2p_rx(vif, buff, size); @@ -842,7 +828,7 @@ void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size) break; } } - mutex_unlock(&wilc->vif_mutex); + srcu_read_unlock(&wilc->srcu, srcu_idx); } static const struct net_device_ops wilc_netdev_ops = { @@ -856,7 +842,8 @@ static const struct net_device_ops wilc_netdev_ops = { void wilc_netdev_cleanup(struct wilc *wilc) { - int i; + struct wilc_vif *vif; + int srcu_idx; if (!wilc) return; @@ -866,21 +853,57 @@ void wilc_netdev_cleanup(struct wilc *wilc) wilc->firmware = NULL; } - for (i = 0; i < wilc->vif_num; i++) { - if (wilc->vif[i] && wilc->vif[i]->ndev) - unregister_netdev(wilc->vif[i]->ndev); + srcu_idx = srcu_read_lock(&wilc->srcu); + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + if (vif->ndev) + unregister_netdev(vif->ndev); } + srcu_read_unlock(&wilc->srcu, srcu_idx); wilc_wfi_deinit_mon_interface(wilc, false); flush_workqueue(wilc->hif_workqueue); destroy_workqueue(wilc->hif_workqueue); + + do { + mutex_lock(&wilc->vif_mutex); + if (wilc->vif_num <= 0) { + mutex_unlock(&wilc->vif_mutex); + break; + } + vif = wilc_get_wl_to_vif(wilc); + if (!IS_ERR(vif)) + list_del_rcu(&vif->list); + + wilc->vif_num--; + mutex_unlock(&wilc->vif_mutex); + synchronize_srcu(&wilc->srcu); + } while (1); + wilc_wlan_cfg_deinit(wilc); + wlan_deinit_locks(wilc); kfree(wilc->bus_data); wiphy_unregister(wilc->wiphy); wiphy_free(wilc->wiphy); } EXPORT_SYMBOL_GPL(wilc_netdev_cleanup); +static u8 wilc_get_available_idx(struct wilc *wl) +{ + int idx = 0; + struct wilc_vif *vif; + int srcu_idx; + + srcu_idx = srcu_read_lock(&wl->srcu); + list_for_each_entry_rcu(vif, &wl->vif_list, list) { + if (vif->idx == 0) + idx = 1; + else + idx = 0; + } + srcu_read_unlock(&wl->srcu, srcu_idx); + return idx; +} + struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, int vif_type, enum nl80211_iftype type, bool rtnl_locked) @@ -921,10 +944,14 @@ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, ndev->needs_free_netdev = true; vif->iftype = vif_type; - vif->wilc->vif[wl->vif_num] = vif; - vif->idx = wl->vif_num; - wl->vif_num += 1; + vif->idx = wilc_get_available_idx(wl); vif->mac_opened = 0; + mutex_lock(&wl->vif_mutex); + list_add_tail_rcu(&vif->list, &wl->vif_list); + wl->vif_num += 1; + mutex_unlock(&wl->vif_mutex); + synchronize_srcu(&wl->srcu); + return vif; } diff --git a/drivers/staging/wilc1000/wilc_spi.c b/drivers/staging/wilc1000/wilc_spi.c index 3c1ae9e9f9aa..166455a969bf 100644 --- a/drivers/staging/wilc1000/wilc_spi.c +++ b/drivers/staging/wilc1000/wilc_spi.c @@ -4,6 +4,7 @@ * All rights reserved. */ +#include <linux/clk.h> #include <linux/spi/spi.h> #include "wilc_wfi_netdevice.h" @@ -132,6 +133,12 @@ static int wilc_bus_probe(struct spi_device *spi) wilc->bus_data = spi_priv; wilc->gpio_irq = gpio; + wilc->rtc_clk = devm_clk_get(&spi->dev, "rtc_clk"); + if (PTR_ERR_OR_ZERO(wilc->rtc_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (!IS_ERR(wilc->rtc_clk)) + clk_prepare_enable(wilc->rtc_clk); + return 0; } @@ -142,6 +149,10 @@ static int wilc_bus_remove(struct spi_device *spi) /* free the GPIO in module remove */ if (wilc->gpio_irq) gpiod_put(wilc->gpio_irq); + + if (!IS_ERR(wilc->rtc_clk)) + clk_disable_unprepare(wilc->rtc_clk); + wilc_netdev_cleanup(wilc); return 0; } diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c index 22f21831649b..cc56abc2fe37 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c @@ -186,15 +186,15 @@ static void cfg_connect_result(enum conn_event conn_disconn_evt, u8 mac_status, } } -static struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl) +struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl) { - int i; + struct wilc_vif *vif; - for (i = 0; i < wl->vif_num; i++) - if (wl->vif[i]) - return wl->vif[i]; + vif = list_first_or_null_rcu(&wl->vif_list, typeof(*vif), list); + if (!vif) + return ERR_PTR(-EINVAL); - return ERR_PTR(-EINVAL); + return vif; } static int set_channel(struct wiphy *wiphy, @@ -204,11 +204,12 @@ static int set_channel(struct wiphy *wiphy, struct wilc_vif *vif; u32 channelnum; int result; + int srcu_idx; - mutex_lock(&wl->vif_mutex); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - mutex_unlock(&wl->vif_mutex); + srcu_read_unlock(&wl->srcu, srcu_idx); return PTR_ERR(vif); } @@ -219,7 +220,7 @@ static int set_channel(struct wiphy *wiphy, if (result) netdev_err(vif->ndev, "Error in setting channel\n"); - mutex_unlock(&wl->vif_mutex); + srcu_read_unlock(&wl->srcu, srcu_idx); return result; } @@ -620,29 +621,26 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev, bool pairwise, const u8 *mac_addr) { - struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif = netdev_priv(netdev); struct wilc_priv *priv = &vif->priv; - if (netdev == wl->vif[0]->ndev) { - if (priv->wilc_gtk[key_index]) { - kfree(priv->wilc_gtk[key_index]->key); - priv->wilc_gtk[key_index]->key = NULL; - kfree(priv->wilc_gtk[key_index]->seq); - priv->wilc_gtk[key_index]->seq = NULL; + if (priv->wilc_gtk[key_index]) { + kfree(priv->wilc_gtk[key_index]->key); + priv->wilc_gtk[key_index]->key = NULL; + kfree(priv->wilc_gtk[key_index]->seq); + priv->wilc_gtk[key_index]->seq = NULL; - kfree(priv->wilc_gtk[key_index]); - priv->wilc_gtk[key_index] = NULL; - } + kfree(priv->wilc_gtk[key_index]); + priv->wilc_gtk[key_index] = NULL; + } - if (priv->wilc_ptk[key_index]) { - kfree(priv->wilc_ptk[key_index]->key); - priv->wilc_ptk[key_index]->key = NULL; - kfree(priv->wilc_ptk[key_index]->seq); - priv->wilc_ptk[key_index]->seq = NULL; - kfree(priv->wilc_ptk[key_index]); - priv->wilc_ptk[key_index] = NULL; - } + if (priv->wilc_ptk[key_index]) { + kfree(priv->wilc_ptk[key_index]->key); + priv->wilc_ptk[key_index]->key = NULL; + kfree(priv->wilc_ptk[key_index]->seq); + priv->wilc_ptk[key_index]->seq = NULL; + kfree(priv->wilc_ptk[key_index]); + priv->wilc_ptk[key_index] = NULL; } if (key_index <= 3 && priv->wep_key_len[key_index]) { @@ -752,33 +750,19 @@ static int change_bss(struct wiphy *wiphy, struct net_device *dev, return 0; } -struct wilc_vif *wilc_get_interface(struct wilc *wl) -{ - int i; - struct wilc_vif *vif = NULL; - - mutex_lock(&wl->vif_mutex); - for (i = 0; i < wl->vif_num; i++) { - if (wl->vif[i]) { - vif = wl->vif[i]; - break; - } - } - mutex_unlock(&wl->vif_mutex); - return vif; -} - static int set_wiphy_params(struct wiphy *wiphy, u32 changed) { - int ret; + int ret = -EINVAL; struct cfg_param_attr cfg_param_val; struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; struct wilc_priv *priv; + int srcu_idx; - vif = wilc_get_interface(wl); - if (!vif) - return -EINVAL; + srcu_idx = srcu_read_lock(&wl->srcu); + vif = wilc_get_wl_to_vif(wl); + if (IS_ERR(vif)) + goto out; priv = &vif->priv; cfg_param_val.flag = 0; @@ -808,7 +792,7 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) } else { netdev_err(vif->ndev, "Fragmentation threshold out of range\n"); - return -EINVAL; + goto out; } } @@ -821,7 +805,7 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) cfg_param_val.rts_threshold = wiphy->rts_threshold; } else { netdev_err(vif->ndev, "RTS threshold out of range\n"); - return -EINVAL; + goto out; } } @@ -829,6 +813,8 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) if (ret) netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n"); +out: + srcu_read_unlock(&wl->srcu, srcu_idx); return ret; } @@ -1557,20 +1543,16 @@ static int change_station(struct wiphy *wiphy, struct net_device *dev, return ret; } -static int wilc_get_vif_from_type(struct wilc *wl, int type) +static struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type) { - int i; + struct wilc_vif *vif; - mutex_lock(&wl->vif_mutex); - for (i = 0; i < wl->vif_num; i++) { - if (wl->vif[i]->iftype == type) { - mutex_unlock(&wl->vif_mutex); - return i; - } + list_for_each_entry_rcu(vif, &wl->vif_list, list) { + if (vif->iftype == type) + return vif; } - mutex_unlock(&wl->vif_mutex); - return -EINVAL; + return NULL; } static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, @@ -1583,29 +1565,36 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, struct wilc_vif *vif; struct wireless_dev *wdev; int iftype; - int ret; if (type == NL80211_IFTYPE_MONITOR) { struct net_device *ndev; - int ap_index = wilc_get_vif_from_type(wl, WILC_AP_MODE); - - if (ap_index < 0) { - ap_index = wilc_get_vif_from_type(wl, WILC_GO_MODE); - if (ap_index < 0) + int srcu_idx; + + srcu_idx = srcu_read_lock(&wl->srcu); + vif = wilc_get_vif_from_type(wl, WILC_AP_MODE); + if (!vif) { + vif = wilc_get_vif_from_type(wl, WILC_GO_MODE); + if (!vif) { + srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; + } } - vif = wl->vif[ap_index]; - if (vif->monitor_flag) + if (vif->monitor_flag) { + srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; + } ndev = wilc_wfi_init_mon_interface(wl, name, vif->ndev); - if (ndev) + if (ndev) { vif->monitor_flag = 1; - else + } else { + srcu_read_unlock(&wl->srcu, srcu_idx); return ERR_PTR(-EINVAL); + } wdev = &vif->priv.wdev; + srcu_read_unlock(&wl->srcu, srcu_idx); return wdev; } @@ -1613,9 +1602,10 @@ validate_interface: mutex_lock(&wl->vif_mutex); if (wl->vif_num == WILC_NUM_CONCURRENT_IFC) { pr_err("Reached maximum number of interface\n"); - ret = -EINVAL; - goto out_err; + mutex_unlock(&wl->vif_mutex); + return ERR_PTR(-EINVAL); } + mutex_unlock(&wl->vif_mutex); switch (type) { case NL80211_IFTYPE_STATION: @@ -1625,30 +1615,20 @@ validate_interface: iftype = WILC_AP_MODE; break; default: - ret = -EOPNOTSUPP; - goto out_err; + return ERR_PTR(-EOPNOTSUPP); } vif = wilc_netdev_ifc_init(wl, name, iftype, type, true); - if (IS_ERR(vif)) { - ret = PTR_ERR(vif); - goto out_err; - } - - mutex_unlock(&wl->vif_mutex); + if (IS_ERR(vif)) + return ERR_CAST(vif); return &vif->priv.wdev; - -out_err: - mutex_unlock(&wl->vif_mutex); - return ERR_PTR(ret); } static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) { struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; - int i; if (wdev->iftype == NL80211_IFTYPE_AP || wdev->iftype == NL80211_IFTYPE_P2P_GO) @@ -1658,22 +1638,12 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) unregister_netdevice(vif->ndev); vif->monitor_flag = 0; - mutex_lock(&wl->vif_mutex); wilc_set_operation_mode(vif, 0, 0, 0); - for (i = vif->idx; i < wl->vif_num; i++) { - if ((i + 1) >= wl->vif_num) { - wl->vif[i] = NULL; - } else { - vif = wl->vif[i + 1]; - vif->idx = i; - wl->vif[i] = vif; - wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), - vif->iftype, vif->idx); - } - } + mutex_lock(&wl->vif_mutex); + list_del_rcu(&vif->list); wl->vif_num--; mutex_unlock(&wl->vif_mutex); - + synchronize_srcu(&wl->srcu); return 0; } @@ -1698,25 +1668,39 @@ static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled) { struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; + int srcu_idx; - mutex_lock(&wl->vif_mutex); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - mutex_unlock(&wl->vif_mutex); + srcu_read_unlock(&wl->srcu, srcu_idx); return; } netdev_info(vif->ndev, "cfg set wake up = %d\n", enabled); - mutex_unlock(&wl->vif_mutex); + srcu_read_unlock(&wl->srcu, srcu_idx); } static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { int ret; + int srcu_idx; s32 tx_power = MBM_TO_DBM(mbm); - struct wilc_vif *vif = netdev_priv(wdev->netdev); + struct wilc *wl = wiphy_priv(wiphy); + struct wilc_vif *vif; + + if (!wl->initialized) + return -EIO; + srcu_idx = srcu_read_lock(&wl->srcu); + vif = wilc_get_wl_to_vif(wl); + if (IS_ERR(vif)) { + srcu_read_unlock(&wl->srcu, srcu_idx); + return -EINVAL; + } + + netdev_info(vif->ndev, "Setting tx power %d\n", tx_power); if (tx_power < 0) tx_power = 0; else if (tx_power > 18) @@ -1724,6 +1708,7 @@ static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, ret = wilc_set_tx_power(vif, tx_power); if (ret) netdev_err(vif->ndev, "Failed to set tx power\n"); + srcu_read_unlock(&wl->srcu, srcu_idx); return ret; } @@ -1803,6 +1788,17 @@ static void wlan_init_locks(struct wilc *wl) init_completion(&wl->cfg_event); init_completion(&wl->sync_event); init_completion(&wl->txq_thread_started); + init_srcu_struct(&wl->srcu); +} + +void wlan_deinit_locks(struct wilc *wilc) +{ + mutex_destroy(&wilc->hif_cs); + mutex_destroy(&wilc->rxq_cs); + mutex_destroy(&wilc->cfg_cmd_lock); + mutex_destroy(&wilc->txq_add_to_head_cs); + mutex_destroy(&wilc->vif_mutex); + cleanup_srcu_struct(&wilc->srcu); } int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, @@ -1816,6 +1812,8 @@ int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, if (!wl) return -EINVAL; + wlan_init_locks(wl); + ret = wilc_wlan_cfg_init(wl); if (ret) goto free_wl; @@ -1826,6 +1824,7 @@ int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, wl->chip_ps_state = WILC_CHIP_WAKEDUP; INIT_LIST_HEAD(&wl->txq_head.list); INIT_LIST_HEAD(&wl->rxq_head.list); + INIT_LIST_HEAD(&wl->vif_list); wl->hif_workqueue = create_singlethread_workqueue("WILC_wq"); if (!wl->hif_workqueue) { @@ -1839,8 +1838,6 @@ int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, goto free_hq; } - wlan_init_locks(wl); - return 0; free_hq: @@ -1850,6 +1847,7 @@ free_cfg: wilc_wlan_cfg_deinit(wl); free_wl: + wlan_deinit_locks(wl); wiphy_unregister(wl->wiphy); wiphy_free(wl->wiphy); return ret; diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h index 234faaabdb82..7206b6162a8c 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h @@ -24,4 +24,6 @@ struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl, void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg); struct wilc_vif *wilc_get_interface(struct wilc *wl); +struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl); +void wlan_deinit_locks(struct wilc *wilc); #endif diff --git a/drivers/staging/wilc1000/wilc_wfi_netdevice.h b/drivers/staging/wilc1000/wilc_wfi_netdevice.h index 978a8bdbfc40..fa41b46eb245 100644 --- a/drivers/staging/wilc1000/wilc_wfi_netdevice.h +++ b/drivers/staging/wilc1000/wilc_wfi_netdevice.h @@ -208,6 +208,7 @@ struct wilc_vif { struct tcp_ack_filter ack_filter; bool connecting; struct wilc_priv priv; + struct list_head list; }; struct wilc { @@ -221,9 +222,10 @@ struct wilc { int dev_irq_num; int close; u8 vif_num; - struct wilc_vif *vif[WILC_NUM_CONCURRENT_IFC]; + struct list_head vif_list; /*protect vif list*/ struct mutex vif_mutex; + struct srcu_struct srcu; u8 open_ifcs; /*protect head of transmit queue*/ struct mutex txq_add_to_head_cs; diff --git a/drivers/staging/wlan-ng/p80211wep.c b/drivers/staging/wlan-ng/p80211wep.c index 8bd92bba0ac1..51d917c8cdc8 100644 --- a/drivers/staging/wlan-ng/p80211wep.c +++ b/drivers/staging/wlan-ng/p80211wep.c @@ -49,6 +49,7 @@ /*================================================================*/ /* System Includes */ +#include <linux/crc32.h> #include <linux/netdevice.h> #include <linux/wireless.h> #include <linux/random.h> @@ -61,61 +62,6 @@ #define WEP_KEY(x) (((x) & 0xC0) >> 6) -static const u32 wep_crc32_table[256] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; - /* keylen in bytes! */ int wep_change_key(struct wlandevice *wlandev, int keynum, u8 *key, int keylen) @@ -184,7 +130,6 @@ int wep_decrypt(struct wlandevice *wlandev, u8 *buf, u32 len, int key_override, } /* Apply the RC4 to the data, update the CRC32 */ - crc = ~0; i = 0; j = 0; for (k = 0; k < len; k++) { @@ -192,9 +137,8 @@ int wep_decrypt(struct wlandevice *wlandev, u8 *buf, u32 len, int key_override, j = (j + s[i]) & 0xff; swap(i, j); buf[k] ^= s[(s[i] + s[j]) & 0xff]; - crc = wep_crc32_table[(crc ^ buf[k]) & 0xff] ^ (crc >> 8); } - crc = ~crc; + crc = ~crc32_le(~0, buf, len); /* now let's check the crc */ c_crc[0] = crc; @@ -257,17 +201,15 @@ int wep_encrypt(struct wlandevice *wlandev, u8 *buf, } /* Update CRC32 then apply RC4 to the data */ - crc = ~0; i = 0; j = 0; for (k = 0; k < len; k++) { - crc = wep_crc32_table[(crc ^ buf[k]) & 0xff] ^ (crc >> 8); i = (i + 1) & 0xff; j = (j + s[i]) & 0xff; swap(i, j); dst[k] = buf[k] ^ s[(s[i] + s[j]) & 0xff]; } - crc = ~crc; + crc = ~crc32_le(~0, buf, len); /* now let's encrypt the crc */ icv[0] = crc; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index e55c79eb6430..98361acd3053 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -968,6 +968,11 @@ static int __init n_hdlc_init(void) } /* end of init_module() */ +#ifdef CONFIG_SPARC +#undef __exitdata +#define __exitdata +#endif + static const char hdlc_unregister_ok[] __exitdata = KERN_INFO "N_HDLC: line discipline unregistered\n"; static const char hdlc_unregister_fail[] __exitdata = diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index c68e2b3a1634..836e736ae188 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -141,7 +141,7 @@ static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) serial8250_do_set_mctrl(port, mctrl); - if (!up->gpios) { + if (!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS)) { /* * Turn off autoRTS if RTS is lowered and restore autoRTS * setting if RTS is raised @@ -456,7 +456,8 @@ static void omap_8250_set_termios(struct uart_port *port, up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW && - !up->gpios) { + !mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) && + !mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_CTS)) { /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */ up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; priv->efr |= UART_EFR_CTS; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4789b5d62f63..67a9eb3f94ce 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1032,6 +1032,7 @@ config SERIAL_SIFIVE_CONSOLE bool "Console on SiFive UART" depends on SERIAL_SIFIVE=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON help Select this option if you would like to use a SiFive UART as the system console. diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 68d74f2b5106..a32f0d2afd59 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -3,7 +3,7 @@ * Freescale linflexuart serial port driver * * Copyright 2012-2016 Freescale Semiconductor, Inc. - * Copyright 2017-2018 NXP + * Copyright 2017-2019 NXP */ #if defined(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE) && \ @@ -246,12 +246,14 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) struct tty_port *port = &sport->state->port; unsigned long flags, status; unsigned char rx; + bool brk; spin_lock_irqsave(&sport->lock, flags); status = readl(sport->membase + UARTSR); while (status & LINFLEXD_UARTSR_RMB) { rx = readb(sport->membase + BDRM); + brk = false; flg = TTY_NORMAL; sport->icount.rx++; @@ -261,8 +263,11 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) status |= LINFLEXD_UARTSR_SZF; if (status & LINFLEXD_UARTSR_BOF) status |= LINFLEXD_UARTSR_BOF; - if (status & LINFLEXD_UARTSR_FEF) + if (status & LINFLEXD_UARTSR_FEF) { + if (!rx) + brk = true; status |= LINFLEXD_UARTSR_FEF; + } if (status & LINFLEXD_UARTSR_PE) status |= LINFLEXD_UARTSR_PE; } @@ -271,13 +276,15 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) sport->membase + UARTSR); status = readl(sport->membase + UARTSR); - if (uart_handle_sysrq_char(sport, (unsigned char)rx)) - continue; - + if (brk) { + uart_handle_break(sport); + } else { #ifdef SUPPORT_SYSRQ - sport->sysrq = 0; + if (uart_handle_sysrq_char(sport, (unsigned char)rx)) + continue; #endif - tty_insert_flip_char(port, rx, flg); + tty_insert_flip_char(port, rx, flg); + } } spin_unlock_irqrestore(&sport->lock, flags); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 3e17bb8a0b16..537896c4d887 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -548,7 +548,7 @@ static void lpuart_flush_buffer(struct uart_port *port) val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; lpuart32_write(&sport->port, val, UARTFIFO); } else { - val = readb(sport->port.membase + UARTPFIFO); + val = readb(sport->port.membase + UARTCFIFO); val |= UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH; writeb(val, sport->port.membase + UARTCFIFO); } diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 87c58f9f6390..5e08f2657b90 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -2222,8 +2222,8 @@ static int imx_uart_probe(struct platform_device *pdev) return PTR_ERR(base); rxirq = platform_get_irq(pdev, 0); - txirq = platform_get_irq(pdev, 1); - rtsirq = platform_get_irq(pdev, 2); + txirq = platform_get_irq_optional(pdev, 1); + rtsirq = platform_get_irq_optional(pdev, 2); sport->port.dev = &pdev->dev; sport->port.mapbase = res->start; diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index 03963af77b15..d2d8b3494685 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -740,7 +740,7 @@ static int __init owl_uart_init(void) return ret; } -static void __init owl_uart_exit(void) +static void __exit owl_uart_exit(void) { platform_driver_unregister(&owl_uart_platform_driver); uart_unregister_driver(&owl_uart_driver); diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index c1b0d7662ef9..ff9a27d48bca 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -815,7 +815,7 @@ static int __init rda_uart_init(void) return ret; } -static void __init rda_uart_exit(void) +static void __exit rda_uart_exit(void) { platform_driver_unregister(&rda_uart_platform_driver); uart_unregister_driver(&rda_uart_driver); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 6e713be1d4e9..c4a414a46c7f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1964,8 +1964,10 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * console=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options> * * The optional form + * * earlycon=<name>,0x<addr>,<options> * console=<name>,0x<addr>,<options> + * * is also accepted; the returned @iotype will be UPIO_MEM. * * Returns 0 on success or -EINVAL on failure diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index d9074303c88e..fb4781292d40 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -66,6 +66,9 @@ EXPORT_SYMBOL_GPL(mctrl_gpio_set); struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, enum mctrl_gpio_idx gidx) { + if (gpios == NULL) + return NULL; + return gpios->gpio[gidx]; } EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 4e754a4850e6..22e5d4e13714 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2894,8 +2894,12 @@ static int sci_init_single(struct platform_device *dev, port->mapbase = res->start; sci_port->reg_size = resource_size(res); - for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) - sci_port->irqs[i] = platform_get_irq(dev, i); + for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) { + if (i) + sci_port->irqs[i] = platform_get_irq_optional(dev, i); + else + sci_port->irqs[i] = platform_get_irq(dev, i); + } /* The SCI generates several interrupts. They can be muxed together or * connected to different interrupt lines. In the muxed case only one diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index b8b912b5a8b9..06e79c11141d 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -897,7 +897,8 @@ static int __init ulite_init(void) static void __exit ulite_exit(void) { platform_driver_unregister(&ulite_platform_driver); - uart_unregister_driver(&ulite_uart_driver); + if (ulite_uart_driver.state) + uart_unregister_driver(&ulite_uart_driver); } module_init(ulite_init); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index da4563aaaf5c..4e55bc327a54 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1550,7 +1550,6 @@ static int cdns_uart_probe(struct platform_device *pdev) goto err_out_id; } - uartps_major = cdns_uart_uart_driver->tty_driver->major; cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver; /* @@ -1680,6 +1679,7 @@ static int cdns_uart_probe(struct platform_device *pdev) console_port = NULL; #endif + uartps_major = cdns_uart_uart_driver->tty_driver->major; cdns_uart_data->cts_override = of_property_read_bool(pdev->dev.of_node, "cts-override"); return 0; @@ -1741,6 +1741,12 @@ static int cdns_uart_remove(struct platform_device *pdev) console_port = NULL; #endif + /* If this is last instance major number should be initialized */ + mutex_lock(&bitmap_lock); + if (bitmap_empty(bitmap, MAX_UART_INSTANCES)) + uartps_major = 0; + mutex_unlock(&bitmap_lock); + uart_unregister_driver(cdns_uart_data->cdns_uart_driver); return rc; } diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index c41ddb61b857..b0a29efe7d31 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -159,8 +159,9 @@ static int cdns3_pci_probe(struct pci_dev *pdev, wrap->plat_dev = platform_device_register_full(&plat_info); if (IS_ERR(wrap->plat_dev)) { pci_disable_device(pdev); + err = PTR_ERR(wrap->plat_dev); kfree(wrap); - return PTR_ERR(wrap->plat_dev); + return err; } } diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 06f1e105be4e..1109dc5a4c39 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -160,10 +160,28 @@ static int cdns3_core_init_role(struct cdns3 *cdns) if (ret) goto err; - if (cdns->dr_mode != USB_DR_MODE_OTG) { + /* Initialize idle role to start with */ + ret = cdns3_role_start(cdns, USB_ROLE_NONE); + if (ret) + goto err; + + switch (cdns->dr_mode) { + case USB_DR_MODE_UNKNOWN: + case USB_DR_MODE_OTG: ret = cdns3_hw_role_switch(cdns); if (ret) goto err; + break; + case USB_DR_MODE_PERIPHERAL: + ret = cdns3_role_start(cdns, USB_ROLE_DEVICE); + if (ret) + goto err; + break; + case USB_DR_MODE_HOST: + ret = cdns3_role_start(cdns, USB_ROLE_HOST); + if (ret) + goto err; + break; } return ret; diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index 44f652e8b5a2..e71240b386b4 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -234,9 +234,11 @@ static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev, static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, struct usb_ctrlrequest *ctrl) { + struct cdns3_endpoint *priv_ep; __le16 *response_pkt; u16 usb_status = 0; u32 recip; + u8 index; recip = ctrl->bRequestType & USB_RECIP_MASK; @@ -262,9 +264,13 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, case USB_RECIP_INTERFACE: return cdns3_ep0_delegate_req(priv_dev, ctrl); case USB_RECIP_ENDPOINT: - /* check if endpoint is stalled */ + index = cdns3_ep_addr_to_index(ctrl->wIndex); + priv_ep = priv_dev->eps[index]; + + /* check if endpoint is stalled or stall is pending */ cdns3_select_ep(priv_dev, ctrl->wIndex); - if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts))) + if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)) || + (priv_ep->flags & EP_STALL_PENDING)) usb_status = BIT(USB_ENDPOINT_HALT); break; default: @@ -332,7 +338,7 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, * for sending status stage. * This time should be less then 3ms. */ - usleep_range(1000, 2000); + mdelay(1); cdns3_set_register_bit(&priv_dev->regs->usb_cmd, USB_CMD_STMODE | USB_STS_TMODE_SEL(tmode - 1)); diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 228cdc4ab886..2ca280f4c054 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2571,6 +2571,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns) switch (max_speed) { case USB_SPEED_FULL: writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf); + writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf); break; case USB_SPEED_HIGH: writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf); @@ -2662,6 +2663,13 @@ static int __cdns3_gadget_init(struct cdns3 *cdns) { int ret = 0; + /* Ensure 32-bit DMA Mask in case we switched back from Host mode */ + ret = dma_set_mask_and_coherent(cdns->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(cdns->dev, "Failed to set dma mask: %d\n", ret); + return ret; + } + cdns3_drd_switch_gadget(cdns, 1); pm_runtime_get_sync(cdns->dev); diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 7fea4999d352..fb8bd60c83f4 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -461,10 +461,12 @@ static int usblp_release(struct inode *inode, struct file *file) mutex_lock(&usblp_mutex); usblp->used = 0; - if (usblp->present) { + if (usblp->present) usblp_unlink_urbs(usblp); - usb_autopm_put_interface(usblp->intf); - } else /* finish cleanup from disconnect */ + + usb_autopm_put_interface(usblp->intf); + + if (!usblp->present) /* finish cleanup from disconnect */ usblp_cleanup(usblp); mutex_unlock(&usblp_mutex); return 0; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 726100d1ac0d..c946d64142ad 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -139,14 +139,14 @@ static int dwc3_otg_get_irq(struct dwc3 *dwc) struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); int irq; - irq = platform_get_irq_byname(dwc3_pdev, "otg"); + irq = platform_get_irq_byname_optional(dwc3_pdev, "otg"); if (irq > 0) goto out; if (irq == -EPROBE_DEFER) goto out; - irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3"); if (irq > 0) goto out; @@ -157,9 +157,6 @@ static int dwc3_otg_get_irq(struct dwc3 *dwc) if (irq > 0) goto out; - if (irq != -EPROBE_DEFER) - dev_err(dwc->dev, "missing OTG IRQ\n"); - if (!irq) irq = -EINVAL; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8adb59f8e4f1..86dc1db788a9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3264,14 +3264,14 @@ static int dwc3_gadget_get_irq(struct dwc3 *dwc) struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); int irq; - irq = platform_get_irq_byname(dwc3_pdev, "peripheral"); + irq = platform_get_irq_byname_optional(dwc3_pdev, "peripheral"); if (irq > 0) goto out; if (irq == -EPROBE_DEFER) goto out; - irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3"); if (irq > 0) goto out; @@ -3282,9 +3282,6 @@ static int dwc3_gadget_get_irq(struct dwc3 *dwc) if (irq > 0) goto out; - if (irq != -EPROBE_DEFER) - dev_err(dwc->dev, "missing peripheral IRQ\n"); - if (!irq) irq = -EINVAL; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 8deea8c91e03..5567ed2cddbe 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -16,14 +16,14 @@ static int dwc3_host_get_irq(struct dwc3 *dwc) struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); int irq; - irq = platform_get_irq_byname(dwc3_pdev, "host"); + irq = platform_get_irq_byname_optional(dwc3_pdev, "host"); if (irq > 0) goto out; if (irq == -EPROBE_DEFER) goto out; - irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3"); if (irq > 0) goto out; @@ -34,9 +34,6 @@ static int dwc3_host_get_irq(struct dwc3 *dwc) if (irq > 0) goto out; - if (irq != -EPROBE_DEFER) - dev_err(dwc->dev, "missing host IRQ\n"); - if (!irq) irq = -EINVAL; diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index d7e611645533..d354036ff6c8 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -45,7 +45,7 @@ config USB_AT91 config USB_LPC32XX tristate "LPC32XX USB Peripheral Controller" - depends on ARCH_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST depends on I2C select USB_ISP1301 help diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 8414fac74493..3d499d93c083 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -48,6 +48,7 @@ #define DRIVER_VERSION "02 May 2005" #define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */ +#define POWER_BUDGET_3 900 /* in mA */ static const char driver_name[] = "dummy_hcd"; static const char driver_desc[] = "USB Host+Gadget Emulator"; @@ -2432,7 +2433,7 @@ static int dummy_start_ss(struct dummy_hcd *dum_hcd) dum_hcd->rh_state = DUMMY_RH_RUNNING; dum_hcd->stream_en_ep = 0; INIT_LIST_HEAD(&dum_hcd->urbp_list); - dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET; + dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET_3; dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING; dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1; #ifdef CONFIG_USB_OTG diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index b3e073fb88c6..2b1f3cc7819b 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1151,7 +1151,7 @@ static void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) u32 *p32, tmp, cbytes; /* Use optimal data transfer method based on source address and size */ - switch (((u32) data) & 0x3) { + switch (((uintptr_t) data) & 0x3) { case 0: /* 32-bit aligned */ p32 = (u32 *) data; cbytes = (bytes & ~0x3); @@ -1252,7 +1252,7 @@ static void udc_stuff_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) u32 *p32, tmp, cbytes; /* Use optimal data transfer method based on source address and size */ - switch (((u32) data) & 0x3) { + switch (((uintptr_t) data) & 0x3) { case 0: /* 32-bit aligned */ p32 = (u32 *) data; cbytes = (bytes & ~0x3); diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c index f498160df969..3351d07c431f 100644 --- a/drivers/usb/host/xhci-ext-caps.c +++ b/drivers/usb/host/xhci-ext-caps.c @@ -57,6 +57,7 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset) ret = platform_device_add_properties(pdev, role_switch_props); if (ret) { dev_err(dev, "failed to register device properties\n"); + platform_device_put(pdev); return ret; } } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9741cdeea9d7..85ceb43e3405 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3202,10 +3202,10 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, if (usb_urb_dir_out(urb)) { len = sg_pcopy_to_buffer(urb->sg, urb->num_sgs, seg->bounce_buf, new_buff_len, enqd_len); - if (len != seg->bounce_len) + if (len != new_buff_len) xhci_warn(xhci, "WARN Wrong bounce buffer write length: %zu != %d\n", - len, seg->bounce_len); + len, new_buff_len); seg->bounce_dma = dma_map_single(dev, seg->bounce_buf, max_pkt, DMA_TO_DEVICE); } else { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 500865975687..517ec3206f6e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1032,7 +1032,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) writel(command, &xhci->op_regs->command); xhci->broken_suspend = 0; if (xhci_handshake(&xhci->op_regs->status, - STS_SAVE, 0, 10 * 1000)) { + STS_SAVE, 0, 20 * 1000)) { /* * AMD SNPS xHC 3.0 occasionally does not clear the * SSS bit of USBSTS and when driver tries to poll @@ -1108,6 +1108,18 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) hibernated = true; if (!hibernated) { + /* + * Some controllers might lose power during suspend, so wait + * for controller not ready bit to clear, just as in xHC init. + */ + retval = xhci_handshake(&xhci->op_regs->status, + STS_CNR, 0, 10 * 1000 * 1000); + if (retval) { + xhci_warn(xhci, "Controller not ready at resume %d\n", + retval); + spin_unlock_irq(&xhci->lock); + return retval; + } /* step 1: restore register */ xhci_restore_registers(xhci); /* step 2: initialize command ring buffer */ @@ -3083,6 +3095,7 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, unsigned int ep_index; unsigned long flags; u32 ep_flag; + int err; xhci = hcd_to_xhci(hcd); if (!host_ep->hcpriv) @@ -3142,7 +3155,17 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, xhci_free_command(xhci, cfg_cmd); goto cleanup; } - xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, ep_index, 0); + + err = xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, + ep_index, 0); + if (err < 0) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cfg_cmd); + xhci_dbg(xhci, "%s: Failed to queue stop ep command, %d ", + __func__, err); + goto cleanup; + } + xhci_ring_cmd_db(xhci); spin_unlock_irqrestore(&xhci->lock, flags); @@ -3156,8 +3179,16 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, ctrl_ctx, ep_flag, ep_flag); xhci_endpoint_copy(xhci, cfg_cmd->in_ctx, vdev->out_ctx, ep_index); - xhci_queue_configure_endpoint(xhci, cfg_cmd, cfg_cmd->in_ctx->dma, + err = xhci_queue_configure_endpoint(xhci, cfg_cmd, cfg_cmd->in_ctx->dma, udev->slot_id, false); + if (err < 0) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cfg_cmd); + xhci_dbg(xhci, "%s: Failed to queue config ep command, %d ", + __func__, err); + goto cleanup; + } + xhci_ring_cmd_db(xhci); spin_unlock_irqrestore(&xhci->lock, flags); @@ -4674,12 +4705,12 @@ static int xhci_update_timeout_for_endpoint(struct xhci_hcd *xhci, alt_timeout = xhci_call_host_update_timeout_for_endpoint(xhci, udev, desc, state, timeout); - /* If we found we can't enable hub-initiated LPM, or + /* If we found we can't enable hub-initiated LPM, and * the U1 or U2 exit latency was too high to allow - * device-initiated LPM as well, just stop searching. + * device-initiated LPM as well, then we will disable LPM + * for this device, so stop searching any further. */ - if (alt_timeout == USB3_LPM_DISABLED || - alt_timeout == USB3_LPM_DEVICE_INITIATED) { + if (alt_timeout == USB3_LPM_DISABLED) { *timeout = alt_timeout; return -E2BIG; } @@ -4790,10 +4821,12 @@ static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd, if (intf->dev.driver) { driver = to_usb_driver(intf->dev.driver); if (driver && driver->disable_hub_initiated_lpm) { - dev_dbg(&udev->dev, "Hub-initiated %s disabled " - "at request of driver %s\n", - state_name, driver->name); - return xhci_get_timeout_no_hub_lpm(udev, state); + dev_dbg(&udev->dev, "Hub-initiated %s disabled at request of driver %s\n", + state_name, driver->name); + timeout = xhci_get_timeout_no_hub_lpm(udev, + state); + if (timeout == USB3_LPM_DISABLED) + return timeout; } } @@ -5077,11 +5110,18 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) hcd->has_tt = 1; } else { /* - * Some 3.1 hosts return sbrn 0x30, use xhci supported protocol - * minor revision instead of sbrn. Minor revision is a two digit - * BCD containing minor and sub-minor numbers, only show minor. + * Early xHCI 1.1 spec did not mention USB 3.1 capable hosts + * should return 0x31 for sbrn, or that the minor revision + * is a two digit BCD containig minor and sub-minor numbers. + * This was later clarified in xHCI 1.2. + * + * Some USB 3.1 capable hosts therefore have sbrn 0x30, and + * minor revision set to 0x1 instead of 0x10. */ - minor_rev = xhci->usb3_rhub.min_rev / 0x10; + if (xhci->usb3_rhub.min_rev == 0x1) + minor_rev = 1; + else + minor_rev = xhci->usb3_rhub.min_rev / 0x10; switch (minor_rev) { case 2: @@ -5198,8 +5238,16 @@ static void xhci_clear_tt_buffer_complete(struct usb_hcd *hcd, unsigned int ep_index; unsigned long flags; + /* + * udev might be NULL if tt buffer is cleared during a failed device + * enumeration due to a halted control endpoint. Usb core might + * have allocated a new udev for the next enumeration attempt. + */ + xhci = hcd_to_xhci(hcd); udev = (struct usb_device *)ep->hcpriv; + if (!udev) + return; slot_id = udev->slot_id; ep_index = xhci_get_endpoint_index(&ep->desc); diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 0a57c2cc8e5a..7a6b122c833f 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -716,6 +716,10 @@ static int mts_usb_probe(struct usb_interface *intf, } + if (ep_in_current != &ep_in_set[2]) { + MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n"); + return -ENODEV; + } if ( ep_out == -1 ) { MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index bdae62b2ffe0..9bce583aada3 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -47,16 +47,6 @@ config USB_SEVSEG To compile this driver as a module, choose M here: the module will be called usbsevseg. -config USB_RIO500 - tristate "USB Diamond Rio500 support" - help - Say Y here if you want to connect a USB Rio500 mp3 player to your - computer's USB port. Please read <file:Documentation/usb/rio.rst> - for more information. - - To compile this driver as a module, choose M here: the - module will be called rio500. - config USB_LEGOTOWER tristate "USB Lego Infrared Tower support" help diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 109f54f5b9aa..0d416eb624bb 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o -obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_EHSET_TEST_FIXTURE) += ehset.o obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 344d523b0502..6f5edb9fc61e 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -75,6 +75,7 @@ struct adu_device { char serial_number[8]; int open_count; /* number of times this port has been opened */ + unsigned long disconnected:1; char *read_buffer_primary; int read_buffer_length; @@ -116,7 +117,7 @@ static void adu_abort_transfers(struct adu_device *dev) { unsigned long flags; - if (dev->udev == NULL) + if (dev->disconnected) return; /* shutdown transfer */ @@ -148,6 +149,7 @@ static void adu_delete(struct adu_device *dev) kfree(dev->read_buffer_secondary); kfree(dev->interrupt_in_buffer); kfree(dev->interrupt_out_buffer); + usb_put_dev(dev->udev); kfree(dev); } @@ -243,7 +245,7 @@ static int adu_open(struct inode *inode, struct file *file) } dev = usb_get_intfdata(interface); - if (!dev || !dev->udev) { + if (!dev) { retval = -ENODEV; goto exit_no_device; } @@ -326,7 +328,7 @@ static int adu_release(struct inode *inode, struct file *file) } adu_release_internal(dev); - if (dev->udev == NULL) { + if (dev->disconnected) { /* the device was unplugged before the file was released */ if (!dev->open_count) /* ... and we're the last user */ adu_delete(dev); @@ -354,7 +356,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, return -ERESTARTSYS; /* verify that the device wasn't unplugged */ - if (dev->udev == NULL) { + if (dev->disconnected) { retval = -ENODEV; pr_err("No device or device unplugged %d\n", retval); goto exit; @@ -518,7 +520,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, goto exit_nolock; /* verify that the device wasn't unplugged */ - if (dev->udev == NULL) { + if (dev->disconnected) { retval = -ENODEV; pr_err("No device or device unplugged %d\n", retval); goto exit; @@ -663,7 +665,7 @@ static int adu_probe(struct usb_interface *interface, mutex_init(&dev->mtx); spin_lock_init(&dev->buflock); - dev->udev = udev; + dev->udev = usb_get_dev(udev); init_waitqueue_head(&dev->read_wait); init_waitqueue_head(&dev->write_wait); @@ -762,14 +764,18 @@ static void adu_disconnect(struct usb_interface *interface) dev = usb_get_intfdata(interface); - mutex_lock(&dev->mtx); /* not interruptible */ - dev->udev = NULL; /* poison */ usb_deregister_dev(interface, &adu_class); - mutex_unlock(&dev->mtx); + + usb_poison_urb(dev->interrupt_in_urb); + usb_poison_urb(dev->interrupt_out_urb); mutex_lock(&adutux_mutex); usb_set_intfdata(interface, NULL); + mutex_lock(&dev->mtx); /* not interruptible */ + dev->disconnected = 1; + mutex_unlock(&dev->mtx); + /* if the device is not opened, then we clean up right now */ if (!dev->open_count) adu_delete(dev); diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index cf5828ce927a..34e6cd6f40d3 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -98,6 +98,7 @@ static void chaoskey_free(struct chaoskey *dev) usb_free_urb(dev->urb); kfree(dev->name); kfree(dev->buf); + usb_put_intf(dev->interface); kfree(dev); } } @@ -145,6 +146,8 @@ static int chaoskey_probe(struct usb_interface *interface, if (dev == NULL) goto out; + dev->interface = usb_get_intf(interface); + dev->buf = kmalloc(size, GFP_KERNEL); if (dev->buf == NULL) @@ -174,8 +177,6 @@ static int chaoskey_probe(struct usb_interface *interface, goto out; } - dev->interface = interface; - dev->in_ep = in_ep; if (le16_to_cpu(udev->descriptor.idVendor) != ALEA_VENDOR_ID) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index f5bed9f29e56..dce44fbf031f 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -54,11 +54,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -/* Module parameters */ -static DEFINE_MUTEX(iowarrior_mutex); - static struct usb_driver iowarrior_driver; -static DEFINE_MUTEX(iowarrior_open_disc_lock); /*--------------*/ /* data */ @@ -87,6 +83,7 @@ struct iowarrior { char chip_serial[9]; /* the serial number string of the chip connected */ int report_size; /* number of bytes in a report */ u16 product_id; + struct usb_anchor submitted; }; /*--------------*/ @@ -243,6 +240,7 @@ static inline void iowarrior_delete(struct iowarrior *dev) kfree(dev->int_in_buffer); usb_free_urb(dev->int_in_urb); kfree(dev->read_queue); + usb_put_intf(dev->interface); kfree(dev); } @@ -424,11 +422,13 @@ static ssize_t iowarrior_write(struct file *file, retval = -EFAULT; goto error; } + usb_anchor_urb(int_out_urb, &dev->submitted); retval = usb_submit_urb(int_out_urb, GFP_KERNEL); if (retval) { dev_dbg(&dev->interface->dev, "submit error %d for urb nr.%d\n", retval, atomic_read(&dev->write_busy)); + usb_unanchor_urb(int_out_urb); goto error; } /* submit was ok */ @@ -477,8 +477,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, if (!buffer) return -ENOMEM; - /* lock this object */ - mutex_lock(&iowarrior_mutex); mutex_lock(&dev->mutex); /* verify that the device wasn't unplugged */ @@ -571,7 +569,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, error_out: /* unlock the device */ mutex_unlock(&dev->mutex); - mutex_unlock(&iowarrior_mutex); kfree(buffer); return retval; } @@ -586,27 +583,20 @@ static int iowarrior_open(struct inode *inode, struct file *file) int subminor; int retval = 0; - mutex_lock(&iowarrior_mutex); subminor = iminor(inode); interface = usb_find_interface(&iowarrior_driver, subminor); if (!interface) { - mutex_unlock(&iowarrior_mutex); - printk(KERN_ERR "%s - error, can't find device for minor %d\n", + pr_err("%s - error, can't find device for minor %d\n", __func__, subminor); return -ENODEV; } - mutex_lock(&iowarrior_open_disc_lock); dev = usb_get_intfdata(interface); - if (!dev) { - mutex_unlock(&iowarrior_open_disc_lock); - mutex_unlock(&iowarrior_mutex); + if (!dev) return -ENODEV; - } mutex_lock(&dev->mutex); - mutex_unlock(&iowarrior_open_disc_lock); /* Only one process can open each device, no sharing. */ if (dev->opened) { @@ -628,7 +618,6 @@ static int iowarrior_open(struct inode *inode, struct file *file) out: mutex_unlock(&dev->mutex); - mutex_unlock(&iowarrior_mutex); return retval; } @@ -764,11 +753,13 @@ static int iowarrior_probe(struct usb_interface *interface, init_waitqueue_head(&dev->write_wait); dev->udev = udev; - dev->interface = interface; + dev->interface = usb_get_intf(interface); iface_desc = interface->cur_altsetting; dev->product_id = le16_to_cpu(udev->descriptor.idProduct); + init_usb_anchor(&dev->submitted); + res = usb_find_last_int_in_endpoint(iface_desc, &dev->int_in_endpoint); if (res) { dev_err(&interface->dev, "no interrupt-in endpoint found\n"); @@ -836,7 +827,6 @@ static int iowarrior_probe(struct usb_interface *interface, if (retval) { /* something prevented us from registering this driver */ dev_err(&interface->dev, "Not able to get a minor for this device.\n"); - usb_set_intfdata(interface, NULL); goto error; } @@ -860,26 +850,15 @@ error: */ static void iowarrior_disconnect(struct usb_interface *interface) { - struct iowarrior *dev; - int minor; - - dev = usb_get_intfdata(interface); - mutex_lock(&iowarrior_open_disc_lock); - usb_set_intfdata(interface, NULL); - /* prevent device read, write and ioctl */ - dev->present = 0; - - minor = dev->minor; - mutex_unlock(&iowarrior_open_disc_lock); - /* give back our minor - this will call close() locks need to be dropped at this point*/ + struct iowarrior *dev = usb_get_intfdata(interface); + int minor = dev->minor; usb_deregister_dev(interface, &iowarrior_class); mutex_lock(&dev->mutex); /* prevent device read, write and ioctl */ - - mutex_unlock(&dev->mutex); + dev->present = 0; if (dev->opened) { /* There is a process that holds a filedescriptor to the device , @@ -887,10 +866,13 @@ static void iowarrior_disconnect(struct usb_interface *interface) Deleting the device is postponed until close() was called. */ usb_kill_urb(dev->int_in_urb); + usb_kill_anchored_urbs(&dev->submitted); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); + mutex_unlock(&dev->mutex); } else { /* no process is using the device, cleanup now */ + mutex_unlock(&dev->mutex); iowarrior_delete(dev); } diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 6581774bdfa4..f3108d85e768 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -153,6 +153,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in struct ld_usb { struct mutex mutex; /* locks this structure */ struct usb_interface *intf; /* save off the usb interface pointer */ + unsigned long disconnected:1; int open_count; /* number of times this port has been opened */ @@ -192,12 +193,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev) /* shutdown transfer */ if (dev->interrupt_in_running) { dev->interrupt_in_running = 0; - if (dev->intf) - usb_kill_urb(dev->interrupt_in_urb); + usb_kill_urb(dev->interrupt_in_urb); } if (dev->interrupt_out_busy) - if (dev->intf) - usb_kill_urb(dev->interrupt_out_urb); + usb_kill_urb(dev->interrupt_out_urb); } /** @@ -205,8 +204,6 @@ static void ld_usb_abort_transfers(struct ld_usb *dev) */ static void ld_usb_delete(struct ld_usb *dev) { - ld_usb_abort_transfers(dev); - /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -263,7 +260,7 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) resubmit: /* resubmit if we're still running */ - if (dev->interrupt_in_running && !dev->buffer_overflow && dev->intf) { + if (dev->interrupt_in_running && !dev->buffer_overflow) { retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); if (retval) { dev_err(&dev->intf->dev, @@ -392,7 +389,7 @@ static int ld_usb_release(struct inode *inode, struct file *file) retval = -ENODEV; goto unlock_exit; } - if (dev->intf == NULL) { + if (dev->disconnected) { /* the device was unplugged before the file was released */ mutex_unlock(&dev->mutex); /* unlock here as ld_usb_delete frees dev */ @@ -423,7 +420,7 @@ static __poll_t ld_usb_poll(struct file *file, poll_table *wait) dev = file->private_data; - if (!dev->intf) + if (dev->disconnected) return EPOLLERR | EPOLLHUP; poll_wait(file, &dev->read_wait, wait); @@ -462,7 +459,7 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, } /* verify that the device wasn't unplugged */ - if (dev->intf == NULL) { + if (dev->disconnected) { retval = -ENODEV; printk(KERN_ERR "ldusb: No device or device unplugged %d\n", retval); goto unlock_exit; @@ -542,7 +539,7 @@ static ssize_t ld_usb_write(struct file *file, const char __user *buffer, } /* verify that the device wasn't unplugged */ - if (dev->intf == NULL) { + if (dev->disconnected) { retval = -ENODEV; printk(KERN_ERR "ldusb: No device or device unplugged %d\n", retval); goto unlock_exit; @@ -764,6 +761,9 @@ static void ld_usb_disconnect(struct usb_interface *intf) /* give back our minor */ usb_deregister_dev(intf, &ld_usb_class); + usb_poison_urb(dev->interrupt_in_urb); + usb_poison_urb(dev->interrupt_out_urb); + mutex_lock(&dev->mutex); /* if the device is not opened, then we clean up right now */ @@ -771,7 +771,7 @@ static void ld_usb_disconnect(struct usb_interface *intf) mutex_unlock(&dev->mutex); ld_usb_delete(dev); } else { - dev->intf = NULL; + dev->disconnected = 1; /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 006cf13b2199..9d4c52a7ebe0 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -179,7 +179,6 @@ static const struct usb_device_id tower_table[] = { }; MODULE_DEVICE_TABLE (usb, tower_table); -static DEFINE_MUTEX(open_disc_mutex); #define LEGO_USB_TOWER_MINOR_BASE 160 @@ -191,6 +190,7 @@ struct lego_usb_tower { unsigned char minor; /* the starting minor number for this device */ int open_count; /* number of times this port has been opened */ + unsigned long disconnected:1; char* read_buffer; size_t read_buffer_length; /* this much came in */ @@ -290,14 +290,13 @@ static inline void lego_usb_tower_debug_data(struct device *dev, */ static inline void tower_delete (struct lego_usb_tower *dev) { - tower_abort_transfers (dev); - /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); kfree (dev->read_buffer); kfree (dev->interrupt_in_buffer); kfree (dev->interrupt_out_buffer); + usb_put_dev(dev->udev); kfree (dev); } @@ -332,18 +331,14 @@ static int tower_open (struct inode *inode, struct file *file) goto exit; } - mutex_lock(&open_disc_mutex); dev = usb_get_intfdata(interface); - if (!dev) { - mutex_unlock(&open_disc_mutex); retval = -ENODEV; goto exit; } /* lock this device */ if (mutex_lock_interruptible(&dev->lock)) { - mutex_unlock(&open_disc_mutex); retval = -ERESTARTSYS; goto exit; } @@ -351,12 +346,9 @@ static int tower_open (struct inode *inode, struct file *file) /* allow opening only once */ if (dev->open_count) { - mutex_unlock(&open_disc_mutex); retval = -EBUSY; goto unlock_exit; } - dev->open_count = 1; - mutex_unlock(&open_disc_mutex); /* reset the tower */ result = usb_control_msg (dev->udev, @@ -396,13 +388,14 @@ static int tower_open (struct inode *inode, struct file *file) dev_err(&dev->udev->dev, "Couldn't submit interrupt_in_urb %d\n", retval); dev->interrupt_in_running = 0; - dev->open_count = 0; goto unlock_exit; } /* save device in the file's private structure */ file->private_data = dev; + dev->open_count = 1; + unlock_exit: mutex_unlock(&dev->lock); @@ -423,10 +416,9 @@ static int tower_release (struct inode *inode, struct file *file) if (dev == NULL) { retval = -ENODEV; - goto exit_nolock; + goto exit; } - mutex_lock(&open_disc_mutex); if (mutex_lock_interruptible(&dev->lock)) { retval = -ERESTARTSYS; goto exit; @@ -438,7 +430,8 @@ static int tower_release (struct inode *inode, struct file *file) retval = -ENODEV; goto unlock_exit; } - if (dev->udev == NULL) { + + if (dev->disconnected) { /* the device was unplugged before the file was released */ /* unlock here as tower_delete frees dev */ @@ -456,10 +449,7 @@ static int tower_release (struct inode *inode, struct file *file) unlock_exit: mutex_unlock(&dev->lock); - exit: - mutex_unlock(&open_disc_mutex); -exit_nolock: return retval; } @@ -477,10 +467,9 @@ static void tower_abort_transfers (struct lego_usb_tower *dev) if (dev->interrupt_in_running) { dev->interrupt_in_running = 0; mb(); - if (dev->udev) - usb_kill_urb (dev->interrupt_in_urb); + usb_kill_urb(dev->interrupt_in_urb); } - if (dev->interrupt_out_busy && dev->udev) + if (dev->interrupt_out_busy) usb_kill_urb(dev->interrupt_out_urb); } @@ -516,7 +505,7 @@ static __poll_t tower_poll (struct file *file, poll_table *wait) dev = file->private_data; - if (!dev->udev) + if (dev->disconnected) return EPOLLERR | EPOLLHUP; poll_wait(file, &dev->read_wait, wait); @@ -563,7 +552,7 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, } /* verify that the device wasn't unplugged */ - if (dev->udev == NULL) { + if (dev->disconnected) { retval = -ENODEV; pr_err("No device or device unplugged %d\n", retval); goto unlock_exit; @@ -649,7 +638,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t } /* verify that the device wasn't unplugged */ - if (dev->udev == NULL) { + if (dev->disconnected) { retval = -ENODEV; pr_err("No device or device unplugged %d\n", retval); goto unlock_exit; @@ -759,7 +748,7 @@ static void tower_interrupt_in_callback (struct urb *urb) resubmit: /* resubmit if we're still running */ - if (dev->interrupt_in_running && dev->udev) { + if (dev->interrupt_in_running) { retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC); if (retval) dev_err(&dev->udev->dev, @@ -822,8 +811,9 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device mutex_init(&dev->lock); - dev->udev = udev; + dev->udev = usb_get_dev(udev); dev->open_count = 0; + dev->disconnected = 0; dev->read_buffer = NULL; dev->read_buffer_length = 0; @@ -891,8 +881,10 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device get_version_reply, sizeof(*get_version_reply), 1000); - if (result < 0) { - dev_err(idev, "LEGO USB Tower get version control request failed\n"); + if (result < sizeof(*get_version_reply)) { + if (result >= 0) + result = -EIO; + dev_err(idev, "get version request failed: %d\n", result); retval = result; goto error; } @@ -910,7 +902,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device if (retval) { /* something prevented us from registering this driver */ dev_err(idev, "Not able to get a minor for this device.\n"); - usb_set_intfdata (interface, NULL); goto error; } dev->minor = interface->minor; @@ -942,23 +933,24 @@ static void tower_disconnect (struct usb_interface *interface) int minor; dev = usb_get_intfdata (interface); - mutex_lock(&open_disc_mutex); - usb_set_intfdata (interface, NULL); minor = dev->minor; - /* give back our minor */ + /* give back our minor and prevent further open() */ usb_deregister_dev (interface, &tower_class); + /* stop I/O */ + usb_poison_urb(dev->interrupt_in_urb); + usb_poison_urb(dev->interrupt_out_urb); + mutex_lock(&dev->lock); - mutex_unlock(&open_disc_mutex); /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { mutex_unlock(&dev->lock); tower_delete (dev); } else { - dev->udev = NULL; + dev->disconnected = 1; /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c deleted file mode 100644 index 30cae5e1954d..000000000000 --- a/drivers/usb/misc/rio500.c +++ /dev/null @@ -1,554 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* -*- linux-c -*- */ - -/* - * Driver for USB Rio 500 - * - * Cesar Miquel (miquel@df.uba.ar) - * - * based on hp_scanner.c by David E. Nelson (dnelson@jump.net) - * - * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee). - * - * Changelog: - * 30/05/2003 replaced lock/unlock kernel with up/down - * Daniele Bellucci bellucda@tiscali.it - * */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/signal.h> -#include <linux/sched/signal.h> -#include <linux/mutex.h> -#include <linux/errno.h> -#include <linux/random.h> -#include <linux/poll.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/usb.h> -#include <linux/wait.h> - -#include "rio500_usb.h" - -#define DRIVER_AUTHOR "Cesar Miquel <miquel@df.uba.ar>" -#define DRIVER_DESC "USB Rio 500 driver" - -#define RIO_MINOR 64 - -/* stall/wait timeout for rio */ -#define NAK_TIMEOUT (HZ) - -#define IBUF_SIZE 0x1000 - -/* Size of the rio buffer */ -#define OBUF_SIZE 0x10000 - -struct rio_usb_data { - struct usb_device *rio_dev; /* init: probe_rio */ - unsigned int ifnum; /* Interface number of the USB device */ - int isopen; /* nz if open */ - int present; /* Device is present on the bus */ - char *obuf, *ibuf; /* transfer buffers */ - char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ - wait_queue_head_t wait_q; /* for timeouts */ -}; - -static DEFINE_MUTEX(rio500_mutex); -static struct rio_usb_data rio_instance; - -static int open_rio(struct inode *inode, struct file *file) -{ - struct rio_usb_data *rio = &rio_instance; - - /* against disconnect() */ - mutex_lock(&rio500_mutex); - - if (rio->isopen || !rio->present) { - mutex_unlock(&rio500_mutex); - return -EBUSY; - } - rio->isopen = 1; - - init_waitqueue_head(&rio->wait_q); - - - dev_info(&rio->rio_dev->dev, "Rio opened.\n"); - mutex_unlock(&rio500_mutex); - - return 0; -} - -static int close_rio(struct inode *inode, struct file *file) -{ - struct rio_usb_data *rio = &rio_instance; - - /* against disconnect() */ - mutex_lock(&rio500_mutex); - - rio->isopen = 0; - if (!rio->present) { - /* cleanup has been delayed */ - kfree(rio->ibuf); - kfree(rio->obuf); - rio->ibuf = NULL; - rio->obuf = NULL; - } else { - dev_info(&rio->rio_dev->dev, "Rio closed.\n"); - } - mutex_unlock(&rio500_mutex); - return 0; -} - -static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct RioCommand rio_cmd; - struct rio_usb_data *rio = &rio_instance; - void __user *data; - unsigned char *buffer; - int result, requesttype; - int retries; - int retval=0; - - mutex_lock(&rio500_mutex); - /* Sanity check to make sure rio is connected, powered, etc */ - if (rio->present == 0 || rio->rio_dev == NULL) { - retval = -ENODEV; - goto err_out; - } - - switch (cmd) { - case RIO_RECV_COMMAND: - data = (void __user *) arg; - if (data == NULL) - break; - if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand))) { - retval = -EFAULT; - goto err_out; - } - if (rio_cmd.length < 0 || rio_cmd.length > PAGE_SIZE) { - retval = -EINVAL; - goto err_out; - } - buffer = (unsigned char *) __get_free_page(GFP_KERNEL); - if (buffer == NULL) { - retval = -ENOMEM; - goto err_out; - } - if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length)) { - retval = -EFAULT; - free_page((unsigned long) buffer); - goto err_out; - } - - requesttype = rio_cmd.requesttype | USB_DIR_IN | - USB_TYPE_VENDOR | USB_RECIP_DEVICE; - dev_dbg(&rio->rio_dev->dev, - "sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n", - requesttype, rio_cmd.request, rio_cmd.value, - rio_cmd.index, rio_cmd.length); - /* Send rio control message */ - retries = 3; - while (retries) { - result = usb_control_msg(rio->rio_dev, - usb_rcvctrlpipe(rio-> rio_dev, 0), - rio_cmd.request, - requesttype, - rio_cmd.value, - rio_cmd.index, buffer, - rio_cmd.length, - jiffies_to_msecs(rio_cmd.timeout)); - if (result == -ETIMEDOUT) - retries--; - else if (result < 0) { - dev_err(&rio->rio_dev->dev, - "Error executing ioctrl. code = %d\n", - result); - retries = 0; - } else { - dev_dbg(&rio->rio_dev->dev, - "Executed ioctl. Result = %d (data=%02x)\n", - result, buffer[0]); - if (copy_to_user(rio_cmd.buffer, buffer, - rio_cmd.length)) { - free_page((unsigned long) buffer); - retval = -EFAULT; - goto err_out; - } - retries = 0; - } - - /* rio_cmd.buffer contains a raw stream of single byte - data which has been returned from rio. Data is - interpreted at application level. For data that - will be cast to data types longer than 1 byte, data - will be little_endian and will potentially need to - be swapped at the app level */ - - } - free_page((unsigned long) buffer); - break; - - case RIO_SEND_COMMAND: - data = (void __user *) arg; - if (data == NULL) - break; - if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand))) { - retval = -EFAULT; - goto err_out; - } - if (rio_cmd.length < 0 || rio_cmd.length > PAGE_SIZE) { - retval = -EINVAL; - goto err_out; - } - buffer = (unsigned char *) __get_free_page(GFP_KERNEL); - if (buffer == NULL) { - retval = -ENOMEM; - goto err_out; - } - if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length)) { - free_page((unsigned long)buffer); - retval = -EFAULT; - goto err_out; - } - - requesttype = rio_cmd.requesttype | USB_DIR_OUT | - USB_TYPE_VENDOR | USB_RECIP_DEVICE; - dev_dbg(&rio->rio_dev->dev, - "sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n", - requesttype, rio_cmd.request, rio_cmd.value, - rio_cmd.index, rio_cmd.length); - /* Send rio control message */ - retries = 3; - while (retries) { - result = usb_control_msg(rio->rio_dev, - usb_sndctrlpipe(rio-> rio_dev, 0), - rio_cmd.request, - requesttype, - rio_cmd.value, - rio_cmd.index, buffer, - rio_cmd.length, - jiffies_to_msecs(rio_cmd.timeout)); - if (result == -ETIMEDOUT) - retries--; - else if (result < 0) { - dev_err(&rio->rio_dev->dev, - "Error executing ioctrl. code = %d\n", - result); - retries = 0; - } else { - dev_dbg(&rio->rio_dev->dev, - "Executed ioctl. Result = %d\n", result); - retries = 0; - - } - - } - free_page((unsigned long) buffer); - break; - - default: - retval = -ENOTTY; - break; - } - - -err_out: - mutex_unlock(&rio500_mutex); - return retval; -} - -static ssize_t -write_rio(struct file *file, const char __user *buffer, - size_t count, loff_t * ppos) -{ - DEFINE_WAIT(wait); - struct rio_usb_data *rio = &rio_instance; - - unsigned long copy_size; - unsigned long bytes_written = 0; - unsigned int partial; - - int result = 0; - int maxretry; - int errn = 0; - int intr; - - intr = mutex_lock_interruptible(&rio500_mutex); - if (intr) - return -EINTR; - /* Sanity check to make sure rio is connected, powered, etc */ - if (rio->present == 0 || rio->rio_dev == NULL) { - mutex_unlock(&rio500_mutex); - return -ENODEV; - } - - - - do { - unsigned long thistime; - char *obuf = rio->obuf; - - thistime = copy_size = - (count >= OBUF_SIZE) ? OBUF_SIZE : count; - if (copy_from_user(rio->obuf, buffer, copy_size)) { - errn = -EFAULT; - goto error; - } - maxretry = 5; - while (thistime) { - if (!rio->rio_dev) { - errn = -ENODEV; - goto error; - } - if (signal_pending(current)) { - mutex_unlock(&rio500_mutex); - return bytes_written ? bytes_written : -EINTR; - } - - result = usb_bulk_msg(rio->rio_dev, - usb_sndbulkpipe(rio->rio_dev, 2), - obuf, thistime, &partial, 5000); - - dev_dbg(&rio->rio_dev->dev, - "write stats: result:%d thistime:%lu partial:%u\n", - result, thistime, partial); - - if (result == -ETIMEDOUT) { /* NAK - so hold for a while */ - if (!maxretry--) { - errn = -ETIME; - goto error; - } - prepare_to_wait(&rio->wait_q, &wait, TASK_INTERRUPTIBLE); - schedule_timeout(NAK_TIMEOUT); - finish_wait(&rio->wait_q, &wait); - continue; - } else if (!result && partial) { - obuf += partial; - thistime -= partial; - } else - break; - } - if (result) { - dev_err(&rio->rio_dev->dev, "Write Whoops - %x\n", - result); - errn = -EIO; - goto error; - } - bytes_written += copy_size; - count -= copy_size; - buffer += copy_size; - } while (count > 0); - - mutex_unlock(&rio500_mutex); - - return bytes_written ? bytes_written : -EIO; - -error: - mutex_unlock(&rio500_mutex); - return errn; -} - -static ssize_t -read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) -{ - DEFINE_WAIT(wait); - struct rio_usb_data *rio = &rio_instance; - ssize_t read_count; - unsigned int partial; - int this_read; - int result; - int maxretry = 10; - char *ibuf; - int intr; - - intr = mutex_lock_interruptible(&rio500_mutex); - if (intr) - return -EINTR; - /* Sanity check to make sure rio is connected, powered, etc */ - if (rio->present == 0 || rio->rio_dev == NULL) { - mutex_unlock(&rio500_mutex); - return -ENODEV; - } - - ibuf = rio->ibuf; - - read_count = 0; - - - while (count > 0) { - if (signal_pending(current)) { - mutex_unlock(&rio500_mutex); - return read_count ? read_count : -EINTR; - } - if (!rio->rio_dev) { - mutex_unlock(&rio500_mutex); - return -ENODEV; - } - this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; - - result = usb_bulk_msg(rio->rio_dev, - usb_rcvbulkpipe(rio->rio_dev, 1), - ibuf, this_read, &partial, - 8000); - - dev_dbg(&rio->rio_dev->dev, - "read stats: result:%d this_read:%u partial:%u\n", - result, this_read, partial); - - if (partial) { - count = this_read = partial; - } else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */ - if (!maxretry--) { - mutex_unlock(&rio500_mutex); - dev_err(&rio->rio_dev->dev, - "read_rio: maxretry timeout\n"); - return -ETIME; - } - prepare_to_wait(&rio->wait_q, &wait, TASK_INTERRUPTIBLE); - schedule_timeout(NAK_TIMEOUT); - finish_wait(&rio->wait_q, &wait); - continue; - } else if (result != -EREMOTEIO) { - mutex_unlock(&rio500_mutex); - dev_err(&rio->rio_dev->dev, - "Read Whoops - result:%d partial:%u this_read:%u\n", - result, partial, this_read); - return -EIO; - } else { - mutex_unlock(&rio500_mutex); - return (0); - } - - if (this_read) { - if (copy_to_user(buffer, ibuf, this_read)) { - mutex_unlock(&rio500_mutex); - return -EFAULT; - } - count -= this_read; - read_count += this_read; - buffer += this_read; - } - } - mutex_unlock(&rio500_mutex); - return read_count; -} - -static const struct file_operations usb_rio_fops = { - .owner = THIS_MODULE, - .read = read_rio, - .write = write_rio, - .unlocked_ioctl = ioctl_rio, - .open = open_rio, - .release = close_rio, - .llseek = noop_llseek, -}; - -static struct usb_class_driver usb_rio_class = { - .name = "rio500%d", - .fops = &usb_rio_fops, - .minor_base = RIO_MINOR, -}; - -static int probe_rio(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct rio_usb_data *rio = &rio_instance; - int retval = -ENOMEM; - char *ibuf, *obuf; - - if (rio->present) { - dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum); - return -EBUSY; - } - dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum); - - obuf = kmalloc(OBUF_SIZE, GFP_KERNEL); - if (!obuf) { - dev_err(&dev->dev, - "probe_rio: Not enough memory for the output buffer\n"); - goto err_obuf; - } - dev_dbg(&intf->dev, "obuf address: %p\n", obuf); - - ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL); - if (!ibuf) { - dev_err(&dev->dev, - "probe_rio: Not enough memory for the input buffer\n"); - goto err_ibuf; - } - dev_dbg(&intf->dev, "ibuf address: %p\n", ibuf); - - mutex_lock(&rio500_mutex); - rio->rio_dev = dev; - rio->ibuf = ibuf; - rio->obuf = obuf; - rio->present = 1; - mutex_unlock(&rio500_mutex); - - retval = usb_register_dev(intf, &usb_rio_class); - if (retval) { - dev_err(&dev->dev, - "Not able to get a minor for this device.\n"); - goto err_register; - } - - usb_set_intfdata(intf, rio); - return retval; - - err_register: - mutex_lock(&rio500_mutex); - rio->present = 0; - mutex_unlock(&rio500_mutex); - err_ibuf: - kfree(obuf); - err_obuf: - return retval; -} - -static void disconnect_rio(struct usb_interface *intf) -{ - struct rio_usb_data *rio = usb_get_intfdata (intf); - - usb_set_intfdata (intf, NULL); - if (rio) { - usb_deregister_dev(intf, &usb_rio_class); - - mutex_lock(&rio500_mutex); - if (rio->isopen) { - rio->isopen = 0; - /* better let it finish - the release will do whats needed */ - rio->rio_dev = NULL; - mutex_unlock(&rio500_mutex); - return; - } - kfree(rio->ibuf); - kfree(rio->obuf); - - dev_info(&intf->dev, "USB Rio disconnected.\n"); - - rio->present = 0; - mutex_unlock(&rio500_mutex); - } -} - -static const struct usb_device_id rio_table[] = { - { USB_DEVICE(0x0841, 1) }, /* Rio 500 */ - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, rio_table); - -static struct usb_driver rio_driver = { - .name = "rio500", - .probe = probe_rio, - .disconnect = disconnect_rio, - .id_table = rio_table, -}; - -module_usb_driver(rio_driver); - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); - diff --git a/drivers/usb/misc/rio500_usb.h b/drivers/usb/misc/rio500_usb.h deleted file mode 100644 index 6db7a5863496..000000000000 --- a/drivers/usb/misc/rio500_usb.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* ---------------------------------------------------------------------- - Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) - ---------------------------------------------------------------------- */ - -#define RIO_SEND_COMMAND 0x1 -#define RIO_RECV_COMMAND 0x2 - -#define RIO_DIR_OUT 0x0 -#define RIO_DIR_IN 0x1 - -struct RioCommand { - short length; - int request; - int requesttype; - int value; - int index; - void __user *buffer; - int timeout; -}; diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 9ba4a4e68d91..61e9e987fe4a 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/mutex.h> +#include <linux/rwsem.h> #include <linux/uaccess.h> #include <linux/usb.h> @@ -29,16 +30,12 @@ #define IOCTL_GET_DRV_VERSION 2 -static DEFINE_MUTEX(lcd_mutex); static const struct usb_device_id id_table[] = { { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); -static DEFINE_MUTEX(open_disc_mutex); - - struct usb_lcd { struct usb_device *udev; /* init: probe_lcd */ struct usb_interface *interface; /* the interface for @@ -57,6 +54,8 @@ struct usb_lcd { using up all RAM */ struct usb_anchor submitted; /* URBs to wait for before suspend */ + struct rw_semaphore io_rwsem; + unsigned long disconnected:1; }; #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) @@ -81,40 +80,29 @@ static int lcd_open(struct inode *inode, struct file *file) struct usb_interface *interface; int subminor, r; - mutex_lock(&lcd_mutex); subminor = iminor(inode); interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { - mutex_unlock(&lcd_mutex); - printk(KERN_ERR "USBLCD: %s - error, can't find device for minor %d\n", + pr_err("USBLCD: %s - error, can't find device for minor %d\n", __func__, subminor); return -ENODEV; } - mutex_lock(&open_disc_mutex); dev = usb_get_intfdata(interface); - if (!dev) { - mutex_unlock(&open_disc_mutex); - mutex_unlock(&lcd_mutex); - return -ENODEV; - } /* increment our usage count for the device */ kref_get(&dev->kref); - mutex_unlock(&open_disc_mutex); /* grab a power reference */ r = usb_autopm_get_interface(interface); if (r < 0) { kref_put(&dev->kref, lcd_delete); - mutex_unlock(&lcd_mutex); return r; } /* save our object in the file's private structure */ file->private_data = dev; - mutex_unlock(&lcd_mutex); return 0; } @@ -142,6 +130,13 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, dev = file->private_data; + down_read(&dev->io_rwsem); + + if (dev->disconnected) { + retval = -ENODEV; + goto out_up_io; + } + /* do a blocking bulk read to get data from the device */ retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, @@ -158,6 +153,9 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, retval = bytes_read; } +out_up_io: + up_read(&dev->io_rwsem); + return retval; } @@ -173,14 +171,12 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case IOCTL_GET_HARD_VERSION: - mutex_lock(&lcd_mutex); bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); sprintf(buf, "%1d%1d.%1d%1d", (bcdDevice & 0xF000)>>12, (bcdDevice & 0xF00)>>8, (bcdDevice & 0xF0)>>4, (bcdDevice & 0xF)); - mutex_unlock(&lcd_mutex); if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; @@ -237,11 +233,18 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, if (r < 0) return -EINTR; + down_read(&dev->io_rwsem); + + if (dev->disconnected) { + retval = -ENODEV; + goto err_up_io; + } + /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; - goto err_no_buf; + goto err_up_io; } buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, @@ -278,6 +281,7 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, the USB core will eventually free it entirely */ usb_free_urb(urb); + up_read(&dev->io_rwsem); exit: return count; error_unanchor: @@ -285,7 +289,8 @@ error_unanchor: error: usb_free_coherent(dev->udev, count, buf, urb->transfer_dma); usb_free_urb(urb); -err_no_buf: +err_up_io: + up_read(&dev->io_rwsem); up(&dev->limit_sem); return retval; } @@ -325,6 +330,7 @@ static int lcd_probe(struct usb_interface *interface, kref_init(&dev->kref); sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); + init_rwsem(&dev->io_rwsem); init_usb_anchor(&dev->submitted); dev->udev = usb_get_dev(interface_to_usbdev(interface)); @@ -365,7 +371,6 @@ static int lcd_probe(struct usb_interface *interface, /* something prevented us from registering this driver */ dev_err(&interface->dev, "Not able to get a minor for this device.\n"); - usb_set_intfdata(interface, NULL); goto error; } @@ -411,17 +416,18 @@ static int lcd_resume(struct usb_interface *intf) static void lcd_disconnect(struct usb_interface *interface) { - struct usb_lcd *dev; + struct usb_lcd *dev = usb_get_intfdata(interface); int minor = interface->minor; - mutex_lock(&open_disc_mutex); - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - mutex_unlock(&open_disc_mutex); - /* give back our minor */ usb_deregister_dev(interface, &lcd_class); + down_write(&dev->io_rwsem); + dev->disconnected = 1; + up_write(&dev->io_rwsem); + + usb_kill_anchored_urbs(&dev->submitted); + /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 6715a128e6c8..be0505b8b5d4 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -60,6 +60,7 @@ struct usb_yurex { struct kref kref; struct mutex io_mutex; + unsigned long disconnected:1; struct fasync_struct *async_queue; wait_queue_head_t waitq; @@ -107,6 +108,7 @@ static void yurex_delete(struct kref *kref) dev->int_buffer, dev->urb->transfer_dma); usb_free_urb(dev->urb); } + usb_put_intf(dev->interface); usb_put_dev(dev->udev); kfree(dev); } @@ -132,6 +134,7 @@ static void yurex_interrupt(struct urb *urb) switch (status) { case 0: /*success*/ break; + /* The device is terminated or messed up, give up */ case -EOVERFLOW: dev_err(&dev->interface->dev, "%s - overflow with length %d, actual length is %d\n", @@ -140,12 +143,13 @@ static void yurex_interrupt(struct urb *urb) case -ENOENT: case -ESHUTDOWN: case -EILSEQ: - /* The device is terminated, clean up */ + case -EPROTO: + case -ETIME: return; default: dev_err(&dev->interface->dev, "%s - unknown status received: %d\n", __func__, status); - goto exit; + return; } /* handle received message */ @@ -177,7 +181,6 @@ static void yurex_interrupt(struct urb *urb) break; } -exit: retval = usb_submit_urb(dev->urb, GFP_ATOMIC); if (retval) { dev_err(&dev->interface->dev, "%s - usb_submit_urb failed: %d\n", @@ -204,7 +207,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_ init_waitqueue_head(&dev->waitq); dev->udev = usb_get_dev(interface_to_usbdev(interface)); - dev->interface = interface; + dev->interface = usb_get_intf(interface); /* set up the endpoint information */ iface_desc = interface->cur_altsetting; @@ -315,8 +318,9 @@ static void yurex_disconnect(struct usb_interface *interface) /* prevent more I/O from starting */ usb_poison_urb(dev->urb); + usb_poison_urb(dev->cntl_urb); mutex_lock(&dev->io_mutex); - dev->interface = NULL; + dev->disconnected = 1; mutex_unlock(&dev->io_mutex); /* wakeup waiters */ @@ -404,7 +408,7 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, dev = file->private_data; mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* already disconnected */ + if (dev->disconnected) { /* already disconnected */ mutex_unlock(&dev->io_mutex); return -ENODEV; } @@ -439,7 +443,7 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer, goto error; mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* already disconnected */ + if (dev->disconnected) { /* already disconnected */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; goto error; diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index d1a0a35ecfff..0824099b905e 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -211,6 +211,7 @@ struct usbhs_priv; /* DCPCTR */ #define BSTS (1 << 15) /* Buffer Status */ #define SUREQ (1 << 14) /* Sending SETUP Token */ +#define INBUFM (1 << 14) /* (PIPEnCTR) Transfer Buffer Monitor */ #define CSSTS (1 << 12) /* CSSTS Status */ #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ #define SQCLR (1 << 8) /* Toggle Bit Clear */ diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 2a01ceb71641..86637cd066cf 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -89,7 +89,7 @@ static void __usbhsf_pkt_del(struct usbhs_pkt *pkt) list_del_init(&pkt->node); } -static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) +struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) { return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node); } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 88d1816bcda2..c3d3cc35cee0 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -97,5 +97,6 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, void *buf, int len, int zero, int sequence); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); void usbhs_pkt_start(struct usbhs_pipe *pipe); +struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe); #endif /* RENESAS_USB_FIFO_H */ diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 4d571a5205e2..e5ef56991dba 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -722,8 +722,7 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); struct device *dev = usbhsg_gpriv_to_dev(gpriv); unsigned long flags; - - usbhsg_pipe_disable(uep); + int ret = 0; dev_dbg(dev, "set halt %d (pipe %d)\n", halt, usbhs_pipe_number(pipe)); @@ -731,6 +730,18 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) /******************** spin lock ********************/ usbhs_lock(priv, flags); + /* + * According to usb_ep_set_halt()'s description, this function should + * return -EAGAIN if the IN endpoint has any queue or data. Note + * that the usbhs_pipe_is_dir_in() returns false if the pipe is an + * IN endpoint in the gadget mode. + */ + if (!usbhs_pipe_is_dir_in(pipe) && (__usbhsf_pkt_get(pipe) || + usbhs_pipe_contains_transmittable_data(pipe))) { + ret = -EAGAIN; + goto out; + } + if (halt) usbhs_pipe_stall(pipe); else @@ -741,10 +752,11 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) else usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE); +out: usbhs_unlock(priv, flags); /******************** spin unlock ******************/ - return 0; + return ret; } static int usbhsg_ep_set_halt(struct usb_ep *ep, int value) diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index c4922b96c93b..9e5afdde1adb 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -277,6 +277,21 @@ int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe) return -EBUSY; } +bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe) +{ + u16 val; + + /* Do not support for DCP pipe */ + if (usbhs_pipe_is_dcp(pipe)) + return false; + + val = usbhsp_pipectrl_get(pipe); + if (val & INBUFM) + return true; + + return false; +} + /* * PID ctrl */ diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 3080423e600c..3b130529408b 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -83,6 +83,7 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe); void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe, int needs_bfre, int bfre_enable); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); +bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f0688c44b04c..25e81faf4c24 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1030,6 +1030,9 @@ static const struct usb_device_id id_table_combined[] = { /* EZPrototypes devices */ { USB_DEVICE(EZPROTOTYPES_VID, HJELMSLUND_USB485_ISO_PID) }, { USB_DEVICE_INTERFACE_NUMBER(UNJO_VID, UNJO_ISODEBUG_V1_PID, 1) }, + /* Sienna devices */ + { USB_DEVICE(FTDI_VID, FTDI_SIENNA_PID) }, + { USB_DEVICE(ECHELON_VID, ECHELON_U20_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index f12d806220b4..22d66217cb41 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -39,6 +39,9 @@ #define FTDI_LUMEL_PD12_PID 0x6002 +/* Sienna Serial Interface by Secyourit GmbH */ +#define FTDI_SIENNA_PID 0x8348 + /* Cyber Cortex AV by Fabulous Silicon (http://fabuloussilicon.com) */ #define CYBER_CORTEX_AV_PID 0x8698 @@ -689,6 +692,12 @@ #define BANDB_ZZ_PROG1_USB_PID 0xBA02 /* + * Echelon USB Serial Interface + */ +#define ECHELON_VID 0x0920 +#define ECHELON_U20_PID 0x7500 + +/* * Intrepid Control Systems (http://www.intrepidcs.com/) ValueCAN and NeoVI */ #define INTREPID_VID 0x093C diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index d34779fe4a8d..e66a59ef43a1 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1741,8 +1741,8 @@ static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint, ep_desc = find_ep(serial, endpoint); if (!ep_desc) { - /* leak the urb, something's wrong and the callers don't care */ - return urb; + usb_free_urb(urb); + return NULL; } if (usb_endpoint_xfer_int(ep_desc)) { ep_type_name = "INT"; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 38e920ac7f82..06ab016be0b6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -419,6 +419,7 @@ static void option_instat_callback(struct urb *urb); #define CINTERION_PRODUCT_PH8_AUDIO 0x0083 #define CINTERION_PRODUCT_AHXX_2RMNET 0x0084 #define CINTERION_PRODUCT_AHXX_AUDIO 0x0085 +#define CINTERION_PRODUCT_CLS8 0x00b0 /* Olivetti products */ #define OLIVETTI_VENDOR_ID 0x0b3c @@ -1154,6 +1155,14 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1050, 0xff), /* Telit FN980 (rmnet) */ + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1051, 0xff), /* Telit FN980 (MBIM) */ + .driver_info = NCTRL(0) | RSVD(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1052, 0xff), /* Telit FN980 (RNDIS) */ + .driver_info = NCTRL(2) | RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1053, 0xff), /* Telit FN980 (ECM) */ + .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), @@ -1847,6 +1856,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) }, + { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_CLS8, 0xff), + .driver_info = RSVD(0) | RSVD(4) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) }, { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) }, diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a3179fea38c8..8f066bb55d7d 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -314,10 +314,7 @@ static void serial_cleanup(struct tty_struct *tty) serial = port->serial; owner = serial->type->driver.owner; - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) - usb_autopm_put_interface(serial->interface); - mutex_unlock(&serial->disc_mutex); + usb_autopm_put_interface(serial->interface); usb_serial_put(serial); module_put(owner); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 96562744101c..5f61d9977a15 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4409,18 +4409,20 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, /* USB data support is optional */ ret = fwnode_property_read_string(fwnode, "data-role", &cap_str); if (ret == 0) { - port->typec_caps.data = typec_find_port_data_role(cap_str); - if (port->typec_caps.data < 0) - return -EINVAL; + ret = typec_find_port_data_role(cap_str); + if (ret < 0) + return ret; + port->typec_caps.data = ret; } ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); if (ret < 0) return ret; - port->typec_caps.type = typec_find_port_power_role(cap_str); - if (port->typec_caps.type < 0) - return -EINVAL; + ret = typec_find_port_power_role(cap_str); + if (ret < 0) + return ret; + port->typec_caps.type = ret; port->port_type = port->typec_caps.type; if (port->port_type == TYPEC_PORT_SNK) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 6c103697c582..d99700cb4dca 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -75,6 +75,8 @@ static int ucsi_displayport_enter(struct typec_altmode *alt) if (cur != 0xff) { mutex_unlock(&dp->con->lock); + if (dp->con->port_altmode[cur] == alt) + return 0; return -EBUSY; } diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 907e20e1a71e..d772fce51905 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -195,7 +195,6 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; - bool run_isr; /* flag to call ISR routine during resume */ struct work_struct pm_work; }; @@ -224,18 +223,6 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) if (quirks && quirks->max_read_len) max_read_len = quirks->max_read_len; - if (uc->fw_build == CCG_FW_BUILD_NVIDIA && - uc->fw_version <= CCG_OLD_FW_VERSION) { - mutex_lock(&uc->lock); - /* - * Do not schedule pm_work to run ISR in - * ucsi_ccg_runtime_resume() after pm_runtime_get_sync() - * since we are already in ISR path. - */ - uc->run_isr = false; - mutex_unlock(&uc->lock); - } - pm_runtime_get_sync(uc->dev); while (rem_len > 0) { msgs[1].buf = &data[len - rem_len]; @@ -278,18 +265,6 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) msgs[0].len = len + sizeof(rab); msgs[0].buf = buf; - if (uc->fw_build == CCG_FW_BUILD_NVIDIA && - uc->fw_version <= CCG_OLD_FW_VERSION) { - mutex_lock(&uc->lock); - /* - * Do not schedule pm_work to run ISR in - * ucsi_ccg_runtime_resume() after pm_runtime_get_sync() - * since we are already in ISR path. - */ - uc->run_isr = false; - mutex_unlock(&uc->lock); - } - pm_runtime_get_sync(uc->dev); status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (status < 0) { @@ -1130,7 +1105,6 @@ static int ucsi_ccg_probe(struct i2c_client *client, uc->ppm.sync = ucsi_ccg_sync; uc->dev = dev; uc->client = client; - uc->run_isr = true; mutex_init(&uc->lock); INIT_WORK(&uc->work, ccg_update_firmware); INIT_WORK(&uc->pm_work, ccg_pm_workaround_work); @@ -1188,6 +1162,8 @@ static int ucsi_ccg_probe(struct i2c_client *client, pm_runtime_set_active(uc->dev); pm_runtime_enable(uc->dev); + pm_runtime_use_autosuspend(uc->dev); + pm_runtime_set_autosuspend_delay(uc->dev, 5000); pm_runtime_idle(uc->dev); return 0; @@ -1229,7 +1205,6 @@ static int ucsi_ccg_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct ucsi_ccg *uc = i2c_get_clientdata(client); - bool schedule = true; /* * Firmware version 3.1.10 or earlier, built for NVIDIA has known issue @@ -1237,17 +1212,8 @@ static int ucsi_ccg_runtime_resume(struct device *dev) * Schedule a work to call ISR as a workaround. */ if (uc->fw_build == CCG_FW_BUILD_NVIDIA && - uc->fw_version <= CCG_OLD_FW_VERSION) { - mutex_lock(&uc->lock); - if (!uc->run_isr) { - uc->run_isr = true; - schedule = false; - } - mutex_unlock(&uc->lock); - - if (schedule) - schedule_work(&uc->pm_work); - } + uc->fw_version <= CCG_OLD_FW_VERSION) + schedule_work(&uc->pm_work); return 0; } diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index c31d17d05810..2dc58766273a 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -61,6 +61,7 @@ struct usb_skel { spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ + unsigned long disconnected:1; wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ }; #define to_skel_dev(d) container_of(d, struct usb_skel, kref) @@ -73,6 +74,7 @@ static void skel_delete(struct kref *kref) struct usb_skel *dev = to_skel_dev(kref); usb_free_urb(dev->bulk_in_urb); + usb_put_intf(dev->interface); usb_put_dev(dev->udev); kfree(dev->bulk_in_buffer); kfree(dev); @@ -124,10 +126,7 @@ static int skel_release(struct inode *inode, struct file *file) return -ENODEV; /* allow the device to be autosuspended */ - mutex_lock(&dev->io_mutex); - if (dev->interface) - usb_autopm_put_interface(dev->interface); - mutex_unlock(&dev->io_mutex); + usb_autopm_put_interface(dev->interface); /* decrement the count on our device */ kref_put(&dev->kref, skel_delete); @@ -231,8 +230,7 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, dev = file->private_data; - /* if we cannot read at all, return EOF */ - if (!dev->bulk_in_urb || !count) + if (!count) return 0; /* no concurrent readers */ @@ -240,7 +238,7 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, if (rv < 0) return rv; - if (!dev->interface) { /* disconnect() was called */ + if (dev->disconnected) { /* disconnect() was called */ rv = -ENODEV; goto exit; } @@ -422,7 +420,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, /* this lock makes sure we don't submit URBs to gone devices */ mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* disconnect() was called */ + if (dev->disconnected) { /* disconnect() was called */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; goto error; @@ -507,7 +505,7 @@ static int skel_probe(struct usb_interface *interface, init_waitqueue_head(&dev->bulk_in_wait); dev->udev = usb_get_dev(interface_to_usbdev(interface)); - dev->interface = interface; + dev->interface = usb_get_intf(interface); /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ @@ -573,9 +571,10 @@ static void skel_disconnect(struct usb_interface *interface) /* prevent more I/O from starting */ mutex_lock(&dev->io_mutex); - dev->interface = NULL; + dev->disconnected = 1; mutex_unlock(&dev->io_mutex); + usb_kill_urb(dev->bulk_in_urb); usb_kill_anchored_urbs(&dev->submitted); /* decrement our usage count */ diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 585a84d319bd..65850e9c7190 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1195,12 +1195,12 @@ static int vhci_start(struct usb_hcd *hcd) if (id == 0 && usb_hcd_is_primary_hcd(hcd)) { err = vhci_init_attr_group(); if (err) { - pr_err("init attr group\n"); + dev_err(hcd_dev(hcd), "init attr group failed, err = %d\n", err); return err; } err = sysfs_create_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); if (err) { - pr_err("create sysfs files\n"); + dev_err(hcd_dev(hcd), "create sysfs files failed, err = %d\n", err); vhci_finish_attr_group(); return err; } diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index 228a89b9bdd1..16f60c1e1766 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -18,23 +18,6 @@ obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o # How to generate logo's -# Use logo-cfiles to retrieve list of .c files to be built -logo-cfiles = $(notdir $(patsubst %.$(2), %.c, \ - $(wildcard $(srctree)/$(src)/*$(1).$(2)))) - - -# Mono logos -extra-y += $(call logo-cfiles,_mono,pbm) - -# VGA16 logos -extra-y += $(call logo-cfiles,_vga16,ppm) - -# 224 Logos -extra-y += $(call logo-cfiles,_clut224,ppm) - -# Gray 256 -extra-y += $(call logo-cfiles,_gray256,pgm) - pnmtologo := scripts/pnmtologo # Create commands like "pnmtologo -t mono -n logo_mac_mono -o ..." @@ -55,5 +38,5 @@ $(obj)/%_clut224.c: $(src)/%_clut224.ppm $(pnmtologo) FORCE $(obj)/%_gray256.c: $(src)/%_gray256.pgm $(pnmtologo) FORCE $(call if_changed,logo) -# Files generated that shall be removed upon make clean -clean-files := *.o *_mono.c *_vga16.c *_clut224.c *_gray256.c +# generated C files +targets += *_mono.c *_vga16.c *_clut224.c *_gray256.c diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c index 75fd140b02ff..43c391626a00 100644 --- a/drivers/virt/vboxguest/vboxguest_utils.c +++ b/drivers/virt/vboxguest/vboxguest_utils.c @@ -220,6 +220,8 @@ static int hgcm_call_preprocess_linaddr( if (!bounce_buf) return -ENOMEM; + *bounce_buf_ret = bounce_buf; + if (copy_in) { ret = copy_from_user(bounce_buf, (void __user *)buf, len); if (ret) @@ -228,7 +230,6 @@ static int hgcm_call_preprocess_linaddr( memset(bounce_buf, 0, len); } - *bounce_buf_ret = bounce_buf; hgcm_call_add_pagelist_size(bounce_buf, len, extra); return 0; } diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index ebed495b9e69..b7847636501d 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -103,6 +103,7 @@ config W1_SLAVE_DS2438 config W1_SLAVE_DS250X tristate "512b/1kb/16kb EPROM family support" + select CRC16 help Say Y here if you want to use a 1-wire 512b/1kb/16kb EPROM family device (DS250x). diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 4e11de6cde81..5bae515c8e25 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -156,8 +156,10 @@ static DECLARE_DELAYED_WORK(balloon_worker, balloon_process); (GFP_HIGHUSER | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC) /* balloon_append: add the given page to the balloon. */ -static void __balloon_append(struct page *page) +static void balloon_append(struct page *page) { + __SetPageOffline(page); + /* Lowmem is re-populated first, so highmem pages go at list tail. */ if (PageHighMem(page)) { list_add_tail(&page->lru, &ballooned_pages); @@ -169,11 +171,6 @@ static void __balloon_append(struct page *page) wake_up(&balloon_wq); } -static void balloon_append(struct page *page) -{ - __balloon_append(page); -} - /* balloon_retrieve: rescue a page from the balloon, if it is not empty. */ static struct page *balloon_retrieve(bool require_lowmem) { @@ -192,6 +189,7 @@ static struct page *balloon_retrieve(bool require_lowmem) else balloon_stats.balloon_low--; + __ClearPageOffline(page); return page; } @@ -377,8 +375,7 @@ static void xen_online_page(struct page *page, unsigned int order) for (i = 0; i < size; i++) { p = pfn_to_page(start_pfn + i); __online_page_set_limits(p); - __SetPageOffline(p); - __balloon_append(p); + balloon_append(p); } mutex_unlock(&balloon_mutex); } @@ -444,7 +441,6 @@ static enum bp_state increase_reservation(unsigned long nr_pages) xenmem_reservation_va_mapping_update(1, &page, &frame_list[i]); /* Relinquish the page back to the allocator. */ - __ClearPageOffline(page); free_reserved_page(page); } @@ -471,7 +467,6 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) state = BP_EAGAIN; break; } - __SetPageOffline(page); adjust_managed_page_count(page, -1); xenmem_reservation_scrub_page(page); list_add(&page->lru, &pages); @@ -611,7 +606,6 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages) while (pgno < nr_pages) { page = balloon_retrieve(true); if (page) { - __ClearPageOffline(page); pages[pgno++] = page; #ifdef CONFIG_XEN_HAVE_PVMMU /* @@ -653,10 +647,8 @@ void free_xenballooned_pages(int nr_pages, struct page **pages) mutex_lock(&balloon_mutex); for (i = 0; i < nr_pages; i++) { - if (pages[i]) { - __SetPageOffline(pages[i]); + if (pages[i]) balloon_append(pages[i]); - } } balloon_stats.target_unpopulated -= nr_pages; @@ -674,7 +666,6 @@ static void __init balloon_add_region(unsigned long start_pfn, unsigned long pages) { unsigned long pfn, extra_pfn_end; - struct page *page; /* * If the amount of usable memory has been limited (e.g., with @@ -684,11 +675,10 @@ static void __init balloon_add_region(unsigned long start_pfn, extra_pfn_end = min(max_pfn, start_pfn + pages); for (pfn = start_pfn; pfn < extra_pfn_end; pfn++) { - page = pfn_to_page(pfn); /* totalram_pages and totalhigh_pages do not include the boot-time balloon extension, so don't subtract from it. */ - __balloon_append(page); + balloon_append(pfn_to_page(pfn)); } balloon_stats.total_pages += extra_pfn_end - start_pfn; diff --git a/drivers/xen/efi.c b/drivers/xen/efi.c index 89d60f8e3c18..d1ff2186ebb4 100644 --- a/drivers/xen/efi.c +++ b/drivers/xen/efi.c @@ -40,7 +40,7 @@ #define efi_data(op) (op.u.efi_runtime_call) -efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) +static efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) { struct xen_platform_op op = INIT_EFI_OP(get_time); @@ -61,9 +61,8 @@ efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_get_time); -efi_status_t xen_efi_set_time(efi_time_t *tm) +static efi_status_t xen_efi_set_time(efi_time_t *tm) { struct xen_platform_op op = INIT_EFI_OP(set_time); @@ -75,10 +74,10 @@ efi_status_t xen_efi_set_time(efi_time_t *tm) return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_set_time); -efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, - efi_time_t *tm) +static efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, + efi_bool_t *pending, + efi_time_t *tm) { struct xen_platform_op op = INIT_EFI_OP(get_wakeup_time); @@ -98,9 +97,8 @@ efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_get_wakeup_time); -efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) +static efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) { struct xen_platform_op op = INIT_EFI_OP(set_wakeup_time); @@ -117,11 +115,10 @@ efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_set_wakeup_time); -efi_status_t xen_efi_get_variable(efi_char16_t *name, efi_guid_t *vendor, - u32 *attr, unsigned long *data_size, - void *data) +static efi_status_t xen_efi_get_variable(efi_char16_t *name, efi_guid_t *vendor, + u32 *attr, unsigned long *data_size, + void *data) { struct xen_platform_op op = INIT_EFI_OP(get_variable); @@ -141,11 +138,10 @@ efi_status_t xen_efi_get_variable(efi_char16_t *name, efi_guid_t *vendor, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_get_variable); -efi_status_t xen_efi_get_next_variable(unsigned long *name_size, - efi_char16_t *name, - efi_guid_t *vendor) +static efi_status_t xen_efi_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *vendor) { struct xen_platform_op op = INIT_EFI_OP(get_next_variable_name); @@ -165,11 +161,10 @@ efi_status_t xen_efi_get_next_variable(unsigned long *name_size, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_get_next_variable); -efi_status_t xen_efi_set_variable(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, - void *data) +static efi_status_t xen_efi_set_variable(efi_char16_t *name, efi_guid_t *vendor, + u32 attr, unsigned long data_size, + void *data) { struct xen_platform_op op = INIT_EFI_OP(set_variable); @@ -186,11 +181,10 @@ efi_status_t xen_efi_set_variable(efi_char16_t *name, efi_guid_t *vendor, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_set_variable); -efi_status_t xen_efi_query_variable_info(u32 attr, u64 *storage_space, - u64 *remaining_space, - u64 *max_variable_size) +static efi_status_t xen_efi_query_variable_info(u32 attr, u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) { struct xen_platform_op op = INIT_EFI_OP(query_variable_info); @@ -208,9 +202,8 @@ efi_status_t xen_efi_query_variable_info(u32 attr, u64 *storage_space, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_query_variable_info); -efi_status_t xen_efi_get_next_high_mono_count(u32 *count) +static efi_status_t xen_efi_get_next_high_mono_count(u32 *count) { struct xen_platform_op op = INIT_EFI_OP(get_next_high_monotonic_count); @@ -221,10 +214,9 @@ efi_status_t xen_efi_get_next_high_mono_count(u32 *count) return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_get_next_high_mono_count); -efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, - unsigned long count, unsigned long sg_list) +static efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, + unsigned long count, unsigned long sg_list) { struct xen_platform_op op = INIT_EFI_OP(update_capsule); @@ -241,11 +233,9 @@ efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_update_capsule); -efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, - unsigned long count, u64 *max_size, - int *reset_type) +static efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, + unsigned long count, u64 *max_size, int *reset_type) { struct xen_platform_op op = INIT_EFI_OP(query_capsule_capabilities); @@ -264,10 +254,9 @@ efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, return efi_data(op).status; } -EXPORT_SYMBOL_GPL(xen_efi_query_capsule_caps); -void xen_efi_reset_system(int reset_type, efi_status_t status, - unsigned long data_size, efi_char16_t *data) +static void xen_efi_reset_system(int reset_type, efi_status_t status, + unsigned long data_size, efi_char16_t *data) { switch (reset_type) { case EFI_RESET_COLD: @@ -281,4 +270,25 @@ void xen_efi_reset_system(int reset_type, efi_status_t status, BUG(); } } -EXPORT_SYMBOL_GPL(xen_efi_reset_system); + +/* + * Set XEN EFI runtime services function pointers. Other fields of struct efi, + * e.g. efi.systab, will be set like normal EFI. + */ +void __init xen_efi_runtime_setup(void) +{ + efi.get_time = xen_efi_get_time; + efi.set_time = xen_efi_set_time; + efi.get_wakeup_time = xen_efi_get_wakeup_time; + efi.set_wakeup_time = xen_efi_set_wakeup_time; + efi.get_variable = xen_efi_get_variable; + efi.get_next_variable = xen_efi_get_next_variable; + efi.set_variable = xen_efi_set_variable; + efi.set_variable_nonblocking = xen_efi_set_variable; + efi.query_variable_info = xen_efi_query_variable_info; + efi.query_variable_info_nonblocking = xen_efi_query_variable_info; + efi.update_capsule = xen_efi_update_capsule; + efi.query_capsule_caps = xen_efi_query_capsule_caps; + efi.get_next_high_mono_count = xen_efi_get_next_high_mono_count; + efi.reset_system = xen_efi_reset_system; +} diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index a446a7221e13..81401f386c9c 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -22,6 +22,7 @@ #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt +#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -34,9 +35,6 @@ #include <linux/slab.h> #include <linux/highmem.h> #include <linux/refcount.h> -#ifdef CONFIG_XEN_GRANT_DMA_ALLOC -#include <linux/of_device.h> -#endif #include <xen/xen.h> #include <xen/grant_table.h> @@ -625,14 +623,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) flip->private_data = priv; #ifdef CONFIG_XEN_GRANT_DMA_ALLOC priv->dma_dev = gntdev_miscdev.this_device; - - /* - * The device is not spawn from a device tree, so arch_setup_dma_ops - * is not called, thus leaving the device with dummy DMA ops. - * Fix this by calling of_dma_configure() with a NULL node to set - * default DMA ops. - */ - of_dma_configure(priv->dma_dev, NULL, true); + dma_coerce_mask_and_coherent(priv->dma_dev, DMA_BIT_MASK(64)); #endif pr_debug("priv %p\n", priv); diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 7ea6fb6a2e5d..49b381e104ef 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -1363,8 +1363,7 @@ static int gnttab_setup(void) if (xen_feature(XENFEAT_auto_translated_physmap) && gnttab_shared.addr == NULL) { gnttab_shared.addr = xen_auto_xlat_grant_frames.vaddr; if (gnttab_shared.addr == NULL) { - pr_warn("gnttab share frames (addr=0x%08lx) is not mapped!\n", - (unsigned long)xen_auto_xlat_grant_frames.vaddr); + pr_warn("gnttab share frames is not mapped!\n"); return -ENOMEM; } } diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index 08adc590f631..597af455a522 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -55,6 +55,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/miscdevice.h> +#include <linux/workqueue.h> #include <xen/xenbus.h> #include <xen/xen.h> @@ -116,6 +117,8 @@ struct xenbus_file_priv { wait_queue_head_t read_waitq; struct kref kref; + + struct work_struct wq; }; /* Read out any raw xenbus messages queued up. */ @@ -300,14 +303,14 @@ static void watch_fired(struct xenbus_watch *watch, mutex_unlock(&adap->dev_data->reply_mutex); } -static void xenbus_file_free(struct kref *kref) +static void xenbus_worker(struct work_struct *wq) { struct xenbus_file_priv *u; struct xenbus_transaction_holder *trans, *tmp; struct watch_adapter *watch, *tmp_watch; struct read_buffer *rb, *tmp_rb; - u = container_of(kref, struct xenbus_file_priv, kref); + u = container_of(wq, struct xenbus_file_priv, wq); /* * No need for locking here because there are no other users, @@ -333,6 +336,18 @@ static void xenbus_file_free(struct kref *kref) kfree(u); } +static void xenbus_file_free(struct kref *kref) +{ + struct xenbus_file_priv *u; + + /* + * We might be called in xenbus_thread(). + * Use workqueue to avoid deadlock. + */ + u = container_of(kref, struct xenbus_file_priv, kref); + schedule_work(&u->wq); +} + static struct xenbus_transaction_holder *xenbus_get_transaction( struct xenbus_file_priv *u, uint32_t tx_id) { @@ -650,6 +665,7 @@ static int xenbus_file_open(struct inode *inode, struct file *filp) INIT_LIST_HEAD(&u->watches); INIT_LIST_HEAD(&u->read_buffers); init_waitqueue_head(&u->read_waitq); + INIT_WORK(&u->wq, xenbus_worker); mutex_init(&u->reply_mutex); mutex_init(&u->msgbuffer_mutex); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ad4c6b1d5074..c5642bcb6b46 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -879,7 +879,7 @@ out_free_interp: the correct location in memory. */ for(i = 0, elf_ppnt = elf_phdata; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) { - int elf_prot, elf_flags, elf_fixed = MAP_FIXED_NOREPLACE; + int elf_prot, elf_flags; unsigned long k, vaddr; unsigned long total_size = 0; @@ -911,13 +911,6 @@ out_free_interp: */ } } - - /* - * Some binaries have overlapping elf segments and then - * we have to forcefully map over an existing mapping - * e.g. over this newly established brk mapping. - */ - elf_fixed = MAP_FIXED; } elf_prot = make_prot(elf_ppnt->p_flags); @@ -930,7 +923,7 @@ out_free_interp: * the ET_DYN load_addr calculations, proceed normally. */ if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) { - elf_flags |= elf_fixed; + elf_flags |= MAP_FIXED; } else if (loc->elf_ex.e_type == ET_DYN) { /* * This logic is run once for the first LOAD Program @@ -966,7 +959,7 @@ out_free_interp: load_bias = ELF_ET_DYN_BASE; if (current->flags & PF_RANDOMIZE) load_bias += arch_mmap_rnd(); - elf_flags |= elf_fixed; + elf_flags |= MAP_FIXED; } else load_bias = 0; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 8fe4eb7e5045..27e5b269e729 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1591,7 +1591,6 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_root *root = BTRFS_I(inode)->root; struct page **pages = NULL; - struct extent_state *cached_state = NULL; struct extent_changeset *data_reserved = NULL; u64 release_bytes = 0; u64 lockstart; @@ -1611,6 +1610,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, return -ENOMEM; while (iov_iter_count(i) > 0) { + struct extent_state *cached_state = NULL; size_t offset = offset_in_page(pos); size_t sector_offset; size_t write_bytes = min(iov_iter_count(i), @@ -1758,9 +1758,20 @@ again: if (copied > 0) ret = btrfs_dirty_pages(inode, pages, dirty_pages, pos, copied, &cached_state); + + /* + * If we have not locked the extent range, because the range's + * start offset is >= i_size, we might still have a non-NULL + * cached extent state, acquired while marking the extent range + * as delalloc through btrfs_dirty_pages(). Therefore free any + * possible cached extent state to avoid a memory leak. + */ if (extents_locked) unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, &cached_state); + else + free_extent_state(cached_state); + btrfs_delalloc_release_extents(BTRFS_I(inode), reserve_bytes, true); if (ret) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a0546401bc0a..0f2754eaa05b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6305,13 +6305,16 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, u32 sizes[2]; int nitems = name ? 2 : 1; unsigned long ptr; + unsigned int nofs_flag; int ret; path = btrfs_alloc_path(); if (!path) return ERR_PTR(-ENOMEM); + nofs_flag = memalloc_nofs_save(); inode = new_inode(fs_info->sb); + memalloc_nofs_restore(nofs_flag); if (!inode) { btrfs_free_path(path); return ERR_PTR(-ENOMEM); diff --git a/fs/btrfs/ref-verify.c b/fs/btrfs/ref-verify.c index e87cbdad02a3..b57f3618e58e 100644 --- a/fs/btrfs/ref-verify.c +++ b/fs/btrfs/ref-verify.c @@ -500,7 +500,7 @@ static int process_leaf(struct btrfs_root *root, struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; u32 count; - int i = 0, tree_block_level = 0, ret; + int i = 0, tree_block_level = 0, ret = 0; struct btrfs_key key; int nritems = btrfs_header_nritems(leaf); diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index f3215028235c..123ac54af071 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -5085,7 +5085,7 @@ static int clone_range(struct send_ctx *sctx, struct btrfs_path *path; struct btrfs_key key; int ret; - u64 clone_src_i_size; + u64 clone_src_i_size = 0; /* * Prevent cloning from a zero offset with a length matching the sector diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 29b82a795522..8a6cc600bf18 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2932,7 +2932,8 @@ out: * in the tree of log roots */ static int update_log_root(struct btrfs_trans_handle *trans, - struct btrfs_root *log) + struct btrfs_root *log, + struct btrfs_root_item *root_item) { struct btrfs_fs_info *fs_info = log->fs_info; int ret; @@ -2940,10 +2941,10 @@ static int update_log_root(struct btrfs_trans_handle *trans, if (log->log_transid == 1) { /* insert root item on the first sync */ ret = btrfs_insert_root(trans, fs_info->log_root_tree, - &log->root_key, &log->root_item); + &log->root_key, root_item); } else { ret = btrfs_update_root(trans, fs_info->log_root_tree, - &log->root_key, &log->root_item); + &log->root_key, root_item); } return ret; } @@ -3041,6 +3042,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_root *log = root->log_root; struct btrfs_root *log_root_tree = fs_info->log_root_tree; + struct btrfs_root_item new_root_item; int log_transid = 0; struct btrfs_log_ctx root_log_ctx; struct blk_plug plug; @@ -3104,18 +3106,26 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, goto out; } + /* + * We _must_ update under the root->log_mutex in order to make sure we + * have a consistent view of the log root we are trying to commit at + * this moment. + * + * We _must_ copy this into a local copy, because we are not holding the + * log_root_tree->log_mutex yet. This is important because when we + * commit the log_root_tree we must have a consistent view of the + * log_root_tree when we update the super block to point at the + * log_root_tree bytenr. If we update the log_root_tree here we'll race + * with the commit and possibly point at the new block which we may not + * have written out. + */ btrfs_set_root_node(&log->root_item, log->node); + memcpy(&new_root_item, &log->root_item, sizeof(new_root_item)); root->log_transid++; log->log_transid = root->log_transid; root->log_start_pid = 0; /* - * Update or create log root item under the root's log_mutex to prevent - * races with concurrent log syncs that can lead to failure to update - * log root item because it was not created yet. - */ - ret = update_log_root(trans, log); - /* * IO has been started, blocks of the log tree have WRITTEN flag set * in their headers. new modifications of the log will be written to * new positions. so it's safe to allow log writers to go in. @@ -3135,6 +3145,14 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, mutex_unlock(&log_root_tree->log_mutex); mutex_lock(&log_root_tree->log_mutex); + + /* + * Now we are safe to update the log_root_tree because we're under the + * log_mutex, and we're a current writer so we're holding the commit + * open until we drop the log_mutex. + */ + ret = update_log_root(trans, log, &new_root_item); + if (atomic_dec_and_test(&log_root_tree->log_writers)) { /* atomic_dec_and_test implies a barrier */ cond_wake_up_nomb(&log_root_tree->log_writer_wait); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index cdd7af424033..bdfe4493e43a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3845,7 +3845,11 @@ static int alloc_profile_is_valid(u64 flags, int extended) return !extended; /* "0" is valid for usual profiles */ /* true if exactly one bit set */ - return is_power_of_2(flags); + /* + * Don't use is_power_of_2(unsigned long) because it won't work + * for the single profile (1ULL << 48) on 32-bit CPUs. + */ + return flags != 0 && (flags & (flags - 1)) == 0; } static inline int balance_need_close(struct btrfs_fs_info *fs_info) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 2e9c7f493f99..c049c7b3aa87 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -169,18 +169,26 @@ cifs_read_super(struct super_block *sb) else sb->s_maxbytes = MAX_NON_LFS; - /* BB FIXME fix time_gran to be larger for LANMAN sessions */ - sb->s_time_gran = 100; - - if (tcon->unix_ext) { - ts = cifs_NTtimeToUnix(0); + /* Some very old servers like DOS and OS/2 used 2 second granularity */ + if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) && + ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) && + !tcon->unix_ext) { + sb->s_time_gran = 1000000000; /* 1 second is max allowed gran */ + ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0); sb->s_time_min = ts.tv_sec; - ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX)); + ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), + cpu_to_le16(SMB_TIME_MAX), 0); sb->s_time_max = ts.tv_sec; } else { - ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0); + /* + * Almost every server, including all SMB2+, uses DCE TIME + * ie 100 nanosecond units, since 1601. See MS-DTYP and MS-FSCC + */ + sb->s_time_gran = 100; + ts = cifs_NTtimeToUnix(0); sb->s_time_min = ts.tv_sec; - ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), cpu_to_le16(SMB_TIME_MAX), 0); + ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX)); sb->s_time_max = ts.tv_sec; } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2e960e1049db..50dfd9049370 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1210,7 +1210,7 @@ struct cifs_search_info { bool smallBuf:1; /* so we know which buf_release function to call */ }; -#define ACL_NO_MODE -1 +#define ACL_NO_MODE ((umode_t)(-1)) struct cifs_open_parms { struct cifs_tcon *tcon; struct cifs_sb_info *cifs_sb; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2850c3ce4391..a64dfa95a925 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -4264,7 +4264,7 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, server->ops->qfs_tcon(*xid, tcon); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { if (tcon->fsDevInfo.DeviceCharacteristics & - FILE_READ_ONLY_DEVICE) + cpu_to_le32(FILE_READ_ONLY_DEVICE)) cifs_dbg(VFS, "mounted to read only share\n"); else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) == 0) @@ -4445,7 +4445,7 @@ static int setup_dfs_tgt_conn(const char *path, int rc; struct dfs_info3_param ref = {0}; char *mdata = NULL, *fake_devname = NULL; - struct smb_vol fake_vol = {0}; + struct smb_vol fake_vol = {NULL}; cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index dd5ac841aefa..7ce689d31aa2 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -738,10 +738,16 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, static int cifs_d_revalidate(struct dentry *direntry, unsigned int flags) { + struct inode *inode; + if (flags & LOOKUP_RCU) return -ECHILD; if (d_really_is_positive(direntry)) { + inode = d_inode(direntry); + if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode))) + CIFS_I(inode)->time = 0; /* force reval */ + if (cifs_revalidate_dentry(direntry)) return 0; else { @@ -752,7 +758,7 @@ cifs_d_revalidate(struct dentry *direntry, unsigned int flags) * attributes will have been updated by * cifs_revalidate_dentry(). */ - if (IS_AUTOMOUNT(d_inode(direntry)) && + if (IS_AUTOMOUNT(inode) && !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { spin_lock(&direntry->d_lock); direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 4b95700c507c..5ad15de2bb4f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -253,6 +253,12 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, xid, fid); + if (rc) { + server->ops->close(xid, tcon, fid); + if (rc == -ESTALE) + rc = -EOPENSTALE; + } + out: kfree(buf); return rc; @@ -1840,13 +1846,12 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, { struct cifsFileInfo *open_file = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); /* only filter by fsuid on multiuser mounts */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) fsuid_only = false; - spin_lock(&tcon->open_file_lock); + spin_lock(&cifs_inode->open_file_lock); /* we could simply get the first_list_entry since write-only entries are always at the end of the list but since the first entry might have a close pending, we go through the whole list */ @@ -1858,7 +1863,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, /* found a good file */ /* lock it so it will not be closed on us */ cifsFileInfo_get(open_file); - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); return open_file; } /* else might as well continue, and look for another, or simply have the caller reopen it @@ -1866,7 +1871,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, } else /* write only file */ break; /* write only files are last so must be done */ } - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); return NULL; } @@ -1877,7 +1882,6 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only, { struct cifsFileInfo *open_file, *inv_file = NULL; struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; bool any_available = false; int rc = -EBADF; unsigned int refind = 0; @@ -1897,16 +1901,15 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only, } cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); - tcon = cifs_sb_master_tcon(cifs_sb); /* only filter by fsuid on multiuser mounts */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) fsuid_only = false; - spin_lock(&tcon->open_file_lock); + spin_lock(&cifs_inode->open_file_lock); refind_writable: if (refind > MAX_REOPEN_ATT) { - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); return rc; } list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { @@ -1918,7 +1921,7 @@ refind_writable: if (!open_file->invalidHandle) { /* found a good writable file */ cifsFileInfo_get(open_file); - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); *ret_file = open_file; return 0; } else { @@ -1938,7 +1941,7 @@ refind_writable: cifsFileInfo_get(inv_file); } - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); if (inv_file) { rc = cifs_reopen_file(inv_file, false); @@ -1953,7 +1956,7 @@ refind_writable: cifsFileInfo_put(inv_file); ++refind; inv_file = NULL; - spin_lock(&tcon->open_file_lock); + spin_lock(&cifs_inode->open_file_lock); goto refind_writable; } @@ -4461,17 +4464,15 @@ static int cifs_readpage(struct file *file, struct page *page) static int is_inode_writable(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; - struct cifs_tcon *tcon = - cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb)); - spin_lock(&tcon->open_file_lock); + spin_lock(&cifs_inode->open_file_lock); list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); return 1; } } - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); return 0; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 3bae2e53f0b8..5dcc95b38310 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -414,6 +414,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, /* if uniqueid is different, return error */ if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*pinode)->time = 0; /* force reval */ rc = -ESTALE; goto cgiiu_exit; } @@ -421,6 +422,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, /* if filetype is different, return error */ if (unlikely(((*pinode)->i_mode & S_IFMT) != (fattr.cf_mode & S_IFMT))) { + CIFS_I(*pinode)->time = 0; /* force reval */ rc = -ESTALE; goto cgiiu_exit; } @@ -933,6 +935,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, /* if uniqueid is different, return error */ if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*inode)->time = 0; /* force reval */ rc = -ESTALE; goto cgii_exit; } @@ -940,6 +943,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, /* if filetype is different, return error */ if (unlikely(((*inode)->i_mode & S_IFMT) != (fattr.cf_mode & S_IFMT))) { + CIFS_I(*inode)->time = 0; /* force reval */ rc = -ESTALE; goto cgii_exit; } diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 49c17ee18254..9b41436fb8db 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -117,10 +117,6 @@ static const struct smb_to_posix_error mapping_table_ERRSRV[] = { {0, 0} }; -static const struct smb_to_posix_error mapping_table_ERRHRD[] = { - {0, 0} -}; - /* * Convert a string containing text IPv4 or IPv6 address to binary form. * diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 85f9d614d968..05149862aea4 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -751,8 +751,8 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) unsigned int num = *num_iovec; iov[num].iov_base = create_posix_buf(mode); - if (mode == -1) - cifs_dbg(VFS, "illegal mode\n"); /* BB REMOVEME */ + if (mode == ACL_NO_MODE) + cifs_dbg(FYI, "illegal mode\n"); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_posix); @@ -2521,11 +2521,8 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, return rc; } - /* TODO: add handling for the mode on create */ - if (oparms->disposition == FILE_CREATE) - cifs_dbg(VFS, "mode is 0x%x\n", oparms->mode); /* BB REMOVEME */ - - if ((oparms->disposition == FILE_CREATE) && (oparms->mode != -1)) { + if ((oparms->disposition == FILE_CREATE) && + (oparms->mode != ACL_NO_MODE)) { if (n_iov > 2) { struct create_context *ccontext = (struct create_context *)iov[n_iov-1].iov_base; @@ -3217,7 +3214,8 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; - req->OutputBufferLength = SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE; + req->OutputBufferLength = + cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); req->CompletionFilter = cpu_to_le32(completion_filter); if (watch_tree) req->Flags = cpu_to_le16(SMB2_WATCH_TREE); diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index da3a6d580808..71b2930b8e0b 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -150,6 +150,10 @@ extern int SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, bool is_fsctl, char *in_data, u32 indatalen, __u32 max_response_size); extern void SMB2_ioctl_free(struct smb_rqst *rqst); +extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter); + extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id); extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 8a9fcbd0e8ac..fc3a8d8064f8 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -34,11 +34,15 @@ static void erofs_readendio(struct bio *bio) struct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr) { - struct inode *const bd_inode = sb->s_bdev->bd_inode; - struct address_space *const mapping = bd_inode->i_mapping; + struct address_space *const mapping = sb->s_bdev->bd_inode->i_mapping; + struct page *page; - return read_cache_page_gfp(mapping, blkaddr, + page = read_cache_page_gfp(mapping, blkaddr, mapping_gfp_constraint(mapping, ~__GFP_FS)); + /* should already be PageUptodate */ + if (!IS_ERR(page)) + lock_page(page); + return page; } static int erofs_map_blocks_flatmode(struct inode *inode, diff --git a/fs/erofs/super.c b/fs/erofs/super.c index caf9a95173b0..0e369494f2f2 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -105,9 +105,9 @@ static int erofs_read_superblock(struct super_block *sb) int ret; page = read_mapping_page(sb->s_bdev->bd_inode->i_mapping, 0, NULL); - if (!page) { + if (IS_ERR(page)) { erofs_err(sb, "cannot read erofs superblock"); - return -EIO; + return PTR_ERR(page); } sbi = EROFS_SB(sb); diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 96e34c90f814..fad80c97d247 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -575,7 +575,7 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, struct erofs_map_blocks *const map = &fe->map; struct z_erofs_collector *const clt = &fe->clt; const loff_t offset = page_offset(page); - bool tight = (clt->mode >= COLLECT_PRIMARY_HOOKED); + bool tight = true; enum z_erofs_cache_alloctype cache_strategy; enum z_erofs_page_type page_type; @@ -628,8 +628,16 @@ restart_now: preload_compressed_pages(clt, MNGD_MAPPING(sbi), cache_strategy, pagepool); - tight &= (clt->mode >= COLLECT_PRIMARY_HOOKED); hitted: + /* + * Ensure the current partial page belongs to this submit chain rather + * than other concurrent submit chains or the noio(bypass) chain since + * those chains are handled asynchronously thus the page cannot be used + * for inplace I/O or pagevec (should be processed in strict order.) + */ + tight &= (clt->mode >= COLLECT_PRIMARY_HOOKED && + clt->mode != COLLECT_PRIMARY_FOLLOWED_NOINPLACE); + cur = end - min_t(unsigned int, offset + end - map->m_la, end); if (!(map->m_flags & EROFS_MAP_MAPPED)) { zero_user_segment(page, cur, end); diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 8aaa7eec7b74..e88421d9a48d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -164,8 +164,13 @@ static void finish_writeback_work(struct bdi_writeback *wb, if (work->auto_free) kfree(work); - if (done && atomic_dec_and_test(&done->cnt)) - wake_up_all(done->waitq); + if (done) { + wait_queue_head_t *waitq = done->waitq; + + /* @done can't be accessed after the following dec */ + if (atomic_dec_and_test(&done->cnt)) + wake_up_all(waitq); + } } static void wb_queue_work(struct bdi_writeback *wb, diff --git a/fs/io_uring.c b/fs/io_uring.c index aa8ac557493c..76fdbe84aff5 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -415,27 +415,27 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p) return ctx; } +static inline bool __io_sequence_defer(struct io_ring_ctx *ctx, + struct io_kiocb *req) +{ + return req->sequence != ctx->cached_cq_tail + ctx->rings->sq_dropped; +} + static inline bool io_sequence_defer(struct io_ring_ctx *ctx, struct io_kiocb *req) { - /* timeout requests always honor sequence */ - if (!(req->flags & REQ_F_TIMEOUT) && - (req->flags & (REQ_F_IO_DRAIN|REQ_F_IO_DRAINED)) != REQ_F_IO_DRAIN) + if ((req->flags & (REQ_F_IO_DRAIN|REQ_F_IO_DRAINED)) != REQ_F_IO_DRAIN) return false; - return req->sequence != ctx->cached_cq_tail + ctx->rings->sq_dropped; + return __io_sequence_defer(ctx, req); } -static struct io_kiocb *__io_get_deferred_req(struct io_ring_ctx *ctx, - struct list_head *list) +static struct io_kiocb *io_get_deferred_req(struct io_ring_ctx *ctx) { struct io_kiocb *req; - if (list_empty(list)) - return NULL; - - req = list_first_entry(list, struct io_kiocb, list); - if (!io_sequence_defer(ctx, req)) { + req = list_first_entry_or_null(&ctx->defer_list, struct io_kiocb, list); + if (req && !io_sequence_defer(ctx, req)) { list_del_init(&req->list); return req; } @@ -443,14 +443,17 @@ static struct io_kiocb *__io_get_deferred_req(struct io_ring_ctx *ctx, return NULL; } -static struct io_kiocb *io_get_deferred_req(struct io_ring_ctx *ctx) -{ - return __io_get_deferred_req(ctx, &ctx->defer_list); -} - static struct io_kiocb *io_get_timeout_req(struct io_ring_ctx *ctx) { - return __io_get_deferred_req(ctx, &ctx->timeout_list); + struct io_kiocb *req; + + req = list_first_entry_or_null(&ctx->timeout_list, struct io_kiocb, list); + if (req && !__io_sequence_defer(ctx, req)) { + list_del_init(&req->list); + return req; + } + + return NULL; } static void __io_commit_cqring(struct io_ring_ctx *ctx) @@ -591,14 +594,6 @@ static void io_cqring_add_event(struct io_ring_ctx *ctx, u64 user_data, io_cqring_ev_posted(ctx); } -static void io_ring_drop_ctx_refs(struct io_ring_ctx *ctx, unsigned refs) -{ - percpu_ref_put_many(&ctx->refs, refs); - - if (waitqueue_active(&ctx->wait)) - wake_up(&ctx->wait); -} - static struct io_kiocb *io_get_req(struct io_ring_ctx *ctx, struct io_submit_state *state) { @@ -646,7 +641,7 @@ static struct io_kiocb *io_get_req(struct io_ring_ctx *ctx, req->result = 0; return req; out: - io_ring_drop_ctx_refs(ctx, 1); + percpu_ref_put(&ctx->refs); return NULL; } @@ -654,7 +649,7 @@ static void io_free_req_many(struct io_ring_ctx *ctx, void **reqs, int *nr) { if (*nr) { kmem_cache_free_bulk(req_cachep, *nr, reqs); - io_ring_drop_ctx_refs(ctx, *nr); + percpu_ref_put_many(&ctx->refs, *nr); *nr = 0; } } @@ -663,7 +658,7 @@ static void __io_free_req(struct io_kiocb *req) { if (req->file && !(req->flags & REQ_F_FIXED_FILE)) fput(req->file); - io_ring_drop_ctx_refs(req->ctx, 1); + percpu_ref_put(&req->ctx->refs); kmem_cache_free(req_cachep, req); } @@ -1892,15 +1887,15 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe) unsigned count, req_dist, tail_index; struct io_ring_ctx *ctx = req->ctx; struct list_head *entry; - struct timespec ts; + struct timespec64 ts; if (unlikely(ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->flags || sqe->ioprio || sqe->buf_index || sqe->timeout_flags || sqe->len != 1) return -EINVAL; - if (copy_from_user(&ts, (void __user *) (unsigned long) sqe->addr, - sizeof(ts))) + + if (get_timespec64(&ts, u64_to_user_ptr(sqe->addr))) return -EFAULT; /* @@ -1934,7 +1929,7 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe) hrtimer_init(&req->timeout.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); req->timeout.timer.function = io_timeout_fn; - hrtimer_start(&req->timeout.timer, timespec_to_ktime(ts), + hrtimer_start(&req->timeout.timer, timespec64_to_ktime(ts), HRTIMER_MODE_REL); return 0; } @@ -2761,7 +2756,7 @@ out: if (link) io_queue_link_head(ctx, link, &link->submit, shadow_req, - block_for_last); + !block_for_last); if (statep) io_submit_state_end(statep); @@ -2920,8 +2915,12 @@ static void io_finish_async(struct io_ring_ctx *ctx) static void io_destruct_skb(struct sk_buff *skb) { struct io_ring_ctx *ctx = skb->sk->sk_user_data; + int i; + + for (i = 0; i < ARRAY_SIZE(ctx->sqo_wq); i++) + if (ctx->sqo_wq[i]) + flush_workqueue(ctx->sqo_wq[i]); - io_finish_async(ctx); unix_destruct_scm(skb); } @@ -3630,7 +3629,7 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit, } } - io_ring_drop_ctx_refs(ctx, 1); + percpu_ref_put(&ctx->refs); out_fput: fdput(f); return submitted ? submitted : ret; diff --git a/fs/libfs.c b/fs/libfs.c index c9b2850c0f7c..540611b99b9a 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -89,58 +89,45 @@ int dcache_dir_close(struct inode *inode, struct file *file) EXPORT_SYMBOL(dcache_dir_close); /* parent is locked at least shared */ -static struct dentry *next_positive(struct dentry *parent, - struct list_head *from, - int count) +/* + * Returns an element of siblings' list. + * We are looking for <count>th positive after <p>; if + * found, dentry is grabbed and returned to caller. + * If no such element exists, NULL is returned. + */ +static struct dentry *scan_positives(struct dentry *cursor, + struct list_head *p, + loff_t count, + struct dentry *last) { - unsigned *seq = &parent->d_inode->i_dir_seq, n; - struct dentry *res; - struct list_head *p; - bool skipped; - int i; + struct dentry *dentry = cursor->d_parent, *found = NULL; -retry: - i = count; - skipped = false; - n = smp_load_acquire(seq) & ~1; - res = NULL; - rcu_read_lock(); - for (p = from->next; p != &parent->d_subdirs; p = p->next) { + spin_lock(&dentry->d_lock); + while ((p = p->next) != &dentry->d_subdirs) { struct dentry *d = list_entry(p, struct dentry, d_child); - if (!simple_positive(d)) { - skipped = true; - } else if (!--i) { - res = d; - break; + // we must at least skip cursors, to avoid livelocks + if (d->d_flags & DCACHE_DENTRY_CURSOR) + continue; + if (simple_positive(d) && !--count) { + spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); + if (simple_positive(d)) + found = dget_dlock(d); + spin_unlock(&d->d_lock); + if (likely(found)) + break; + count = 1; + } + if (need_resched()) { + list_move(&cursor->d_child, p); + p = &cursor->d_child; + spin_unlock(&dentry->d_lock); + cond_resched(); + spin_lock(&dentry->d_lock); } } - rcu_read_unlock(); - if (skipped) { - smp_rmb(); - if (unlikely(*seq != n)) - goto retry; - } - return res; -} - -static void move_cursor(struct dentry *cursor, struct list_head *after) -{ - struct dentry *parent = cursor->d_parent; - unsigned n, *seq = &parent->d_inode->i_dir_seq; - spin_lock(&parent->d_lock); - for (;;) { - n = *seq; - if (!(n & 1) && cmpxchg(seq, n, n + 1) == n) - break; - cpu_relax(); - } - __list_del(cursor->d_child.prev, cursor->d_child.next); - if (after) - list_add(&cursor->d_child, after); - else - list_add_tail(&cursor->d_child, &parent->d_subdirs); - smp_store_release(seq, n + 2); - spin_unlock(&parent->d_lock); + spin_unlock(&dentry->d_lock); + dput(last); + return found; } loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence) @@ -158,17 +145,25 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence) return -EINVAL; } if (offset != file->f_pos) { + struct dentry *cursor = file->private_data; + struct dentry *to = NULL; + + inode_lock_shared(dentry->d_inode); + + if (offset > 2) + to = scan_positives(cursor, &dentry->d_subdirs, + offset - 2, NULL); + spin_lock(&dentry->d_lock); + if (to) + list_move(&cursor->d_child, &to->d_child); + else + list_del_init(&cursor->d_child); + spin_unlock(&dentry->d_lock); + dput(to); + file->f_pos = offset; - if (file->f_pos >= 2) { - struct dentry *cursor = file->private_data; - struct dentry *to; - loff_t n = file->f_pos - 2; - - inode_lock_shared(dentry->d_inode); - to = next_positive(dentry, &dentry->d_subdirs, n); - move_cursor(cursor, to ? &to->d_child : NULL); - inode_unlock_shared(dentry->d_inode); - } + + inode_unlock_shared(dentry->d_inode); } return offset; } @@ -190,25 +185,35 @@ int dcache_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; struct dentry *cursor = file->private_data; - struct list_head *p = &cursor->d_child; - struct dentry *next; - bool moved = false; + struct list_head *anchor = &dentry->d_subdirs; + struct dentry *next = NULL; + struct list_head *p; if (!dir_emit_dots(file, ctx)) return 0; if (ctx->pos == 2) - p = &dentry->d_subdirs; - while ((next = next_positive(dentry, p, 1)) != NULL) { + p = anchor; + else if (!list_empty(&cursor->d_child)) + p = &cursor->d_child; + else + return 0; + + while ((next = scan_positives(cursor, p, 1, next)) != NULL) { if (!dir_emit(ctx, next->d_name.name, next->d_name.len, d_inode(next)->i_ino, dt_type(d_inode(next)))) break; - moved = true; - p = &next->d_child; ctx->pos++; + p = &next->d_child; } - if (moved) - move_cursor(cursor, p); + spin_lock(&dentry->d_lock); + if (next) + list_move_tail(&cursor->d_child, &next->d_child); + else + list_del_init(&cursor->d_child); + spin_unlock(&dentry->d_lock); + dput(next); + return 0; } EXPORT_SYMBOL(dcache_readdir); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 222d7115db71..040a50fd9bf3 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -64,13 +64,6 @@ static struct kmem_cache *nfs_direct_cachep; -/* - * This represents a set of asynchronous requests that we're waiting on - */ -struct nfs_direct_mirror { - ssize_t count; -}; - struct nfs_direct_req { struct kref kref; /* release manager */ @@ -84,9 +77,6 @@ struct nfs_direct_req { atomic_t io_count; /* i/os we're waiting for */ spinlock_t lock; /* protect completion state */ - struct nfs_direct_mirror mirrors[NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX]; - int mirror_count; - loff_t io_start; /* Start offset for I/O */ ssize_t count, /* bytes actually processed */ max_count, /* max expected count */ @@ -123,32 +113,42 @@ static inline int put_dreq(struct nfs_direct_req *dreq) } static void -nfs_direct_good_bytes(struct nfs_direct_req *dreq, struct nfs_pgio_header *hdr) +nfs_direct_handle_truncated(struct nfs_direct_req *dreq, + const struct nfs_pgio_header *hdr, + ssize_t dreq_len) { - int i; - ssize_t count; + if (!(test_bit(NFS_IOHDR_ERROR, &hdr->flags) || + test_bit(NFS_IOHDR_EOF, &hdr->flags))) + return; + if (dreq->max_count >= dreq_len) { + dreq->max_count = dreq_len; + if (dreq->count > dreq_len) + dreq->count = dreq_len; + + if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) + dreq->error = hdr->error; + else /* Clear outstanding error if this is EOF */ + dreq->error = 0; + } +} - WARN_ON_ONCE(dreq->count >= dreq->max_count); +static void +nfs_direct_count_bytes(struct nfs_direct_req *dreq, + const struct nfs_pgio_header *hdr) +{ + loff_t hdr_end = hdr->io_start + hdr->good_bytes; + ssize_t dreq_len = 0; - if (dreq->mirror_count == 1) { - dreq->mirrors[hdr->pgio_mirror_idx].count += hdr->good_bytes; - dreq->count += hdr->good_bytes; - } else { - /* mirrored writes */ - count = dreq->mirrors[hdr->pgio_mirror_idx].count; - if (count + dreq->io_start < hdr->io_start + hdr->good_bytes) { - count = hdr->io_start + hdr->good_bytes - dreq->io_start; - dreq->mirrors[hdr->pgio_mirror_idx].count = count; - } - /* update the dreq->count by finding the minimum agreed count from all - * mirrors */ - count = dreq->mirrors[0].count; + if (hdr_end > dreq->io_start) + dreq_len = hdr_end - dreq->io_start; - for (i = 1; i < dreq->mirror_count; i++) - count = min(count, dreq->mirrors[i].count); + nfs_direct_handle_truncated(dreq, hdr, dreq_len); - dreq->count = count; - } + if (dreq_len > dreq->max_count) + dreq_len = dreq->max_count; + + if (dreq->count < dreq_len) + dreq->count = dreq_len; } /* @@ -293,18 +293,6 @@ void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo, cinfo->completion_ops = &nfs_direct_commit_completion_ops; } -static inline void nfs_direct_setup_mirroring(struct nfs_direct_req *dreq, - struct nfs_pageio_descriptor *pgio, - struct nfs_page *req) -{ - int mirror_count = 1; - - if (pgio->pg_ops->pg_get_mirror_count) - mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req); - - dreq->mirror_count = mirror_count; -} - static inline struct nfs_direct_req *nfs_direct_req_alloc(void) { struct nfs_direct_req *dreq; @@ -319,7 +307,6 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void) INIT_LIST_HEAD(&dreq->mds_cinfo.list); dreq->verf.committed = NFS_INVALID_STABLE_HOW; /* not set yet */ INIT_WORK(&dreq->work, nfs_direct_write_schedule_work); - dreq->mirror_count = 1; spin_lock_init(&dreq->lock); return dreq; @@ -402,20 +389,12 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) struct nfs_direct_req *dreq = hdr->dreq; spin_lock(&dreq->lock); - if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) - dreq->error = hdr->error; - if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) { spin_unlock(&dreq->lock); goto out_put; } - if (hdr->good_bytes != 0) - nfs_direct_good_bytes(dreq, hdr); - - if (test_bit(NFS_IOHDR_EOF, &hdr->flags)) - dreq->error = 0; - + nfs_direct_count_bytes(dreq, hdr); spin_unlock(&dreq->lock); while (!list_empty(&hdr->pages)) { @@ -646,29 +625,22 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) LIST_HEAD(reqs); struct nfs_commit_info cinfo; LIST_HEAD(failed); - int i; nfs_init_cinfo_from_dreq(&cinfo, dreq); nfs_direct_write_scan_commit_list(dreq->inode, &reqs, &cinfo); dreq->count = 0; + dreq->max_count = 0; + list_for_each_entry(req, &reqs, wb_list) + dreq->max_count += req->wb_bytes; dreq->verf.committed = NFS_INVALID_STABLE_HOW; nfs_clear_pnfs_ds_commit_verifiers(&dreq->ds_cinfo); - for (i = 0; i < dreq->mirror_count; i++) - dreq->mirrors[i].count = 0; get_dreq(dreq); nfs_pageio_init_write(&desc, dreq->inode, FLUSH_STABLE, false, &nfs_direct_write_completion_ops); desc.pg_dreq = dreq; - req = nfs_list_entry(reqs.next); - nfs_direct_setup_mirroring(dreq, &desc, req); - if (desc.pg_error < 0) { - list_splice_init(&reqs, &failed); - goto out_failed; - } - list_for_each_entry_safe(req, tmp, &reqs, wb_list) { /* Bump the transmission count */ req->wb_nio++; @@ -686,7 +658,6 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) } nfs_pageio_complete(&desc); -out_failed: while (!list_empty(&failed)) { req = nfs_list_entry(failed.next); nfs_list_remove_request(req); @@ -791,17 +762,13 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) nfs_init_cinfo_from_dreq(&cinfo, dreq); spin_lock(&dreq->lock); - - if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) - dreq->error = hdr->error; - if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) { spin_unlock(&dreq->lock); goto out_put; } + nfs_direct_count_bytes(dreq, hdr); if (hdr->good_bytes != 0) { - nfs_direct_good_bytes(dreq, hdr); if (nfs_write_need_commit(hdr)) { if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) request_commit = true; @@ -923,7 +890,6 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, break; } - nfs_direct_setup_mirroring(dreq, &desc, req); if (desc.pg_error < 0) { nfs_free_request(req); result = desc.pg_error; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 11eafcfc490b..ab8ca20fd579 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6106,6 +6106,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, status = nfs4_call_sync_custom(&task_setup_data); if (setclientid.sc_cred) { + kfree(clp->cl_acceptor); clp->cl_acceptor = rpcauth_stringify_acceptor(setclientid.sc_cred); put_rpccred(setclientid.sc_cred); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 85ca49549b39..52cab65f91cf 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -786,7 +786,6 @@ static void nfs_inode_remove_request(struct nfs_page *req) struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *head; - atomic_long_dec(&nfsi->nrequests); if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) { head = req->wb_head; @@ -799,8 +798,10 @@ static void nfs_inode_remove_request(struct nfs_page *req) spin_unlock(&mapping->private_lock); } - if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) + if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) { nfs_release_request(req); + atomic_long_dec(&nfsi->nrequests); + } } static void diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 8de1c9d644f6..9cd0a6815933 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2049,7 +2049,8 @@ out_write_size: inode->i_mtime = inode->i_ctime = current_time(inode); di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec); di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); - ocfs2_update_inode_fsync_trans(handle, inode, 1); + if (handle) + ocfs2_update_inode_fsync_trans(handle, inode, 1); } if (handle) ocfs2_journal_dirty(handle, wc->w_di_bh); @@ -2146,13 +2147,30 @@ static int ocfs2_dio_wr_get_block(struct inode *inode, sector_t iblock, struct ocfs2_dio_write_ctxt *dwc = NULL; struct buffer_head *di_bh = NULL; u64 p_blkno; - loff_t pos = iblock << inode->i_sb->s_blocksize_bits; + unsigned int i_blkbits = inode->i_sb->s_blocksize_bits; + loff_t pos = iblock << i_blkbits; + sector_t endblk = (i_size_read(inode) - 1) >> i_blkbits; unsigned len, total_len = bh_result->b_size; int ret = 0, first_get_block = 0; len = osb->s_clustersize - (pos & (osb->s_clustersize - 1)); len = min(total_len, len); + /* + * bh_result->b_size is count in get_more_blocks according to write + * "pos" and "end", we need map twice to return different buffer state: + * 1. area in file size, not set NEW; + * 2. area out file size, set NEW. + * + * iblock endblk + * |--------|---------|---------|--------- + * |<-------area in file------->| + */ + + if ((iblock <= endblk) && + ((iblock + ((len - 1) >> i_blkbits)) > endblk)) + len = (endblk - iblock + 1) << i_blkbits; + mlog(0, "get block of %lu at %llu:%u req %u\n", inode->i_ino, pos, len, total_len); @@ -2236,6 +2254,9 @@ static int ocfs2_dio_wr_get_block(struct inode *inode, sector_t iblock, if (desc->c_needs_zero) set_buffer_new(bh_result); + if (iblock > endblk) + set_buffer_new(bh_result); + /* May sleep in end_io. It should not happen in a irq context. So defer * it to dio work queue. */ set_buffer_defer_completion(bh_result); diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index d6f7b299eb23..efeea208fdeb 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -283,7 +283,7 @@ static int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb, if (inode_alloc) inode_lock(inode_alloc); - if (o2info_coherent(&fi->ifi_req)) { + if (inode_alloc && o2info_coherent(&fi->ifi_req)) { status = ocfs2_inode_lock(inode_alloc, &bh, 0); if (status < 0) { mlog_errno(status); diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 90c830e3758e..d8507972ee13 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -1490,18 +1490,6 @@ static int ocfs2_xa_check_space(struct ocfs2_xa_loc *loc, return loc->xl_ops->xlo_check_space(loc, xi); } -static void ocfs2_xa_add_entry(struct ocfs2_xa_loc *loc, u32 name_hash) -{ - loc->xl_ops->xlo_add_entry(loc, name_hash); - loc->xl_entry->xe_name_hash = cpu_to_le32(name_hash); - /* - * We can't leave the new entry's xe_name_offset at zero or - * add_namevalue() will go nuts. We set it to the size of our - * storage so that it can never be less than any other entry. - */ - loc->xl_entry->xe_name_offset = cpu_to_le16(loc->xl_size); -} - static void ocfs2_xa_add_namevalue(struct ocfs2_xa_loc *loc, struct ocfs2_xattr_info *xi) { @@ -2133,29 +2121,31 @@ static int ocfs2_xa_prepare_entry(struct ocfs2_xa_loc *loc, if (rc) goto out; - if (loc->xl_entry) { - if (ocfs2_xa_can_reuse_entry(loc, xi)) { - orig_value_size = loc->xl_entry->xe_value_size; - rc = ocfs2_xa_reuse_entry(loc, xi, ctxt); - if (rc) - goto out; - goto alloc_value; - } + if (!loc->xl_entry) { + rc = -EINVAL; + goto out; + } - if (!ocfs2_xattr_is_local(loc->xl_entry)) { - orig_clusters = ocfs2_xa_value_clusters(loc); - rc = ocfs2_xa_value_truncate(loc, 0, ctxt); - if (rc) { - mlog_errno(rc); - ocfs2_xa_cleanup_value_truncate(loc, - "overwriting", - orig_clusters); - goto out; - } + if (ocfs2_xa_can_reuse_entry(loc, xi)) { + orig_value_size = loc->xl_entry->xe_value_size; + rc = ocfs2_xa_reuse_entry(loc, xi, ctxt); + if (rc) + goto out; + goto alloc_value; + } + + if (!ocfs2_xattr_is_local(loc->xl_entry)) { + orig_clusters = ocfs2_xa_value_clusters(loc); + rc = ocfs2_xa_value_truncate(loc, 0, ctxt); + if (rc) { + mlog_errno(rc); + ocfs2_xa_cleanup_value_truncate(loc, + "overwriting", + orig_clusters); + goto out; } - ocfs2_xa_wipe_namevalue(loc); - } else - ocfs2_xa_add_entry(loc, name_hash); + } + ocfs2_xa_wipe_namevalue(loc); /* * If we get here, we have a blank entry. Fill it. We grow our diff --git a/fs/readdir.c b/fs/readdir.c index 2f6a4534e0df..6e2623e57b2e 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -20,9 +20,23 @@ #include <linux/syscalls.h> #include <linux/unistd.h> #include <linux/compat.h> - #include <linux/uaccess.h> +#include <asm/unaligned.h> + +/* + * Note the "unsafe_put_user() semantics: we goto a + * label for errors. + */ +#define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \ + char __user *dst = (_dst); \ + const char *src = (_src); \ + size_t len = (_len); \ + unsafe_put_user(0, dst+len, label); \ + unsafe_copy_to_user(dst, src, len, label); \ +} while (0) + + int iterate_dir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); @@ -65,6 +79,40 @@ out: EXPORT_SYMBOL(iterate_dir); /* + * POSIX says that a dirent name cannot contain NULL or a '/'. + * + * It's not 100% clear what we should really do in this case. + * The filesystem is clearly corrupted, but returning a hard + * error means that you now don't see any of the other names + * either, so that isn't a perfect alternative. + * + * And if you return an error, what error do you use? Several + * filesystems seem to have decided on EUCLEAN being the error + * code for EFSCORRUPTED, and that may be the error to use. Or + * just EIO, which is perhaps more obvious to users. + * + * In order to see the other file names in the directory, the + * caller might want to make this a "soft" error: skip the + * entry, and return the error at the end instead. + * + * Note that this should likely do a "memchr(name, 0, len)" + * check too, since that would be filesystem corruption as + * well. However, that case can't actually confuse user space, + * which has to do a strlen() on the name anyway to find the + * filename length, and the above "soft error" worry means + * that it's probably better left alone until we have that + * issue clarified. + */ +static int verify_dirent_name(const char *name, int len) +{ + if (WARN_ON_ONCE(!len)) + return -EIO; + if (WARN_ON_ONCE(memchr(name, '/', len))) + return -EIO; + return 0; +} + +/* * Traditional linux readdir() handling.. * * "count=1" is a special case, meaning that the buffer is one @@ -173,6 +221,9 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); + buf->error = verify_dirent_name(name, namlen); + if (unlikely(buf->error)) + return buf->error; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; @@ -182,28 +233,31 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, return -EOVERFLOW; } dirent = buf->previous; - if (dirent) { - if (signal_pending(current)) - return -EINTR; - if (__put_user(offset, &dirent->d_off)) - goto efault; - } - dirent = buf->current_dir; - if (__put_user(d_ino, &dirent->d_ino)) - goto efault; - if (__put_user(reclen, &dirent->d_reclen)) - goto efault; - if (copy_to_user(dirent->d_name, name, namlen)) - goto efault; - if (__put_user(0, dirent->d_name + namlen)) - goto efault; - if (__put_user(d_type, (char __user *) dirent + reclen - 1)) + if (dirent && signal_pending(current)) + return -EINTR; + + /* + * Note! This range-checks 'previous' (which may be NULL). + * The real range was checked in getdents + */ + if (!user_access_begin(dirent, sizeof(*dirent))) goto efault; + if (dirent) + unsafe_put_user(offset, &dirent->d_off, efault_end); + dirent = buf->current_dir; + unsafe_put_user(d_ino, &dirent->d_ino, efault_end); + unsafe_put_user(reclen, &dirent->d_reclen, efault_end); + unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); + user_access_end(); + buf->previous = dirent; dirent = (void __user *)dirent + reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; +efault_end: + user_access_end(); efault: buf->error = -EFAULT; return -EFAULT; @@ -259,34 +313,38 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); + buf->error = verify_dirent_name(name, namlen); + if (unlikely(buf->error)) + return buf->error; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; dirent = buf->previous; - if (dirent) { - if (signal_pending(current)) - return -EINTR; - if (__put_user(offset, &dirent->d_off)) - goto efault; - } - dirent = buf->current_dir; - if (__put_user(ino, &dirent->d_ino)) - goto efault; - if (__put_user(0, &dirent->d_off)) - goto efault; - if (__put_user(reclen, &dirent->d_reclen)) - goto efault; - if (__put_user(d_type, &dirent->d_type)) - goto efault; - if (copy_to_user(dirent->d_name, name, namlen)) - goto efault; - if (__put_user(0, dirent->d_name + namlen)) + if (dirent && signal_pending(current)) + return -EINTR; + + /* + * Note! This range-checks 'previous' (which may be NULL). + * The real range was checked in getdents + */ + if (!user_access_begin(dirent, sizeof(*dirent))) goto efault; + if (dirent) + unsafe_put_user(offset, &dirent->d_off, efault_end); + dirent = buf->current_dir; + unsafe_put_user(ino, &dirent->d_ino, efault_end); + unsafe_put_user(reclen, &dirent->d_reclen, efault_end); + unsafe_put_user(d_type, &dirent->d_type, efault_end); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); + user_access_end(); + buf->previous = dirent; dirent = (void __user *)dirent + reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; +efault_end: + user_access_end(); efault: buf->error = -EFAULT; return -EFAULT; diff --git a/fs/statfs.c b/fs/statfs.c index eea7af6f2f22..2616424012ea 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -318,19 +318,10 @@ COMPAT_SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct compat_statfs __user *, static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf) { struct compat_statfs64 buf; - if (sizeof(ubuf->f_bsize) == 4) { - if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen | - kbuf->f_frsize | kbuf->f_flags) & 0xffffffff00000000ULL) - return -EOVERFLOW; - /* f_files and f_ffree may be -1; it's okay - * to stuff that into 32 bits */ - if (kbuf->f_files != 0xffffffffffffffffULL - && (kbuf->f_files & 0xffffffff00000000ULL)) - return -EOVERFLOW; - if (kbuf->f_ffree != 0xffffffffffffffffULL - && (kbuf->f_ffree & 0xffffffff00000000ULL)) - return -EOVERFLOW; - } + + if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL) + return -EOVERFLOW; + memset(&buf, 0, sizeof(struct compat_statfs64)); buf.f_type = kbuf->f_type; buf.f_bsize = kbuf->f_bsize; diff --git a/fs/super.c b/fs/super.c index f627b7c53d2b..cfadab2cbf35 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1300,6 +1300,7 @@ int get_tree_bdev(struct fs_context *fc, mutex_lock(&bdev->bd_fsfreeze_mutex); if (bdev->bd_fsfreeze_count > 0) { mutex_unlock(&bdev->bd_fsfreeze_mutex); + blkdev_put(bdev, mode); warnf(fc, "%pg: Can't mount, blockdev is frozen", bdev); return -EBUSY; } @@ -1308,8 +1309,10 @@ int get_tree_bdev(struct fs_context *fc, fc->sget_key = bdev; s = sget_fc(fc, test_bdev_super_fc, set_bdev_super_fc); mutex_unlock(&bdev->bd_fsfreeze_mutex); - if (IS_ERR(s)) + if (IS_ERR(s)) { + blkdev_put(bdev, mode); return PTR_ERR(s); + } if (s->s_root) { /* Don't summarily change the RO/RW state. */ diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 9fc14e38927f..0caa151cae4e 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -16,11 +16,11 @@ #include <linux/namei.h> #include <linux/tracefs.h> #include <linux/fsnotify.h> +#include <linux/security.h> #include <linux/seq_file.h> #include <linux/parser.h> #include <linux/magic.h> #include <linux/slab.h> -#include <linux/security.h> #define TRACEFS_DEFAULT_MODE 0700 @@ -28,25 +28,6 @@ static struct vfsmount *tracefs_mount; static int tracefs_mount_count; static bool tracefs_registered; -static int default_open_file(struct inode *inode, struct file *filp) -{ - struct dentry *dentry = filp->f_path.dentry; - struct file_operations *real_fops; - int ret; - - if (!dentry) - return -EINVAL; - - ret = security_locked_down(LOCKDOWN_TRACEFS); - if (ret) - return ret; - - real_fops = dentry->d_fsdata; - if (!real_fops->open) - return 0; - return real_fops->open(inode, filp); -} - static ssize_t default_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -241,12 +222,6 @@ static int tracefs_apply_options(struct super_block *sb) return 0; } -static void tracefs_destroy_inode(struct inode *inode) -{ - if (S_ISREG(inode->i_mode)) - kfree(inode->i_fop); -} - static int tracefs_remount(struct super_block *sb, int *flags, char *data) { int err; @@ -283,7 +258,6 @@ static int tracefs_show_options(struct seq_file *m, struct dentry *root) static const struct super_operations tracefs_super_operations = { .statfs = simple_statfs, .remount_fs = tracefs_remount, - .destroy_inode = tracefs_destroy_inode, .show_options = tracefs_show_options, }; @@ -414,10 +388,12 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { - struct file_operations *proxy_fops; struct dentry *dentry; struct inode *inode; + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + if (!(mode & S_IFMT)) mode |= S_IFREG; BUG_ON(!S_ISREG(mode)); @@ -430,20 +406,8 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, if (unlikely(!inode)) return failed_creating(dentry); - proxy_fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL); - if (unlikely(!proxy_fops)) { - iput(inode); - return failed_creating(dentry); - } - - if (!fops) - fops = &tracefs_file_operations; - - dentry->d_fsdata = (void *)fops; - memcpy(proxy_fops, fops, sizeof(*proxy_fops)); - proxy_fops->open = default_open_file; inode->i_mode = mode; - inode->i_fop = proxy_fops; + inode->i_fop = fops ? fops : &tracefs_file_operations; inode->i_private = data; d_instantiate(dentry, inode); fsnotify_create(dentry->d_parent->d_inode, dentry); diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c index 5de296b34ab1..14fbdf22b7e7 100644 --- a/fs/xfs/libxfs/xfs_ag.c +++ b/fs/xfs/libxfs/xfs_ag.c @@ -28,12 +28,11 @@ xfs_get_aghdr_buf( struct xfs_mount *mp, xfs_daddr_t blkno, size_t numblks, - int flags, const struct xfs_buf_ops *ops) { struct xfs_buf *bp; - bp = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, flags); + bp = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, 0); if (!bp) return NULL; @@ -345,7 +344,7 @@ xfs_ag_init_hdr( { struct xfs_buf *bp; - bp = xfs_get_aghdr_buf(mp, id->daddr, id->numblks, 0, ops); + bp = xfs_get_aghdr_buf(mp, id->daddr, id->numblks, ops); if (!bp) return -ENOMEM; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index b9f019603d0b..f0089e862216 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -826,32 +826,17 @@ xfs_attr_shortform_to_leaf( sf = (xfs_attr_shortform_t *)tmpbuffer; xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); - xfs_bmap_local_to_extents_empty(dp, XFS_ATTR_FORK); + xfs_bmap_local_to_extents_empty(args->trans, dp, XFS_ATTR_FORK); bp = NULL; error = xfs_da_grow_inode(args, &blkno); - if (error) { - /* - * If we hit an IO error middle of the transaction inside - * grow_inode(), we may have inconsistent data. Bail out. - */ - if (error == -EIO) - goto out; - xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */ - memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */ + if (error) goto out; - } ASSERT(blkno == 0); error = xfs_attr3_leaf_create(args, blkno, &bp); - if (error) { - /* xfs_attr3_leaf_create may not have instantiated a block */ - if (bp && (xfs_da_shrink_inode(args, 0, bp) != 0)) - goto out; - xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */ - memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */ + if (error) goto out; - } memset((char *)&nargs, 0, sizeof(nargs)); nargs.dp = dp; diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 4edc25a2ba80..02469d59c787 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -792,6 +792,7 @@ out_root_realloc: */ void xfs_bmap_local_to_extents_empty( + struct xfs_trans *tp, struct xfs_inode *ip, int whichfork) { @@ -808,6 +809,7 @@ xfs_bmap_local_to_extents_empty( ifp->if_u1.if_root = NULL; ifp->if_height = 0; XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS); + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); } @@ -840,7 +842,7 @@ xfs_bmap_local_to_extents( ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL); if (!ifp->if_bytes) { - xfs_bmap_local_to_extents_empty(ip, whichfork); + xfs_bmap_local_to_extents_empty(tp, ip, whichfork); flags = XFS_ILOG_CORE; goto done; } @@ -887,7 +889,7 @@ xfs_bmap_local_to_extents( /* account for the change in fork size */ xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); - xfs_bmap_local_to_extents_empty(ip, whichfork); + xfs_bmap_local_to_extents_empty(tp, ip, whichfork); flags |= XFS_ILOG_CORE; ifp->if_u1.if_root = NULL; diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index 5bb446d80542..e2798c6f3a5f 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -182,7 +182,8 @@ void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno, xfs_filblks_t len); int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd); int xfs_bmap_set_attrforkoff(struct xfs_inode *ip, int size, int *version); -void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork); +void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp, + struct xfs_inode *ip, int whichfork); void __xfs_bmap_add_free(struct xfs_trans *tp, xfs_fsblock_t bno, xfs_filblks_t len, const struct xfs_owner_info *oinfo, bool skip_discard); diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c index 9595ced393dc..49e4bc39e7bb 100644 --- a/fs/xfs/libxfs/xfs_dir2_block.c +++ b/fs/xfs/libxfs/xfs_dir2_block.c @@ -1096,7 +1096,7 @@ xfs_dir2_sf_to_block( memcpy(sfp, oldsfp, ifp->if_bytes); xfs_idata_realloc(dp, -ifp->if_bytes, XFS_DATA_FORK); - xfs_bmap_local_to_extents_empty(dp, XFS_DATA_FORK); + xfs_bmap_local_to_extents_empty(tp, dp, XFS_DATA_FORK); dp->i_d.di_size = 0; /* diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c index 93b3793bc5b3..0cab11a5d390 100644 --- a/fs/xfs/scrub/refcount.c +++ b/fs/xfs/scrub/refcount.c @@ -341,7 +341,6 @@ xchk_refcountbt_rec( xfs_extlen_t len; xfs_nlink_t refcount; bool has_cowflag; - int error = 0; bno = be32_to_cpu(rec->refc.rc_startblock); len = be32_to_cpu(rec->refc.rc_blockcount); @@ -366,7 +365,7 @@ xchk_refcountbt_rec( xchk_refcountbt_xref(bs->sc, bno, len, refcount); - return error; + return 0; } /* Make sure we have as many refc blocks as the rmap says. */ diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 0910cb75b65d..4f443703065e 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -864,6 +864,7 @@ xfs_alloc_file_space( xfs_filblks_t allocatesize_fsb; xfs_extlen_t extsz, temp; xfs_fileoff_t startoffset_fsb; + xfs_fileoff_t endoffset_fsb; int nimaps; int quota_flag; int rt; @@ -891,7 +892,8 @@ xfs_alloc_file_space( imapp = &imaps[0]; nimaps = 1; startoffset_fsb = XFS_B_TO_FSBT(mp, offset); - allocatesize_fsb = XFS_B_TO_FSB(mp, count); + endoffset_fsb = XFS_B_TO_FSB(mp, offset + count); + allocatesize_fsb = endoffset_fsb - startoffset_fsb; /* * Allocate file space until done or until there is an error diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 21c243622a79..0abba171aa89 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -345,6 +345,15 @@ xfs_buf_allocate_memory( unsigned short page_count, i; xfs_off_t start, end; int error; + xfs_km_flags_t kmflag_mask = 0; + + /* + * assure zeroed buffer for non-read cases. + */ + if (!(flags & XBF_READ)) { + kmflag_mask |= KM_ZERO; + gfp_mask |= __GFP_ZERO; + } /* * for buffers that are contained within a single page, just allocate @@ -354,7 +363,8 @@ xfs_buf_allocate_memory( size = BBTOB(bp->b_length); if (size < PAGE_SIZE) { int align_mask = xfs_buftarg_dma_alignment(bp->b_target); - bp->b_addr = kmem_alloc_io(size, align_mask, KM_NOFS); + bp->b_addr = kmem_alloc_io(size, align_mask, + KM_NOFS | kmflag_mask); if (!bp->b_addr) { /* low memory - use alloc_page loop instead */ goto use_alloc_page; diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index a2beee9f74da..641d07f30a27 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1443,7 +1443,7 @@ xlog_alloc_log( prev_iclog = iclog; iclog->ic_data = kmem_alloc_io(log->l_iclog_size, align_mask, - KM_MAYFAIL); + KM_MAYFAIL | KM_ZERO); if (!iclog->ic_data) goto out_free_iclog; #ifdef DEBUG diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 508319039dce..c1a514ffff55 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -127,7 +127,7 @@ xlog_alloc_buffer( if (nbblks > 1 && log->l_sectBBsize > 1) nbblks += log->l_sectBBsize; nbblks = round_up(nbblks, log->l_sectBBsize); - return kmem_alloc_io(BBTOB(nbblks), align_mask, KM_MAYFAIL); + return kmem_alloc_io(BBTOB(nbblks), align_mask, KM_MAYFAIL | KM_ZERO); } /* diff --git a/include/dt-bindings/iio/adc/ingenic,adc.h b/include/dt-bindings/iio/adc/ingenic,adc.h index 82706b2706ac..42f871ab3272 100644 --- a/include/dt-bindings/iio/adc/ingenic,adc.h +++ b/include/dt-bindings/iio/adc/ingenic,adc.h @@ -6,5 +6,6 @@ /* ADC channel idx. */ #define INGENIC_ADC_AUX 0 #define INGENIC_ADC_BATTERY 1 +#define INGENIC_ADC_AUX2 2 #endif diff --git a/include/linux/bitops.h b/include/linux/bitops.h index cf074bce3eb3..c94a9ff9f082 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -4,6 +4,13 @@ #include <asm/types.h> #include <linux/bits.h> +/* Set bits in the first 'n' bytes when loaded from memory */ +#ifdef __LITTLE_ENDIAN +# define aligned_byte_mask(n) ((1UL << 8*(n))-1) +#else +# define aligned_byte_mask(n) (~0xffUL << (BITS_PER_LONG - 8 - 8*(n))) +#endif + #define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(long)) diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h index 6b318efd8a74..cdf016596659 100644 --- a/include/linux/compiler_attributes.h +++ b/include/linux/compiler_attributes.h @@ -40,6 +40,7 @@ # define __GCC4_has_attribute___noclone__ 1 # define __GCC4_has_attribute___nonstring__ 0 # define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8) +# define __GCC4_has_attribute___fallthrough__ 0 #endif /* @@ -186,6 +187,22 @@ #endif /* + * Add the pseudo keyword 'fallthrough' so case statement blocks + * must end with any of these keywords: + * break; + * fallthrough; + * goto <label>; + * return [expression]; + * + * gcc: https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes + */ +#if __has_attribute(__fallthrough__) +# define fallthrough __attribute__((__fallthrough__)) +#else +# define fallthrough do {} while (0) /* fallthrough */ +#endif + +/* * Note the missing underscores. * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noinline-function-attribute diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h index 79435cfc20eb..897e799dbcb9 100644 --- a/include/linux/dsa/sja1105.h +++ b/include/linux/dsa/sja1105.h @@ -31,6 +31,8 @@ #define SJA1105_META_SMAC 0x222222222222ull #define SJA1105_META_DMAC 0x0180C200000Eull +#define SJA1105_HWTS_RX_EN 0 + /* Global tagger data: each struct sja1105_port has a reference to * the structure defined in struct sja1105_private. */ @@ -42,7 +44,7 @@ struct sja1105_tagger_data { * from taggers running on multiple ports on SMP systems */ spinlock_t meta_lock; - bool hwts_rx_en; + unsigned long state; }; struct sja1105_skb_cb { diff --git a/include/linux/export.h b/include/linux/export.h index 95f55b7f83a0..621158ecd2e2 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -52,10 +52,10 @@ extern struct module __this_module; __ADDRESSABLE(sym) \ asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \ " .balign 4 \n" \ - "__ksymtab_" #sym NS_SEPARATOR #ns ": \n" \ + "__ksymtab_" #ns NS_SEPARATOR #sym ": \n" \ " .long " #sym "- . \n" \ " .long __kstrtab_" #sym "- . \n" \ - " .long __kstrtab_ns_" #sym "- . \n" \ + " .long __kstrtabns_" #sym "- . \n" \ " .previous \n") #define __KSYMTAB_ENTRY(sym, sec) \ @@ -76,10 +76,10 @@ struct kernel_symbol { #else #define __KSYMTAB_ENTRY_NS(sym, sec, ns) \ static const struct kernel_symbol __ksymtab_##sym##__##ns \ - asm("__ksymtab_" #sym NS_SEPARATOR #ns) \ + asm("__ksymtab_" #ns NS_SEPARATOR #sym) \ __attribute__((section("___ksymtab" sec "+" #sym), used)) \ __aligned(sizeof(void *)) \ - = { (unsigned long)&sym, __kstrtab_##sym, __kstrtab_ns_##sym } + = { (unsigned long)&sym, __kstrtab_##sym, __kstrtabns_##sym } #define __KSYMTAB_ENTRY(sym, sec) \ static const struct kernel_symbol __ksymtab_##sym \ @@ -112,7 +112,7 @@ struct kernel_symbol { /* For every exported symbol, place a struct in the __ksymtab section */ #define ___EXPORT_SYMBOL_NS(sym, sec, ns) \ ___export_symbol_common(sym, sec); \ - static const char __kstrtab_ns_##sym[] \ + static const char __kstrtabns_##sym[] \ __attribute__((section("__ksymtab_strings"), used, aligned(1))) \ = #ns; \ __KSYMTAB_ENTRY_NS(sym, sec, ns) diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 04c36b7a61dd..72579168189d 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -235,7 +235,7 @@ enum hwmon_power_attributes { #define HWMON_P_LABEL BIT(hwmon_power_label) #define HWMON_P_ALARM BIT(hwmon_power_alarm) #define HWMON_P_CAP_ALARM BIT(hwmon_power_cap_alarm) -#define HWMON_P_MIN_ALARM BIT(hwmon_power_max_alarm) +#define HWMON_P_MIN_ALARM BIT(hwmon_power_min_alarm) #define HWMON_P_MAX_ALARM BIT(hwmon_power_max_alarm) #define HWMON_P_LCRIT_ALARM BIT(hwmon_power_lcrit_alarm) #define HWMON_P_CRIT_ALARM BIT(hwmon_power_crit_alarm) diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 7716fa0c9fce..8a4e25a7080c 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -119,6 +119,8 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val); +int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, + unsigned int mode, unsigned int channel); int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, const struct ad_sd_calib_data *cd, unsigned int n); int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 8e132cf819e4..862ce0019eba 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -510,6 +510,7 @@ struct iio_buffer_setup_ops { * attributes * @chan_attr_group: [INTERN] group for all attrs in base directory * @name: [DRIVER] name of the device. + * @label: [DRIVER] unique name to identify which device this is * @info: [DRIVER] callbacks and constant info from driver * @clock_id: [INTERN] timestamping clock posix identifier * @info_exist_lock: [INTERN] lock to prevent use during removal @@ -553,6 +554,7 @@ struct iio_dev { struct list_head channel_attr_list; struct attribute_group chan_attr_group; const char *name; + const char *label; const struct iio_info *info; clockid_t clock_id; struct mutex info_exist_lock; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index fcb46b3374c6..719fc3e15ea4 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1090,6 +1090,7 @@ enum kvm_stat_kind { struct kvm_stat_data { int offset; + int mode; struct kvm *kvm; }; @@ -1097,6 +1098,7 @@ struct kvm_stats_debugfs_item { const char *name; int offset; enum kvm_stat_kind kind; + int mode; }; extern struct kvm_stats_debugfs_item debugfs_entries[]; extern struct dentry *kvm_debugfs_dir; diff --git a/include/linux/leds.h b/include/linux/leds.h index b8df71193329..efb309dba914 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -247,7 +247,7 @@ extern void led_set_brightness(struct led_classdev *led_cdev, /** * led_set_brightness_sync - set LED brightness synchronously * @led_cdev: the LED to set - * @brightness: the brightness to set it to + * @value: the brightness to set it to * * Set an LED's brightness immediately. This function will block * the caller for the time required for accessing device registers, @@ -301,8 +301,7 @@ extern void led_sysfs_enable(struct led_classdev *led_cdev); /** * led_compose_name - compose LED class device name * @dev: LED controller device object - * @child: child fwnode_handle describing a LED or a group of synchronized LEDs; - * it must be provided only for fwnode based LEDs + * @init_data: the LED class device initialization data * @led_classdev_name: composed LED class device name * * Create LED class device name basing on the provided init_data argument. diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 9b60863429cc..ae703ea3ef48 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -356,6 +356,19 @@ static inline bool mem_cgroup_disabled(void) return !cgroup_subsys_enabled(memory_cgrp_subsys); } +static inline unsigned long mem_cgroup_protection(struct mem_cgroup *memcg, + bool in_low_reclaim) +{ + if (mem_cgroup_disabled()) + return 0; + + if (in_low_reclaim) + return READ_ONCE(memcg->memory.emin); + + return max(READ_ONCE(memcg->memory.emin), + READ_ONCE(memcg->memory.elow)); +} + enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root, struct mem_cgroup *memcg); @@ -537,6 +550,8 @@ void mem_cgroup_handle_over_high(void); unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg); +unsigned long mem_cgroup_size(struct mem_cgroup *memcg); + void mem_cgroup_print_oom_context(struct mem_cgroup *memcg, struct task_struct *p); @@ -829,6 +844,12 @@ static inline void memcg_memory_event_mm(struct mm_struct *mm, { } +static inline unsigned long mem_cgroup_protection(struct mem_cgroup *memcg, + bool in_low_reclaim) +{ + return 0; +} + static inline enum mem_cgroup_protection mem_cgroup_protected( struct mem_cgroup *root, struct mem_cgroup *memcg) { @@ -968,6 +989,11 @@ static inline unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg) return 0; } +static inline unsigned long mem_cgroup_size(struct mem_cgroup *memcg) +{ + return 0; +} + static inline void mem_cgroup_print_oom_context(struct mem_cgroup *memcg, struct task_struct *p) { @@ -1264,6 +1290,9 @@ void mem_cgroup_track_foreign_dirty_slowpath(struct page *page, static inline void mem_cgroup_track_foreign_dirty(struct page *page, struct bdi_writeback *wb) { + if (mem_cgroup_disabled()) + return; + if (unlikely(&page->mem_cgroup->css != wb->memcg_css)) mem_cgroup_track_foreign_dirty_slowpath(page, wb); } diff --git a/include/linux/mii.h b/include/linux/mii.h index 5cd824c1c0ca..4ce8901a1af6 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -455,6 +455,15 @@ static inline void mii_lpa_mod_linkmode_lpa_t(unsigned long *lp_advertising, lp_advertising, lpa & LPA_LPACK); } +static inline void mii_ctrl1000_mod_linkmode_adv_t(unsigned long *advertising, + u32 ctrl1000) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertising, + ctrl1000 & ADVERTISE_1000HALF); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertising, + ctrl1000 & ADVERTISE_1000FULL); +} + /** * linkmode_adv_to_lcl_adv_t * @advertising:pointer to linkmode advertising diff --git a/include/linux/phy.h b/include/linux/phy.h index a7ecbe0e55aa..9a0e981df502 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -678,6 +678,7 @@ static inline bool phy_is_started(struct phy_device *phydev) return phydev->state >= PHY_UP; } +void phy_resolve_aneg_pause(struct phy_device *phydev); void phy_resolve_aneg_linkmode(struct phy_device *phydev); /** @@ -1076,6 +1077,7 @@ int genphy_config_eee_advert(struct phy_device *phydev); int __genphy_config_aneg(struct phy_device *phydev, bool changed); int genphy_aneg_done(struct phy_device *phydev); int genphy_update_link(struct phy_device *phydev); +int genphy_read_lpa(struct phy_device *phydev); int genphy_read_status(struct phy_device *phydev); int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h index 30929c22227d..e40b28ca892e 100644 --- a/include/linux/platform_data/st_sensors_pdata.h +++ b/include/linux/platform_data/st_sensors_pdata.h @@ -18,12 +18,14 @@ * @open_drain: set the interrupt line to be open drain if possible. * @spi_3wire: enable spi-3wire mode. * @pullups: enable/disable i2c controller pullup resistors. + * @wakeup_source: enable/disable device as wakeup generator. */ struct st_sensors_platform_data { u8 drdy_int_pin; bool open_drain; bool spi_3wire; bool pullups; + bool wakeup_source; }; #endif /* ST_SENSORS_PDATA_H */ diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 1b5cec067533..f2688404d1cd 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -64,6 +64,8 @@ extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *); extern int platform_get_irq_byname(struct platform_device *, const char *); +extern int platform_get_irq_byname_optional(struct platform_device *dev, + const char *name); extern int platform_add_devices(struct platform_device **, int); struct platform_device_info { diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e7d3b1a513ef..4351577b14d7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -4160,15 +4160,12 @@ static inline void __skb_ext_copy(struct sk_buff *d, const struct sk_buff *s) {} static inline void skb_ext_copy(struct sk_buff *dst, const struct sk_buff *s) {} #endif /* CONFIG_SKB_EXTENSIONS */ -static inline void nf_reset(struct sk_buff *skb) +static inline void nf_reset_ct(struct sk_buff *skb) { #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put(skb_nfct(skb)); skb->_nfct = 0; #endif -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - skb_ext_del(skb, SKB_EXT_BRIDGE_NF); -#endif } static inline void nf_reset_trace(struct sk_buff *skb) diff --git a/include/linux/slab.h b/include/linux/slab.h index ab2b98ad76e1..4d2a2fa55ed5 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -493,6 +493,10 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags) * kmalloc is the normal method of allocating memory * for objects smaller than page size in the kernel. * + * The allocated object address is aligned to at least ARCH_KMALLOC_MINALIGN + * bytes. For @size of power of two bytes, the alignment is also guaranteed + * to be at least to the size. + * * The @flags argument may be one of the GFP flags defined at * include/linux/gfp.h and described at * :ref:`Documentation/core-api/mm-api.rst <mm-api-gfp-flags>` diff --git a/include/linux/string.h b/include/linux/string.h index b2f9df7f0761..b6ccdc2c7f02 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -227,7 +227,26 @@ static inline bool strstarts(const char *str, const char *prefix) } size_t memweight(const void *ptr, size_t bytes); -void memzero_explicit(void *s, size_t count); + +/** + * memzero_explicit - Fill a region of memory (e.g. sensitive + * keying data) with 0s. + * @s: Pointer to the start of the area. + * @count: The size of the area. + * + * Note: usually using memset() is just fine (!), but in cases + * where clearing out _local_ data at the end of a scope is + * necessary, memzero_explicit() should be used instead in + * order to prevent the compiler from optimising away zeroing. + * + * memzero_explicit() doesn't need an arch-specific version as + * it just invokes the one of memset() implicitly. + */ +static inline void memzero_explicit(void *s, size_t count) +{ + memset(s, 0, count); + barrier_data(s); +} /** * kbasename - return the last part of a pathname. diff --git a/include/linux/sunrpc/xprtsock.h b/include/linux/sunrpc/xprtsock.h index 7638dbe7bc50..a940de03808d 100644 --- a/include/linux/sunrpc/xprtsock.h +++ b/include/linux/sunrpc/xprtsock.h @@ -61,6 +61,7 @@ struct sock_xprt { struct mutex recv_mutex; struct sockaddr_storage srcaddr; unsigned short srcport; + int xprt_err; /* * UDP socket buffer size parameters diff --git a/include/linux/tpm_eventlog.h b/include/linux/tpm_eventlog.h index 63238c84dc0b..131ea1bad458 100644 --- a/include/linux/tpm_eventlog.h +++ b/include/linux/tpm_eventlog.h @@ -152,7 +152,7 @@ struct tcg_algorithm_info { * total. Once we've done this we know the offset of the data length field, * and can calculate the total size of the event. * - * Return: size of the event on success, <0 on failure + * Return: size of the event on success, 0 on failure */ static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event, @@ -170,6 +170,7 @@ static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event, u16 halg; int i; int j; + u32 count, event_type; marker = event; marker_start = marker; @@ -190,16 +191,22 @@ static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event, } event = (struct tcg_pcr_event2_head *)mapping; + /* + * The loop below will unmap these fields if the log is larger than + * one page, so save them here for reference: + */ + count = READ_ONCE(event->count); + event_type = READ_ONCE(event->event_type); efispecid = (struct tcg_efi_specid_event_head *)event_header->event; /* Check if event is malformed. */ - if (event->count > efispecid->num_algs) { + if (count > efispecid->num_algs) { size = 0; goto out; } - for (i = 0; i < event->count; i++) { + for (i = 0; i < count; i++) { halg_size = sizeof(event->digests[i].alg_id); /* Map the digest's algorithm identifier */ @@ -256,8 +263,9 @@ static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event, + event_field->event_size; size = marker - marker_start; - if ((event->event_type == 0) && (event_field->event_size == 0)) + if (event_type == 0 && event_field->event_size == 0) size = 0; + out: if (do_mapping) TPM_MEMUNMAP(mapping, mapping_size); diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 70bbdc38dc37..d4ee6e942562 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -231,6 +231,76 @@ __copy_from_user_inatomic_nocache(void *to, const void __user *from, #endif /* ARCH_HAS_NOCACHE_UACCESS */ +extern __must_check int check_zeroed_user(const void __user *from, size_t size); + +/** + * copy_struct_from_user: copy a struct from userspace + * @dst: Destination address, in kernel space. This buffer must be @ksize + * bytes long. + * @ksize: Size of @dst struct. + * @src: Source address, in userspace. + * @usize: (Alleged) size of @src struct. + * + * Copies a struct from userspace to kernel space, in a way that guarantees + * backwards-compatibility for struct syscall arguments (as long as future + * struct extensions are made such that all new fields are *appended* to the + * old struct, and zeroed-out new fields have the same meaning as the old + * struct). + * + * @ksize is just sizeof(*dst), and @usize should've been passed by userspace. + * The recommended usage is something like the following: + * + * SYSCALL_DEFINE2(foobar, const struct foo __user *, uarg, size_t, usize) + * { + * int err; + * struct foo karg = {}; + * + * if (usize > PAGE_SIZE) + * return -E2BIG; + * if (usize < FOO_SIZE_VER0) + * return -EINVAL; + * + * err = copy_struct_from_user(&karg, sizeof(karg), uarg, usize); + * if (err) + * return err; + * + * // ... + * } + * + * There are three cases to consider: + * * If @usize == @ksize, then it's copied verbatim. + * * If @usize < @ksize, then the userspace has passed an old struct to a + * newer kernel. The rest of the trailing bytes in @dst (@ksize - @usize) + * are to be zero-filled. + * * If @usize > @ksize, then the userspace has passed a new struct to an + * older kernel. The trailing bytes unknown to the kernel (@usize - @ksize) + * are checked to ensure they are zeroed, otherwise -E2BIG is returned. + * + * Returns (in all cases, some data may have been copied): + * * -E2BIG: (@usize > @ksize) and there are non-zero trailing bytes in @src. + * * -EFAULT: access to userspace failed. + */ +static __always_inline __must_check int +copy_struct_from_user(void *dst, size_t ksize, const void __user *src, + size_t usize) +{ + size_t size = min(ksize, usize); + size_t rest = max(ksize, usize) - size; + + /* Deal with trailing bytes. */ + if (usize < ksize) { + memset(dst + size, 0, rest); + } else if (usize > ksize) { + int ret = check_zeroed_user(src + size, rest); + if (ret <= 0) + return ret ?: -E2BIG; + } + /* Copy the interoperable parts of the struct. */ + if (copy_from_user(dst, src, size)) + return -EFAULT; + return 0; +} + /* * probe_kernel_read(): safely attempt to read from a location * @dst: pointer to the buffer that shall take the data @@ -285,8 +355,10 @@ extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count); #ifndef user_access_begin #define user_access_begin(ptr,len) access_ok(ptr, len) #define user_access_end() do { } while (0) -#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0) -#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0) +#define unsafe_op_wrap(op, err) do { if (unlikely(op)) goto err; } while (0) +#define unsafe_get_user(x,p,e) unsafe_op_wrap(__get_user(x,p),e) +#define unsafe_put_user(x,p,e) unsafe_op_wrap(__put_user(x,p),e) +#define unsafe_copy_to_user(d,s,l,e) unsafe_op_wrap(__copy_to_user(d,s,l),e) static inline unsigned long user_access_save(void) { return 0UL; } static inline void user_access_restore(unsigned long flags) { } #endif diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index a13a62db3565..edc5c887a44c 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -1068,7 +1068,7 @@ TRACE_EVENT(rxrpc_recvmsg, ), TP_fast_assign( - __entry->call = call->debug_id; + __entry->call = call ? call->debug_id : 0; __entry->why = why; __entry->seq = seq; __entry->offset = offset; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index c99b4f2482c6..4fe35d600ab8 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -1003,6 +1003,8 @@ struct drm_amdgpu_info_device { __u64 high_va_max; /* gfx10 pa_sc_tile_steering_override */ __u32 pa_sc_tile_steering_override; + /* disabled TCCs */ + __u64 tcc_disabled_mask; }; struct drm_amdgpu_info_hw_ip { diff --git a/include/uapi/linux/nvme_ioctl.h b/include/uapi/linux/nvme_ioctl.h index 1c215ea1798e..e168dc59e9a0 100644 --- a/include/uapi/linux/nvme_ioctl.h +++ b/include/uapi/linux/nvme_ioctl.h @@ -45,6 +45,27 @@ struct nvme_passthru_cmd { __u32 result; }; +struct nvme_passthru_cmd64 { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u64 result; +}; + #define nvme_admin_cmd nvme_passthru_cmd #define NVME_IOCTL_ID _IO('N', 0x40) @@ -54,5 +75,7 @@ struct nvme_passthru_cmd { #define NVME_IOCTL_RESET _IO('N', 0x44) #define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) #define NVME_IOCTL_RESCAN _IO('N', 0x46) +#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64) +#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64) #endif /* _UAPI_LINUX_NVME_IOCTL_H */ diff --git a/include/uapi/linux/pg.h b/include/uapi/linux/pg.h index 364c350e85cd..62b6f69bd9fb 100644 --- a/include/uapi/linux/pg.h +++ b/include/uapi/linux/pg.h @@ -35,6 +35,9 @@ */ +#ifndef _UAPI_LINUX_PG_H +#define _UAPI_LINUX_PG_H + #define PG_MAGIC 'P' #define PG_RESET 'Z' #define PG_COMMAND 'C' @@ -61,4 +64,4 @@ struct pg_read_hdr { }; -/* end of pg.h */ +#endif /* _UAPI_LINUX_PG_H */ diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index b3105ac1381a..99335e1f4a27 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -33,8 +33,31 @@ #define CLONE_NEWNET 0x40000000 /* New network namespace */ #define CLONE_IO 0x80000000 /* Clone io context */ -/* - * Arguments for the clone3 syscall +#ifndef __ASSEMBLY__ +/** + * struct clone_args - arguments for the clone3 syscall + * @flags: Flags for the new process as listed above. + * All flags are valid except for CSIGNAL and + * CLONE_DETACHED. + * @pidfd: If CLONE_PIDFD is set, a pidfd will be + * returned in this argument. + * @child_tid: If CLONE_CHILD_SETTID is set, the TID of the + * child process will be returned in the child's + * memory. + * @parent_tid: If CLONE_PARENT_SETTID is set, the TID of + * the child process will be returned in the + * parent's memory. + * @exit_signal: The exit_signal the parent process will be + * sent when the child exits. + * @stack: Specify the location of the stack for the + * child process. + * @stack_size: The size of the stack for the child process. + * @tls: If CLONE_SETTLS is set, the tls descriptor + * is set to tls. + * + * The structure is versioned by size and thus extensible. + * New struct members must go at the end of the struct and + * must be properly 64bit aligned. */ struct clone_args { __aligned_u64 flags; @@ -46,6 +69,9 @@ struct clone_args { __aligned_u64 stack_size; __aligned_u64 tls; }; +#endif + +#define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */ /* * Scheduling policies diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 0f4f87a6fd54..e7fe550b6038 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -291,6 +291,6 @@ #define PORT_SUNIX 121 /* Freescale Linflex UART */ -#define PORT_LINFLEXUART 121 +#define PORT_LINFLEXUART 122 #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 98b30c1613b2..d89969aa9942 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -212,30 +212,7 @@ int xen_xlate_map_ballooned_pages(xen_pfn_t **pfns, void **vaddr, bool xen_running_on_version_or_later(unsigned int major, unsigned int minor); -efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc); -efi_status_t xen_efi_set_time(efi_time_t *tm); -efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, - efi_time_t *tm); -efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm); -efi_status_t xen_efi_get_variable(efi_char16_t *name, efi_guid_t *vendor, - u32 *attr, unsigned long *data_size, - void *data); -efi_status_t xen_efi_get_next_variable(unsigned long *name_size, - efi_char16_t *name, efi_guid_t *vendor); -efi_status_t xen_efi_set_variable(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, - void *data); -efi_status_t xen_efi_query_variable_info(u32 attr, u64 *storage_space, - u64 *remaining_space, - u64 *max_variable_size); -efi_status_t xen_efi_get_next_high_mono_count(u32 *count); -efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, - unsigned long count, unsigned long sg_list); -efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, - unsigned long count, u64 *max_size, - int *reset_type); -void xen_efi_reset_system(int reset_type, efi_status_t status, - unsigned long data_size, efi_char16_t *data); +void xen_efi_runtime_setup(void); #ifdef CONFIG_PREEMPT diff --git a/kernel/dma/remap.c b/kernel/dma/remap.c index ca4e5d44b571..c00b9258fa6a 100644 --- a/kernel/dma/remap.c +++ b/kernel/dma/remap.c @@ -87,9 +87,9 @@ void *dma_common_contiguous_remap(struct page *page, size_t size, */ void dma_common_free_remap(void *cpu_addr, size_t size) { - struct page **pages = dma_common_find_pages(cpu_addr); + struct vm_struct *area = find_vm_area(cpu_addr); - if (!pages) { + if (!area || area->flags != VM_DMA_COHERENT) { WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); return; } diff --git a/kernel/events/core.c b/kernel/events/core.c index 4655adbbae10..9ec0b0bfddbd 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3779,11 +3779,23 @@ static void rotate_ctx(struct perf_event_context *ctx, struct perf_event *event) perf_event_groups_insert(&ctx->flexible_groups, event); } +/* pick an event from the flexible_groups to rotate */ static inline struct perf_event * -ctx_first_active(struct perf_event_context *ctx) +ctx_event_to_rotate(struct perf_event_context *ctx) { - return list_first_entry_or_null(&ctx->flexible_active, - struct perf_event, active_list); + struct perf_event *event; + + /* pick the first active flexible event */ + event = list_first_entry_or_null(&ctx->flexible_active, + struct perf_event, active_list); + + /* if no active flexible event, pick the first event */ + if (!event) { + event = rb_entry_safe(rb_first(&ctx->flexible_groups.tree), + typeof(*event), group_node); + } + + return event; } static bool perf_rotate_context(struct perf_cpu_context *cpuctx) @@ -3808,9 +3820,9 @@ static bool perf_rotate_context(struct perf_cpu_context *cpuctx) perf_pmu_disable(cpuctx->ctx.pmu); if (task_rotate) - task_event = ctx_first_active(task_ctx); + task_event = ctx_event_to_rotate(task_ctx); if (cpu_rotate) - cpu_event = ctx_first_active(&cpuctx->ctx); + cpu_event = ctx_event_to_rotate(&cpuctx->ctx); /* * As per the order given at ctx_resched() first 'pop' task flexible @@ -5668,7 +5680,8 @@ again: * undo the VM accounting. */ - atomic_long_sub((size >> PAGE_SHIFT) + 1, &mmap_user->locked_vm); + atomic_long_sub((size >> PAGE_SHIFT) + 1 - mmap_locked, + &mmap_user->locked_vm); atomic64_sub(mmap_locked, &vma->vm_mm->pinned_vm); free_uid(mmap_user); @@ -5812,8 +5825,20 @@ accounting: user_locked = atomic_long_read(&user->locked_vm) + user_extra; - if (user_locked > user_lock_limit) + if (user_locked <= user_lock_limit) { + /* charge all to locked_vm */ + } else if (atomic_long_read(&user->locked_vm) >= user_lock_limit) { + /* charge all to pinned_vm */ + extra = user_extra; + user_extra = 0; + } else { + /* + * charge locked_vm until it hits user_lock_limit; + * charge the rest from pinned_vm + */ extra = user_locked - user_lock_limit; + user_extra -= extra; + } lock_limit = rlimit(RLIMIT_MEMLOCK); lock_limit >>= PAGE_SHIFT; @@ -10586,55 +10611,26 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, u32 size; int ret; - if (!access_ok(uattr, PERF_ATTR_SIZE_VER0)) - return -EFAULT; - - /* - * zero the full structure, so that a short copy will be nice. - */ + /* Zero the full structure, so that a short copy will be nice. */ memset(attr, 0, sizeof(*attr)); ret = get_user(size, &uattr->size); if (ret) return ret; - if (size > PAGE_SIZE) /* silly large */ - goto err_size; - - if (!size) /* abi compat */ + /* ABI compatibility quirk: */ + if (!size) size = PERF_ATTR_SIZE_VER0; - - if (size < PERF_ATTR_SIZE_VER0) + if (size < PERF_ATTR_SIZE_VER0 || size > PAGE_SIZE) goto err_size; - /* - * If we're handed a bigger struct than we know of, - * ensure all the unknown bits are 0 - i.e. new - * user-space does not rely on any kernel feature - * extensions we dont know about yet. - */ - if (size > sizeof(*attr)) { - unsigned char __user *addr; - unsigned char __user *end; - unsigned char val; - - addr = (void __user *)uattr + sizeof(*attr); - end = (void __user *)uattr + size; - - for (; addr < end; addr++) { - ret = get_user(val, addr); - if (ret) - return ret; - if (val) - goto err_size; - } - size = sizeof(*attr); + ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size); + if (ret) { + if (ret == -E2BIG) + goto err_size; + return ret; } - ret = copy_from_user(attr, uattr, size); - if (ret) - return -EFAULT; - attr->size = size; if (attr->__reserved_1) @@ -11891,6 +11887,10 @@ static int inherit_group(struct perf_event *parent_event, child, leader, child_ctx); if (IS_ERR(child_ctr)) return PTR_ERR(child_ctr); + + if (sub->aux_event == parent_event && + !perf_get_aux_event(child_ctr, leader)) + return -EINVAL; } return 0; } diff --git a/kernel/fork.c b/kernel/fork.c index f9572f416126..bcdf53125210 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2525,39 +2525,19 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, #ifdef __ARCH_WANT_SYS_CLONE3 noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, struct clone_args __user *uargs, - size_t size) + size_t usize) { + int err; struct clone_args args; - if (unlikely(size > PAGE_SIZE)) + if (unlikely(usize > PAGE_SIZE)) return -E2BIG; - - if (unlikely(size < sizeof(struct clone_args))) + if (unlikely(usize < CLONE_ARGS_SIZE_VER0)) return -EINVAL; - if (unlikely(!access_ok(uargs, size))) - return -EFAULT; - - if (size > sizeof(struct clone_args)) { - unsigned char __user *addr; - unsigned char __user *end; - unsigned char val; - - addr = (void __user *)uargs + sizeof(struct clone_args); - end = (void __user *)uargs + size; - - for (; addr < end; addr++) { - if (get_user(val, addr)) - return -EFAULT; - if (val) - return -E2BIG; - } - - size = sizeof(struct clone_args); - } - - if (copy_from_user(&args, uargs, size)) - return -EFAULT; + err = copy_struct_from_user(&args, sizeof(args), uargs, usize); + if (err) + return err; /* * Verify that higher 32bits of exit_signal are unset and that @@ -2604,6 +2584,17 @@ static bool clone3_args_valid(const struct kernel_clone_args *kargs) return true; } +/** + * clone3 - create a new process with specific properties + * @uargs: argument structure + * @size: size of @uargs + * + * clone3() is the extensible successor to clone()/clone2(). + * It takes a struct as argument that is versioned by its size. + * + * Return: On success, a positive PID for the child process. + * On error, a negative errno number. + */ SYSCALL_DEFINE2(clone3, struct clone_args __user *, uargs, size_t, size) { int err; @@ -2934,7 +2925,7 @@ int sysctl_max_threads(struct ctl_table *table, int write, struct ctl_table t; int ret; int threads = max_threads; - int min = MIN_THREADS; + int min = 1; int max = MAX_THREADS; t = *table; @@ -2946,7 +2937,7 @@ int sysctl_max_threads(struct ctl_table *table, int write, if (ret || !write) return ret; - set_max_threads(threads); + max_threads = threads; return 0; } diff --git a/kernel/freezer.c b/kernel/freezer.c index c0738424bb43..dc520f01f99d 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -22,12 +22,6 @@ EXPORT_SYMBOL(system_freezing_cnt); bool pm_freezing; bool pm_nosig_freezing; -/* - * Temporary export for the deadlock workaround in ata_scsi_hotplug(). - * Remove once the hack becomes unnecessary. - */ -EXPORT_SYMBOL_GPL(pm_freezing); - /* protects freezing and frozen transitions */ static DEFINE_SPINLOCK(freezer_lock); diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh index 9ff449888d9c..aff79e461fc9 100755 --- a/kernel/gen_kheaders.sh +++ b/kernel/gen_kheaders.sh @@ -71,7 +71,10 @@ done | cpio --quiet -pd $cpio_dir >/dev/null 2>&1 find $cpio_dir -type f -print0 | xargs -0 -P8 -n1 perl -pi -e 'BEGIN {undef $/;}; s/\/\*((?!SPDX).)*?\*\///smg;' -tar -Jcf $tarfile -C $cpio_dir/ . > /dev/null +# Create archive and try to normalize metadata for reproducibility +tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \ + --owner=0 --group=0 --sort=name --numeric-owner \ + -Jcf $tarfile -C $cpio_dir/ . > /dev/null echo "$src_files_md5" > kernel/kheaders.md5 echo "$obj_files_md5" >> kernel/kheaders.md5 diff --git a/kernel/panic.c b/kernel/panic.c index 47e8ebccc22b..f470a038b05b 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -180,6 +180,7 @@ void panic(const char *fmt, ...) * after setting panic_cpu) from invoking panic() again. */ local_irq_disable(); + preempt_disable_notrace(); /* * It's possible to come here directly from a panic-assertion and diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 7880f4f64d0e..dd05a378631a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5106,9 +5106,6 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a u32 size; int ret; - if (!access_ok(uattr, SCHED_ATTR_SIZE_VER0)) - return -EFAULT; - /* Zero the full structure, so that a short copy will be nice: */ memset(attr, 0, sizeof(*attr)); @@ -5116,45 +5113,19 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a if (ret) return ret; - /* Bail out on silly large: */ - if (size > PAGE_SIZE) - goto err_size; - /* ABI compatibility quirk: */ if (!size) size = SCHED_ATTR_SIZE_VER0; - - if (size < SCHED_ATTR_SIZE_VER0) + if (size < SCHED_ATTR_SIZE_VER0 || size > PAGE_SIZE) goto err_size; - /* - * If we're handed a bigger struct than we know of, - * ensure all the unknown bits are 0 - i.e. new - * user-space does not rely on any kernel feature - * extensions we dont know about yet. - */ - if (size > sizeof(*attr)) { - unsigned char __user *addr; - unsigned char __user *end; - unsigned char val; - - addr = (void __user *)uattr + sizeof(*attr); - end = (void __user *)uattr + size; - - for (; addr < end; addr++) { - ret = get_user(val, addr); - if (ret) - return ret; - if (val) - goto err_size; - } - size = sizeof(*attr); + ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size); + if (ret) { + if (ret == -E2BIG) + goto err_size; + return ret; } - ret = copy_from_user(attr, uattr, size); - if (ret) - return -EFAULT; - if ((attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) && size < SCHED_ATTR_SIZE_VER1) return -EINVAL; @@ -5354,7 +5325,7 @@ sched_attr_copy_to_user(struct sched_attr __user *uattr, * sys_sched_getattr - similar to sched_getparam, but with sched_attr * @pid: the pid in question. * @uattr: structure containing the extended parameters. - * @usize: sizeof(attr) that user-space knows about, for forwards and backwards compatibility. + * @usize: sizeof(attr) for fwd/bwd comp. * @flags: for future extension. */ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 2305ce89a26c..46ed4e1383e2 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -740,7 +740,7 @@ void vtime_account_system(struct task_struct *tsk) write_seqcount_begin(&vtime->seqcount); /* We might have scheduled out from guest path */ - if (current->flags & PF_VCPU) + if (tsk->flags & PF_VCPU) vtime_account_guest(tsk, vtime); else __vtime_account_system(tsk, vtime); @@ -783,7 +783,7 @@ void vtime_guest_enter(struct task_struct *tsk) */ write_seqcount_begin(&vtime->seqcount); __vtime_account_system(tsk, vtime); - current->flags |= PF_VCPU; + tsk->flags |= PF_VCPU; write_seqcount_end(&vtime->seqcount); } EXPORT_SYMBOL_GPL(vtime_guest_enter); @@ -794,7 +794,7 @@ void vtime_guest_exit(struct task_struct *tsk) write_seqcount_begin(&vtime->seqcount); vtime_account_guest(tsk, vtime); - current->flags &= ~PF_VCPU; + tsk->flags &= ~PF_VCPU; write_seqcount_end(&vtime->seqcount); } EXPORT_SYMBOL_GPL(vtime_guest_exit); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 83ab35e2374f..682a754ea3e1 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4926,20 +4926,28 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer) if (++count > 3) { u64 new, old = ktime_to_ns(cfs_b->period); - new = (old * 147) / 128; /* ~115% */ - new = min(new, max_cfs_quota_period); - - cfs_b->period = ns_to_ktime(new); - - /* since max is 1s, this is limited to 1e9^2, which fits in u64 */ - cfs_b->quota *= new; - cfs_b->quota = div64_u64(cfs_b->quota, old); - - pr_warn_ratelimited( - "cfs_period_timer[cpu%d]: period too short, scaling up (new cfs_period_us %lld, cfs_quota_us = %lld)\n", - smp_processor_id(), - div_u64(new, NSEC_PER_USEC), - div_u64(cfs_b->quota, NSEC_PER_USEC)); + /* + * Grow period by a factor of 2 to avoid losing precision. + * Precision loss in the quota/period ratio can cause __cfs_schedulable + * to fail. + */ + new = old * 2; + if (new < max_cfs_quota_period) { + cfs_b->period = ns_to_ktime(new); + cfs_b->quota *= 2; + + pr_warn_ratelimited( + "cfs_period_timer[cpu%d]: period too short, scaling up (new cfs_period_us = %lld, cfs_quota_us = %lld)\n", + smp_processor_id(), + div_u64(new, NSEC_PER_USEC), + div_u64(cfs_b->quota, NSEC_PER_USEC)); + } else { + pr_warn_ratelimited( + "cfs_period_timer[cpu%d]: period too short, but cannot scale up without losing precision (cfs_period_us = %lld, cfs_quota_us = %lld)\n", + smp_processor_id(), + div_u64(old, NSEC_PER_USEC), + div_u64(cfs_b->quota, NSEC_PER_USEC)); + } /* reset count so we don't come right back in here */ count = 0; diff --git a/kernel/sched/membarrier.c b/kernel/sched/membarrier.c index a39bed2c784f..168479a7d61b 100644 --- a/kernel/sched/membarrier.c +++ b/kernel/sched/membarrier.c @@ -174,7 +174,6 @@ static int membarrier_private_expedited(int flags) */ if (cpu == raw_smp_processor_id()) continue; - rcu_read_lock(); p = rcu_dereference(cpu_rq(cpu)->curr); if (p && p->mm == mm) __cpumask_set_cpu(cpu, tmpmask); diff --git a/kernel/time/tick-broadcast-hrtimer.c b/kernel/time/tick-broadcast-hrtimer.c index c1f5bb590b5e..b5a65e212df2 100644 --- a/kernel/time/tick-broadcast-hrtimer.c +++ b/kernel/time/tick-broadcast-hrtimer.c @@ -42,39 +42,39 @@ static int bc_shutdown(struct clock_event_device *evt) */ static int bc_set_next(ktime_t expires, struct clock_event_device *bc) { - int bc_moved; /* - * We try to cancel the timer first. If the callback is on - * flight on some other cpu then we let it handle it. If we - * were able to cancel the timer nothing can rearm it as we - * own broadcast_lock. + * This is called either from enter/exit idle code or from the + * broadcast handler. In all cases tick_broadcast_lock is held. * - * However we can also be called from the event handler of - * ce_broadcast_hrtimer itself when it expires. We cannot - * restart the timer because we are in the callback, but we - * can set the expiry time and let the callback return - * HRTIMER_RESTART. + * hrtimer_cancel() cannot be called here neither from the + * broadcast handler nor from the enter/exit idle code. The idle + * code can run into the problem described in bc_shutdown() and the + * broadcast handler cannot wait for itself to complete for obvious + * reasons. * - * Since we are in the idle loop at this point and because - * hrtimer_{start/cancel} functions call into tracing, - * calls to these functions must be bound within RCU_NONIDLE. + * Each caller tries to arm the hrtimer on its own CPU, but if the + * hrtimer callbback function is currently running, then + * hrtimer_start() cannot move it and the timer stays on the CPU on + * which it is assigned at the moment. + * + * As this can be called from idle code, the hrtimer_start() + * invocation has to be wrapped with RCU_NONIDLE() as + * hrtimer_start() can call into tracing. */ - RCU_NONIDLE( - { - bc_moved = hrtimer_try_to_cancel(&bctimer) >= 0; - if (bc_moved) { - hrtimer_start(&bctimer, expires, - HRTIMER_MODE_ABS_PINNED_HARD); - } - } - ); - - if (bc_moved) { - /* Bind the "device" to the cpu */ - bc->bound_on = smp_processor_id(); - } else if (bc->bound_on == smp_processor_id()) { - hrtimer_set_expires(&bctimer, expires); - } + RCU_NONIDLE( { + hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED_HARD); + /* + * The core tick broadcast mode expects bc->bound_on to be set + * correctly to prevent a CPU which has the broadcast hrtimer + * armed from going deep idle. + * + * As tick_broadcast_lock is held, nothing can change the cpu + * base which was just established in hrtimer_start() above. So + * the below access is safe even without holding the hrtimer + * base lock. + */ + bc->bound_on = bctimer.base->cpu_base->cpu; + } ); return 0; } @@ -100,10 +100,6 @@ static enum hrtimer_restart bc_handler(struct hrtimer *t) { ce_broadcast_hrtimer.event_handler(&ce_broadcast_hrtimer); - if (clockevent_state_oneshot(&ce_broadcast_hrtimer)) - if (ce_broadcast_hrtimer.next_event != KTIME_MAX) - return HRTIMER_RESTART; - return HRTIMER_NORESTART; } diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 62a50bf399d6..f296d89be757 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -18,6 +18,7 @@ #include <linux/clocksource.h> #include <linux/sched/task.h> #include <linux/kallsyms.h> +#include <linux/security.h> #include <linux/seq_file.h> #include <linux/tracefs.h> #include <linux/hardirq.h> @@ -3486,6 +3487,11 @@ static int ftrace_avail_open(struct inode *inode, struct file *file) { struct ftrace_iterator *iter; + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; if (unlikely(ftrace_disabled)) return -ENODEV; @@ -3505,6 +3511,15 @@ ftrace_enabled_open(struct inode *inode, struct file *file) { struct ftrace_iterator *iter; + /* + * This shows us what functions are currently being + * traced and by what. Not sure if we want lockdown + * to hide such critical information for an admin. + * Although, perhaps it can show information we don't + * want people to see, but if something is tracing + * something, we probably want to know about it. + */ + iter = __seq_open_private(file, &show_ftrace_seq_ops, sizeof(*iter)); if (!iter) return -ENOMEM; @@ -3540,21 +3555,22 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, struct ftrace_hash *hash; struct list_head *mod_head; struct trace_array *tr = ops->private; - int ret = 0; + int ret = -ENOMEM; ftrace_ops_init(ops); if (unlikely(ftrace_disabled)) return -ENODEV; + if (tracing_check_open_get_tr(tr)) + return -ENODEV; + iter = kzalloc(sizeof(*iter), GFP_KERNEL); if (!iter) - return -ENOMEM; + goto out; - if (trace_parser_get_init(&iter->parser, FTRACE_BUFF_MAX)) { - kfree(iter); - return -ENOMEM; - } + if (trace_parser_get_init(&iter->parser, FTRACE_BUFF_MAX)) + goto out; iter->ops = ops; iter->flags = flag; @@ -3584,13 +3600,13 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, if (!iter->hash) { trace_parser_put(&iter->parser); - kfree(iter); - ret = -ENOMEM; goto out_unlock; } } else iter->hash = hash; + ret = 0; + if (file->f_mode & FMODE_READ) { iter->pg = ftrace_pages_start; @@ -3602,7 +3618,6 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, /* Failed */ free_ftrace_hash(iter->hash); trace_parser_put(&iter->parser); - kfree(iter); } } else file->private_data = iter; @@ -3610,6 +3625,13 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, out_unlock: mutex_unlock(&ops->func_hash->regex_lock); + out: + if (ret) { + kfree(iter); + if (tr) + trace_array_put(tr); + } + return ret; } @@ -3618,6 +3640,7 @@ ftrace_filter_open(struct inode *inode, struct file *file) { struct ftrace_ops *ops = inode->i_private; + /* Checks for tracefs lockdown */ return ftrace_regex_open(ops, FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES, inode, file); @@ -3628,6 +3651,7 @@ ftrace_notrace_open(struct inode *inode, struct file *file) { struct ftrace_ops *ops = inode->i_private; + /* Checks for tracefs lockdown */ return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE, inode, file); } @@ -5037,6 +5061,8 @@ int ftrace_regex_release(struct inode *inode, struct file *file) mutex_unlock(&iter->ops->func_hash->regex_lock); free_ftrace_hash(iter->hash); + if (iter->tr) + trace_array_put(iter->tr); kfree(iter); return 0; @@ -5194,9 +5220,13 @@ static int __ftrace_graph_open(struct inode *inode, struct file *file, struct ftrace_graph_data *fgd) { - int ret = 0; + int ret; struct ftrace_hash *new_hash = NULL; + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + if (file->f_mode & FMODE_WRITE) { const int size_bits = FTRACE_HASH_DEFAULT_BITS; @@ -6537,8 +6567,9 @@ ftrace_pid_open(struct inode *inode, struct file *file) struct seq_file *m; int ret = 0; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 252f79c435f8..6a0ee9178365 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -17,6 +17,7 @@ #include <linux/stacktrace.h> #include <linux/writeback.h> #include <linux/kallsyms.h> +#include <linux/security.h> #include <linux/seq_file.h> #include <linux/notifier.h> #include <linux/irqflags.h> @@ -304,6 +305,23 @@ void trace_array_put(struct trace_array *this_tr) mutex_unlock(&trace_types_lock); } +int tracing_check_open_get_tr(struct trace_array *tr) +{ + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + + if (tracing_disabled) + return -ENODEV; + + if (tr && trace_array_get(tr) < 0) + return -ENODEV; + + return 0; +} + int call_filter_check_discard(struct trace_event_call *call, void *rec, struct ring_buffer *buffer, struct ring_buffer_event *event) @@ -4140,8 +4158,11 @@ release: int tracing_open_generic(struct inode *inode, struct file *filp) { - if (tracing_disabled) - return -ENODEV; + int ret; + + ret = tracing_check_open_get_tr(NULL); + if (ret) + return ret; filp->private_data = inode->i_private; return 0; @@ -4156,15 +4177,14 @@ bool tracing_is_disabled(void) * Open and update trace_array ref count. * Must have the current trace_array passed to it. */ -static int tracing_open_generic_tr(struct inode *inode, struct file *filp) +int tracing_open_generic_tr(struct inode *inode, struct file *filp) { struct trace_array *tr = inode->i_private; + int ret; - if (tracing_disabled) - return -ENODEV; - - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; filp->private_data = inode->i_private; @@ -4233,10 +4253,11 @@ static int tracing_open(struct inode *inode, struct file *file) { struct trace_array *tr = inode->i_private; struct trace_iterator *iter; - int ret = 0; + int ret; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; /* If this file was open for write, then erase contents */ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { @@ -4352,12 +4373,15 @@ static int show_traces_open(struct inode *inode, struct file *file) struct seq_file *m; int ret; - if (tracing_disabled) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; ret = seq_open(file, &show_traces_seq_ops); - if (ret) + if (ret) { + trace_array_put(tr); return ret; + } m = file->private_data; m->private = tr; @@ -4365,6 +4389,14 @@ static int show_traces_open(struct inode *inode, struct file *file) return 0; } +static int show_traces_release(struct inode *inode, struct file *file) +{ + struct trace_array *tr = inode->i_private; + + trace_array_put(tr); + return seq_release(inode, file); +} + static ssize_t tracing_write_stub(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos) @@ -4395,8 +4427,8 @@ static const struct file_operations tracing_fops = { static const struct file_operations show_traces_fops = { .open = show_traces_open, .read = seq_read, - .release = seq_release, .llseek = seq_lseek, + .release = show_traces_release, }; static ssize_t @@ -4697,11 +4729,9 @@ static int tracing_trace_options_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; int ret; - if (tracing_disabled) - return -ENODEV; - - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; ret = single_open(file, tracing_trace_options_show, inode->i_private); if (ret < 0) @@ -5038,8 +5068,11 @@ static const struct seq_operations tracing_saved_tgids_seq_ops = { static int tracing_saved_tgids_open(struct inode *inode, struct file *filp) { - if (tracing_disabled) - return -ENODEV; + int ret; + + ret = tracing_check_open_get_tr(NULL); + if (ret) + return ret; return seq_open(filp, &tracing_saved_tgids_seq_ops); } @@ -5115,8 +5148,11 @@ static const struct seq_operations tracing_saved_cmdlines_seq_ops = { static int tracing_saved_cmdlines_open(struct inode *inode, struct file *filp) { - if (tracing_disabled) - return -ENODEV; + int ret; + + ret = tracing_check_open_get_tr(NULL); + if (ret) + return ret; return seq_open(filp, &tracing_saved_cmdlines_seq_ops); } @@ -5280,8 +5316,11 @@ static const struct seq_operations tracing_eval_map_seq_ops = { static int tracing_eval_map_open(struct inode *inode, struct file *filp) { - if (tracing_disabled) - return -ENODEV; + int ret; + + ret = tracing_check_open_get_tr(NULL); + if (ret) + return ret; return seq_open(filp, &tracing_eval_map_seq_ops); } @@ -5804,13 +5843,11 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) { struct trace_array *tr = inode->i_private; struct trace_iterator *iter; - int ret = 0; - - if (tracing_disabled) - return -ENODEV; + int ret; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; mutex_lock(&trace_types_lock); @@ -5999,6 +6036,7 @@ waitagain: sizeof(struct trace_iterator) - offsetof(struct trace_iterator, seq)); cpumask_clear(iter->started); + trace_seq_init(&iter->seq); iter->pos = -1; trace_event_read_lock(); @@ -6547,11 +6585,9 @@ static int tracing_clock_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; int ret; - if (tracing_disabled) - return -ENODEV; - - if (trace_array_get(tr)) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; ret = single_open(file, tracing_clock_show, inode->i_private); if (ret < 0) @@ -6581,11 +6617,9 @@ static int tracing_time_stamp_mode_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; int ret; - if (tracing_disabled) - return -ENODEV; - - if (trace_array_get(tr)) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; ret = single_open(file, tracing_time_stamp_mode_show, inode->i_private); if (ret < 0) @@ -6638,10 +6672,11 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; struct trace_iterator *iter; struct seq_file *m; - int ret = 0; + int ret; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; if (file->f_mode & FMODE_READ) { iter = __tracing_open(inode, file, true); @@ -6786,6 +6821,7 @@ static int snapshot_raw_open(struct inode *inode, struct file *filp) struct ftrace_buffer_info *info; int ret; + /* The following checks for tracefs lockdown */ ret = tracing_buffers_open(inode, filp); if (ret < 0) return ret; @@ -7105,8 +7141,9 @@ static int tracing_err_log_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; int ret = 0; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; /* If this file was opened for write, then erase contents */ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) @@ -7157,11 +7194,9 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) struct ftrace_buffer_info *info; int ret; - if (tracing_disabled) - return -ENODEV; - - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f801d154ff6a..d685c61085c0 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -338,6 +338,7 @@ extern struct mutex trace_types_lock; extern int trace_array_get(struct trace_array *tr); extern void trace_array_put(struct trace_array *tr); +extern int tracing_check_open_get_tr(struct trace_array *tr); extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs); extern int tracing_set_clock(struct trace_array *tr, const char *clockstr); @@ -681,6 +682,7 @@ void tracing_reset_online_cpus(struct trace_buffer *buf); void tracing_reset_current(int cpu); void tracing_reset_all_online_cpus(void); int tracing_open_generic(struct inode *inode, struct file *filp); +int tracing_open_generic_tr(struct inode *inode, struct file *filp); bool tracing_is_disabled(void); bool tracer_tracing_is_on(struct trace_array *tr); void tracer_tracing_on(struct trace_array *tr); diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c index a41fed46c285..89779eb84a07 100644 --- a/kernel/trace/trace_dynevent.c +++ b/kernel/trace/trace_dynevent.c @@ -174,6 +174,10 @@ static int dyn_event_open(struct inode *inode, struct file *file) { int ret; + ret = tracing_check_open_get_tr(NULL); + if (ret) + return ret; + if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { ret = dyn_events_release_all(NULL); if (ret < 0) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index b89cdfe20bc1..fba87d10f0c1 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) fmt #include <linux/workqueue.h> +#include <linux/security.h> #include <linux/spinlock.h> #include <linux/kthread.h> #include <linux/tracefs.h> @@ -1294,6 +1295,8 @@ static int trace_format_open(struct inode *inode, struct file *file) struct seq_file *m; int ret; + /* Do we want to hide event format files on tracefs lockdown? */ + ret = seq_open(file, &trace_format_seq_ops); if (ret < 0) return ret; @@ -1440,28 +1443,17 @@ static int system_tr_open(struct inode *inode, struct file *filp) struct trace_array *tr = inode->i_private; int ret; - if (tracing_is_disabled()) - return -ENODEV; - - if (trace_array_get(tr) < 0) - return -ENODEV; - /* Make a temporary dir that has no system but points to tr */ dir = kzalloc(sizeof(*dir), GFP_KERNEL); - if (!dir) { - trace_array_put(tr); + if (!dir) return -ENOMEM; - } - dir->tr = tr; - - ret = tracing_open_generic(inode, filp); + ret = tracing_open_generic_tr(inode, filp); if (ret < 0) { - trace_array_put(tr); kfree(dir); return ret; } - + dir->tr = tr; filp->private_data = dir; return 0; @@ -1771,6 +1763,10 @@ ftrace_event_open(struct inode *inode, struct file *file, struct seq_file *m; int ret; + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + ret = seq_open(file, seq_ops); if (ret < 0) return ret; @@ -1795,6 +1791,7 @@ ftrace_event_avail_open(struct inode *inode, struct file *file) { const struct seq_operations *seq_ops = &show_event_seq_ops; + /* Checks for tracefs lockdown */ return ftrace_event_open(inode, file, seq_ops); } @@ -1805,8 +1802,9 @@ ftrace_event_set_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; int ret; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) @@ -1825,8 +1823,9 @@ ftrace_event_set_pid_open(struct inode *inode, struct file *file) struct trace_array *tr = inode->i_private; int ret; - if (trace_array_get(tr) < 0) - return -ENODEV; + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 9468bd8d44a2..57648c5aa679 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -7,6 +7,7 @@ #include <linux/module.h> #include <linux/kallsyms.h> +#include <linux/security.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/stacktrace.h> @@ -1448,6 +1449,10 @@ static int synth_events_open(struct inode *inode, struct file *file) { int ret; + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { ret = dyn_events_release_all(&synth_event_ops); if (ret < 0) @@ -1680,7 +1685,7 @@ static int save_hist_vars(struct hist_trigger_data *hist_data) if (var_data) return 0; - if (trace_array_get(tr) < 0) + if (tracing_check_open_get_tr(tr)) return -ENODEV; var_data = kzalloc(sizeof(*var_data), GFP_KERNEL); @@ -5515,6 +5520,12 @@ static int hist_show(struct seq_file *m, void *v) static int event_hist_open(struct inode *inode, struct file *file) { + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + return single_open(file, hist_show, file); } diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 2a2912cb4533..2cd53ca21b51 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -5,6 +5,7 @@ * Copyright (C) 2013 Tom Zanussi <tom.zanussi@linux.intel.com> */ +#include <linux/security.h> #include <linux/module.h> #include <linux/ctype.h> #include <linux/mutex.h> @@ -173,7 +174,11 @@ static const struct seq_operations event_triggers_seq_ops = { static int event_trigger_regex_open(struct inode *inode, struct file *file) { - int ret = 0; + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; mutex_lock(&event_mutex); @@ -292,6 +297,7 @@ event_trigger_write(struct file *filp, const char __user *ubuf, static int event_trigger_open(struct inode *inode, struct file *filp) { + /* Checks for tracefs lockdown */ return event_trigger_regex_open(inode, filp); } diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c index fa95139445b2..862f4b0139fc 100644 --- a/kernel/trace/trace_hwlat.c +++ b/kernel/trace/trace_hwlat.c @@ -150,7 +150,7 @@ void trace_hwlat_callback(bool enter) if (enter) nmi_ts_start = time_get(); else - nmi_total_ts = time_get() - nmi_ts_start; + nmi_total_ts += time_get() - nmi_ts_start; } if (enter) @@ -256,6 +256,8 @@ static int get_sample(void) /* Keep a running maximum ever recorded hardware latency */ if (sample > tr->max_latency) tr->max_latency = sample; + if (outer_sample > tr->max_latency) + tr->max_latency = outer_sample; } out: diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 324ffbea3556..1552a95c743b 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -7,11 +7,11 @@ */ #define pr_fmt(fmt) "trace_kprobe: " fmt +#include <linux/security.h> #include <linux/module.h> #include <linux/uaccess.h> #include <linux/rculist.h> #include <linux/error-injection.h> -#include <linux/security.h> #include <asm/setup.h> /* for COMMAND_LINE_SIZE */ @@ -936,6 +936,10 @@ static int probes_open(struct inode *inode, struct file *file) { int ret; + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { ret = dyn_events_release_all(&trace_kprobe_ops); if (ret < 0) @@ -988,6 +992,12 @@ static const struct seq_operations profile_seq_op = { static int profile_open(struct inode *inode, struct file *file) { + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + return seq_open(file, &profile_seq_op); } diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index c3fd849d4a8f..d4e31e969206 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -6,6 +6,7 @@ * */ #include <linux/seq_file.h> +#include <linux/security.h> #include <linux/uaccess.h> #include <linux/kernel.h> #include <linux/ftrace.h> @@ -348,6 +349,12 @@ static const struct seq_operations show_format_seq_ops = { static int ftrace_formats_open(struct inode *inode, struct file *file) { + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + return seq_open(file, &show_format_seq_ops); } diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index ec9a34a97129..4df9a209f7ca 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -5,6 +5,7 @@ */ #include <linux/sched/task_stack.h> #include <linux/stacktrace.h> +#include <linux/security.h> #include <linux/kallsyms.h> #include <linux/seq_file.h> #include <linux/spinlock.h> @@ -470,6 +471,12 @@ static const struct seq_operations stack_trace_seq_ops = { static int stack_trace_open(struct inode *inode, struct file *file) { + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + return seq_open(file, &stack_trace_seq_ops); } @@ -487,6 +494,7 @@ stack_trace_filter_open(struct inode *inode, struct file *file) { struct ftrace_ops *ops = inode->i_private; + /* Checks for tracefs lockdown */ return ftrace_regex_open(ops, FTRACE_ITER_FILTER, inode, file); } diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c index 75bf1bcb4a8a..9ab0a1a7ad5e 100644 --- a/kernel/trace/trace_stat.c +++ b/kernel/trace/trace_stat.c @@ -9,7 +9,7 @@ * */ - +#include <linux/security.h> #include <linux/list.h> #include <linux/slab.h> #include <linux/rbtree.h> @@ -238,6 +238,10 @@ static int tracing_stat_open(struct inode *inode, struct file *file) struct seq_file *m; struct stat_session *session = inode->i_private; + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + ret = stat_seq_init(session); if (ret) return ret; diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index dd884341f5c5..352073d36585 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -7,6 +7,7 @@ */ #define pr_fmt(fmt) "trace_uprobe: " fmt +#include <linux/security.h> #include <linux/ctype.h> #include <linux/module.h> #include <linux/uaccess.h> @@ -769,6 +770,10 @@ static int probes_open(struct inode *inode, struct file *file) { int ret; + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { ret = dyn_events_release_all(&trace_uprobe_ops); if (ret) @@ -818,6 +823,12 @@ static const struct seq_operations profile_seq_op = { static int profile_open(struct inode *inode, struct file *file) { + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + return seq_open(file, &profile_seq_op); } diff --git a/lib/string.c b/lib/string.c index cd7a10c19210..08ec58cc673b 100644 --- a/lib/string.c +++ b/lib/string.c @@ -748,27 +748,6 @@ void *memset(void *s, int c, size_t count) EXPORT_SYMBOL(memset); #endif -/** - * memzero_explicit - Fill a region of memory (e.g. sensitive - * keying data) with 0s. - * @s: Pointer to the start of the area. - * @count: The size of the area. - * - * Note: usually using memset() is just fine (!), but in cases - * where clearing out _local_ data at the end of a scope is - * necessary, memzero_explicit() should be used instead in - * order to prevent the compiler from optimising away zeroing. - * - * memzero_explicit() doesn't need an arch-specific version as - * it just invokes the one of memset() implicitly. - */ -void memzero_explicit(void *s, size_t count) -{ - memset(s, 0, count); - barrier_data(s); -} -EXPORT_SYMBOL(memzero_explicit); - #ifndef __HAVE_ARCH_MEMSET16 /** * memset16() - Fill a memory area with a uint16_t diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c index 28ff554a1be8..6c0005d5dd5c 100644 --- a/lib/strnlen_user.c +++ b/lib/strnlen_user.c @@ -3,16 +3,10 @@ #include <linux/export.h> #include <linux/uaccess.h> #include <linux/mm.h> +#include <linux/bitops.h> #include <asm/word-at-a-time.h> -/* Set bits in the first 'n' bytes when loaded from memory */ -#ifdef __LITTLE_ENDIAN -# define aligned_byte_mask(n) ((1ul << 8*(n))-1) -#else -# define aligned_byte_mask(n) (~0xfful << (BITS_PER_LONG - 8 - 8*(n))) -#endif - /* * Do a strnlen, return length of string *with* final '\0'. * 'count' is the user-supplied count, while 'max' is the diff --git a/lib/test_user_copy.c b/lib/test_user_copy.c index 67bcd5dfd847..e365ace06538 100644 --- a/lib/test_user_copy.c +++ b/lib/test_user_copy.c @@ -31,14 +31,133 @@ # define TEST_U64 #endif -#define test(condition, msg) \ -({ \ - int cond = (condition); \ - if (cond) \ - pr_warn("%s\n", msg); \ - cond; \ +#define test(condition, msg, ...) \ +({ \ + int cond = (condition); \ + if (cond) \ + pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \ + cond; \ }) +static bool is_zeroed(void *from, size_t size) +{ + return memchr_inv(from, 0x0, size) == NULL; +} + +static int test_check_nonzero_user(char *kmem, char __user *umem, size_t size) +{ + int ret = 0; + size_t start, end, i; + size_t zero_start = size / 4; + size_t zero_end = size - zero_start; + + /* + * We conduct a series of check_nonzero_user() tests on a block of memory + * with the following byte-pattern (trying every possible [start,end] + * pair): + * + * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ] + * + * And we verify that check_nonzero_user() acts identically to memchr_inv(). + */ + + memset(kmem, 0x0, size); + for (i = 1; i < zero_start; i += 2) + kmem[i] = 0xff; + for (i = zero_end; i < size; i += 2) + kmem[i] = 0xff; + + ret |= test(copy_to_user(umem, kmem, size), + "legitimate copy_to_user failed"); + + for (start = 0; start <= size; start++) { + for (end = start; end <= size; end++) { + size_t len = end - start; + int retval = check_zeroed_user(umem + start, len); + int expected = is_zeroed(kmem + start, len); + + ret |= test(retval != expected, + "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)", + retval, expected, start, end); + } + } + + return ret; +} + +static int test_copy_struct_from_user(char *kmem, char __user *umem, + size_t size) +{ + int ret = 0; + char *umem_src = NULL, *expected = NULL; + size_t ksize, usize; + + umem_src = kmalloc(size, GFP_KERNEL); + if ((ret |= test(umem_src == NULL, "kmalloc failed"))) + goto out_free; + + expected = kmalloc(size, GFP_KERNEL); + if ((ret |= test(expected == NULL, "kmalloc failed"))) + goto out_free; + + /* Fill umem with a fixed byte pattern. */ + memset(umem_src, 0x3e, size); + ret |= test(copy_to_user(umem, umem_src, size), + "legitimate copy_to_user failed"); + + /* Check basic case -- (usize == ksize). */ + ksize = size; + usize = size; + + memcpy(expected, umem_src, ksize); + + memset(kmem, 0x0, size); + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), + "copy_struct_from_user(usize == ksize) failed"); + ret |= test(memcmp(kmem, expected, ksize), + "copy_struct_from_user(usize == ksize) gives unexpected copy"); + + /* Old userspace case -- (usize < ksize). */ + ksize = size; + usize = size / 2; + + memcpy(expected, umem_src, usize); + memset(expected + usize, 0x0, ksize - usize); + + memset(kmem, 0x0, size); + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), + "copy_struct_from_user(usize < ksize) failed"); + ret |= test(memcmp(kmem, expected, ksize), + "copy_struct_from_user(usize < ksize) gives unexpected copy"); + + /* New userspace (-E2BIG) case -- (usize > ksize). */ + ksize = size / 2; + usize = size; + + memset(kmem, 0x0, size); + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG, + "copy_struct_from_user(usize > ksize) didn't give E2BIG"); + + /* New userspace (success) case -- (usize > ksize). */ + ksize = size / 2; + usize = size; + + memcpy(expected, umem_src, ksize); + ret |= test(clear_user(umem + ksize, usize - ksize), + "legitimate clear_user failed"); + + memset(kmem, 0x0, size); + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), + "copy_struct_from_user(usize > ksize) failed"); + ret |= test(memcmp(kmem, expected, ksize), + "copy_struct_from_user(usize > ksize) gives unexpected copy"); + +out_free: + kfree(expected); + kfree(umem_src); + return ret; +} + static int __init test_user_copy_init(void) { int ret = 0; @@ -106,6 +225,11 @@ static int __init test_user_copy_init(void) #endif #undef test_legit + /* Test usage of check_nonzero_user(). */ + ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE); + /* Test usage of copy_struct_from_user(). */ + ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE); + /* * Invalid usage: none of these copies should succeed. */ diff --git a/lib/textsearch.c b/lib/textsearch.c index 4f16eec5d554..f68dea8806be 100644 --- a/lib/textsearch.c +++ b/lib/textsearch.c @@ -89,9 +89,9 @@ * goto errout; * } * - * pos = textsearch_find_continuous(conf, \&state, example, strlen(example)); + * pos = textsearch_find_continuous(conf, &state, example, strlen(example)); * if (pos != UINT_MAX) - * panic("Oh my god, dancing chickens at \%d\n", pos); + * panic("Oh my god, dancing chickens at %d\n", pos); * * textsearch_destroy(conf); */ diff --git a/lib/usercopy.c b/lib/usercopy.c index c2bfbcaeb3dc..cbb4d9ec00f2 100644 --- a/lib/usercopy.c +++ b/lib/usercopy.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/uaccess.h> +#include <linux/bitops.h> /* out-of-line parts */ @@ -31,3 +32,57 @@ unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n) } EXPORT_SYMBOL(_copy_to_user); #endif + +/** + * check_zeroed_user: check if a userspace buffer only contains zero bytes + * @from: Source address, in userspace. + * @size: Size of buffer. + * + * This is effectively shorthand for "memchr_inv(from, 0, size) == NULL" for + * userspace addresses (and is more efficient because we don't care where the + * first non-zero byte is). + * + * Returns: + * * 0: There were non-zero bytes present in the buffer. + * * 1: The buffer was full of zero bytes. + * * -EFAULT: access to userspace failed. + */ +int check_zeroed_user(const void __user *from, size_t size) +{ + unsigned long val; + uintptr_t align = (uintptr_t) from % sizeof(unsigned long); + + if (unlikely(size == 0)) + return 1; + + from -= align; + size += align; + + if (!user_access_begin(from, size)) + return -EFAULT; + + unsafe_get_user(val, (unsigned long __user *) from, err_fault); + if (align) + val &= ~aligned_byte_mask(align); + + while (size > sizeof(unsigned long)) { + if (unlikely(val)) + goto done; + + from += sizeof(unsigned long); + size -= sizeof(unsigned long); + + unsafe_get_user(val, (unsigned long __user *) from, err_fault); + } + + if (size < sizeof(unsigned long)) + val &= aligned_byte_mask(size); + +done: + user_access_end(); + return (val == 0); +err_fault: + user_access_end(); + return -EFAULT; +} +EXPORT_SYMBOL(check_zeroed_user); diff --git a/lib/vdso/Kconfig b/lib/vdso/Kconfig index cc00364bd2c2..9fe698ff62ec 100644 --- a/lib/vdso/Kconfig +++ b/lib/vdso/Kconfig @@ -24,13 +24,4 @@ config GENERIC_COMPAT_VDSO help This config option enables the compat VDSO layer. -config CROSS_COMPILE_COMPAT_VDSO - string "32 bit Toolchain prefix for compat vDSO" - default "" - depends on GENERIC_COMPAT_VDSO - help - Defines the cross-compiler prefix for compiling compat vDSO. - If a 64 bit compiler (i.e. x86_64) can compile the VDSO for - 32 bit, it does not need to define this parameter. - endif diff --git a/mm/backing-dev.c b/mm/backing-dev.c index d9daa3e422d0..c360f6a6c844 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -239,8 +239,8 @@ static int __init default_bdi_init(void) { int err; - bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_FREEZABLE | - WQ_UNBOUND | WQ_SYSFS, 0); + bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_UNBOUND | + WQ_SYSFS, 0); if (!bdi_wq) return -ENOMEM; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c313c49074ca..bdac56009a38 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1567,6 +1567,11 @@ unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg) return max; } +unsigned long mem_cgroup_size(struct mem_cgroup *memcg) +{ + return page_counter_read(&memcg->memory); +} + static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order) { diff --git a/mm/memremap.c b/mm/memremap.c index 32c79b51af86..68204912cc0a 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -13,8 +13,6 @@ #include <linux/xarray.h> static DEFINE_XARRAY(pgmap_array); -#define SECTION_MASK ~((1UL << PA_SECTION_SHIFT) - 1) -#define SECTION_SIZE (1UL << PA_SECTION_SHIFT) #ifdef CONFIG_DEV_PAGEMAP_OPS DEFINE_STATIC_KEY_FALSE(devmap_managed_key); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 15c2050c629b..c0b2e0306720 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1175,11 +1175,17 @@ static __always_inline bool free_pages_prepare(struct page *page, debug_check_no_obj_freed(page_address(page), PAGE_SIZE << order); } - arch_free_page(page, order); if (want_init_on_free()) kernel_init_free_pages(page, 1 << order); kernel_poison_pages(page, 1 << order, 0); + /* + * arch_free_page() can make the page's contents inaccessible. s390 + * does this. So nothing which can access the page's contents should + * happen after this. + */ + arch_free_page(page, order); + if (debug_pagealloc_enabled()) kernel_map_pages(page, 1 << order, 0); diff --git a/mm/shmem.c b/mm/shmem.c index cd570cc79c76..220be9fa2c41 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3482,6 +3482,12 @@ static int shmem_parse_options(struct fs_context *fc, void *data) { char *options = data; + if (options) { + int err = security_sb_eat_lsm_opts(options, &fc->security); + if (err) + return err; + } + while (options != NULL) { char *this_char = options; for (;;) { diff --git a/mm/shuffle.c b/mm/shuffle.c index 3ce12481b1dc..b3fe97fd6654 100644 --- a/mm/shuffle.c +++ b/mm/shuffle.c @@ -33,7 +33,7 @@ __meminit void page_alloc_shuffle(enum mm_shuffle_ctl ctl) } static bool shuffle_param; -extern int shuffle_show(char *buffer, const struct kernel_param *kp) +static int shuffle_show(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%c\n", test_bit(SHUFFLE_ENABLE, &shuffle_state) ? 'Y' : 'N'); diff --git a/mm/slab_common.c b/mm/slab_common.c index 6491c3a41805..c29f03adca91 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1030,10 +1030,19 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name, unsigned int useroffset, unsigned int usersize) { int err; + unsigned int align = ARCH_KMALLOC_MINALIGN; s->name = name; s->size = s->object_size = size; - s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size); + + /* + * For power of two sizes, guarantee natural alignment for kmalloc + * caches, regardless of SL*B debugging options. + */ + if (is_power_of_2(size)) + align = max(align, size); + s->align = calculate_alignment(flags, align, size); + s->useroffset = useroffset; s->usersize = usersize; @@ -1287,12 +1296,16 @@ void __init create_kmalloc_caches(slab_flags_t flags) */ void *kmalloc_order(size_t size, gfp_t flags, unsigned int order) { - void *ret; + void *ret = NULL; struct page *page; flags |= __GFP_COMP; page = alloc_pages(flags, order); - ret = page ? page_address(page) : NULL; + if (likely(page)) { + ret = page_address(page); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, + 1 << order); + } ret = kasan_kmalloc_large(ret, size, flags); /* As ret might get tagged, call kmemleak hook after KASAN. */ kmemleak_alloc(ret, size, 1, flags); diff --git a/mm/slob.c b/mm/slob.c index cf377beab962..fa53e9f73893 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -190,7 +190,7 @@ static int slob_last(slob_t *s) static void *slob_new_pages(gfp_t gfp, int order, int node) { - void *page; + struct page *page; #ifdef CONFIG_NUMA if (node != NUMA_NO_NODE) @@ -202,14 +202,21 @@ static void *slob_new_pages(gfp_t gfp, int order, int node) if (!page) return NULL; + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, + 1 << order); return page_address(page); } static void slob_free_pages(void *b, int order) { + struct page *sp = virt_to_page(b); + if (current->reclaim_state) current->reclaim_state->reclaimed_slab += 1 << order; - free_pages((unsigned long)b, order); + + mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE, + -(1 << order)); + __free_pages(sp, order); } /* @@ -217,6 +224,7 @@ static void slob_free_pages(void *b, int order) * @sp: Page to look in. * @size: Size of the allocation. * @align: Allocation alignment. + * @align_offset: Offset in the allocated block that will be aligned. * @page_removed_from_list: Return parameter. * * Tries to find a chunk of memory at least @size bytes big within @page. @@ -227,7 +235,7 @@ static void slob_free_pages(void *b, int order) * true (set to false otherwise). */ static void *slob_page_alloc(struct page *sp, size_t size, int align, - bool *page_removed_from_list) + int align_offset, bool *page_removed_from_list) { slob_t *prev, *cur, *aligned = NULL; int delta = 0, units = SLOB_UNITS(size); @@ -236,8 +244,17 @@ static void *slob_page_alloc(struct page *sp, size_t size, int align, for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) { slobidx_t avail = slob_units(cur); + /* + * 'aligned' will hold the address of the slob block so that the + * address 'aligned'+'align_offset' is aligned according to the + * 'align' parameter. This is for kmalloc() which prepends the + * allocated block with its size, so that the block itself is + * aligned when needed. + */ if (align) { - aligned = (slob_t *)ALIGN((unsigned long)cur, align); + aligned = (slob_t *) + (ALIGN((unsigned long)cur + align_offset, align) + - align_offset); delta = aligned - cur; } if (avail >= units + delta) { /* room enough? */ @@ -281,7 +298,8 @@ static void *slob_page_alloc(struct page *sp, size_t size, int align, /* * slob_alloc: entry point into the slob allocator. */ -static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) +static void *slob_alloc(size_t size, gfp_t gfp, int align, int node, + int align_offset) { struct page *sp; struct list_head *slob_list; @@ -312,7 +330,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) if (sp->units < SLOB_UNITS(size)) continue; - b = slob_page_alloc(sp, size, align, &page_removed_from_list); + b = slob_page_alloc(sp, size, align, align_offset, &page_removed_from_list); if (!b) continue; @@ -349,7 +367,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) INIT_LIST_HEAD(&sp->slab_list); set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE)); set_slob_page_free(sp, slob_list); - b = slob_page_alloc(sp, size, align, &_unused); + b = slob_page_alloc(sp, size, align, align_offset, &_unused); BUG_ON(!b); spin_unlock_irqrestore(&slob_lock, flags); } @@ -451,7 +469,7 @@ static __always_inline void * __do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller) { unsigned int *m; - int align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); + int minalign = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); void *ret; gfp &= gfp_allowed_mask; @@ -459,19 +477,28 @@ __do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller) fs_reclaim_acquire(gfp); fs_reclaim_release(gfp); - if (size < PAGE_SIZE - align) { + if (size < PAGE_SIZE - minalign) { + int align = minalign; + + /* + * For power of two sizes, guarantee natural alignment for + * kmalloc()'d objects. + */ + if (is_power_of_2(size)) + align = max(minalign, (int) size); + if (!size) return ZERO_SIZE_PTR; - m = slob_alloc(size + align, gfp, align, node); + m = slob_alloc(size + minalign, gfp, align, node, minalign); if (!m) return NULL; *m = size; - ret = (void *)m + align; + ret = (void *)m + minalign; trace_kmalloc_node(caller, ret, - size, size + align, gfp, node); + size, size + minalign, gfp, node); } else { unsigned int order = get_order(size); @@ -521,8 +548,13 @@ void kfree(const void *block) int align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); unsigned int *m = (unsigned int *)(block - align); slob_free(m, *m + align); - } else - __free_pages(sp, compound_order(sp)); + } else { + unsigned int order = compound_order(sp); + mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE, + -(1 << order)); + __free_pages(sp, order); + + } } EXPORT_SYMBOL(kfree); @@ -567,7 +599,7 @@ static void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node) fs_reclaim_release(flags); if (c->size < PAGE_SIZE) { - b = slob_alloc(c->size, flags, c->align, node); + b = slob_alloc(c->size, flags, c->align, node, 0); trace_kmem_cache_alloc_node(_RET_IP_, b, c->object_size, SLOB_UNITS(c->size) * SLOB_UNIT, flags, node); diff --git a/mm/slub.c b/mm/slub.c index 42c1b3af3c98..3d63ae320d31 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3821,11 +3821,15 @@ static void *kmalloc_large_node(size_t size, gfp_t flags, int node) { struct page *page; void *ptr = NULL; + unsigned int order = get_order(size); flags |= __GFP_COMP; - page = alloc_pages_node(node, flags, get_order(size)); - if (page) + page = alloc_pages_node(node, flags, order); + if (page) { ptr = page_address(page); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, + 1 << order); + } return kmalloc_large_node_hook(ptr, size, flags); } @@ -3951,9 +3955,13 @@ void kfree(const void *x) page = virt_to_head_page(x); if (unlikely(!PageSlab(page))) { + unsigned int order = compound_order(page); + BUG_ON(!PageCompound(page)); kfree_hook(object); - __free_pages(page, compound_order(page)); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, + -(1 << order)); + __free_pages(page, order); return; } slab_free(page->slab_cache, page, object, NULL, 1, _RET_IP_); diff --git a/mm/sparse.c b/mm/sparse.c index bf32de9e666b..f6891c1992b1 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -219,7 +219,7 @@ static inline unsigned long first_present_section_nr(void) return next_present_section_nr(-1); } -void subsection_mask_set(unsigned long *map, unsigned long pfn, +static void subsection_mask_set(unsigned long *map, unsigned long pfn, unsigned long nr_pages) { int idx = subsection_map_index(pfn); diff --git a/mm/vmpressure.c b/mm/vmpressure.c index f3b50811497a..4bac22fe1aa2 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -355,6 +355,9 @@ void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio) * "hierarchy" or "local"). * * To be used as memcg event method. + * + * Return: 0 on success, -ENOMEM on memory failure or -EINVAL if @args could + * not be parsed. */ int vmpressure_register_event(struct mem_cgroup *memcg, struct eventfd_ctx *eventfd, const char *args) @@ -362,7 +365,7 @@ int vmpressure_register_event(struct mem_cgroup *memcg, struct vmpressure *vmpr = memcg_to_vmpressure(memcg); struct vmpressure_event *ev; enum vmpressure_modes mode = VMPRESSURE_NO_PASSTHROUGH; - enum vmpressure_levels level = -1; + enum vmpressure_levels level; char *spec, *spec_orig; char *token; int ret = 0; @@ -375,20 +378,18 @@ int vmpressure_register_event(struct mem_cgroup *memcg, /* Find required level */ token = strsep(&spec, ","); - level = match_string(vmpressure_str_levels, VMPRESSURE_NUM_LEVELS, token); - if (level < 0) { - ret = level; + ret = match_string(vmpressure_str_levels, VMPRESSURE_NUM_LEVELS, token); + if (ret < 0) goto out; - } + level = ret; /* Find optional mode */ token = strsep(&spec, ","); if (token) { - mode = match_string(vmpressure_str_modes, VMPRESSURE_NUM_MODES, token); - if (mode < 0) { - ret = mode; + ret = match_string(vmpressure_str_modes, VMPRESSURE_NUM_MODES, token); + if (ret < 0) goto out; - } + mode = ret; } ev = kzalloc(sizeof(*ev), GFP_KERNEL); @@ -404,6 +405,7 @@ int vmpressure_register_event(struct mem_cgroup *memcg, mutex_lock(&vmpr->events_lock); list_add(&ev->node, &vmpr->events); mutex_unlock(&vmpr->events_lock); + ret = 0; out: kfree(spec_orig); return ret; diff --git a/mm/vmscan.c b/mm/vmscan.c index e5d52d6a24af..c6659bb758a4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2459,17 +2459,70 @@ out: *lru_pages = 0; for_each_evictable_lru(lru) { int file = is_file_lru(lru); - unsigned long size; + unsigned long lruvec_size; unsigned long scan; + unsigned long protection; + + lruvec_size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx); + protection = mem_cgroup_protection(memcg, + sc->memcg_low_reclaim); + + if (protection) { + /* + * Scale a cgroup's reclaim pressure by proportioning + * its current usage to its memory.low or memory.min + * setting. + * + * This is important, as otherwise scanning aggression + * becomes extremely binary -- from nothing as we + * approach the memory protection threshold, to totally + * nominal as we exceed it. This results in requiring + * setting extremely liberal protection thresholds. It + * also means we simply get no protection at all if we + * set it too low, which is not ideal. + * + * If there is any protection in place, we reduce scan + * pressure by how much of the total memory used is + * within protection thresholds. + * + * There is one special case: in the first reclaim pass, + * we skip over all groups that are within their low + * protection. If that fails to reclaim enough pages to + * satisfy the reclaim goal, we come back and override + * the best-effort low protection. However, we still + * ideally want to honor how well-behaved groups are in + * that case instead of simply punishing them all + * equally. As such, we reclaim them based on how much + * memory they are using, reducing the scan pressure + * again by how much of the total memory used is under + * hard protection. + */ + unsigned long cgroup_size = mem_cgroup_size(memcg); + + /* Avoid TOCTOU with earlier protection check */ + cgroup_size = max(cgroup_size, protection); + + scan = lruvec_size - lruvec_size * protection / + cgroup_size; + + /* + * Minimally target SWAP_CLUSTER_MAX pages to keep + * reclaim moving forwards, avoiding decremeting + * sc->priority further than desirable. + */ + scan = max(scan, SWAP_CLUSTER_MAX); + } else { + scan = lruvec_size; + } + + scan >>= sc->priority; - size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx); - scan = size >> sc->priority; /* * If the cgroup's already been deleted, make sure to * scrape out the remaining cache. */ if (!scan && !mem_cgroup_online(memcg)) - scan = min(size, SWAP_CLUSTER_MAX); + scan = min(lruvec_size, SWAP_CLUSTER_MAX); switch (scan_balance) { case SCAN_EQUAL: @@ -2489,7 +2542,7 @@ out: case SCAN_ANON: /* Scan one type exclusively */ if ((scan_balance == SCAN_FILE) != file) { - size = 0; + lruvec_size = 0; scan = 0; } break; @@ -2498,7 +2551,7 @@ out: BUG(); } - *lru_pages += size; + *lru_pages += lruvec_size; nr[lru] = scan; } } @@ -2742,6 +2795,13 @@ static bool shrink_node(pg_data_t *pgdat, struct scan_control *sc) memcg_memory_event(memcg, MEMCG_LOW); break; case MEMCG_PROT_NONE: + /* + * All protection thresholds breached. We may + * still choose to vary the scan pressure + * applied based on by how much the cgroup in + * question has exceeded its protection + * thresholds (see get_scan_count). + */ break; } diff --git a/mm/z3fold.c b/mm/z3fold.c index 05bdf90646e7..6d3d3f698ebb 100644 --- a/mm/z3fold.c +++ b/mm/z3fold.c @@ -998,9 +998,11 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) struct z3fold_header *zhdr; struct page *page; enum buddy bud; + bool page_claimed; zhdr = handle_to_z3fold_header(handle); page = virt_to_page(zhdr); + page_claimed = test_and_set_bit(PAGE_CLAIMED, &page->private); if (test_bit(PAGE_HEADLESS, &page->private)) { /* if a headless page is under reclaim, just leave. @@ -1008,7 +1010,7 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) * has not been set before, we release this page * immediately so we don't care about its value any more. */ - if (!test_and_set_bit(PAGE_CLAIMED, &page->private)) { + if (!page_claimed) { spin_lock(&pool->lock); list_del(&page->lru); spin_unlock(&pool->lock); @@ -1044,13 +1046,15 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) atomic64_dec(&pool->pages_nr); return; } - if (test_bit(PAGE_CLAIMED, &page->private)) { + if (page_claimed) { + /* the page has not been claimed by us */ z3fold_page_unlock(zhdr); return; } if (unlikely(PageIsolated(page)) || test_and_set_bit(NEEDS_COMPACTING, &page->private)) { z3fold_page_unlock(zhdr); + clear_bit(PAGE_CLAIMED, &page->private); return; } if (zhdr->cpu < 0 || !cpu_online(zhdr->cpu)) { @@ -1060,10 +1064,12 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) zhdr->cpu = -1; kref_get(&zhdr->refcount); do_compact_page(zhdr, true); + clear_bit(PAGE_CLAIMED, &page->private); return; } kref_get(&zhdr->refcount); queue_work_on(zhdr->cpu, pool->compact_wq, &zhdr->work); + clear_bit(PAGE_CLAIMED, &page->private); z3fold_page_unlock(zhdr); } diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index a1146cb10919..9cbed6f5a85a 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -436,7 +436,7 @@ void batadv_interface_rx(struct net_device *soft_iface, /* clean the netfilter state now that the batman-adv header has been * removed */ - nf_reset(skb); + nf_reset_ct(skb); if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) goto dropped; diff --git a/net/core/devlink.c b/net/core/devlink.c index e48680efe54a..f80151eeaf51 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -3172,7 +3172,7 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI); - if (err) { + if (err && err != -EOPNOTSUPP) { mutex_unlock(&devlink->lock); goto out; } @@ -3432,7 +3432,7 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI); - if (err) { + if (err && err != -EOPNOTSUPP) { mutex_unlock(&devlink->lock); goto out; } @@ -4088,7 +4088,7 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->extack); mutex_unlock(&devlink->lock); - if (err) + if (err && err != -EOPNOTSUPP) break; idx++; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 01d65206f4fb..529133611ea2 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5120,7 +5120,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet) skb->ignore_df = 0; skb_dst_drop(skb); skb_ext_reset(skb); - nf_reset(skb); + nf_reset_ct(skb); nf_reset_trace(skb); #ifdef CONFIG_NET_SWITCHDEV diff --git a/net/core/sock.c b/net/core/sock.c index 07863edbe6fc..fac2b4d80de5 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1700,8 +1700,6 @@ static void __sk_destruct(struct rcu_head *head) sk_filter_uncharge(sk, filter); RCU_INIT_POINTER(sk->sk_filter, NULL); } - if (rcu_access_pointer(sk->sk_reuseport_cb)) - reuseport_detach_sock(sk); sock_disable_timestamp(sk, SK_FLAGS_TIMESTAMP); @@ -1728,7 +1726,14 @@ static void __sk_destruct(struct rcu_head *head) void sk_destruct(struct sock *sk) { - if (sock_flag(sk, SOCK_RCU_FREE)) + bool use_call_rcu = sock_flag(sk, SOCK_RCU_FREE); + + if (rcu_access_pointer(sk->sk_reuseport_cb)) { + reuseport_detach_sock(sk); + use_call_rcu = true; + } + + if (use_call_rcu) call_rcu(&sk->sk_rcu, __sk_destruct); else __sk_destruct(&sk->sk_rcu); @@ -3492,7 +3497,7 @@ static long sock_prot_memory_allocated(struct proto *proto) return proto->memory_allocated != NULL ? proto_memory_allocated(proto) : -1L; } -static char *sock_prot_memory_pressure(struct proto *proto) +static const char *sock_prot_memory_pressure(struct proto *proto) { return proto->memory_pressure != NULL ? proto_memory_pressure(proto) ? "yes" : "no" : "NI"; diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index b685bc82f8d0..d9b4200ed12d 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -871,7 +871,7 @@ lookup: if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; - nf_reset(skb); + nf_reset_ct(skb); return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4, refcounted); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 9c9aff3e52cf..63ef2a14c934 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -156,7 +156,11 @@ static struct sk_buff /* Step 1: A timestampable frame was received. * Buffer it until we get its meta frame. */ - if (is_link_local && sp->data->hwts_rx_en) { + if (is_link_local) { + if (!test_bit(SJA1105_HWTS_RX_EN, &sp->data->state)) + /* Do normal processing. */ + return skb; + spin_lock(&sp->data->meta_lock); /* Was this a link-local frame instead of the meta * that we were expecting? @@ -187,6 +191,12 @@ static struct sk_buff } else if (is_meta) { struct sk_buff *stampable_skb; + /* Drop the meta frame if we're not in the right state + * to process it. + */ + if (!test_bit(SJA1105_HWTS_RX_EN, &sp->data->state)) + return NULL; + spin_lock(&sp->data->meta_lock); stampable_skb = sp->data->stampable_skb; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index a53a543fe055..52690bb3e40f 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1446,6 +1446,7 @@ static void erspan_setup(struct net_device *dev) struct ip_tunnel *t = netdev_priv(dev); ether_setup(dev); + dev->max_mtu = 0; dev->netdev_ops = &erspan_netdev_ops; dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 1e2392b7c64e..c59a78a267c3 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -199,7 +199,7 @@ resubmit: kfree_skb(skb); return; } - nf_reset(skb); + nf_reset_ct(skb); } ret = INDIRECT_CALL_2(ipprot->handler, tcp_v4_rcv, udp_rcv, skb); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 313470f6bb14..716d5472c022 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1794,7 +1794,7 @@ static void ip_encap(struct net *net, struct sk_buff *skb, ip_send_check(iph); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); - nf_reset(skb); + nf_reset_ct(skb); } static inline int ipmr_forward_finish(struct net *net, struct sock *sk, @@ -2140,7 +2140,7 @@ int ip_mr_input(struct sk_buff *skb) mroute_sk = rcu_dereference(mrt->mroute_sk); if (mroute_sk) { - nf_reset(skb); + nf_reset_ct(skb); raw_rcv(mroute_sk, skb); return 0; } diff --git a/net/ipv4/netfilter/nf_dup_ipv4.c b/net/ipv4/netfilter/nf_dup_ipv4.c index af3fbf76dbd3..6cc5743c553a 100644 --- a/net/ipv4/netfilter/nf_dup_ipv4.c +++ b/net/ipv4/netfilter/nf_dup_ipv4.c @@ -65,7 +65,7 @@ void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum, #if IS_ENABLED(CONFIG_NF_CONNTRACK) /* Avoid counting cloned packets towards the original connection. */ - nf_reset(skb); + nf_reset_ct(skb); nf_ct_set(skb, NULL, IP_CT_UNTRACKED); #endif /* diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 80da5a66d5d7..3183413ebc6c 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -332,7 +332,7 @@ int raw_rcv(struct sock *sk, struct sk_buff *skb) kfree_skb(skb); return NET_RX_DROP; } - nf_reset(skb); + nf_reset_ct(skb); skb_push(skb, skb->data - skb_network_header(skb)); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 7dcce724c78b..14654876127e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -916,16 +916,15 @@ void ip_rt_send_redirect(struct sk_buff *skb) if (peer->rate_tokens == 0 || time_after(jiffies, (peer->rate_last + - (ip_rt_redirect_load << peer->rate_tokens)))) { + (ip_rt_redirect_load << peer->n_redirects)))) { __be32 gw = rt_nexthop(rt, ip_hdr(skb)->daddr); icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw); peer->rate_last = jiffies; - ++peer->rate_tokens; ++peer->n_redirects; #ifdef CONFIG_IP_ROUTE_VERBOSE if (log_martians && - peer->rate_tokens == ip_rt_redirect_number) + peer->n_redirects == ip_rt_redirect_number) net_warn_ratelimited("host %pI4/if%d ignores redirects for %pI4 to %pI4\n", &ip_hdr(skb)->saddr, inet_iif(skb), &ip_hdr(skb)->daddr, &gw); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 79c325a07ba5..f98a1882e537 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1798,13 +1798,11 @@ static int tcp_zerocopy_receive(struct sock *sk, } if (skb_frag_size(frags) != PAGE_SIZE || skb_frag_off(frags)) { int remaining = zc->recv_skip_hint; - int size = skb_frag_size(frags); - while (remaining && (size != PAGE_SIZE || + while (remaining && (skb_frag_size(frags) != PAGE_SIZE || skb_frag_off(frags))) { - remaining -= size; + remaining -= skb_frag_size(frags); frags++; - size = skb_frag_size(frags); } zc->recv_skip_hint -= remaining; break; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2ee45e3755e9..bf124b1742df 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1916,7 +1916,7 @@ process: if (tcp_v4_inbound_md5_hash(sk, skb)) goto discard_and_relse; - nf_reset(skb); + nf_reset_ct(skb); if (tcp_filter(sk, skb)) goto discard_and_relse; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 40de2d2364a1..05be564414e9 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -198,8 +198,13 @@ static bool retransmits_timed_out(struct sock *sk, return false; start_ts = tcp_sk(sk)->retrans_stamp; - if (likely(timeout == 0)) - timeout = tcp_model_timeout(sk, boundary, TCP_RTO_MIN); + if (likely(timeout == 0)) { + unsigned int rto_base = TCP_RTO_MIN; + + if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) + rto_base = tcp_timeout_init(sk); + timeout = tcp_model_timeout(sk, boundary, rto_base); + } return (s32)(tcp_time_stamp(tcp_sk(sk)) - start_ts - timeout) >= 0; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index cf755156a684..14bc654b6842 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -821,6 +821,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4, int is_udplite = IS_UDPLITE(sk); int offset = skb_transport_offset(skb); int len = skb->len - offset; + int datalen = len - sizeof(*uh); __wsum csum = 0; /* @@ -854,10 +855,12 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4, return -EIO; } - skb_shinfo(skb)->gso_size = cork->gso_size; - skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4; - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(len - sizeof(uh), - cork->gso_size); + if (datalen > cork->gso_size) { + skb_shinfo(skb)->gso_size = cork->gso_size; + skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4; + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(datalen, + cork->gso_size); + } goto csum_partial; } @@ -1969,7 +1972,7 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) */ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto drop; - nf_reset(skb); + nf_reset_ct(skb); if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) { int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); @@ -2298,7 +2301,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; - nf_reset(skb); + nf_reset_ct(skb); /* No socket. Drop packet silently, if checksum is wrong */ if (udp_lib_checksum_complete(skb)) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 6a576ff92c39..34ccef18b40e 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5964,13 +5964,20 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) switch (event) { case RTM_NEWADDR: /* - * If the address was optimistic - * we inserted the route at the start of - * our DAD process, so we don't need - * to do it again + * If the address was optimistic we inserted the route at the + * start of our DAD process, so we don't need to do it again. + * If the device was taken down in the middle of the DAD + * cycle there is a race where we could get here without a + * host route, so nothing to insert. That will be fixed when + * the device is brought up. */ - if (!rcu_access_pointer(ifp->rt->fib6_node)) + if (ifp->rt && !rcu_access_pointer(ifp->rt->fib6_node)) { ip6_ins_rt(net, ifp->rt); + } else if (!ifp->rt && (ifp->idev->dev->flags & IFF_UP)) { + pr_warn("BUG: Address %pI6c on device %s is missing its host route.\n", + &ifp->addr, ifp->idev->dev->name); + } + if (ifp->idev->cnf.forwarding) addrconf_join_anycast(ifp); if (!ipv6_addr_any(&ifp->peer_addr)) diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index d432d0011c16..3d71c7d6102c 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -223,6 +223,16 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, if (ipv6_addr_is_multicast(&hdr->saddr)) goto err; + /* While RFC4291 is not explicit about v4mapped addresses + * in IPv6 headers, it seems clear linux dual-stack + * model can not deal properly with these. + * Security models could be fooled by ::ffff:127.0.0.1 for example. + * + * https://tools.ietf.org/html/draft-itojun-v6ops-v4mapped-harmful-02 + */ + if (ipv6_addr_v4mapped(&hdr->saddr)) + goto err; + skb->transport_header = skb->network_header + sizeof(*hdr); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); @@ -371,7 +381,7 @@ resubmit_final: /* Free reference early: we don't need it any more, and it may hold ip_conntrack module loaded indefinitely. */ - nf_reset(skb); + nf_reset_ct(skb); skb_postpull_rcsum(skb, skb_network_header(skb), skb_network_header_len(skb)); diff --git a/net/ipv6/netfilter/nf_dup_ipv6.c b/net/ipv6/netfilter/nf_dup_ipv6.c index e6c9da9866b1..a0a2de30be3e 100644 --- a/net/ipv6/netfilter/nf_dup_ipv6.c +++ b/net/ipv6/netfilter/nf_dup_ipv6.c @@ -54,7 +54,7 @@ void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, return; #if IS_ENABLED(CONFIG_NF_CONNTRACK) - nf_reset(skb); + nf_reset_ct(skb); nf_ct_set(skb, NULL, IP_CT_UNTRACKED); #endif if (hooknum == NF_INET_PRE_ROUTING || diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 6e1888ee4036..a77f6b7d3a7c 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -215,7 +215,7 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) /* Not releasing hash table! */ if (clone) { - nf_reset(clone); + nf_reset_ct(clone); rawv6_rcv(sk, clone); } } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index aae4938f3dea..6324d3a8cb53 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1109,6 +1109,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, __wsum csum = 0; int offset = skb_transport_offset(skb); int len = skb->len - offset; + int datalen = len - sizeof(*uh); /* * Create a UDP header @@ -1141,8 +1142,12 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, return -EIO; } - skb_shinfo(skb)->gso_size = cork->gso_size; - skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4; + if (datalen > cork->gso_size) { + skb_shinfo(skb)->gso_size = cork->gso_size; + skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4; + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(datalen, + cork->gso_size); + } goto csum_partial; } diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 105e5a7092e7..f82ea12bac37 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1078,7 +1078,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); - nf_reset(skb); + nf_reset_ct(skb); bh_lock_sock(sk); if (sock_owned_by_user(sk)) { diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index bd3f39349d40..fd5ac2788e45 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -151,7 +151,7 @@ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, skb->ip_summed = CHECKSUM_NONE; skb_dst_drop(skb); - nf_reset(skb); + nf_reset_ct(skb); rcu_read_lock(); dev = rcu_dereference(spriv->dev); diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 622833317dcb..0d7c887a2b75 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -193,7 +193,7 @@ pass_up: if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_put; - nf_reset(skb); + nf_reset_ct(skb); return sk_receive_skb(sk, skb, 1); diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 687e23a8b326..802f19aba7e3 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -206,7 +206,7 @@ pass_up: if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_put; - nf_reset(skb); + nf_reset_ct(skb); return sk_receive_skb(sk, skb, 1); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index b1438fd4d876..64b544ae9966 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -487,9 +487,14 @@ static ssize_t ieee80211_if_fmt_aqm( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { struct ieee80211_local *local = sdata->local; - struct txq_info *txqi = to_txq_info(sdata->vif.txq); + struct txq_info *txqi; int len; + if (!sdata->vif.txq) + return 0; + + txqi = to_txq_info(sdata->vif.txq); + spin_lock_bh(&local->fq.lock); rcu_read_lock(); @@ -658,7 +663,9 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz); DEBUGFS_ADD(hw_queues); - if (sdata->local->ops->wake_tx_queue) + if (sdata->local->ops->wake_tx_queue && + sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && + sdata->vif.type != NL80211_IFTYPE_NAN) DEBUGFS_ADD(aqm); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 051a02ddcb85..32a7a53833c0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -247,7 +247,8 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) struct sta_info *sta; int i; - spin_lock_bh(&fq->lock); + local_bh_disable(); + spin_lock(&fq->lock); if (sdata->vif.type == NL80211_IFTYPE_AP) ps = &sdata->bss->ps; @@ -273,9 +274,9 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) &txqi->flags)) continue; - spin_unlock_bh(&fq->lock); + spin_unlock(&fq->lock); drv_wake_tx_queue(local, txqi); - spin_lock_bh(&fq->lock); + spin_lock(&fq->lock); } } @@ -288,12 +289,14 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) goto out; - spin_unlock_bh(&fq->lock); + spin_unlock(&fq->lock); drv_wake_tx_queue(local, txqi); + local_bh_enable(); return; out: - spin_unlock_bh(&fq->lock); + spin_unlock(&fq->lock); + local_bh_enable(); } static void diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 9c464d24beec..888d3068a492 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -613,7 +613,7 @@ static inline int ip_vs_tunnel_xmit_prepare(struct sk_buff *skb, if (unlikely(cp->flags & IP_VS_CONN_F_NFCT)) ret = ip_vs_confirm_conntrack(skb); if (ret == NF_ACCEPT) { - nf_reset(skb); + nf_reset_ct(skb); skb_forward_csum(skb); } return ret; diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c index af1497ab9464..69d6173f91e2 100644 --- a/net/netfilter/nft_connlimit.c +++ b/net/netfilter/nft_connlimit.c @@ -218,8 +218,13 @@ static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx, static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr) { struct nft_connlimit *priv = nft_expr_priv(expr); + bool ret; - return nf_conncount_gc_list(net, &priv->list); + local_bh_disable(); + ret = nf_conncount_gc_list(net, &priv->list); + local_bh_enable(); + + return ret; } static struct nft_expr_type nft_connlimit_type; diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 8dfea26536c9..ccdd790e163a 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -107,9 +107,14 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) llcp_sock->service_name = kmemdup(llcp_addr.service_name, llcp_sock->service_name_len, GFP_KERNEL); - + if (!llcp_sock->service_name) { + ret = -ENOMEM; + goto put_dev; + } llcp_sock->ssap = nfc_llcp_get_sdp_ssap(local, llcp_sock); if (llcp_sock->ssap == LLCP_SAP_MAX) { + kfree(llcp_sock->service_name); + llcp_sock->service_name = NULL; ret = -EADDRINUSE; goto put_dev; } diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index d2437b5b2f6a..21c90d3a7ebf 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -237,7 +237,7 @@ static netdev_tx_t internal_dev_recv(struct sk_buff *skb) } skb_dst_drop(skb); - nf_reset(skb); + nf_reset_ct(skb); secpath_reset(skb); skb->pkt_type = PACKET_HOST; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e2742b006d25..82a50e850245 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1821,7 +1821,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, skb_dst_drop(skb); /* drop conntrack reference */ - nf_reset(skb); + nf_reset_ct(skb); spkt = &PACKET_SKB_CB(skb)->sa.pkt; @@ -2121,7 +2121,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, skb_dst_drop(skb); /* drop conntrack reference */ - nf_reset(skb); + nf_reset_ct(skb); spin_lock(&sk->sk_receive_queue.lock); po->stats.stats1.tp_packets++; diff --git a/net/rds/ib.c b/net/rds/ib.c index 45acab2de0cf..9de2ae22d583 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -143,6 +143,9 @@ static void rds_ib_add_one(struct ib_device *device) refcount_set(&rds_ibdev->refcount, 1); INIT_WORK(&rds_ibdev->free_work, rds_ib_dev_free); + INIT_LIST_HEAD(&rds_ibdev->ipaddr_list); + INIT_LIST_HEAD(&rds_ibdev->conn_list); + rds_ibdev->max_wrs = device->attrs.max_qp_wr; rds_ibdev->max_sge = min(device->attrs.max_send_sge, RDS_IB_MAX_SGE); @@ -203,9 +206,6 @@ static void rds_ib_add_one(struct ib_device *device) device->name, rds_ibdev->use_fastreg ? "FRMR" : "FMR"); - INIT_LIST_HEAD(&rds_ibdev->ipaddr_list); - INIT_LIST_HEAD(&rds_ibdev->conn_list); - down_write(&rds_ib_devices_lock); list_add_tail_rcu(&rds_ibdev->list, &rds_ib_devices); up_write(&rds_ib_devices_lock); diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 06c7a2da21bc..39b427dc7512 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1127,6 +1127,33 @@ static const struct nla_policy cbq_policy[TCA_CBQ_MAX + 1] = { [TCA_CBQ_POLICE] = { .len = sizeof(struct tc_cbq_police) }, }; +static int cbq_opt_parse(struct nlattr *tb[TCA_CBQ_MAX + 1], + struct nlattr *opt, + struct netlink_ext_ack *extack) +{ + int err; + + if (!opt) { + NL_SET_ERR_MSG(extack, "CBQ options are required for this operation"); + return -EINVAL; + } + + err = nla_parse_nested_deprecated(tb, TCA_CBQ_MAX, opt, + cbq_policy, extack); + if (err < 0) + return err; + + if (tb[TCA_CBQ_WRROPT]) { + const struct tc_cbq_wrropt *wrr = nla_data(tb[TCA_CBQ_WRROPT]); + + if (wrr->priority > TC_CBQ_MAXPRIO) { + NL_SET_ERR_MSG(extack, "priority is bigger than TC_CBQ_MAXPRIO"); + err = -EINVAL; + } + } + return err; +} + static int cbq_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { @@ -1139,13 +1166,7 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt, hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED); q->delay_timer.function = cbq_undelay; - if (!opt) { - NL_SET_ERR_MSG(extack, "CBQ options are required for this operation"); - return -EINVAL; - } - - err = nla_parse_nested_deprecated(tb, TCA_CBQ_MAX, opt, cbq_policy, - extack); + err = cbq_opt_parse(tb, opt, extack); if (err < 0) return err; @@ -1464,13 +1485,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t struct cbq_class *parent; struct qdisc_rate_table *rtab = NULL; - if (!opt) { - NL_SET_ERR_MSG(extack, "Mandatory qdisc options missing"); - return -EINVAL; - } - - err = nla_parse_nested_deprecated(tb, TCA_CBQ_MAX, opt, cbq_policy, - extack); + err = cbq_opt_parse(tb, opt, extack); if (err < 0) return err; diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index 1bef152c5721..b2905b03a432 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -306,7 +306,7 @@ static void cbs_set_port_rate(struct net_device *dev, struct cbs_sched_data *q) if (err < 0) goto skip; - if (ecmd.base.speed != SPEED_UNKNOWN) + if (ecmd.base.speed && ecmd.base.speed != SPEED_UNKNOWN) speed = ecmd.base.speed; skip: diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index bad1cbe59a56..05605b30bef3 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -361,6 +361,8 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt, goto errout; err = -EINVAL; + if (!tb[TCA_DSMARK_INDICES]) + goto errout; indices = nla_get_u16(tb[TCA_DSMARK_INDICES]); if (hweight32(indices) != 1) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 2f7b34205c82..68b543f85a96 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -1044,12 +1044,11 @@ static void taprio_set_picos_per_byte(struct net_device *dev, if (err < 0) goto skip; - if (ecmd.base.speed != SPEED_UNKNOWN) + if (ecmd.base.speed && ecmd.base.speed != SPEED_UNKNOWN) speed = ecmd.base.speed; skip: - picos_per_byte = div64_s64(NSEC_PER_SEC * 1000LL * 8, - speed * 1000 * 1000); + picos_per_byte = (USEC_PER_SEC * 8) / speed; atomic64_set(&q->picos_per_byte, picos_per_byte); netdev_dbg(dev, "taprio: set %s's picos_per_byte to: %lld, linkspeed: %d\n", diff --git a/net/sctp/input.c b/net/sctp/input.c index 1008cdc44dd6..5a070fb5b278 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -201,7 +201,7 @@ int sctp_rcv(struct sk_buff *skb) if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family)) goto discard_release; - nf_reset(skb); + nf_reset_ct(skb); if (sk_filter(sk, skb)) goto discard_release; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e41ed2e0ae7d..48d63956a68c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2155,7 +2155,7 @@ static enum sctp_ierror sctp_verify_param(struct net *net, case SCTP_PARAM_SET_PRIMARY: if (ep->asconf_enable) break; - goto fallthrough; + goto unhandled; case SCTP_PARAM_HOST_NAME_ADDRESS: /* Tell the peer, we won't support this param. */ @@ -2166,11 +2166,11 @@ static enum sctp_ierror sctp_verify_param(struct net *net, case SCTP_PARAM_FWD_TSN_SUPPORT: if (ep->prsctp_enable) break; - goto fallthrough; + goto unhandled; case SCTP_PARAM_RANDOM: if (!ep->auth_enable) - goto fallthrough; + goto unhandled; /* SCTP-AUTH: Secion 6.1 * If the random number is not 32 byte long the association @@ -2187,7 +2187,7 @@ static enum sctp_ierror sctp_verify_param(struct net *net, case SCTP_PARAM_CHUNKS: if (!ep->auth_enable) - goto fallthrough; + goto unhandled; /* SCTP-AUTH: Section 3.2 * The CHUNKS parameter MUST be included once in the INIT or @@ -2203,7 +2203,7 @@ static enum sctp_ierror sctp_verify_param(struct net *net, case SCTP_PARAM_HMAC_ALGO: if (!ep->auth_enable) - goto fallthrough; + goto unhandled; hmacs = (struct sctp_hmac_algo_param *)param.p; n_elt = (ntohs(param.p->length) - @@ -2226,7 +2226,7 @@ static enum sctp_ierror sctp_verify_param(struct net *net, retval = SCTP_IERROR_ABORT; } break; -fallthrough: +unhandled: default: pr_debug("%s: unrecognized param:%d for chunk:%d\n", __func__, ntohs(param.p->type), cid); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 9ac88722fa83..70e52f567b2a 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1249,19 +1249,21 @@ static void xs_error_report(struct sock *sk) { struct sock_xprt *transport; struct rpc_xprt *xprt; - int err; read_lock_bh(&sk->sk_callback_lock); if (!(xprt = xprt_from_sock(sk))) goto out; transport = container_of(xprt, struct sock_xprt, xprt); - err = -sk->sk_err; - if (err == 0) + transport->xprt_err = -sk->sk_err; + if (transport->xprt_err == 0) goto out; dprintk("RPC: xs_error_report client %p, error=%d...\n", - xprt, -err); - trace_rpc_socket_error(xprt, sk->sk_socket, err); + xprt, -transport->xprt_err); + trace_rpc_socket_error(xprt, sk->sk_socket, transport->xprt_err); + + /* barrier ensures xprt_err is set before XPRT_SOCK_WAKE_ERROR */ + smp_mb__before_atomic(); xs_run_error_worker(transport, XPRT_SOCK_WAKE_ERROR); out: read_unlock_bh(&sk->sk_callback_lock); @@ -2476,7 +2478,6 @@ static void xs_wake_write(struct sock_xprt *transport) static void xs_wake_error(struct sock_xprt *transport) { int sockerr; - int sockerr_len = sizeof(sockerr); if (!test_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state)) return; @@ -2485,9 +2486,7 @@ static void xs_wake_error(struct sock_xprt *transport) goto out; if (!test_and_clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state)) goto out; - if (kernel_getsockopt(transport->sock, SOL_SOCKET, SO_ERROR, - (char *)&sockerr, &sockerr_len) != 0) - goto out; + sockerr = xchg(&transport->xprt_err, 0); if (sockerr < 0) xprt_wake_pending_tasks(&transport->xprt, sockerr); out: diff --git a/net/tipc/link.c b/net/tipc/link.c index 6cc75ffd9e2c..999eab592de8 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -160,6 +160,7 @@ struct tipc_link { struct { u16 len; u16 limit; + struct sk_buff *target_bskb; } backlog[5]; u16 snd_nxt; u16 window; @@ -880,6 +881,7 @@ static void link_prepare_wakeup(struct tipc_link *l) void tipc_link_reset(struct tipc_link *l) { struct sk_buff_head list; + u32 imp; __skb_queue_head_init(&list); @@ -901,11 +903,10 @@ void tipc_link_reset(struct tipc_link *l) __skb_queue_purge(&l->deferdq); __skb_queue_purge(&l->backlogq); __skb_queue_purge(&l->failover_deferdq); - l->backlog[TIPC_LOW_IMPORTANCE].len = 0; - l->backlog[TIPC_MEDIUM_IMPORTANCE].len = 0; - l->backlog[TIPC_HIGH_IMPORTANCE].len = 0; - l->backlog[TIPC_CRITICAL_IMPORTANCE].len = 0; - l->backlog[TIPC_SYSTEM_IMPORTANCE].len = 0; + for (imp = 0; imp <= TIPC_SYSTEM_IMPORTANCE; imp++) { + l->backlog[imp].len = 0; + l->backlog[imp].target_bskb = NULL; + } kfree_skb(l->reasm_buf); kfree_skb(l->reasm_tnlmsg); kfree_skb(l->failover_reasm_skb); @@ -947,7 +948,7 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list, u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1; struct sk_buff_head *transmq = &l->transmq; struct sk_buff_head *backlogq = &l->backlogq; - struct sk_buff *skb, *_skb, *bskb; + struct sk_buff *skb, *_skb, **tskb; int pkt_cnt = skb_queue_len(list); int rc = 0; @@ -999,19 +1000,21 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list, seqno++; continue; } - if (tipc_msg_bundle(skb_peek_tail(backlogq), hdr, mtu)) { + tskb = &l->backlog[imp].target_bskb; + if (tipc_msg_bundle(*tskb, hdr, mtu)) { kfree_skb(__skb_dequeue(list)); l->stats.sent_bundled++; continue; } - if (tipc_msg_make_bundle(&bskb, hdr, mtu, l->addr)) { + if (tipc_msg_make_bundle(tskb, hdr, mtu, l->addr)) { kfree_skb(__skb_dequeue(list)); - __skb_queue_tail(backlogq, bskb); - l->backlog[msg_importance(buf_msg(bskb))].len++; + __skb_queue_tail(backlogq, *tskb); + l->backlog[imp].len++; l->stats.sent_bundled++; l->stats.sent_bundles++; continue; } + l->backlog[imp].target_bskb = NULL; l->backlog[imp].len += skb_queue_len(list); skb_queue_splice_tail_init(list, backlogq); } @@ -1027,6 +1030,7 @@ static void tipc_link_advance_backlog(struct tipc_link *l, u16 seqno = l->snd_nxt; u16 ack = l->rcv_nxt - 1; u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1; + u32 imp; while (skb_queue_len(&l->transmq) < l->window) { skb = skb_peek(&l->backlogq); @@ -1037,7 +1041,10 @@ static void tipc_link_advance_backlog(struct tipc_link *l, break; __skb_dequeue(&l->backlogq); hdr = buf_msg(skb); - l->backlog[msg_importance(hdr)].len--; + imp = msg_importance(hdr); + l->backlog[imp].len--; + if (unlikely(skb == l->backlog[imp].target_bskb)) + l->backlog[imp].target_bskb = NULL; __skb_queue_tail(&l->transmq, skb); /* next retransmit attempt */ if (link_is_bc_sndlink(l)) diff --git a/net/tipc/msg.c b/net/tipc/msg.c index e6d49cdc61b4..922d262e153f 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -543,10 +543,7 @@ bool tipc_msg_make_bundle(struct sk_buff **skb, struct tipc_msg *msg, bmsg = buf_msg(_skb); tipc_msg_init(msg_prevnode(msg), bmsg, MSG_BUNDLER, 0, INT_H_SIZE, dnode); - if (msg_isdata(msg)) - msg_set_importance(bmsg, TIPC_CRITICAL_IMPORTANCE); - else - msg_set_importance(bmsg, TIPC_SYSTEM_IMPORTANCE); + msg_set_importance(bmsg, msg_importance(msg)); msg_set_seqno(bmsg, msg_seqno(msg)); msg_set_ack(bmsg, msg_ack(msg)); msg_set_bcast_ack(bmsg, msg_bcast_ack(msg)); diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index ab47bf3ab66e..2ab43b2bba31 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -638,7 +638,7 @@ struct sock *__vsock_create(struct net *net, } EXPORT_SYMBOL_GPL(__vsock_create); -static void __vsock_release(struct sock *sk) +static void __vsock_release(struct sock *sk, int level) { if (sk) { struct sk_buff *skb; @@ -648,9 +648,17 @@ static void __vsock_release(struct sock *sk) vsk = vsock_sk(sk); pending = NULL; /* Compiler warning. */ + /* The release call is supposed to use lock_sock_nested() + * rather than lock_sock(), if a sock lock should be acquired. + */ transport->release(vsk); - lock_sock(sk); + /* When "level" is SINGLE_DEPTH_NESTING, use the nested + * version to avoid the warning "possible recursive locking + * detected". When "level" is 0, lock_sock_nested(sk, level) + * is the same as lock_sock(sk). + */ + lock_sock_nested(sk, level); sock_orphan(sk); sk->sk_shutdown = SHUTDOWN_MASK; @@ -659,7 +667,7 @@ static void __vsock_release(struct sock *sk) /* Clean up any sockets that never were accepted. */ while ((pending = vsock_dequeue_accept(sk)) != NULL) { - __vsock_release(pending); + __vsock_release(pending, SINGLE_DEPTH_NESTING); sock_put(pending); } @@ -708,7 +716,7 @@ EXPORT_SYMBOL_GPL(vsock_stream_has_space); static int vsock_release(struct socket *sock) { - __vsock_release(sock->sk); + __vsock_release(sock->sk, 0); sock->sk = NULL; sock->state = SS_FREE; diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c index 261521d286d6..c443db7af8d4 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -559,7 +559,7 @@ static void hvs_release(struct vsock_sock *vsk) struct sock *sk = sk_vsock(vsk); bool remove_sock; - lock_sock(sk); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); remove_sock = hvs_close_lock_held(vsk); release_sock(sk); if (remove_sock) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 5bb70c692b1e..a666ef8fc54e 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -820,7 +820,7 @@ void virtio_transport_release(struct vsock_sock *vsk) struct sock *sk = &vsk->sk; bool remove_sock = true; - lock_sock(sk); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); if (sk->sk_type == SOCK_STREAM) remove_sock = virtio_transport_close(vsk); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d21b1581a665..141cdb171665 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -201,6 +201,38 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) return __cfg80211_rdev_from_attrs(netns, info->attrs); } +static int validate_beacon_head(const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + const struct element *elem; + const struct ieee80211_mgmt *mgmt = (void *)data; + unsigned int fixedlen = offsetof(struct ieee80211_mgmt, + u.beacon.variable); + + if (len < fixedlen) + goto err; + + if (ieee80211_hdrlen(mgmt->frame_control) != + offsetof(struct ieee80211_mgmt, u.beacon)) + goto err; + + data += fixedlen; + len -= fixedlen; + + for_each_element(elem, data, len) { + /* nothing */ + } + + if (for_each_element_completed(elem, data, len)) + return 0; + +err: + NL_SET_ERR_MSG_ATTR(extack, attr, "malformed beacon head"); + return -EINVAL; +} + static int validate_ie_attr(const struct nlattr *attr, struct netlink_ext_ack *extack) { @@ -338,8 +370,9 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, - [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_BEACON_HEAD] = + NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_beacon_head, + IEEE80211_MAX_DATA_LEN), [NL80211_ATTR_BEACON_TAIL] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, IEEE80211_MAX_DATA_LEN), @@ -2636,6 +2669,8 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, control_freq = nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ]); + memset(chandef, 0, sizeof(*chandef)); + chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); chandef->width = NL80211_CHAN_WIDTH_20_NOHT; chandef->center_freq1 = control_freq; @@ -3176,7 +3211,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag if (rdev->ops->get_channel) { int ret; - struct cfg80211_chan_def chandef; + struct cfg80211_chan_def chandef = {}; ret = rdev_get_channel(rdev, wdev, &chandef); if (ret == 0) { @@ -6270,6 +6305,9 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_mpath) return -EOPNOTSUPP; + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + return rdev_del_mpath(rdev, dev, dst); } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 5311d0ae2454..420c4207ab59 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2108,7 +2108,7 @@ static void reg_call_notifier(struct wiphy *wiphy, static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) { - struct cfg80211_chan_def chandef; + struct cfg80211_chan_def chandef = {}; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); enum nl80211_iftype iftype; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d313c9befa23..ff1016607f0b 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1723,7 +1723,12 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, return; new_ie_len -= trans_ssid[1]; mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen); - if (!mbssid) + /* + * It's not valid to have the MBSSID element before SSID + * ignore if that happens - the code below assumes it is + * after (while copying things inbetween). + */ + if (!mbssid || mbssid < trans_ssid) return; new_ie_len -= mbssid[1]; rcu_read_lock(); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 7b6529d81c61..cac9e28d852b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -798,7 +798,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct cfg80211_chan_def chandef; + struct cfg80211_chan_def chandef = {}; int ret; switch (wdev->iftype) { diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 6088bc2dc11e..9b599ed66d97 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -706,7 +706,7 @@ resume: if (err) goto drop; - nf_reset(skb); + nf_reset_ct(skb); if (decaps) { sp = skb_sec_path(skb); diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c index 2ab4859df55a..0f5131bc3342 100644 --- a/net/xfrm/xfrm_interface.c +++ b/net/xfrm/xfrm_interface.c @@ -185,7 +185,7 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) skb->skb_iif = 0; skb->ignore_df = 0; skb_dst_drop(skb); - nf_reset(skb); + nf_reset_ct(skb); nf_reset_trace(skb); if (!xnet) diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 9499b35feb92..b1db55b50ba1 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -502,7 +502,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err) struct net *net = xs_net(skb_dst(skb)->xfrm); while (likely((err = xfrm_output_one(skb, err)) == 0)) { - nf_reset(skb); + nf_reset_ct(skb); err = skb_dst(skb)->ops->local_out(net, skb->sk, skb); if (unlikely(err != 1)) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 21e939235b39..f2d1e573ea55 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2808,7 +2808,7 @@ static void xfrm_policy_queue_process(struct timer_list *t) continue; } - nf_reset(skb); + nf_reset_ct(skb); skb_dst_drop(skb); skb_dst_set(skb, dst); diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 4b0432e095ae..10ba926ae292 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -143,11 +143,6 @@ cc-ifversion = $(shell [ $(CONFIG_GCC_VERSION)0 $(1) $(2)000 ] && echo $(3) || e # Usage: KBUILD_LDFLAGS += $(call ld-option, -X, -Y) ld-option = $(call try-run, $(LD) $(KBUILD_LDFLAGS) $(1) -v,$(1),$(2),$(3)) -# ar-option -# Usage: KBUILD_ARFLAGS := $(call ar-option,D) -# Important: no spaces around options -ar-option = $(call try-run, $(AR) rc$(1) "$$TMP",$(1),$(2)) - # ld-version # Note this is mainly for HJ Lu's 3 number binutil versions ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index f72aba64d611..a9e47953ca53 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -389,7 +389,7 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ; ifdef builtin-target quiet_cmd_ar_builtin = AR $@ - cmd_ar_builtin = rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(real-prereqs) + cmd_ar_builtin = rm -f $@; $(AR) cDPrST $@ $(real-prereqs) $(builtin-target): $(real-obj-y) FORCE $(call if_changed,ar_builtin) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 4a0cdd6f5909..179d55af5852 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -232,7 +232,7 @@ quiet_cmd_ld = LD $@ # --------------------------------------------------------------------------- quiet_cmd_ar = AR $@ - cmd_ar = rm -f $@; $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(real-prereqs) + cmd_ar = rm -f $@; $(AR) cDPrsT $@ $(real-prereqs) # Objcopy # --------------------------------------------------------------------------- diff --git a/scripts/coccinelle/misc/add_namespace.cocci b/scripts/coccinelle/misc/add_namespace.cocci index c832bb6445a8..99e93a6c2e24 100644 --- a/scripts/coccinelle/misc/add_namespace.cocci +++ b/scripts/coccinelle/misc/add_namespace.cocci @@ -6,6 +6,8 @@ /// add a missing namespace tag to a module source file. /// +virtual report + @has_ns_import@ declarer name MODULE_IMPORT_NS; identifier virtual.ns; diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 3961941e8e7a..936d3ad23c83 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -166,7 +166,7 @@ struct symbol { struct module *module; unsigned int crc; int crc_valid; - const char *namespace; + char *namespace; unsigned int weak:1; unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */ unsigned int kernel:1; /* 1 if symbol is from kernel @@ -348,20 +348,18 @@ static enum export export_from_sec(struct elf_info *elf, unsigned int sec) return export_unknown; } -static const char *sym_extract_namespace(const char **symname) +static char *sym_extract_namespace(const char **symname) { - size_t n; - char *dupsymname; + char *namespace = NULL; + char *ns_separator; - n = strcspn(*symname, "."); - if (n < strlen(*symname) - 1) { - dupsymname = NOFAIL(strdup(*symname)); - dupsymname[n] = '\0'; - *symname = dupsymname; - return dupsymname + n + 1; + ns_separator = strchr(*symname, '.'); + if (ns_separator) { + namespace = NOFAIL(strndup(*symname, ns_separator - *symname)); + *symname = ns_separator + 1; } - return NULL; + return namespace; } /** @@ -375,7 +373,6 @@ static struct symbol *sym_add_exported(const char *name, const char *namespace, if (!s) { s = new_symbol(name, mod, export); - s->namespace = namespace; } else { if (!s->preloaded) { warn("%s: '%s' exported twice. Previous export was in %s%s\n", @@ -386,6 +383,8 @@ static struct symbol *sym_add_exported(const char *name, const char *namespace, s->module = mod; } } + free(s->namespace); + s->namespace = namespace ? strdup(namespace) : NULL; s->preloaded = 0; s->vmlinux = is_vmlinux(mod->name); s->kernel = 0; @@ -672,7 +671,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info, unsigned int crc; enum export export; bool is_crc = false; - const char *name, *namespace; + const char *name; + char *namespace; if ((!is_vmlinux(mod->name) || mod->is_dot_o) && strstarts(symname, "__ksymtab")) @@ -747,6 +747,7 @@ static void handle_modversions(struct module *mod, struct elf_info *info, name = symname + strlen("__ksymtab_"); namespace = sym_extract_namespace(&name); sym_add_exported(name, namespace, mod, export); + free(namespace); } if (strcmp(symname, "init_module") == 0) mod->has_init = 1; @@ -2195,7 +2196,7 @@ static int check_exports(struct module *mod) else basename = mod->name; - if (exp->namespace) { + if (exp->namespace && exp->namespace[0]) { add_namespace(&mod->required_namespaces, exp->namespace); @@ -2652,15 +2653,20 @@ int main(int argc, char **argv) fatal("modpost: Section mismatches detected.\n" "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); for (n = 0; n < SYMBOL_HASH_SIZE; n++) { - struct symbol *s = symbolhash[n]; + struct symbol *s; + + for (s = symbolhash[n]; s; s = s->next) { + /* + * Do not check "vmlinux". This avoids the same warnings + * shown twice, and false-positives for ARCH=um. + */ + if (is_vmlinux(s->module->name) && !s->module->is_dot_o) + continue; - while (s) { if (s->is_static) warn("\"%s\" [%s] is a static %s\n", s->name, s->module->name, export_str(s->export)); - - s = s->next; } } diff --git a/scripts/namespace.pl b/scripts/namespace.pl index 6135574a6f39..1da7bca201a4 100755 --- a/scripts/namespace.pl +++ b/scripts/namespace.pl @@ -65,13 +65,14 @@ use warnings; use strict; use File::Find; +use File::Spec; my $nm = ($ENV{'NM'} || "nm") . " -p"; my $objdump = ($ENV{'OBJDUMP'} || "objdump") . " -s -j .comment"; -my $srctree = ""; -my $objtree = ""; -$srctree = "$ENV{'srctree'}/" if (exists($ENV{'srctree'})); -$objtree = "$ENV{'objtree'}/" if (exists($ENV{'objtree'})); +my $srctree = File::Spec->curdir(); +my $objtree = File::Spec->curdir(); +$srctree = File::Spec->rel2abs($ENV{'srctree'}) if (exists($ENV{'srctree'})); +$objtree = File::Spec->rel2abs($ENV{'objtree'}) if (exists($ENV{'objtree'})); if ($#ARGV != -1) { print STDERR "usage: $0 takes no parameters\n"; @@ -231,9 +232,9 @@ sub do_nm } ($source = $basename) =~ s/\.o$//; if (-e "$source.c" || -e "$source.S") { - $source = "$objtree$File::Find::dir/$source"; + $source = File::Spec->catfile($objtree, $File::Find::dir, $source) } else { - $source = "$srctree$File::Find::dir/$source"; + $source = File::Spec->catfile($srctree, $File::Find::dir, $source) } if (! -e "$source.c" && ! -e "$source.S") { # No obvious source, exclude the object if it is conglomerate diff --git a/scripts/nsdeps b/scripts/nsdeps index ac2b6031dd13..3754dac13b31 100644 --- a/scripts/nsdeps +++ b/scripts/nsdeps @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # SPDX-License-Identifier: GPL-2.0 # Linux kernel symbol namespace import generator # @@ -41,7 +41,7 @@ generate_deps() { for source_file in $mod_source_files; do sed '/MODULE_IMPORT_NS/Q' $source_file > ${source_file}.tmp offset=$(wc -l ${source_file}.tmp | awk '{print $1;}') - cat $source_file | grep MODULE_IMPORT_NS | sort -u >> ${source_file}.tmp + cat $source_file | grep MODULE_IMPORT_NS | LANG=C sort -u >> ${source_file}.tmp tail -n +$((offset +1)) ${source_file} | grep -v MODULE_IMPORT_NS >> ${source_file}.tmp if ! diff -q ${source_file} ${source_file}.tmp; then mv ${source_file}.tmp ${source_file} diff --git a/scripts/recordmcount.h b/scripts/recordmcount.h index 8f0a278ce0af..74eab03e31d4 100644 --- a/scripts/recordmcount.h +++ b/scripts/recordmcount.h @@ -389,11 +389,8 @@ static int nop_mcount(Elf_Shdr const *const relhdr, mcountsym = get_mcountsym(sym0, relp, str0); if (mcountsym == Elf_r_sym(relp) && !is_fake_mcount(relp)) { - if (make_nop) { + if (make_nop) ret = make_nop((void *)ehdr, _w(shdr->sh_offset) + _w(relp->r_offset)); - if (ret < 0) - return -1; - } if (warn_on_notrace_sect && !once) { printf("Section %s has mcount callers being ignored\n", txtname); diff --git a/scripts/setlocalversion b/scripts/setlocalversion index 365b3c2b8f43..220dae0db3f1 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -126,7 +126,7 @@ scm_version() collect_files() { - local file res + local file res= for file; do case "$file" in diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 19faace69644..35e6ca773734 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -13,9 +13,6 @@ integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyrin integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_uefi.o integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o -$(obj)/load_uefi.o: KBUILD_CFLAGS += -fshort-wchar -subdir-$(CONFIG_IMA) += ima obj-$(CONFIG_IMA) += ima/ -subdir-$(CONFIG_EVM) += evm obj-$(CONFIG_EVM) += evm/ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 3a29e7c24ba9..a5813c7629c1 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1946,7 +1946,14 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) rc = string_to_context_struct(args->newp, NULL, s, newc, SECSID_NULL); if (rc == -EINVAL) { - /* Retain string representation for later mapping. */ + /* + * Retain string representation for later mapping. + * + * IMPORTANT: We need to copy the contents of oldc->str + * back into s again because string_to_context_struct() + * may have garbled it. + */ + memcpy(s, oldc->str, oldc->len); context_init(newc); newc->str = s; newc->len = oldc->len; diff --git a/tools/arch/arm/include/uapi/asm/kvm.h b/tools/arch/arm/include/uapi/asm/kvm.h index a4217c1a5d01..2769360f195c 100644 --- a/tools/arch/arm/include/uapi/asm/kvm.h +++ b/tools/arch/arm/include/uapi/asm/kvm.h @@ -266,8 +266,10 @@ struct kvm_vcpu_events { #define KVM_DEV_ARM_ITS_CTRL_RESET 4 /* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_VCPU2_SHIFT 28 +#define KVM_ARM_IRQ_VCPU2_MASK 0xf #define KVM_ARM_IRQ_TYPE_SHIFT 24 -#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_TYPE_MASK 0xf #define KVM_ARM_IRQ_VCPU_SHIFT 16 #define KVM_ARM_IRQ_VCPU_MASK 0xff #define KVM_ARM_IRQ_NUM_SHIFT 0 diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h index 9a507716ae2f..67c21f9bdbad 100644 --- a/tools/arch/arm64/include/uapi/asm/kvm.h +++ b/tools/arch/arm64/include/uapi/asm/kvm.h @@ -325,8 +325,10 @@ struct kvm_vcpu_events { #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 /* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_VCPU2_SHIFT 28 +#define KVM_ARM_IRQ_VCPU2_MASK 0xf #define KVM_ARM_IRQ_TYPE_SHIFT 24 -#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_TYPE_MASK 0xf #define KVM_ARM_IRQ_VCPU_SHIFT 16 #define KVM_ARM_IRQ_VCPU_MASK 0xff #define KVM_ARM_IRQ_NUM_SHIFT 0 diff --git a/tools/arch/s390/include/uapi/asm/kvm.h b/tools/arch/s390/include/uapi/asm/kvm.h index 47104e5b47fd..436ec7636927 100644 --- a/tools/arch/s390/include/uapi/asm/kvm.h +++ b/tools/arch/s390/include/uapi/asm/kvm.h @@ -231,6 +231,12 @@ struct kvm_guest_debug_arch { #define KVM_SYNC_GSCB (1UL << 9) #define KVM_SYNC_BPBC (1UL << 10) #define KVM_SYNC_ETOKEN (1UL << 11) + +#define KVM_SYNC_S390_VALID_FIELDS \ + (KVM_SYNC_PREFIX | KVM_SYNC_GPRS | KVM_SYNC_ACRS | KVM_SYNC_CRS | \ + KVM_SYNC_ARCH0 | KVM_SYNC_PFAULT | KVM_SYNC_VRS | KVM_SYNC_RICCB | \ + KVM_SYNC_FPRS | KVM_SYNC_GSCB | KVM_SYNC_BPBC | KVM_SYNC_ETOKEN) + /* length and alignment of the sdnx as a power of two */ #define SDNXC 8 #define SDNXL (1UL << SDNXC) diff --git a/tools/arch/x86/include/uapi/asm/vmx.h b/tools/arch/x86/include/uapi/asm/vmx.h index f0b0c90dd398..f01950aa7fae 100644 --- a/tools/arch/x86/include/uapi/asm/vmx.h +++ b/tools/arch/x86/include/uapi/asm/vmx.h @@ -31,6 +31,7 @@ #define EXIT_REASON_EXCEPTION_NMI 0 #define EXIT_REASON_EXTERNAL_INTERRUPT 1 #define EXIT_REASON_TRIPLE_FAULT 2 +#define EXIT_REASON_INIT_SIGNAL 3 #define EXIT_REASON_PENDING_INTERRUPT 7 #define EXIT_REASON_NMI_WINDOW 8 @@ -90,6 +91,7 @@ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \ { EXIT_REASON_EXTERNAL_INTERRUPT, "EXTERNAL_INTERRUPT" }, \ { EXIT_REASON_TRIPLE_FAULT, "TRIPLE_FAULT" }, \ + { EXIT_REASON_INIT_SIGNAL, "INIT_SIGNAL" }, \ { EXIT_REASON_PENDING_INTERRUPT, "PENDING_INTERRUPT" }, \ { EXIT_REASON_NMI_WINDOW, "NMI_WINDOW" }, \ { EXIT_REASON_TASK_SWITCH, "TASK_SWITCH" }, \ diff --git a/tools/include/uapi/asm-generic/mman-common.h b/tools/include/uapi/asm-generic/mman-common.h index 63b1f506ea67..c160a5354eb6 100644 --- a/tools/include/uapi/asm-generic/mman-common.h +++ b/tools/include/uapi/asm-generic/mman-common.h @@ -67,6 +67,9 @@ #define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */ #define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */ +#define MADV_COLD 20 /* deactivate these pages */ +#define MADV_PAGEOUT 21 /* reclaim these pages */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/tools/include/uapi/drm/i915_drm.h b/tools/include/uapi/drm/i915_drm.h index 328d05e77d9f..469dc512cca3 100644 --- a/tools/include/uapi/drm/i915_drm.h +++ b/tools/include/uapi/drm/i915_drm.h @@ -521,6 +521,7 @@ typedef struct drm_i915_irq_wait { #define I915_SCHEDULER_CAP_PRIORITY (1ul << 1) #define I915_SCHEDULER_CAP_PREEMPTION (1ul << 2) #define I915_SCHEDULER_CAP_SEMAPHORES (1ul << 3) +#define I915_SCHEDULER_CAP_ENGINE_BUSY_STATS (1ul << 4) #define I915_PARAM_HUC_STATUS 42 diff --git a/tools/include/uapi/linux/fs.h b/tools/include/uapi/linux/fs.h index 2a616aa3f686..379a612f8f1d 100644 --- a/tools/include/uapi/linux/fs.h +++ b/tools/include/uapi/linux/fs.h @@ -13,6 +13,9 @@ #include <linux/limits.h> #include <linux/ioctl.h> #include <linux/types.h> +#ifndef __KERNEL__ +#include <linux/fscrypt.h> +#endif /* Use of MS_* flags within the kernel is restricted to core mount(2) code. */ #if !defined(__KERNEL__) @@ -213,57 +216,6 @@ struct fsxattr { #define FS_IOC_SETFSLABEL _IOW(0x94, 50, char[FSLABEL_MAX]) /* - * File system encryption support - */ -/* Policy provided via an ioctl on the topmost directory */ -#define FS_KEY_DESCRIPTOR_SIZE 8 - -#define FS_POLICY_FLAGS_PAD_4 0x00 -#define FS_POLICY_FLAGS_PAD_8 0x01 -#define FS_POLICY_FLAGS_PAD_16 0x02 -#define FS_POLICY_FLAGS_PAD_32 0x03 -#define FS_POLICY_FLAGS_PAD_MASK 0x03 -#define FS_POLICY_FLAG_DIRECT_KEY 0x04 /* use master key directly */ -#define FS_POLICY_FLAGS_VALID 0x07 - -/* Encryption algorithms */ -#define FS_ENCRYPTION_MODE_INVALID 0 -#define FS_ENCRYPTION_MODE_AES_256_XTS 1 -#define FS_ENCRYPTION_MODE_AES_256_GCM 2 -#define FS_ENCRYPTION_MODE_AES_256_CBC 3 -#define FS_ENCRYPTION_MODE_AES_256_CTS 4 -#define FS_ENCRYPTION_MODE_AES_128_CBC 5 -#define FS_ENCRYPTION_MODE_AES_128_CTS 6 -#define FS_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* Removed, do not use. */ -#define FS_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* Removed, do not use. */ -#define FS_ENCRYPTION_MODE_ADIANTUM 9 - -struct fscrypt_policy { - __u8 version; - __u8 contents_encryption_mode; - __u8 filenames_encryption_mode; - __u8 flags; - __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; -}; - -#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) -#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) -#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) - -/* Parameters for passing an encryption key into the kernel keyring */ -#define FS_KEY_DESC_PREFIX "fscrypt:" -#define FS_KEY_DESC_PREFIX_SIZE 8 - -/* Structure that userspace passes to the kernel keyring */ -#define FS_MAX_KEY_SIZE 64 - -struct fscrypt_key { - __u32 mode; - __u8 raw[FS_MAX_KEY_SIZE]; - __u32 size; -}; - -/* * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) * * Note: for historical reasons, these flags were originally used and @@ -306,6 +258,7 @@ struct fscrypt_key { #define FS_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define FS_HUGE_FILE_FL 0x00040000 /* Reserved for ext4 */ #define FS_EXTENT_FL 0x00080000 /* Extents */ +#define FS_VERITY_FL 0x00100000 /* Verity protected inode */ #define FS_EA_INODE_FL 0x00200000 /* Inode used for large EA */ #define FS_EOFBLOCKS_FL 0x00400000 /* Reserved for ext4 */ #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ diff --git a/tools/include/uapi/linux/fscrypt.h b/tools/include/uapi/linux/fscrypt.h new file mode 100644 index 000000000000..39ccfe9311c3 --- /dev/null +++ b/tools/include/uapi/linux/fscrypt.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * fscrypt user API + * + * These ioctls can be used on filesystems that support fscrypt. See the + * "User API" section of Documentation/filesystems/fscrypt.rst. + */ +#ifndef _UAPI_LINUX_FSCRYPT_H +#define _UAPI_LINUX_FSCRYPT_H + +#include <linux/types.h> + +/* Encryption policy flags */ +#define FSCRYPT_POLICY_FLAGS_PAD_4 0x00 +#define FSCRYPT_POLICY_FLAGS_PAD_8 0x01 +#define FSCRYPT_POLICY_FLAGS_PAD_16 0x02 +#define FSCRYPT_POLICY_FLAGS_PAD_32 0x03 +#define FSCRYPT_POLICY_FLAGS_PAD_MASK 0x03 +#define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 +#define FSCRYPT_POLICY_FLAGS_VALID 0x07 + +/* Encryption algorithms */ +#define FSCRYPT_MODE_AES_256_XTS 1 +#define FSCRYPT_MODE_AES_256_CTS 4 +#define FSCRYPT_MODE_AES_128_CBC 5 +#define FSCRYPT_MODE_AES_128_CTS 6 +#define FSCRYPT_MODE_ADIANTUM 9 +#define __FSCRYPT_MODE_MAX 9 + +/* + * Legacy policy version; ad-hoc KDF and no key verification. + * For new encrypted directories, use fscrypt_policy_v2 instead. + * + * Careful: the .version field for this is actually 0, not 1. + */ +#define FSCRYPT_POLICY_V1 0 +#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 +struct fscrypt_policy_v1 { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; +}; +#define fscrypt_policy fscrypt_policy_v1 + +/* + * Process-subscribed "logon" key description prefix and payload format. + * Deprecated; prefer FS_IOC_ADD_ENCRYPTION_KEY instead. + */ +#define FSCRYPT_KEY_DESC_PREFIX "fscrypt:" +#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8 +#define FSCRYPT_MAX_KEY_SIZE 64 +struct fscrypt_key { + __u32 mode; + __u8 raw[FSCRYPT_MAX_KEY_SIZE]; + __u32 size; +}; + +/* + * New policy version with HKDF and key verification (recommended). + */ +#define FSCRYPT_POLICY_V2 2 +#define FSCRYPT_KEY_IDENTIFIER_SIZE 16 +struct fscrypt_policy_v2 { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 __reserved[4]; + __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; +}; + +/* Struct passed to FS_IOC_GET_ENCRYPTION_POLICY_EX */ +struct fscrypt_get_policy_ex_arg { + __u64 policy_size; /* input/output */ + union { + __u8 version; + struct fscrypt_policy_v1 v1; + struct fscrypt_policy_v2 v2; + } policy; /* output */ +}; + +/* + * v1 policy keys are specified by an arbitrary 8-byte key "descriptor", + * matching fscrypt_policy_v1::master_key_descriptor. + */ +#define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 + +/* + * v2 policy keys are specified by a 16-byte key "identifier" which the kernel + * calculates as a cryptographic hash of the key itself, + * matching fscrypt_policy_v2::master_key_identifier. + */ +#define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 + +/* + * Specifies a key, either for v1 or v2 policies. This doesn't contain the + * actual key itself; this is just the "name" of the key. + */ +struct fscrypt_key_specifier { + __u32 type; /* one of FSCRYPT_KEY_SPEC_TYPE_* */ + __u32 __reserved; + union { + __u8 __reserved[32]; /* reserve some extra space */ + __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + } u; +}; + +/* Struct passed to FS_IOC_ADD_ENCRYPTION_KEY */ +struct fscrypt_add_key_arg { + struct fscrypt_key_specifier key_spec; + __u32 raw_size; + __u32 __reserved[9]; + __u8 raw[]; +}; + +/* Struct passed to FS_IOC_REMOVE_ENCRYPTION_KEY */ +struct fscrypt_remove_key_arg { + struct fscrypt_key_specifier key_spec; +#define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY 0x00000001 +#define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS 0x00000002 + __u32 removal_status_flags; /* output */ + __u32 __reserved[5]; +}; + +/* Struct passed to FS_IOC_GET_ENCRYPTION_KEY_STATUS */ +struct fscrypt_get_key_status_arg { + /* input */ + struct fscrypt_key_specifier key_spec; + __u32 __reserved[6]; + + /* output */ +#define FSCRYPT_KEY_STATUS_ABSENT 1 +#define FSCRYPT_KEY_STATUS_PRESENT 2 +#define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 + __u32 status; +#define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001 + __u32 status_flags; + __u32 user_count; + __u32 __out_reserved[13]; +}; + +#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) +#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) +#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) +#define FS_IOC_GET_ENCRYPTION_POLICY_EX _IOWR('f', 22, __u8[9]) /* size + version */ +#define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 23, struct fscrypt_add_key_arg) +#define FS_IOC_REMOVE_ENCRYPTION_KEY _IOWR('f', 24, struct fscrypt_remove_key_arg) +#define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct fscrypt_remove_key_arg) +#define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 26, struct fscrypt_get_key_status_arg) + +/**********************************************************************/ + +/* old names; don't add anything new here! */ +#ifndef __KERNEL__ +#define FS_KEY_DESCRIPTOR_SIZE FSCRYPT_KEY_DESCRIPTOR_SIZE +#define FS_POLICY_FLAGS_PAD_4 FSCRYPT_POLICY_FLAGS_PAD_4 +#define FS_POLICY_FLAGS_PAD_8 FSCRYPT_POLICY_FLAGS_PAD_8 +#define FS_POLICY_FLAGS_PAD_16 FSCRYPT_POLICY_FLAGS_PAD_16 +#define FS_POLICY_FLAGS_PAD_32 FSCRYPT_POLICY_FLAGS_PAD_32 +#define FS_POLICY_FLAGS_PAD_MASK FSCRYPT_POLICY_FLAGS_PAD_MASK +#define FS_POLICY_FLAG_DIRECT_KEY FSCRYPT_POLICY_FLAG_DIRECT_KEY +#define FS_POLICY_FLAGS_VALID FSCRYPT_POLICY_FLAGS_VALID +#define FS_ENCRYPTION_MODE_INVALID 0 /* never used */ +#define FS_ENCRYPTION_MODE_AES_256_XTS FSCRYPT_MODE_AES_256_XTS +#define FS_ENCRYPTION_MODE_AES_256_GCM 2 /* never used */ +#define FS_ENCRYPTION_MODE_AES_256_CBC 3 /* never used */ +#define FS_ENCRYPTION_MODE_AES_256_CTS FSCRYPT_MODE_AES_256_CTS +#define FS_ENCRYPTION_MODE_AES_128_CBC FSCRYPT_MODE_AES_128_CBC +#define FS_ENCRYPTION_MODE_AES_128_CTS FSCRYPT_MODE_AES_128_CTS +#define FS_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* removed */ +#define FS_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* removed */ +#define FS_ENCRYPTION_MODE_ADIANTUM FSCRYPT_MODE_ADIANTUM +#define FS_KEY_DESC_PREFIX FSCRYPT_KEY_DESC_PREFIX +#define FS_KEY_DESC_PREFIX_SIZE FSCRYPT_KEY_DESC_PREFIX_SIZE +#define FS_MAX_KEY_SIZE FSCRYPT_MAX_KEY_SIZE +#endif /* !__KERNEL__ */ + +#endif /* _UAPI_LINUX_FSCRYPT_H */ diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 5e3f12d5359e..233efbb1c81c 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -243,6 +243,8 @@ struct kvm_hyperv_exit { #define KVM_INTERNAL_ERROR_SIMUL_EX 2 /* Encounter unexpected vm-exit due to delivery event. */ #define KVM_INTERNAL_ERROR_DELIVERY_EV 3 +/* Encounter unexpected vm-exit reason */ +#define KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON 4 /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { @@ -996,6 +998,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ARM_PTRAUTH_ADDRESS 171 #define KVM_CAP_ARM_PTRAUTH_GENERIC 172 #define KVM_CAP_PMU_EVENT_FILTER 173 +#define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/tools/include/uapi/linux/usbdevice_fs.h b/tools/include/uapi/linux/usbdevice_fs.h index 78efe870c2b7..cf525cddeb94 100644 --- a/tools/include/uapi/linux/usbdevice_fs.h +++ b/tools/include/uapi/linux/usbdevice_fs.h @@ -158,6 +158,7 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_MMAP 0x20 #define USBDEVFS_CAP_DROP_PRIVILEGES 0x40 #define USBDEVFS_CAP_CONNINFO_EX 0x80 +#define USBDEVFS_CAP_SUSPEND 0x100 /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ @@ -223,5 +224,8 @@ struct usbdevfs_streams { * extending size of the data returned. */ #define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len) +#define USBDEVFS_FORBID_SUSPEND _IO('U', 33) +#define USBDEVFS_ALLOW_SUSPEND _IO('U', 34) +#define USBDEVFS_WAIT_FOR_RESUME _IO('U', 35) #endif /* _UAPI_LINUX_USBDEVICE_FS_H */ diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile index ed61fb3a46c0..5b2cd5e58df0 100644 --- a/tools/lib/subcmd/Makefile +++ b/tools/lib/subcmd/Makefile @@ -20,7 +20,13 @@ MAKEFLAGS += --no-print-directory LIBFILE = $(OUTPUT)libsubcmd.a CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC +CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -fPIC + +ifeq ($(DEBUG),0) + ifeq ($(feature-fortify-source), 1) + CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 + endif +endif ifeq ($(CC_NO_CLANG), 0) CFLAGS += -O3 diff --git a/tools/perf/Documentation/asciidoc.conf b/tools/perf/Documentation/asciidoc.conf index 356b23a40339..2b62ba1e72b7 100644 --- a/tools/perf/Documentation/asciidoc.conf +++ b/tools/perf/Documentation/asciidoc.conf @@ -71,6 +71,9 @@ ifdef::backend-docbook[] [header] template::[header-declarations] <refentry> +ifdef::perf_date[] +<refentryinfo><date>{perf_date}</date></refentryinfo> +endif::perf_date[] <refmeta> <refentrytitle>{mantitle}</refentrytitle> <manvolnum>{manvolnum}</manvolnum> diff --git a/tools/perf/Documentation/jitdump-specification.txt b/tools/perf/Documentation/jitdump-specification.txt index 4c62b0713651..52152d156ad9 100644 --- a/tools/perf/Documentation/jitdump-specification.txt +++ b/tools/perf/Documentation/jitdump-specification.txt @@ -36,8 +36,8 @@ III/ Jitdump file header format Each jitdump file starts with a fixed size header containing the following fields in order: -* uint32_t magic : a magic number tagging the file type. The value is 4-byte long and represents the string "JiTD" in ASCII form. It is 0x4A695444 or 0x4454694a depending on the endianness. The field can be used to detect the endianness of the file -* uint32_t version : a 4-byte value representing the format version. It is currently set to 2 +* uint32_t magic : a magic number tagging the file type. The value is 4-byte long and represents the string "JiTD" in ASCII form. It written is as 0x4A695444. The reader will detect an endian mismatch when it reads 0x4454694a. +* uint32_t version : a 4-byte value representing the format version. It is currently set to 1 * uint32_t total_size: size in bytes of file header * uint32_t elf_mach : ELF architecture encoding (ELF e_machine value as specified in /usr/include/elf.h) * uint32_t pad1 : padding. Reserved for future use diff --git a/tools/perf/arch/arm/annotate/instructions.c b/tools/perf/arch/arm/annotate/instructions.c index e1d4b484cc4b..2ff6cedeb9c5 100644 --- a/tools/perf/arch/arm/annotate/instructions.c +++ b/tools/perf/arch/arm/annotate/instructions.c @@ -37,7 +37,7 @@ static int arm__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arm = zalloc(sizeof(*arm)); if (!arm) - return -1; + return ENOMEM; #define ARM_CONDS "(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl|vc|vs)" err = regcomp(&arm->call_insn, "^blx?" ARM_CONDS "?$", REG_EXTENDED); @@ -59,5 +59,5 @@ out_free_call: regfree(&arm->call_insn); out_free_arm: free(arm); - return -1; + return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP; } diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c index 43aa93ed8414..037e292ecd8e 100644 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ b/tools/perf/arch/arm64/annotate/instructions.c @@ -95,7 +95,7 @@ static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arm = zalloc(sizeof(*arm)); if (!arm) - return -1; + return ENOMEM; /* bl, blr */ err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED); @@ -118,5 +118,5 @@ out_free_call: regfree(&arm->call_insn); out_free_arm: free(arm); - return -1; + return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP; } diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index b6b7bc7e31a1..3b4cdfc5efd6 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <sys/types.h> +#include <errno.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -30,7 +31,7 @@ get_cpuid(char *buffer, size_t sz) buffer[nb-1] = '\0'; return 0; } - return -1; + return ENOBUFS; } char * diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c index 89bb8f2c54ce..a50e70baf918 100644 --- a/tools/perf/arch/s390/annotate/instructions.c +++ b/tools/perf/arch/s390/annotate/instructions.c @@ -164,8 +164,10 @@ static int s390__annotate_init(struct arch *arch, char *cpuid __maybe_unused) if (!arch->initialized) { arch->initialized = true; arch->associate_instruction_ops = s390__associate_ins_ops; - if (cpuid) - err = s390__cpuid_parse(arch, cpuid); + if (cpuid) { + if (s390__cpuid_parse(arch, cpuid)) + err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING; + } } return err; diff --git a/tools/perf/arch/s390/util/header.c b/tools/perf/arch/s390/util/header.c index 8b0b018d896a..7933f6871c81 100644 --- a/tools/perf/arch/s390/util/header.c +++ b/tools/perf/arch/s390/util/header.c @@ -8,6 +8,7 @@ */ #include <sys/types.h> +#include <errno.h> #include <unistd.h> #include <stdio.h> #include <string.h> @@ -54,7 +55,7 @@ int get_cpuid(char *buffer, size_t sz) sysinfo = fopen(SYSINFO, "r"); if (sysinfo == NULL) - return -1; + return errno; while ((read = getline(&line, &line_sz, sysinfo)) != -1) { if (!strncmp(line, SYSINFO_MANU, strlen(SYSINFO_MANU))) { @@ -89,7 +90,7 @@ int get_cpuid(char *buffer, size_t sz) /* Missing manufacturer, type or model information should not happen */ if (!manufacturer[0] || !type[0] || !model[0]) - return -1; + return EINVAL; /* * Scan /proc/service_levels and return the CPU-MF counter facility @@ -133,14 +134,14 @@ skip_sysinfo: else nbytes = snprintf(buffer, sz, "%s,%s,%s", manufacturer, type, model); - return (nbytes >= sz) ? -1 : 0; + return (nbytes >= sz) ? ENOBUFS : 0; } char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused) { char *buf = malloc(128); - if (buf && get_cpuid(buf, 128) < 0) + if (buf && get_cpuid(buf, 128)) zfree(&buf); return buf; } diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 44f5aba78210..7eb5621c021d 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -196,8 +196,10 @@ static int x86__annotate_init(struct arch *arch, char *cpuid) if (arch->initialized) return 0; - if (cpuid) - err = x86__cpuid_parse(arch, cpuid); + if (cpuid) { + if (x86__cpuid_parse(arch, cpuid)) + err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING; + } arch->initialized = true; return err; diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index 662ecf84a421..aa6deb463bf3 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <sys/types.h> +#include <errno.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -58,7 +59,7 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt) buffer[nb-1] = '\0'; return 0; } - return -1; + return ENOBUFS; } int diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 2227e2f42c09..58a9e0989491 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -705,14 +705,15 @@ static int process_sample_event(struct perf_tool *tool, static int cpu_isa_config(struct perf_kvm_stat *kvm) { - char buf[64], *cpuid; + char buf[128], *cpuid; int err; if (kvm->live) { err = get_cpuid(buf, sizeof(buf)); if (err != 0) { - pr_err("Failed to look up CPU type\n"); - return err; + pr_err("Failed to look up CPU type: %s\n", + str_error_r(err, buf, sizeof(buf))); + return -err; } cpuid = buf; } else diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 286fc70d7402..67be8d31afab 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1063,7 +1063,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, continue; insn = 0; - for (off = 0;; off += ilen) { + for (off = 0; off < (unsigned)len; off += ilen) { uint64_t ip = start + off; printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp); @@ -1074,6 +1074,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, printed += print_srccode(thread, x.cpumode, ip); break; } else { + ilen = 0; printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, dump_insn(&x, ip, buffer + off, len - off, &ilen)); if (ilen == 0) @@ -1083,6 +1084,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, insn++; } } + if (off != (unsigned)len) + printed += fprintf(fp, "\tmismatch of LBR data and executable\n"); } /* @@ -1123,6 +1126,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, goto out; } for (off = 0; off <= end - start; off += ilen) { + ilen = 0; printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", start + off, dump_insn(&x, start + off, buffer + off, len - off, &ilen)); if (ilen == 0) diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index e2e0f06c97d0..cea13cb987d0 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -8,6 +8,7 @@ include/uapi/drm/i915_drm.h include/uapi/linux/fadvise.h include/uapi/linux/fcntl.h include/uapi/linux/fs.h +include/uapi/linux/fscrypt.h include/uapi/linux/kcmp.h include/uapi/linux/kvm.h include/uapi/linux/in.h diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/basic.json b/tools/perf/pmu-events/arch/s390/cf_z15/basic.json index 17fb5241928b..17fb5241928b 100644 --- a/tools/perf/pmu-events/arch/s390/cf_m8561/basic.json +++ b/tools/perf/pmu-events/arch/s390/cf_z15/basic.json diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/crypto.json b/tools/perf/pmu-events/arch/s390/cf_z15/crypto.json index db286f19e7b6..db286f19e7b6 100644 --- a/tools/perf/pmu-events/arch/s390/cf_m8561/crypto.json +++ b/tools/perf/pmu-events/arch/s390/cf_z15/crypto.json diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/crypto6.json b/tools/perf/pmu-events/arch/s390/cf_z15/crypto6.json index 5e36bc2468d0..5e36bc2468d0 100644 --- a/tools/perf/pmu-events/arch/s390/cf_m8561/crypto6.json +++ b/tools/perf/pmu-events/arch/s390/cf_z15/crypto6.json diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/extended.json b/tools/perf/pmu-events/arch/s390/cf_z15/extended.json index 89e070727e1b..89e070727e1b 100644 --- a/tools/perf/pmu-events/arch/s390/cf_m8561/extended.json +++ b/tools/perf/pmu-events/arch/s390/cf_z15/extended.json diff --git a/tools/perf/pmu-events/arch/s390/cf_z15/transaction.json b/tools/perf/pmu-events/arch/s390/cf_z15/transaction.json new file mode 100644 index 000000000000..1a0034f79f73 --- /dev/null +++ b/tools/perf/pmu-events/arch/s390/cf_z15/transaction.json @@ -0,0 +1,7 @@ +[ + { + "BriefDescription": "Transaction count", + "MetricName": "transaction", + "MetricExpr": "TX_C_TEND + TX_NC_TEND + TX_NC_TABORT + TX_C_TABORT_SPECIAL + TX_C_TABORT_NO_SPECIAL" + } +] diff --git a/tools/perf/pmu-events/arch/s390/mapfile.csv b/tools/perf/pmu-events/arch/s390/mapfile.csv index bd3fc577139c..61641a3480e0 100644 --- a/tools/perf/pmu-events/arch/s390/mapfile.csv +++ b/tools/perf/pmu-events/arch/s390/mapfile.csv @@ -4,4 +4,4 @@ Family-model,Version,Filename,EventType ^IBM.282[78].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_zec12,core ^IBM.296[45].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_z13,core ^IBM.390[67].*[13]\.[1-5].[[:xdigit:]]+$,3,cf_z14,core -^IBM.856[12].*3\.6.[[:xdigit:]]+$,3,cf_m8561,core +^IBM.856[12].*3\.6.[[:xdigit:]]+$,3,cf_z15,core diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c index 9e37287da924..e2837260ca4d 100644 --- a/tools/perf/pmu-events/jevents.c +++ b/tools/perf/pmu-events/jevents.c @@ -450,12 +450,12 @@ static struct fixed { const char *name; const char *event; } fixed[] = { - { "inst_retired.any", "event=0xc0" }, - { "inst_retired.any_p", "event=0xc0" }, - { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" }, - { "cpu_clk_unhalted.thread", "event=0x3c" }, - { "cpu_clk_unhalted.core", "event=0x3c" }, - { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" }, + { "inst_retired.any", "event=0xc0,period=2000003" }, + { "inst_retired.any_p", "event=0xc0,period=2000003" }, + { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03,period=2000003" }, + { "cpu_clk_unhalted.thread", "event=0x3c,period=2000003" }, + { "cpu_clk_unhalted.core", "event=0x3c,period=2000003" }, + { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1,period=2000003" }, { NULL, NULL}, }; diff --git a/tools/perf/tests/perf-hooks.c b/tools/perf/tests/perf-hooks.c index dbc27199c65e..dd865e0bea12 100644 --- a/tools/perf/tests/perf-hooks.c +++ b/tools/perf/tests/perf-hooks.c @@ -19,12 +19,11 @@ static void sigsegv_handler(int sig __maybe_unused) static void the_hook(void *_hook_flags) { int *hook_flags = _hook_flags; - int *p = NULL; *hook_flags = 1234; /* Generate a segfault, test perf_hooks__recover */ - *p = 0; + raise(SIGSEGV); } int test__perf_hooks(struct test *test __maybe_unused, int subtest __maybe_unused) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e830eadfca2a..4036c7f7b0fb 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1631,6 +1631,19 @@ int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map * case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF: scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation"); break; + case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP: + scnprintf(buf, buflen, "Problems with arch specific instruction name regular expressions."); + break; + case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING: + scnprintf(buf, buflen, "Problems while parsing the CPUID in the arch specific initialization."); + break; + case SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE: + scnprintf(buf, buflen, "Invalid BPF file: %s.", dso->long_name); + break; + case SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF: + scnprintf(buf, buflen, "The %s BPF file has no BTF section, compile with -g or use pahole -J.", + dso->long_name); + break; default: scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); break; @@ -1662,7 +1675,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil build_id_path = strdup(filename); if (!build_id_path) - return -1; + return ENOMEM; /* * old style build-id cache has name of XX/XXXXXXX.. while @@ -1713,13 +1726,13 @@ static int symbol__disassemble_bpf(struct symbol *sym, char tpath[PATH_MAX]; size_t buf_size; int nr_skip = 0; - int ret = -1; char *buf; bfd *bfdf; + int ret; FILE *s; if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO) - return -1; + return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE; pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__, sym->name, sym->start, sym->end - sym->start); @@ -1732,8 +1745,10 @@ static int symbol__disassemble_bpf(struct symbol *sym, assert(bfd_check_format(bfdf, bfd_object)); s = open_memstream(&buf, &buf_size); - if (!s) + if (!s) { + ret = errno; goto out; + } init_disassemble_info(&info, s, (fprintf_ftype) fprintf); @@ -1742,8 +1757,10 @@ static int symbol__disassemble_bpf(struct symbol *sym, info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, dso->bpf_prog.id); - if (!info_node) + if (!info_node) { + return SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; goto out; + } info_linear = info_node->info_linear; sub_id = dso->bpf_prog.sub_id; @@ -2071,11 +2088,11 @@ int symbol__annotate(struct symbol *sym, struct map *map, int err; if (!arch_name) - return -1; + return errno; args.arch = arch = arch__find(arch_name); if (arch == NULL) - return -ENOTSUP; + return ENOTSUP; if (parch) *parch = arch; @@ -2971,7 +2988,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel, notes->offsets = zalloc(size * sizeof(struct annotation_line *)); if (notes->offsets == NULL) - return -1; + return ENOMEM; if (perf_evsel__is_group_event(evsel)) nr_pcnt = evsel->core.nr_members; @@ -2997,7 +3014,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel, out_free_offsets: zfree(¬es->offsets); - return -1; + return err; } #define ANNOTATION__CFG(n) \ diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index d94be9140e31..d76fd0e81f46 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -370,6 +370,10 @@ enum symbol_disassemble_errno { SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START, SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF, + SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING, + SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP, + SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE, + SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF, __SYMBOL_ANNOTATE_ERRNO__END, }; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 5591af81a070..abc7fda4a0fe 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -30,6 +30,7 @@ #include "counts.h" #include "event.h" #include "evsel.h" +#include "util/env.h" #include "util/evsel_config.h" #include "util/evsel_fprintf.h" #include "evlist.h" @@ -2512,7 +2513,7 @@ struct perf_env *perf_evsel__env(struct evsel *evsel) { if (evsel && evsel->evlist) return evsel->evlist->env; - return NULL; + return &perf_env; } static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist) diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 1bdf4c6ea3e5..e3ccb0ce1938 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -395,7 +395,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) size_t size; u16 idr_size; const char *sym; - uint32_t count; + uint64_t count; int ret, csize, usize; pid_t pid, tid; struct { @@ -418,7 +418,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) return -1; filename = event->mmap2.filename; - size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%u.so", + size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so", jd->dir, pid, count); @@ -529,7 +529,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) return -1; filename = event->mmap2.filename; - size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%"PRIu64, + size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so", jd->dir, pid, jr->move.code_index); diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 8d04e3d070b1..8b14e4a7f1dc 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -233,14 +233,14 @@ static int detect_kbuild_dir(char **kbuild_dir) const char *prefix_dir = ""; const char *suffix_dir = ""; + /* _UTSNAME_LENGTH is 65 */ + char release[128]; + char *autoconf_path; int err; if (!test_dir) { - /* _UTSNAME_LENGTH is 65 */ - char release[128]; - err = fetch_kernel_version(NULL, release, sizeof(release)); if (err) diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 5b83ed1ebbd6..eec9b282c047 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "symbol.h" +#include <assert.h> #include <errno.h> #include <inttypes.h> #include <limits.h> @@ -850,6 +851,8 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp } after->start = map->end; + after->pgoff += map->end - pos->start; + assert(pos->map_ip(pos, map->end) == after->map_ip(after, map->end)); __map_groups__insert(pos->groups, after); if (verbose >= 2 && !use_browser) map__fprintf(after, fp); diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 53f31053a27a..02460362256d 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -14,6 +14,7 @@ #include "thread_map.h" #include "trace-event.h" #include "mmap.h" +#include "util/env.h" #include <internal/lib.h> #include "../perf-sys.h" @@ -54,6 +55,11 @@ int parse_callchain_record(const char *arg __maybe_unused, } /* + * Add this one here not to drag util/env.c + */ +struct perf_env perf_env; + +/* * Support debug printing even though util/debug.c is not linked. That means * implementing 'verbose' and 'eprintf'. */ diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c3feccb99ff5..4cdbae6f4e61 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -63,6 +63,13 @@ TARGETS += zram TARGETS_HOTPLUG = cpu-hotplug TARGETS_HOTPLUG += memory-hotplug +# User can optionally provide a TARGETS skiplist. +SKIP_TARGETS ?= +ifneq ($(SKIP_TARGETS),) + TMP := $(filter-out $(SKIP_TARGETS), $(TARGETS)) + override TARGETS := $(TMP) +endif + # Clear LDFLAGS and MAKEFLAGS if called from main # Makefile to avoid test build failures when test # Makefile doesn't have explicit build rules. @@ -171,9 +178,12 @@ run_pstore_crash: # 1. output_dir=kernel_src # 2. a separate output directory is specified using O= KBUILD_OUTPUT # 3. a separate output directory is specified using KBUILD_OUTPUT +# Avoid conflict with INSTALL_PATH set by the main Makefile # -INSTALL_PATH ?= $(BUILD)/install -INSTALL_PATH := $(abspath $(INSTALL_PATH)) +KSFT_INSTALL_PATH ?= $(BUILD)/kselftest_install +KSFT_INSTALL_PATH := $(abspath $(KSFT_INSTALL_PATH)) +# Avoid changing the rest of the logic here and lib.mk. +INSTALL_PATH := $(KSFT_INSTALL_PATH) ALL_SCRIPT := $(INSTALL_PATH)/run_kselftest.sh install: all @@ -198,11 +208,16 @@ ifdef INSTALL_PATH echo " cat /dev/null > \$$logfile" >> $(ALL_SCRIPT) echo "fi" >> $(ALL_SCRIPT) + @# While building run_kselftest.sh skip also non-existent TARGET dirs: + @# they could be the result of a build failure and should NOT be + @# included in the generated runlist. for TARGET in $(TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ + [ ! -d $$INSTALL_PATH/$$TARGET ] && echo "Skipping non-existent dir: $$TARGET" && continue; \ echo "[ -w /dev/kmsg ] && echo \"kselftest: Running tests in $$TARGET\" >> /dev/kmsg" >> $(ALL_SCRIPT); \ echo "cd $$TARGET" >> $(ALL_SCRIPT); \ echo -n "run_many" >> $(ALL_SCRIPT); \ + echo -n "Emit Tests for $$TARGET\n"; \ $(MAKE) -s --no-print-directory OUTPUT=$$BUILD_TARGET -C $$TARGET emit_tests >> $(ALL_SCRIPT); \ echo "" >> $(ALL_SCRIPT); \ echo "cd \$$ROOT" >> $(ALL_SCRIPT); \ diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 00c9020bdda8..84de7bc74f2c 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -3,9 +3,14 @@ # # Runs a set of tests in a given subdirectory. export skip_rc=4 +export timeout_rc=124 export logfile=/dev/stdout export per_test_logging= +# Defaults for "settings" file fields: +# "timeout" how many seconds to let each test run before failing. +export kselftest_default_timeout=45 + # There isn't a shell-agnostic way to find the path of a sourced file, # so we must rely on BASE_DIR being set to find other tools. if [ -z "$BASE_DIR" ]; then @@ -24,6 +29,16 @@ tap_prefix() fi } +tap_timeout() +{ + # Make sure tests will time out if utility is available. + if [ -x /usr/bin/timeout ] ; then + /usr/bin/timeout "$kselftest_timeout" "$1" + else + "$1" + fi +} + run_one() { DIR="$1" @@ -32,6 +47,18 @@ run_one() BASENAME_TEST=$(basename $TEST) + # Reset any "settings"-file variables. + export kselftest_timeout="$kselftest_default_timeout" + # Load per-test-directory kselftest "settings" file. + settings="$BASE_DIR/$DIR/settings" + if [ -r "$settings" ] ; then + while read line ; do + field=$(echo "$line" | cut -d= -f1) + value=$(echo "$line" | cut -d= -f2-) + eval "kselftest_$field"="$value" + done < "$settings" + fi + TEST_HDR_MSG="selftests: $DIR: $BASENAME_TEST" echo "# $TEST_HDR_MSG" if [ ! -x "$TEST" ]; then @@ -44,14 +71,17 @@ run_one() echo "not ok $test_num $TEST_HDR_MSG" else cd `dirname $TEST` > /dev/null - (((((./$BASENAME_TEST 2>&1; echo $? >&3) | + ((((( tap_timeout ./$BASENAME_TEST 2>&1; echo $? >&3) | tap_prefix >&4) 3>&1) | (read xs; exit $xs)) 4>>"$logfile" && echo "ok $test_num $TEST_HDR_MSG") || - (if [ $? -eq $skip_rc ]; then \ + (rc=$?; \ + if [ $rc -eq $skip_rc ]; then \ echo "not ok $test_num $TEST_HDR_MSG # SKIP" + elif [ $rc -eq $timeout_rc ]; then \ + echo "not ok $test_num $TEST_HDR_MSG # TIMEOUT" else - echo "not ok $test_num $TEST_HDR_MSG" + echo "not ok $test_num $TEST_HDR_MSG # exit=$rc" fi) cd - >/dev/null fi diff --git a/tools/testing/selftests/kselftest_install.sh b/tools/testing/selftests/kselftest_install.sh index ec304463883c..e2e1911d62d5 100755 --- a/tools/testing/selftests/kselftest_install.sh +++ b/tools/testing/selftests/kselftest_install.sh @@ -24,12 +24,12 @@ main() echo "$0: Installing in specified location - $install_loc ..." fi - install_dir=$install_loc/kselftest + install_dir=$install_loc/kselftest_install # Create install directory mkdir -p $install_dir # Build tests - INSTALL_PATH=$install_dir make install + KSFT_INSTALL_PATH=$install_dir make install } main "$@" diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 62c591f87dab..c5ec868fa1e5 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -22,6 +22,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/smm_test TEST_GEN_PROGS_x86_64 += x86_64/state_test TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test +TEST_GEN_PROGS_x86_64 += x86_64/vmx_dirty_log_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test TEST_GEN_PROGS_x86_64 += clear_dirty_log_test @@ -48,7 +49,7 @@ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -Iinclude/$(UNAME_M) -I.. no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ - $(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie) + $(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie) # On s390, build the testcases KVM-enabled pgste-option = $(call try-run, echo 'int main() { return 0; }' | \ diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index 0c17f2ee685e..ff234018219c 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -1083,6 +1083,9 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits); #define VMX_BASIC_MEM_TYPE_WB 6LLU #define VMX_BASIC_INOUT 0x0040000000000000LLU +/* VMX_EPT_VPID_CAP bits */ +#define VMX_EPT_VPID_CAP_AD_BITS (1ULL << 21) + /* MSR_IA32_VMX_MISC bits */ #define MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS (1ULL << 29) #define MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE 0x1F diff --git a/tools/testing/selftests/kvm/include/x86_64/vmx.h b/tools/testing/selftests/kvm/include/x86_64/vmx.h index 69b17055f63d..6ae5a47fe067 100644 --- a/tools/testing/selftests/kvm/include/x86_64/vmx.h +++ b/tools/testing/selftests/kvm/include/x86_64/vmx.h @@ -569,6 +569,10 @@ struct vmx_pages { void *enlightened_vmcs_hva; uint64_t enlightened_vmcs_gpa; void *enlightened_vmcs; + + void *eptp_hva; + uint64_t eptp_gpa; + void *eptp; }; struct vmx_pages *vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva); @@ -576,4 +580,14 @@ bool prepare_for_vmx_operation(struct vmx_pages *vmx); void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp); bool load_vmcs(struct vmx_pages *vmx); +void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, uint32_t eptp_memslot); +void nested_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, uint64_t size, + uint32_t eptp_memslot); +void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm, + uint32_t memslot, uint32_t eptp_memslot); +void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm, + uint32_t eptp_memslot); + #endif /* SELFTEST_KVM_VMX_H */ diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 80a338b5403c..41cf45416060 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -705,7 +705,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, * on error (e.g. currently no memory region using memslot as a KVM * memory slot ID). */ -static struct userspace_mem_region * +struct userspace_mem_region * memslot2region(struct kvm_vm *vm, uint32_t memslot) { struct userspace_mem_region *region; diff --git a/tools/testing/selftests/kvm/lib/kvm_util_internal.h b/tools/testing/selftests/kvm/lib/kvm_util_internal.h index f36262e0f655..ac50c42750cf 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util_internal.h +++ b/tools/testing/selftests/kvm/lib/kvm_util_internal.h @@ -68,4 +68,7 @@ void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent); void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent); +struct userspace_mem_region * +memslot2region(struct kvm_vm *vm, uint32_t memslot); + #endif /* SELFTEST_KVM_UTIL_INTERNAL_H */ diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index c53dbc6bc568..6698cb741e10 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -1085,7 +1085,7 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid) for (i = 0; i < nmsrs; i++) state->msrs.entries[i].index = list->indices[i]; r = ioctl(vcpu->fd, KVM_GET_MSRS, &state->msrs); - TEST_ASSERT(r == nmsrs, "Unexpected result from KVM_GET_MSRS, r: %i (failed at %x)", + TEST_ASSERT(r == nmsrs, "Unexpected result from KVM_GET_MSRS, r: %i (failed MSR was 0x%x)", r, r == nmsrs ? -1 : list->indices[r]); r = ioctl(vcpu->fd, KVM_GET_DEBUGREGS, &state->debugregs); diff --git a/tools/testing/selftests/kvm/lib/x86_64/vmx.c b/tools/testing/selftests/kvm/lib/x86_64/vmx.c index 9cef0455b819..fab8f6b0bf52 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86_64/vmx.c @@ -7,11 +7,39 @@ #include "test_util.h" #include "kvm_util.h" +#include "../kvm_util_internal.h" #include "processor.h" #include "vmx.h" +#define PAGE_SHIFT_4K 12 + +#define KVM_EPT_PAGE_TABLE_MIN_PADDR 0x1c0000 + bool enable_evmcs; +struct eptPageTableEntry { + uint64_t readable:1; + uint64_t writable:1; + uint64_t executable:1; + uint64_t memory_type:3; + uint64_t ignore_pat:1; + uint64_t page_size:1; + uint64_t accessed:1; + uint64_t dirty:1; + uint64_t ignored_11_10:2; + uint64_t address:40; + uint64_t ignored_62_52:11; + uint64_t suppress_ve:1; +}; + +struct eptPageTablePointer { + uint64_t memory_type:3; + uint64_t page_walk_length:3; + uint64_t ad_enabled:1; + uint64_t reserved_11_07:5; + uint64_t address:40; + uint64_t reserved_63_52:12; +}; int vcpu_enable_evmcs(struct kvm_vm *vm, int vcpu_id) { uint16_t evmcs_ver; @@ -174,15 +202,35 @@ bool load_vmcs(struct vmx_pages *vmx) */ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) { + uint32_t sec_exec_ctl = 0; + vmwrite(VIRTUAL_PROCESSOR_ID, 0); vmwrite(POSTED_INTR_NV, 0); vmwrite(PIN_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS)); - if (!vmwrite(SECONDARY_VM_EXEC_CONTROL, 0)) + + if (vmx->eptp_gpa) { + uint64_t ept_paddr; + struct eptPageTablePointer eptp = { + .memory_type = VMX_BASIC_MEM_TYPE_WB, + .page_walk_length = 3, /* + 1 */ + .ad_enabled = !!(rdmsr(MSR_IA32_VMX_EPT_VPID_CAP) & VMX_EPT_VPID_CAP_AD_BITS), + .address = vmx->eptp_gpa >> PAGE_SHIFT_4K, + }; + + memcpy(&ept_paddr, &eptp, sizeof(ept_paddr)); + vmwrite(EPT_POINTER, ept_paddr); + sec_exec_ctl |= SECONDARY_EXEC_ENABLE_EPT; + } + + if (!vmwrite(SECONDARY_VM_EXEC_CONTROL, sec_exec_ctl)) vmwrite(CPU_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS); - else + else { vmwrite(CPU_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS)); + GUEST_ASSERT(!sec_exec_ctl); + } + vmwrite(EXCEPTION_BITMAP, 0); vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0); vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, -1); /* Never match */ @@ -327,3 +375,152 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp) init_vmcs_host_state(); init_vmcs_guest_state(guest_rip, guest_rsp); } + +void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, uint32_t eptp_memslot) +{ + uint16_t index[4]; + struct eptPageTableEntry *pml4e; + + TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " + "unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + TEST_ASSERT((nested_paddr % vm->page_size) == 0, + "Nested physical address not on page boundary,\n" + " nested_paddr: 0x%lx vm->page_size: 0x%x", + nested_paddr, vm->page_size); + TEST_ASSERT((nested_paddr >> vm->page_shift) <= vm->max_gfn, + "Physical address beyond beyond maximum supported,\n" + " nested_paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + paddr, vm->max_gfn, vm->page_size); + TEST_ASSERT((paddr % vm->page_size) == 0, + "Physical address not on page boundary,\n" + " paddr: 0x%lx vm->page_size: 0x%x", + paddr, vm->page_size); + TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn, + "Physical address beyond beyond maximum supported,\n" + " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + paddr, vm->max_gfn, vm->page_size); + + index[0] = (nested_paddr >> 12) & 0x1ffu; + index[1] = (nested_paddr >> 21) & 0x1ffu; + index[2] = (nested_paddr >> 30) & 0x1ffu; + index[3] = (nested_paddr >> 39) & 0x1ffu; + + /* Allocate page directory pointer table if not present. */ + pml4e = vmx->eptp_hva; + if (!pml4e[index[3]].readable) { + pml4e[index[3]].address = vm_phy_page_alloc(vm, + KVM_EPT_PAGE_TABLE_MIN_PADDR, eptp_memslot) + >> vm->page_shift; + pml4e[index[3]].writable = true; + pml4e[index[3]].readable = true; + pml4e[index[3]].executable = true; + } + + /* Allocate page directory table if not present. */ + struct eptPageTableEntry *pdpe; + pdpe = addr_gpa2hva(vm, pml4e[index[3]].address * vm->page_size); + if (!pdpe[index[2]].readable) { + pdpe[index[2]].address = vm_phy_page_alloc(vm, + KVM_EPT_PAGE_TABLE_MIN_PADDR, eptp_memslot) + >> vm->page_shift; + pdpe[index[2]].writable = true; + pdpe[index[2]].readable = true; + pdpe[index[2]].executable = true; + } + + /* Allocate page table if not present. */ + struct eptPageTableEntry *pde; + pde = addr_gpa2hva(vm, pdpe[index[2]].address * vm->page_size); + if (!pde[index[1]].readable) { + pde[index[1]].address = vm_phy_page_alloc(vm, + KVM_EPT_PAGE_TABLE_MIN_PADDR, eptp_memslot) + >> vm->page_shift; + pde[index[1]].writable = true; + pde[index[1]].readable = true; + pde[index[1]].executable = true; + } + + /* Fill in page table entry. */ + struct eptPageTableEntry *pte; + pte = addr_gpa2hva(vm, pde[index[1]].address * vm->page_size); + pte[index[0]].address = paddr >> vm->page_shift; + pte[index[0]].writable = true; + pte[index[0]].readable = true; + pte[index[0]].executable = true; + + /* + * For now mark these as accessed and dirty because the only + * testcase we have needs that. Can be reconsidered later. + */ + pte[index[0]].accessed = true; + pte[index[0]].dirty = true; +} + +/* + * Map a range of EPT guest physical addresses to the VM's physical address + * + * Input Args: + * vm - Virtual Machine + * nested_paddr - Nested guest physical address to map + * paddr - VM Physical Address + * size - The size of the range to map + * eptp_memslot - Memory region slot for new virtual translation tables + * + * Output Args: None + * + * Return: None + * + * Within the VM given by vm, creates a nested guest translation for the + * page range starting at nested_paddr to the page range starting at paddr. + */ +void nested_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, uint64_t size, + uint32_t eptp_memslot) +{ + size_t page_size = vm->page_size; + size_t npages = size / page_size; + + TEST_ASSERT(nested_paddr + size > nested_paddr, "Vaddr overflow"); + TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); + + while (npages--) { + nested_pg_map(vmx, vm, nested_paddr, paddr, eptp_memslot); + nested_paddr += page_size; + paddr += page_size; + } +} + +/* Prepare an identity extended page table that maps all the + * physical pages in VM. + */ +void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm, + uint32_t memslot, uint32_t eptp_memslot) +{ + sparsebit_idx_t i, last; + struct userspace_mem_region *region = + memslot2region(vm, memslot); + + i = (region->region.guest_phys_addr >> vm->page_shift) - 1; + last = i + (region->region.memory_size >> vm->page_shift); + for (;;) { + i = sparsebit_next_clear(region->unused_phy_pages, i); + if (i > last) + break; + + nested_map(vmx, vm, + (uint64_t)i << vm->page_shift, + (uint64_t)i << vm->page_shift, + 1 << vm->page_shift, + eptp_memslot); + } +} + +void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm, + uint32_t eptp_memslot) +{ + vmx->eptp = (void *)vm_vaddr_alloc(vm, getpagesize(), 0x10000, 0, 0); + vmx->eptp_hva = addr_gva2hva(vm, (uintptr_t)vmx->eptp); + vmx->eptp_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->eptp); +} diff --git a/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c b/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c new file mode 100644 index 000000000000..0bca1cfe2c1e --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KVM dirty page logging test + * + * Copyright (C) 2018, Red Hat, Inc. + */ + +#define _GNU_SOURCE /* for program_invocation_name */ + +#include <stdio.h> +#include <stdlib.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "vmx.h" + +#define VCPU_ID 1 + +/* The memory slot index to track dirty pages */ +#define TEST_MEM_SLOT_INDEX 1 +#define TEST_MEM_SIZE 3 + +/* L1 guest test virtual memory offset */ +#define GUEST_TEST_MEM 0xc0000000 + +/* L2 guest test virtual memory offset */ +#define NESTED_TEST_MEM1 0xc0001000 +#define NESTED_TEST_MEM2 0xc0002000 + +static void l2_guest_code(void) +{ + *(volatile uint64_t *)NESTED_TEST_MEM1; + *(volatile uint64_t *)NESTED_TEST_MEM1 = 1; + GUEST_SYNC(true); + GUEST_SYNC(false); + + *(volatile uint64_t *)NESTED_TEST_MEM2 = 1; + GUEST_SYNC(true); + *(volatile uint64_t *)NESTED_TEST_MEM2 = 1; + GUEST_SYNC(true); + GUEST_SYNC(false); + + /* Exit to L1 and never come back. */ + vmcall(); +} + +void l1_guest_code(struct vmx_pages *vmx) +{ +#define L2_GUEST_STACK_SIZE 64 + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + + GUEST_ASSERT(vmx->vmcs_gpa); + GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + GUEST_ASSERT(load_vmcs(vmx)); + + prepare_vmcs(vmx, l2_guest_code, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + GUEST_SYNC(false); + GUEST_ASSERT(!vmlaunch()); + GUEST_SYNC(false); + GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + vm_vaddr_t vmx_pages_gva = 0; + struct vmx_pages *vmx; + unsigned long *bmap; + uint64_t *host_test_mem; + + struct kvm_vm *vm; + struct kvm_run *run; + struct ucall uc; + bool done = false; + + /* Create VM */ + vm = vm_create_default(VCPU_ID, 0, l1_guest_code); + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + vmx = vcpu_alloc_vmx(vm, &vmx_pages_gva); + vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + run = vcpu_state(vm, VCPU_ID); + + /* Add an extra memory slot for testing dirty logging */ + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, + GUEST_TEST_MEM, + TEST_MEM_SLOT_INDEX, + TEST_MEM_SIZE, + KVM_MEM_LOG_DIRTY_PAGES); + + /* + * Add an identity map for GVA range [0xc0000000, 0xc0002000). This + * affects both L1 and L2. However... + */ + virt_map(vm, GUEST_TEST_MEM, GUEST_TEST_MEM, + TEST_MEM_SIZE * 4096, 0); + + /* + * ... pages in the L2 GPA range [0xc0001000, 0xc0003000) will map to + * 0xc0000000. + * + * Note that prepare_eptp should be called only L1's GPA map is done, + * meaning after the last call to virt_map. + */ + prepare_eptp(vmx, vm, 0); + nested_map_memslot(vmx, vm, 0, 0); + nested_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, 4096, 0); + nested_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, 4096, 0); + + bmap = bitmap_alloc(TEST_MEM_SIZE); + host_test_mem = addr_gpa2hva(vm, GUEST_TEST_MEM); + + while (!done) { + memset(host_test_mem, 0xaa, TEST_MEM_SIZE * 4096); + _vcpu_run(vm, VCPU_ID); + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Unexpected exit reason: %u (%s),\n", + run->exit_reason, + exit_reason_str(run->exit_reason)); + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_ABORT: + TEST_ASSERT(false, "%s at %s:%d", (const char *)uc.args[0], + __FILE__, uc.args[1]); + /* NOT REACHED */ + case UCALL_SYNC: + /* + * The nested guest wrote at offset 0x1000 in the memslot, but the + * dirty bitmap must be filled in according to L1 GPA, not L2. + */ + kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap); + if (uc.args[1]) { + TEST_ASSERT(test_bit(0, bmap), "Page 0 incorrectly reported clean\n"); + TEST_ASSERT(host_test_mem[0] == 1, "Page 0 not written by guest\n"); + } else { + TEST_ASSERT(!test_bit(0, bmap), "Page 0 incorrectly reported dirty\n"); + TEST_ASSERT(host_test_mem[0] == 0xaaaaaaaaaaaaaaaaULL, "Page 0 written by guest\n"); + } + + TEST_ASSERT(!test_bit(1, bmap), "Page 1 incorrectly reported dirty\n"); + TEST_ASSERT(host_test_mem[4096 / 8] == 0xaaaaaaaaaaaaaaaaULL, "Page 1 written by guest\n"); + TEST_ASSERT(!test_bit(2, bmap), "Page 2 incorrectly reported dirty\n"); + TEST_ASSERT(host_test_mem[8192 / 8] == 0xaaaaaaaaaaaaaaaaULL, "Page 2 written by guest\n"); + break; + case UCALL_DONE: + done = true; + break; + default: + TEST_ASSERT(false, "Unknown ucall 0x%x.", uc.cmd); + } + } +} diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index c7cced739c34..8aefd81fbc86 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -21,3 +21,4 @@ ipv6_flowlabel ipv6_flowlabel_mgr so_txtime tcp_fastopen_backup_key +nettest diff --git a/tools/testing/selftests/net/udpgso.c b/tools/testing/selftests/net/udpgso.c index b8265ee9923f..614b31aad168 100644 --- a/tools/testing/selftests/net/udpgso.c +++ b/tools/testing/selftests/net/udpgso.c @@ -89,12 +89,9 @@ struct testcase testcases_v4[] = { .tfail = true, }, { - /* send a single MSS: will fail with GSO, because the segment - * logic in udp4_ufo_fragment demands a gso skb to be > MTU - */ + /* send a single MSS: will fall back to no GSO */ .tlen = CONST_MSS_V4, .gso_len = CONST_MSS_V4, - .tfail = true, .r_num_mss = 1, }, { @@ -139,10 +136,9 @@ struct testcase testcases_v4[] = { .tfail = true, }, { - /* send a single 1B MSS: will fail, see single MSS above */ + /* send a single 1B MSS: will fall back to no GSO */ .tlen = 1, .gso_len = 1, - .tfail = true, .r_num_mss = 1, }, { @@ -196,12 +192,9 @@ struct testcase testcases_v6[] = { .tfail = true, }, { - /* send a single MSS: will fail with GSO, because the segment - * logic in udp4_ufo_fragment demands a gso skb to be > MTU - */ + /* send a single MSS: will fall back to no GSO */ .tlen = CONST_MSS_V6, .gso_len = CONST_MSS_V6, - .tfail = true, .r_num_mss = 1, }, { @@ -246,10 +239,9 @@ struct testcase testcases_v6[] = { .tfail = true, }, { - /* send a single 1B MSS: will fail, see single MSS above */ + /* send a single 1B MSS: will fall back to no GSO */ .tlen = 1, .gso_len = 1, - .tfail = true, .r_num_mss = 1, }, { diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile index 464c9b76148f..7550f08822a3 100644 --- a/tools/testing/selftests/pidfd/Makefile +++ b/tools/testing/selftests/pidfd/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -CFLAGS += -g -I../../../../usr/include/ -lpthread +CFLAGS += -g -I../../../../usr/include/ -pthread TEST_GEN_PROGS := pidfd_test pidfd_open_test pidfd_poll_test pidfd_wait diff --git a/tools/testing/selftests/powerpc/mm/tlbie_test.c b/tools/testing/selftests/powerpc/mm/tlbie_test.c index 9868a5ddd847..f85a0938ab25 100644 --- a/tools/testing/selftests/powerpc/mm/tlbie_test.c +++ b/tools/testing/selftests/powerpc/mm/tlbie_test.c @@ -636,7 +636,7 @@ int main(int argc, char *argv[]) nrthreads = strtoul(optarg, NULL, 10); break; case 'l': - strncpy(logdir, optarg, LOGDIR_NAME_SIZE); + strncpy(logdir, optarg, LOGDIR_NAME_SIZE - 1); break; case 't': run_time = strtoul(optarg, NULL, 10); diff --git a/tools/testing/selftests/rtc/settings b/tools/testing/selftests/rtc/settings new file mode 100644 index 000000000000..ba4d85f74cd6 --- /dev/null +++ b/tools/testing/selftests/rtc/settings @@ -0,0 +1 @@ +timeout=90 diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c index afff120c7be6..f45e510500c0 100644 --- a/tools/testing/selftests/watchdog/watchdog-test.c +++ b/tools/testing/selftests/watchdog/watchdog-test.c @@ -19,7 +19,7 @@ int fd; const char v = 'V'; -static const char sopts[] = "bdehp:t:Tn:NLf:"; +static const char sopts[] = "bdehp:t:Tn:NLf:i"; static const struct option lopts[] = { {"bootstatus", no_argument, NULL, 'b'}, {"disable", no_argument, NULL, 'd'}, @@ -32,6 +32,7 @@ static const struct option lopts[] = { {"getpretimeout", no_argument, NULL, 'N'}, {"gettimeleft", no_argument, NULL, 'L'}, {"file", required_argument, NULL, 'f'}, + {"info", no_argument, NULL, 'i'}, {NULL, no_argument, NULL, 0x0} }; @@ -72,6 +73,7 @@ static void usage(char *progname) printf("Usage: %s [options]\n", progname); printf(" -f, --file\t\tOpen watchdog device file\n"); printf("\t\t\tDefault is /dev/watchdog\n"); + printf(" -i, --info\t\tShow watchdog_info\n"); printf(" -b, --bootstatus\tGet last boot status (Watchdog/POR)\n"); printf(" -d, --disable\t\tTurn off the watchdog timer\n"); printf(" -e, --enable\t\tTurn on the watchdog timer\n"); @@ -97,6 +99,7 @@ int main(int argc, char *argv[]) int c; int oneshot = 0; char *file = "/dev/watchdog"; + struct watchdog_info info; setbuf(stdout, NULL); @@ -118,6 +121,16 @@ int main(int argc, char *argv[]) exit(-1); } + /* + * Validate that `file` is a watchdog device + */ + ret = ioctl(fd, WDIOC_GETSUPPORT, &info); + if (ret) { + printf("WDIOC_GETSUPPORT error '%s'\n", strerror(errno)); + close(fd); + exit(ret); + } + optind = 0; while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { @@ -205,6 +218,18 @@ int main(int argc, char *argv[]) case 'f': /* Handled above */ break; + case 'i': + /* + * watchdog_info was obtained as part of file open + * validation. So we just show it here. + */ + oneshot = 1; + printf("watchdog_info:\n"); + printf(" identity:\t\t%s\n", info.identity); + printf(" firmware_version:\t%u\n", + info.firmware_version); + printf(" options:\t\t%08x\n", info.options); + break; default: usage(argv[0]); diff --git a/usr/include/Makefile b/usr/include/Makefile index c9449aaf438d..57b20f7b6729 100644 --- a/usr/include/Makefile +++ b/usr/include/Makefile @@ -29,13 +29,11 @@ header-test- += linux/android/binderfs.h header-test-$(CONFIG_CPU_BIG_ENDIAN) += linux/byteorder/big_endian.h header-test-$(CONFIG_CPU_LITTLE_ENDIAN) += linux/byteorder/little_endian.h header-test- += linux/coda.h -header-test- += linux/coda_psdev.h header-test- += linux/elfcore.h header-test- += linux/errqueue.h header-test- += linux/fsmap.h header-test- += linux/hdlc/ioctl.h header-test- += linux/ivtv.h -header-test- += linux/jffs2.h header-test- += linux/kexec.h header-test- += linux/matroxfb.h header-test- += linux/netfilter_ipv4/ipt_LOG.h @@ -55,20 +53,12 @@ header-test- += linux/v4l2-mediabus.h header-test- += linux/v4l2-subdev.h header-test- += linux/videodev2.h header-test- += linux/vm_sockets.h -header-test- += scsi/scsi_bsg_fc.h -header-test- += scsi/scsi_netlink.h -header-test- += scsi/scsi_netlink_fc.h header-test- += sound/asequencer.h header-test- += sound/asoc.h header-test- += sound/asound.h header-test- += sound/compress_offload.h header-test- += sound/emu10k1.h header-test- += sound/sfnt_info.h -header-test- += sound/sof/eq.h -header-test- += sound/sof/fw.h -header-test- += sound/sof/header.h -header-test- += sound/sof/manifest.h -header-test- += sound/sof/trace.h header-test- += xen/evtchn.h header-test- += xen/gntdev.h header-test- += xen/privcmd.h diff --git a/virt/kvm/arm/vgic/trace.h b/virt/kvm/arm/vgic/trace.h index 55fed77a9f73..4fd4f6db181b 100644 --- a/virt/kvm/arm/vgic/trace.h +++ b/virt/kvm/arm/vgic/trace.h @@ -30,7 +30,7 @@ TRACE_EVENT(vgic_update_irq_pending, #endif /* _TRACE_VGIC_H */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../../virt/kvm/arm/vgic +#define TRACE_INCLUDE_PATH ../../virt/kvm/arm/vgic #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e6de3159e682..fd68fbe0a75d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -617,8 +617,9 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) stat_data->kvm = kvm; stat_data->offset = p->offset; + stat_data->mode = p->mode ? p->mode : 0644; kvm->debugfs_stat_data[p - debugfs_entries] = stat_data; - debugfs_create_file(p->name, 0644, kvm->debugfs_dentry, + debugfs_create_file(p->name, stat_data->mode, kvm->debugfs_dentry, stat_data, stat_fops_per_vm[p->kind]); } return 0; @@ -3929,7 +3930,9 @@ static int kvm_debugfs_open(struct inode *inode, struct file *file, if (!refcount_inc_not_zero(&stat_data->kvm->users_count)) return -ENOENT; - if (simple_attr_open(inode, file, get, set, fmt)) { + if (simple_attr_open(inode, file, get, + stat_data->mode & S_IWUGO ? set : NULL, + fmt)) { kvm_put_kvm(stat_data->kvm); return -ENOMEM; } @@ -4177,7 +4180,8 @@ static void kvm_init_debug(void) kvm_debugfs_num_entries = 0; for (p = debugfs_entries; p->name; ++p, kvm_debugfs_num_entries++) { - debugfs_create_file(p->name, 0644, kvm_debugfs_dir, + int mode = p->mode ? p->mode : 0644; + debugfs_create_file(p->name, mode, kvm_debugfs_dir, (void *)(long)p->offset, stat_fops[p->kind]); } |