Merge branch 'next' into for-linus
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 27 May 2022 22:48:45 +0000 (15:48 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 27 May 2022 22:48:45 +0000 (15:48 -0700)
Prepare input updates for 5.19 merge window.

94 files changed:
Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml
Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/google,cros-ec-keyb.yaml
MAINTAINERS
Makefile
arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
arch/arm64/include/asm/vectors.h
arch/arm64/kernel/cpu_errata.c
arch/x86/kvm/emulate.c
block/blk-core.c
drivers/acpi/scan.c
drivers/atm/eni.c
drivers/counter/counter-sysfs.c
drivers/crypto/qcom-rng.c
drivers/firmware/efi/apple-properties.c
drivers/firmware/efi/efi.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/imx/parallel-display.c
drivers/gpu/drm/mgag200/mgag200_pll.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/panel-simple.c
drivers/input/input.c
drivers/input/joystick/Kconfig
drivers/input/joystick/Makefile
drivers/input/joystick/sensehat-joystick.c [new file with mode: 0644]
drivers/input/keyboard/bcm-keypad.c
drivers/input/keyboard/clps711x-keypad.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/keyboard/ep93xx_keypad.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/mt6779-keypad.c
drivers/input/keyboard/sun4i-lradc-keys.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/iqs7222.c [new file with mode: 0644]
drivers/input/misc/pm8941-pwrkey.c
drivers/input/misc/sparcspkr.c
drivers/input/mouse/cypress_ps2.c
drivers/input/mouse/psmouse-smbus.c
drivers/input/mouse/vmmouse.c
drivers/input/rmi4/rmi_f54.c
drivers/input/tablet/aiptek.c
drivers/input/touchscreen/stmfts.c
drivers/net/dsa/microchip/ksz8795_spi.c
drivers/net/dsa/microchip/ksz9477_spi.c
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/mscc/ocelot_flower.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/mdio/mdio-mscc-miim.c
drivers/net/phy/marvell.c
drivers/net/phy/mscc/mscc_main.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/nvme/target/configfs.c
drivers/nvme/target/core.c
drivers/scsi/fnic/fnic_scsi.c
drivers/scsi/mpt3sas/mpt3sas_base.c
drivers/usb/class/usbtmc.c
drivers/usb/gadget/function/rndis.c
drivers/usb/gadget/udc/core.c
drivers/usb/musb/omap2430.c
drivers/vhost/vhost.c
drivers/vhost/vsock.c
fs/cifs/connect.c
fs/ocfs2/super.c
include/linux/if_arp.h
include/net/af_vsock.h
include/net/netfilter/nf_conntrack.h
kernel/configs/debug.config
mm/swap_state.c
net/dsa/dsa2.c
net/ipv6/esp6.c
net/ipv6/ip6_output.c
net/key/af_key.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_tables_api.c
net/packet/af_packet.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/virtio_transport.c
net/vmw_vsock/vmci_transport.c
tools/perf/arch/x86/util/evlist.c
tools/perf/util/evlist.c
tools/perf/util/symbol.c
tools/testing/selftests/netfilter/nft_nat.sh
tools/testing/selftests/vm/Makefile

index d74f200..3399fc2 100644 (file)
@@ -18,10 +18,20 @@ properties:
       - items:
           - const: allwinner,sun50i-a64-lradc
           - const: allwinner,sun8i-a83t-r-lradc
+      - const: allwinner,sun50i-r329-lradc
+      - items:
+          - const: allwinner,sun20i-d1-lradc
+          - const: allwinner,sun50i-r329-lradc
 
   reg:
     maxItems: 1
 
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
   interrupts:
     maxItems: 1
 
@@ -68,6 +78,18 @@ required:
   - interrupts
   - vref-supply
 
+if:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - allwinner,sun50i-r329-lradc
+
+then:
+  required:
+    - clocks
+    - resets
+
 additionalProperties: false
 
 examples:
diff --git a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml
new file mode 100644 (file)
index 0000000..a3a1e5a
--- /dev/null
@@ -0,0 +1,960 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/azoteq,iqs7222.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS7222A/B/C Capacitive Touch Controller
+
+maintainers:
+  - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+  The Azoteq IQS7222A, IQS7222B and IQS7222C are multichannel capacitive touch
+  controllers that feature additional sensing capabilities.
+
+  Link to datasheets: https://www.azoteq.com/
+
+properties:
+  compatible:
+    enum:
+      - azoteq,iqs7222a
+      - azoteq,iqs7222b
+      - azoteq,iqs7222c
+
+  reg:
+    maxItems: 1
+
+  irq-gpios:
+    maxItems: 1
+    description:
+      Specifies the GPIO connected to the device's active-low RDY output.
+
+  reset-gpios:
+    maxItems: 1
+    description:
+      Specifies the GPIO connected to the device's active-low MCLR input. The
+      device is temporarily held in hardware reset prior to initialization if
+      this property is present.
+
+  azoteq,rf-filt-enable:
+    type: boolean
+    description: Enables the device's internal RF filter.
+
+  azoteq,max-counts:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3]
+    description: |
+      Specifies the maximum number of conversion periods (counts) that can be
+      reported as follows:
+      0: 1023
+      1: 2047
+      2: 4095
+      3: 16384
+
+  azoteq,auto-mode:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3]
+    description: |
+      Specifies the number of conversions to occur before an interrupt is
+      generated as follows:
+      0: 4
+      1: 8
+      2: 16
+      3: 32
+
+  azoteq,ati-frac-div-fine:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 31
+    description: Specifies the preloaded ATI fine fractional divider.
+
+  azoteq,ati-frac-div-coarse:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 31
+    description: Specifies the preloaded ATI coarse fractional divider.
+
+  azoteq,ati-comp-select:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 1023
+    description: Specifies the preloaded ATI compensation selection.
+
+  azoteq,lta-beta-lp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter damping factor to be applied during
+      low-power mode.
+
+  azoteq,lta-beta-np:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter damping factor to be applied during
+      normal-power mode.
+
+  azoteq,counts-beta-lp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the counts filter damping factor to be applied during low-power
+      mode.
+
+  azoteq,counts-beta-np:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the counts filter damping factor to be applied during normal-
+      power mode.
+
+  azoteq,lta-fast-beta-lp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter fast damping factor to be applied
+      during low-power mode.
+
+  azoteq,lta-fast-beta-np:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter fast damping factor to be applied
+      during normal-power mode.
+
+  azoteq,timeout-ati-ms:
+    multipleOf: 500
+    minimum: 0
+    maximum: 32767500
+    description:
+      Specifies the delay (in ms) before ATI is retried following an ATI error.
+
+  azoteq,rate-ati-ms:
+    minimum: 0
+    maximum: 65535
+    description: Specifies the rate (in ms) at which ATI status is evaluated.
+
+  azoteq,timeout-np-ms:
+    minimum: 0
+    maximum: 65535
+    description:
+      Specifies the length of time (in ms) to wait for an event before moving
+      from normal-power mode to low-power mode.
+
+  azoteq,rate-np-ms:
+    minimum: 0
+    maximum: 3000
+    description: Specifies the report rate (in ms) during normal-power mode.
+
+  azoteq,timeout-lp-ms:
+    minimum: 0
+    maximum: 65535
+    description:
+      Specifies the length of time (in ms) to wait for an event before moving
+      from low-power mode to ultra-low-power mode.
+
+  azoteq,rate-lp-ms:
+    minimum: 0
+    maximum: 3000
+    description: Specifies the report rate (in ms) during low-power mode.
+
+  azoteq,timeout-ulp-ms:
+    minimum: 0
+    maximum: 65535
+    description:
+      Specifies the rate (in ms) at which channels not regularly sampled during
+      ultra-low-power mode are updated.
+
+  azoteq,rate-ulp-ms:
+    minimum: 0
+    maximum: 3000
+    description: Specifies the report rate (in ms) during ultra-low-power mode.
+
+patternProperties:
+  "^cycle-[0-9]$":
+    type: object
+    description: Represents a conversion cycle serving two sensing channels.
+
+    properties:
+      azoteq,conv-period:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the cycle's conversion period.
+
+      azoteq,conv-frac:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the cycle's conversion frequency fraction.
+
+      azoteq,tx-enable:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 9
+        items:
+          minimum: 0
+          maximum: 8
+        description: Specifies the CTx pin(s) associated with the cycle.
+
+      azoteq,rx-float-inactive:
+        type: boolean
+        description: Floats any inactive CRx pins instead of grounding them.
+
+      azoteq,dead-time-enable:
+        type: boolean
+        description:
+          Increases the denominator of the conversion frequency formula by one.
+
+      azoteq,tx-freq-fosc:
+        type: boolean
+        description:
+          Fixes the conversion frequency to that of the device's core clock.
+
+      azoteq,vbias-enable:
+        type: boolean
+        description: Enables the bias voltage for use during inductive sensing.
+
+      azoteq,sense-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the cycle's sensing mode as follows:
+          0: None
+          1: Self capacitive
+          2: Mutual capacitive
+          3: Inductive
+
+          Note that in the case of IQS7222A, cycles 5 and 6 are restricted to
+          Hall-effect sensing.
+
+      azoteq,iref-enable:
+        type: boolean
+        description:
+          Enables the current reference for use during various sensing modes.
+
+      azoteq,iref-level:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the cycle's current reference level.
+
+      azoteq,iref-trim:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the cycle's current reference trim.
+
+    dependencies:
+      azoteq,iref-level: ["azoteq,iref-enable"]
+      azoteq,iref-trim: ["azoteq,iref-enable"]
+
+    additionalProperties: false
+
+  "^channel-([0-9]|1[0-9])$":
+    type: object
+    description:
+      Represents a single sensing channel. A channel is active if defined and
+      inactive otherwise.
+
+      Note that in the case of IQS7222A, channels 10 and 11 are restricted to
+      Hall-effect sensing with events reported on channel 10 only.
+
+    properties:
+      azoteq,ulp-allow:
+        type: boolean
+        description:
+          Permits the device to enter ultra-low-power mode while the channel
+          lies in a state of touch or proximity.
+
+      azoteq,ref-select:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 9
+        description: Specifies a separate reference channel to be followed.
+
+      azoteq,ref-weight:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 65535
+        description: Specifies the relative weight of the reference channel.
+
+      azoteq,use-prox:
+        type: boolean
+        description:
+          Activates the reference channel in response to proximity events
+          instead of touch events.
+
+      azoteq,ati-band:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the channel's ATI band as a fraction of its ATI target as
+          follows:
+          0: 1/16
+          1: 1/8
+          2: 1/4
+          3: 1/2
+
+      azoteq,global-halt:
+        type: boolean
+        description:
+          Specifies that the channel's long-term average is to freeze if any
+          other participating channel lies in a proximity or touch state.
+
+      azoteq,invert-enable:
+        type: boolean
+        description:
+          Inverts the polarity of the states reported for proximity and touch
+          events relative to their respective thresholds.
+
+      azoteq,dual-direction:
+        type: boolean
+        description:
+          Specifies that the channel's long-term average is to freeze in the
+          presence of either increasing or decreasing counts, thereby permit-
+          ting events to be reported in either direction.
+
+      azoteq,rx-enable:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 4
+        items:
+          minimum: 0
+          maximum: 7
+        description: Specifies the CRx pin(s) associated with the channel.
+
+      azoteq,samp-cap-double:
+        type: boolean
+        description: Doubles the sampling capacitance from 40 pF to 80 pF.
+
+      azoteq,vref-half:
+        type: boolean
+        description: Halves the discharge threshold from 1.0 V to 0.5 V.
+
+      azoteq,proj-bias:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the bias current applied during mutual (projected)
+          capacitive sensing as follows:
+          0: 2 uA
+          1: 5 uA
+          2: 7 uA
+          3: 10 uA
+
+      azoteq,ati-target:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 8
+        minimum: 0
+        maximum: 2040
+        description: Specifies the channel's ATI target.
+
+      azoteq,ati-base:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 16
+        minimum: 0
+        maximum: 496
+        description: Specifies the channel's ATI base.
+
+      azoteq,ati-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3, 4, 5]
+        description: |
+          Specifies the channel's ATI mode as follows:
+          0: Disabled
+          1: Compensation
+          2: Compensation divider
+          3: Fine fractional divider
+          4: Coarse fractional divider
+          5: Full
+
+      azoteq,ati-frac-div-fine:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 31
+        description: Specifies the channel's ATI fine fractional divider.
+
+      azoteq,ati-frac-mult-coarse:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the channel's ATI coarse fractional multiplier.
+
+      azoteq,ati-frac-div-coarse:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 31
+        description: Specifies the channel's ATI coarse fractional divider.
+
+      azoteq,ati-comp-div:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 31
+        description: Specifies the channel's ATI compensation divider.
+
+      azoteq,ati-comp-select:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 1023
+        description: Specifies the channel's ATI compensation selection.
+
+      azoteq,debounce-enter:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the channel's debounce entrance factor.
+
+      azoteq,debounce-exit:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the channel's debounce exit factor.
+
+    patternProperties:
+      "^event-(prox|touch)$":
+        type: object
+        description:
+          Represents a proximity or touch event reported by the channel.
+
+        properties:
+          azoteq,gpio-select:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            minItems: 1
+            maxItems: 3
+            items:
+              minimum: 0
+              maximum: 2
+            description: |
+              Specifies one or more GPIO mapped to the event as follows:
+              0: GPIO0
+              1: GPIO3 (IQS7222C only)
+              2: GPIO4 (IQS7222C only)
+
+              Note that although multiple events can be mapped to a single
+              GPIO, they must all be of the same type (proximity, touch or
+              slider gesture).
+
+          azoteq,thresh:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Specifies the threshold for the event. Valid entries range from
+              0-127 and 0-255 for proximity and touch events, respectively.
+
+          azoteq,hyst:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            minimum: 0
+            maximum: 255
+            description:
+              Specifies the hysteresis for the event (touch events only).
+
+          azoteq,timeout-press-ms:
+            multipleOf: 500
+            minimum: 0
+            maximum: 127500
+            description:
+              Specifies the length of time (in ms) to wait before automatically
+              releasing a press event. Specify zero to allow the press state to
+              persist indefinitely.
+
+              The IQS7222B does not feature channel-specific timeouts; the time-
+              out specified for any one channel applies to all channels.
+
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Numeric key or switch code associated with the event. Specify
+              KEY_RESERVED (0) to opt out of event reporting.
+
+          linux,input-type:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            enum: [1, 5]
+            default: 1
+            description:
+              Specifies whether the event is to be interpreted as a key (1)
+              or a switch (5).
+
+        required:
+          - linux,code
+
+        additionalProperties: false
+
+    dependencies:
+      azoteq,ref-weight: ["azoteq,ref-select"]
+      azoteq,use-prox: ["azoteq,ref-select"]
+
+    additionalProperties: false
+
+  "^slider-[0-1]$":
+    type: object
+    description: Represents a slider comprising three or four channels.
+
+    properties:
+      azoteq,channel-select:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 3
+        maxItems: 4
+        items:
+          minimum: 0
+          maximum: 9
+        description:
+          Specifies the order of the channels that participate in the slider.
+
+      azoteq,slider-size:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 65535
+        description:
+          Specifies the slider's one-dimensional resolution, equal to the
+          maximum coordinate plus one.
+
+      azoteq,lower-cal:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the slider's lower starting point.
+
+      azoteq,upper-cal:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the slider's upper starting point.
+
+      azoteq,top-speed:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 65535
+        description:
+          Specifies the speed of movement after which coordinate filtering is
+          no longer applied.
+
+      azoteq,bottom-speed:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 4
+        minimum: 0
+        maximum: 1020
+        description:
+          Specifies the speed of movement after which coordinate filtering is
+          linearly reduced.
+
+      azoteq,bottom-beta:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 7
+        description:
+          Specifies the coordinate filter damping factor to be applied
+          while the speed of movement is below that which is specified
+          by azoteq,bottom-speed.
+
+      azoteq,static-beta:
+        type: boolean
+        description:
+          Applies the coordinate filter damping factor specified by
+          azoteq,bottom-beta regardless of the speed of movement.
+
+      azoteq,use-prox:
+        type: boolean
+        description:
+          Directs the slider to respond to the proximity states of the selected
+          channels instead of their corresponding touch states. Note the slider
+          cannot report granular coordinates during a state of proximity.
+
+      linux,axis:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Specifies the absolute axis to which coordinates are mapped. Specify
+          ABS_WHEEL to operate the slider as a wheel (IQS7222C only).
+
+    patternProperties:
+      "^event-(press|tap|(swipe|flick)-(pos|neg))$":
+        type: object
+        description:
+          Represents a press or gesture (IQS7222A only) event reported by
+          the slider.
+
+        properties:
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: Numeric key code associated with the event.
+
+          azoteq,gesture-max-ms:
+            multipleOf: 4
+            minimum: 0
+            maximum: 1020
+            description:
+              Specifies the length of time (in ms) within which a tap, swipe
+              or flick gesture must be completed in order to be acknowledged
+              by the device. The number specified for any one swipe or flick
+              gesture applies to all remaining swipe or flick gestures.
+
+          azoteq,gesture-min-ms:
+            multipleOf: 4
+            minimum: 0
+            maximum: 124
+            description:
+              Specifies the length of time (in ms) for which a tap gesture must
+              be held in order to be acknowledged by the device.
+
+          azoteq,gesture-dist:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            multipleOf: 16
+            minimum: 0
+            maximum: 4080
+            description:
+              Specifies the distance across which a swipe or flick gesture must
+              travel in order to be acknowledged by the device. The number spec-
+              ified for any one swipe or flick gesture applies to all remaining
+              swipe or flick gestures.
+
+          azoteq,gpio-select:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            minItems: 1
+            maxItems: 1
+            items:
+              minimum: 0
+              maximum: 0
+            description: |
+              Specifies an individual GPIO mapped to a tap, swipe or flick
+              gesture as follows:
+              0: GPIO0
+              1: GPIO3 (reserved)
+              2: GPIO4 (reserved)
+
+              Note that although multiple events can be mapped to a single
+              GPIO, they must all be of the same type (proximity, touch or
+              slider gesture).
+
+        required:
+          - linux,code
+
+        additionalProperties: false
+
+    required:
+      - azoteq,channel-select
+
+    additionalProperties: false
+
+  "^gpio-[0-2]$":
+    type: object
+    description: |
+      Represents a GPIO mapped to one or more events as follows:
+      gpio-0: GPIO0
+      gpio-1: GPIO3 (IQS7222C only)
+      gpio-2: GPIO4 (IQS7222C only)
+
+    allOf:
+      - $ref: ../pinctrl/pincfg-node.yaml#
+
+    properties:
+      drive-open-drain: true
+
+    additionalProperties: false
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: azoteq,iqs7222b
+
+    then:
+      patternProperties:
+        "^cycle-[0-9]$":
+          properties:
+            azoteq,iref-enable: false
+
+        "^channel-([0-9]|1[0-9])$":
+          properties:
+            azoteq,ref-select: false
+
+          patternProperties:
+            "^event-(prox|touch)$":
+              properties:
+                azoteq,gpio-select: false
+
+        "^slider-[0-1]$": false
+
+        "^gpio-[0-2]$": false
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: azoteq,iqs7222a
+
+    then:
+      patternProperties:
+        "^channel-([0-9]|1[0-9])$":
+          patternProperties:
+            "^event-(prox|touch)$":
+              properties:
+                azoteq,gpio-select:
+                  maxItems: 1
+                  items:
+                    maximum: 0
+
+        "^slider-[0-1]$":
+          properties:
+            azoteq,slider-size:
+              multipleOf: 16
+              maximum: 4080
+
+            azoteq,top-speed:
+              multipleOf: 4
+              maximum: 1020
+
+    else:
+      patternProperties:
+        "^channel-([0-9]|1[0-9])$":
+          properties:
+            azoteq,ulp-allow: false
+
+        "^slider-[0-1]$":
+          patternProperties:
+            "^event-(press|tap|(swipe|flick)-(pos|neg))$":
+              properties:
+                azoteq,gesture-max-ms: false
+
+                azoteq,gesture-min-ms: false
+
+                azoteq,gesture-dist: false
+
+                azoteq,gpio-select: false
+
+required:
+  - compatible
+  - reg
+  - irq-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/input/input.h>
+
+    i2c {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            iqs7222a@44 {
+                    compatible = "azoteq,iqs7222a";
+                    reg = <0x44>;
+                    irq-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+                    azoteq,lta-beta-lp = <7>;
+                    azoteq,lta-beta-np = <8>;
+                    azoteq,counts-beta-lp = <2>;
+                    azoteq,counts-beta-np = <3>;
+                    azoteq,lta-fast-beta-lp = <3>;
+                    azoteq,lta-fast-beta-np = <4>;
+
+                    cycle-0 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <1>, <2>, <4>, <5>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-1 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <5>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-2 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <4>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-3 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <2>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-4 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <1>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-5 {
+                            azoteq,conv-period = <2>;
+                            azoteq,conv-frac = <0>;
+                    };
+
+                    cycle-6 {
+                            azoteq,conv-period = <2>;
+                            azoteq,conv-frac = <0>;
+                    };
+
+                    channel-0 {
+                            azoteq,ulp-allow;
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <800>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-1 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-2 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-3 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-4 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-5 {
+                            azoteq,ulp-allow;
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <800>;
+                            azoteq,ati-base = <144>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-6 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_MUTE>;
+                            };
+                    };
+
+                    channel-7 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_VOLUMEDOWN>;
+                            };
+                    };
+
+                    channel-8 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_VOLUMEUP>;
+                            };
+                    };
+
+                    channel-9 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_POWER>;
+                            };
+                    };
+
+                    channel-10 {
+                            azoteq,ulp-allow;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <112>;
+
+                            event-touch {
+                                    linux,code = <SW_LID>;
+                                    linux,input-type = <EV_SW>;
+                            };
+                    };
+
+                    channel-11 {
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <112>;
+                    };
+
+                    slider-0 {
+                            azoteq,channel-select = <1>, <2>, <3>, <4>;
+                            azoteq,slider-size = <4080>;
+                            azoteq,upper-cal = <50>;
+                            azoteq,lower-cal = <30>;
+                            azoteq,top-speed = <200>;
+                            azoteq,bottom-speed = <1>;
+                            azoteq,bottom-beta = <3>;
+
+                            event-tap {
+                                    linux,code = <KEY_PLAYPAUSE>;
+                                    azoteq,gesture-max-ms = <600>;
+                                    azoteq,gesture-min-ms = <24>;
+                            };
+
+                            event-flick-pos {
+                                    linux,code = <KEY_NEXTSONG>;
+                                    azoteq,gesture-max-ms = <600>;
+                                    azoteq,gesture-dist = <816>;
+                            };
+
+                            event-flick-neg {
+                                    linux,code = <KEY_PREVIOUSSONG>;
+                            };
+                    };
+            };
+    };
+
+...
index e8f137a..c31a021 100644 (file)
@@ -15,14 +15,16 @@ description: |
   Google's ChromeOS EC Keyboard is a simple matrix keyboard
   implemented on a separate EC (Embedded Controller) device. It provides
   a message for reading key scans from the EC. These are then converted
-  into keycodes for processing by the kernel.
-
-allOf:
-  - $ref: "/schemas/input/matrix-keymap.yaml#"
+  into keycodes for processing by the kernel. This device also supports
+  switches/buttons like power and volume buttons.
 
 properties:
   compatible:
-    const: google,cros-ec-keyb
+    oneOf:
+      - description: ChromeOS EC with only buttons/switches
+        const: google,cros-ec-keyb-switches
+      - description: ChromeOS EC with keyboard and possibly buttons/switches
+        const: google,cros-ec-keyb
 
   google,needs-ghost-filter:
     description:
@@ -41,15 +43,31 @@ properties:
       where the lower 16 bits are reserved. This property is specified only
       when the keyboard has a custom design for the top row keys.
 
+dependencies:
+  function-row-phsymap: [ 'linux,keymap' ]
+  google,needs-ghost-filter: [ 'linux,keymap' ]
+
 required:
   - compatible
 
+if:
+  properties:
+    compatible:
+      contains:
+        const: google,cros-ec-keyb
+then:
+  $ref: "/schemas/input/matrix-keymap.yaml#"
+  required:
+    - keypad,num-rows
+    - keypad,num-columns
+    - linux,keymap
+
 unevaluatedProperties: false
 
 examples:
   - |
     #include <dt-bindings/input/input.h>
-    cros-ec-keyb {
+    keyboard-controller {
         compatible = "google,cros-ec-keyb";
         keypad,num-rows = <8>;
         keypad,num-columns = <13>;
@@ -113,3 +131,9 @@ examples:
             /* UP      LEFT    */
             0x070b0067 0x070c0069>;
     };
+  - |
+    /* No matrix keyboard, just buttons/switches */
+    keyboard-controller {
+        compatible = "google,cros-ec-keyb-switches";
+    };
+...
index 5936f2d..91439d6 100644 (file)
@@ -13382,6 +13382,7 @@ F:      net/core/drop_monitor.c
 NETWORKING DRIVERS
 M:     "David S. Miller" <davem@davemloft.net>
 M:     Jakub Kicinski <kuba@kernel.org>
+M:     Paolo Abeni <pabeni@redhat.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 Q:     https://patchwork.kernel.org/project/netdevbpf/list/
@@ -13428,6 +13429,7 @@ F:      tools/testing/selftests/drivers/net/dsa/
 NETWORKING [GENERAL]
 M:     "David S. Miller" <davem@davemloft.net>
 M:     Jakub Kicinski <kuba@kernel.org>
+M:     Paolo Abeni <pabeni@redhat.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 Q:     https://patchwork.kernel.org/project/netdevbpf/list/
index 55a30ca..7214f07 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 5
 PATCHLEVEL = 17
 SUBLEVEL = 0
-EXTRAVERSION = -rc8
+EXTRAVERSION =
 NAME = Superb Owl
 
 # *DOCUMENTATION*
index 3ed1f2c..18e5291 100644 (file)
                                interrupt-controller;
                                reg = <0x14 4>;
                                interrupt-map =
-                                       <0 0 &gic 0 0 GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
-                                       <1 0 &gic 0 0 GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
-                                       <2 0 &gic 0 0 GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
-                                       <3 0 &gic 0 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
-                                       <4 0 &gic 0 0 GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
-                                       <5 0 &gic 0 0 GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
-                                       <6 0 &gic 0 0 GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
-                                       <7 0 &gic 0 0 GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
-                                       <8 0 &gic 0 0 GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
-                                       <9 0 &gic 0 0 GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
-                                       <10 0 &gic 0 0 GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
-                                       <11 0 &gic 0 0 GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+                                       <0 0 &gic GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
+                                       <1 0 &gic GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
+                                       <2 0 &gic GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
+                                       <3 0 &gic GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
+                                       <4 0 &gic GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
+                                       <5 0 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
+                                       <6 0 &gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+                                       <7 0 &gic GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+                                       <8 0 &gic GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+                                       <9 0 &gic GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+                                       <10 0 &gic GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
+                                       <11 0 &gic GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
                                interrupt-map-mask = <0xffffffff 0x0>;
                        };
                };
index 3cb9c21..1282b61 100644 (file)
                                interrupt-controller;
                                reg = <0x14 4>;
                                interrupt-map =
-                                       <0 0 &gic 0 0 GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
-                                       <1 0 &gic 0 0 GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
-                                       <2 0 &gic 0 0 GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
-                                       <3 0 &gic 0 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
-                                       <4 0 &gic 0 0 GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
-                                       <5 0 &gic 0 0 GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
-                                       <6 0 &gic 0 0 GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
-                                       <7 0 &gic 0 0 GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
-                                       <8 0 &gic 0 0 GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
-                                       <9 0 &gic 0 0 GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
-                                       <10 0 &gic 0 0 GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
-                                       <11 0 &gic 0 0 GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+                                       <0 0 &gic GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
+                                       <1 0 &gic GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
+                                       <2 0 &gic GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
+                                       <3 0 &gic GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
+                                       <4 0 &gic GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
+                                       <5 0 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
+                                       <6 0 &gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+                                       <7 0 &gic GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+                                       <8 0 &gic GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+                                       <9 0 &gic GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+                                       <10 0 &gic GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
+                                       <11 0 &gic GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
                                interrupt-map-mask = <0xffffffff 0x0>;
                        };
                };
index 7032505..3c611cb 100644 (file)
                                interrupt-controller;
                                reg = <0x14 4>;
                                interrupt-map =
-                                       <0 0 &gic 0 0 GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
-                                       <1 0 &gic 0 0 GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
-                                       <2 0 &gic 0 0 GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
-                                       <3 0 &gic 0 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
-                                       <4 0 &gic 0 0 GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
-                                       <5 0 &gic 0 0 GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
-                                       <6 0 &gic 0 0 GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
-                                       <7 0 &gic 0 0 GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
-                                       <8 0 &gic 0 0 GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
-                                       <9 0 &gic 0 0 GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
-                                       <10 0 &gic 0 0 GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
-                                       <11 0 &gic 0 0 GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+                                       <0 0 &gic GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
+                                       <1 0 &gic GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
+                                       <2 0 &gic GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
+                                       <3 0 &gic GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
+                                       <4 0 &gic GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
+                                       <5 0 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
+                                       <6 0 &gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+                                       <7 0 &gic GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+                                       <8 0 &gic GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+                                       <9 0 &gic GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+                                       <10 0 &gic GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
+                                       <11 0 &gic GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
                                interrupt-map-mask = <0xffffffff 0x0>;
                        };
                };
index f64613a..bc9a214 100644 (file)
@@ -56,14 +56,14 @@ enum arm64_bp_harden_el1_vectors {
 DECLARE_PER_CPU_READ_MOSTLY(const char *, this_cpu_vector);
 
 #ifndef CONFIG_UNMAP_KERNEL_AT_EL0
-#define TRAMP_VALIAS   0
+#define TRAMP_VALIAS   0ul
 #endif
 
 static inline const char *
 arm64_get_bp_hardening_vector(enum arm64_bp_harden_el1_vectors slot)
 {
        if (arm64_kernel_unmapped_at_el0())
-               return (char *)TRAMP_VALIAS + SZ_2K * slot;
+               return (char *)(TRAMP_VALIAS + SZ_2K * slot);
 
        WARN_ON_ONCE(slot == EL1_VECTOR_KPTI);
 
index a401180..146fa2e 100644 (file)
@@ -611,7 +611,6 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
        {
                .desc = "ARM erratum 2077057",
                .capability = ARM64_WORKAROUND_2077057,
-               .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
                ERRATA_MIDR_REV_RANGE(MIDR_CORTEX_A510, 0, 0, 2),
        },
 #endif
index 5719d8c..e86d610 100644 (file)
@@ -429,8 +429,23 @@ static int fastop(struct x86_emulate_ctxt *ctxt, fastop_t fop);
        FOP_END
 
 /* Special case for SETcc - 1 instruction per cc */
+
+/*
+ * Depending on .config the SETcc functions look like:
+ *
+ * SETcc %al   [3 bytes]
+ * RET         [1 byte]
+ * INT3        [1 byte; CONFIG_SLS]
+ *
+ * Which gives possible sizes 4 or 5.  When rounded up to the
+ * next power-of-two alignment they become 4 or 8.
+ */
+#define SETCC_LENGTH   (4 + IS_ENABLED(CONFIG_SLS))
+#define SETCC_ALIGN    (4 << IS_ENABLED(CONFIG_SLS))
+static_assert(SETCC_LENGTH <= SETCC_ALIGN);
+
 #define FOP_SETCC(op) \
-       ".align 4 \n\t" \
+       ".align " __stringify(SETCC_ALIGN) " \n\t" \
        ".type " #op ", @function \n\t" \
        #op ": \n\t" \
        #op " %al \n\t" \
@@ -1047,7 +1062,7 @@ static int em_bsr_c(struct x86_emulate_ctxt *ctxt)
 static __always_inline u8 test_cc(unsigned int condition, unsigned long flags)
 {
        u8 rc;
-       void (*fop)(void) = (void *)em_setcc + 4 * (condition & 0xf);
+       void (*fop)(void) = (void *)em_setcc + SETCC_ALIGN * (condition & 0xf);
 
        flags = (flags & EFLAGS_MASK) | X86_EFLAGS_IF;
        asm("push %[flags]; popf; " CALL_NOSPEC
index 1039515..779b4a1 100644 (file)
@@ -50,6 +50,7 @@
 #include "blk-mq-sched.h"
 #include "blk-pm.h"
 #include "blk-throttle.h"
+#include "blk-rq-qos.h"
 
 struct dentry *blk_debugfs_root;
 
@@ -314,6 +315,9 @@ void blk_cleanup_queue(struct request_queue *q)
         */
        blk_freeze_queue(q);
 
+       /* cleanup rq qos structures for queue without disk */
+       rq_qos_exit(q);
+
        blk_queue_flag_set(QUEUE_FLAG_DEAD, q);
 
        blk_sync_queue(q);
index 1331756..8b2e5ef 100644 (file)
@@ -1377,11 +1377,11 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
                if (info->valid & ACPI_VALID_HID) {
                        acpi_add_id(pnp, info->hardware_id.string);
                        pnp->type.platform_id = 1;
-                       if (info->valid & ACPI_VALID_CID) {
-                               cid_list = &info->compatible_id_list;
-                               for (i = 0; i < cid_list->count; i++)
-                                       acpi_add_id(pnp, cid_list->ids[i].string);
-                       }
+               }
+               if (info->valid & ACPI_VALID_CID) {
+                       cid_list = &info->compatible_id_list;
+                       for (i = 0; i < cid_list->count; i++)
+                               acpi_add_id(pnp, cid_list->ids[i].string);
                }
                if (info->valid & ACPI_VALID_ADR) {
                        pnp->bus_address = info->address;
index 422753d..a31ffe1 100644 (file)
@@ -1112,6 +1112,8 @@ DPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags);
        skb_data3 = skb->data[3];
        paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len,
                               DMA_TO_DEVICE);
+       if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr))
+               return enq_next;
        ENI_PRV_PADDR(skb) = paddr;
        /* prepare DMA queue entries */
        j = 0;
index 7cc4d1d..04eac41 100644 (file)
 
 #include "counter-sysfs.h"
 
+static inline struct counter_device *counter_from_dev(struct device *dev)
+{
+       return container_of(dev, struct counter_device, dev);
+}
+
 /**
  * struct counter_attribute - Counter sysfs attribute
  * @dev_attr:  device attribute for sysfs
@@ -90,7 +95,7 @@ static ssize_t counter_comp_u8_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
        const struct counter_attribute *const a = to_counter_attribute(attr);
-       struct counter_device *const counter = dev_get_drvdata(dev);
+       struct counter_device *const counter = counter_from_dev(dev);
        int err;
        u8 data = 0;
 
@@ -122,7 +127,7 @@ static ssize_t counter_comp_u8_store(struct device *dev,
                                     const char *buf, size_t len)
 {
        const struct counter_attribute *const a = to_counter_attribute(attr);
-       struct counter_device *const counter = dev_get_drvdata(dev);
+       struct counter_device *const counter = counter_from_dev(dev);
        int err;
        bool bool_data = 0;
        u8 data = 0;
@@ -158,7 +163,7 @@ static ssize_t counter_comp_u32_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
        const struct counter_attribute *const a = to_counter_attribute(attr);
-       struct counter_device *const counter = dev_get_drvdata(dev);
+       struct counter_device *const counter = counter_from_dev(dev);
        const struct counter_available *const avail = a->comp.priv;
        int err;
        u32 data = 0;
@@ -221,7 +226,7 @@ static ssize_t counter_comp_u32_store(struct device *dev,
                                      const char *buf, size_t len)
 {
        const struct counter_attribute *const a = to_counter_attribute(attr);
-       struct counter_device *const counter = dev_get_drvdata(dev);
+       struct counter_device *const counter = counter_from_dev(dev);
        struct counter_count *const count = a->parent;
        struct counter_synapse *const synapse = a->comp.priv;
        const struct counter_available *const avail = a->comp.priv;
@@ -281,7 +286,7 @@ static ssize_t counter_comp_u64_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
        const struct counter_attribute *const a = to_counter_attribute(attr);
-       struct counter_device *const counter = dev_get_drvdata(dev);
+       struct counter_device *const counter = counter_from_dev(dev);
        int err;
        u64 data = 0;
 
@@ -309,7 +314,7 @@ static ssize_t counter_comp_u64_store(struct device *dev,
                                      const char *buf, size_t len)
 {
        const struct counter_attribute *const a = to_counter_attribute(attr);
-       struct counter_device *const counter = dev_get_drvdata(dev);
+       struct counter_device *const counter = counter_from_dev(dev);
        int err;
        u64 data = 0;
 
index 99ba8d5..11f30fd 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/clk.h>
 #include <linux/crypto.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -43,16 +44,19 @@ static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
 {
        unsigned int currsize = 0;
        u32 val;
+       int ret;
 
        /* read random data from hardware */
        do {
-               val = readl_relaxed(rng->base + PRNG_STATUS);
-               if (!(val & PRNG_STATUS_DATA_AVAIL))
-                       break;
+               ret = readl_poll_timeout(rng->base + PRNG_STATUS, val,
+                                        val & PRNG_STATUS_DATA_AVAIL,
+                                        200, 10000);
+               if (ret)
+                       return ret;
 
                val = readl_relaxed(rng->base + PRNG_DATA_OUT);
                if (!val)
-                       break;
+                       return -EINVAL;
 
                if ((max - currsize) >= WORD_SZ) {
                        memcpy(data, &val, WORD_SZ);
@@ -61,11 +65,10 @@ static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
                } else {
                        /* copy only remaining bytes */
                        memcpy(data, &val, max - currsize);
-                       break;
                }
        } while (currsize < max);
 
-       return currsize;
+       return 0;
 }
 
 static int qcom_rng_generate(struct crypto_rng *tfm,
@@ -87,7 +90,7 @@ static int qcom_rng_generate(struct crypto_rng *tfm,
        mutex_unlock(&rng->lock);
        clk_disable_unprepare(rng->clk);
 
-       return 0;
+       return ret;
 }
 
 static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
index 4c3201e..ea84108 100644 (file)
@@ -24,7 +24,7 @@ static bool dump_properties __initdata;
 static int __init dump_properties_enable(char *arg)
 {
        dump_properties = true;
-       return 0;
+       return 1;
 }
 
 __setup("dump_apple_properties", dump_properties_enable);
index 7de3f5b..5502e17 100644 (file)
@@ -212,7 +212,7 @@ static int __init efivar_ssdt_setup(char *str)
                memcpy(efivar_ssdt, str, strlen(str));
        else
                pr_warn("efivar_ssdt: name too long: %s\n", str);
-       return 0;
+       return 1;
 }
 __setup("efivar_ssdt=", efivar_ssdt_setup);
 
index defb7c4..6630d92 100644 (file)
@@ -1701,6 +1701,11 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc)
  */
 int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset)
 {
+#ifdef CONFIG_PINCTRL
+       if (list_empty(&gc->gpiodev->pin_ranges))
+               return 0;
+#endif
+
        return pinctrl_gpio_request(gc->gpiodev->base + offset);
 }
 EXPORT_SYMBOL_GPL(gpiochip_generic_request);
@@ -1712,6 +1717,11 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request);
  */
 void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset)
 {
+#ifdef CONFIG_PINCTRL
+       if (list_empty(&gc->gpiodev->pin_ranges))
+               return;
+#endif
+
        pinctrl_gpio_free(gc->gpiodev->base + offset);
 }
 EXPORT_SYMBOL_GPL(gpiochip_generic_free);
index 61db5a6..44ad709 100644 (file)
@@ -8,7 +8,6 @@ config DRM_BRIDGE
 config DRM_PANEL_BRIDGE
        def_bool y
        depends on DRM_BRIDGE
-       depends on DRM_KMS_HELPER
        select DRM_PANEL
        help
          DRM bridge wrapper of DRM panels
@@ -30,6 +29,7 @@ config DRM_CDNS_DSI
 config DRM_CHIPONE_ICN6211
        tristate "Chipone ICN6211 MIPI-DSI/RGB Converter bridge"
        depends on OF
+       select DRM_KMS_HELPER
        select DRM_MIPI_DSI
        select DRM_PANEL_BRIDGE
        help
index a8aba01..06cb1a5 100644 (file)
@@ -217,14 +217,6 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
        if (!imx_pd_format_supported(bus_fmt))
                return -EINVAL;
 
-       if (bus_flags &
-           ~(DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_DE_HIGH |
-             DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
-             DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)) {
-               dev_warn(imxpd->dev, "invalid bus_flags (%x)\n", bus_flags);
-               return -EINVAL;
-       }
-
        bridge_state->output_bus_cfg.flags = bus_flags;
        bridge_state->input_bus_cfg.flags = bus_flags;
        imx_crtc_state->bus_flags = bus_flags;
index e9ae22b..52be08b 100644 (file)
@@ -404,9 +404,9 @@ mgag200_pixpll_update_g200wb(struct mgag200_pll *pixpll, const struct mgag200_pl
                udelay(50);
 
                /* program pixel pll register */
-               WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
-               WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
-               WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
+               WREG_DAC(MGA1064_WB_PIX_PLLC_N, xpixpllcn);
+               WREG_DAC(MGA1064_WB_PIX_PLLC_M, xpixpllcm);
+               WREG_DAC(MGA1064_WB_PIX_PLLC_P, xpixpllcp);
 
                udelay(50);
 
index 0aec5a1..9989a31 100644 (file)
@@ -107,6 +107,7 @@ config DRM_PANEL_EDP
        select VIDEOMODE_HELPERS
        select DRM_DP_AUX_BUS
        select DRM_DP_HELPER
+       select DRM_KMS_HELPER
        help
          DRM panel driver for dumb eDP panels that need at most a regulator and
          a GPIO to be powered up. Optionally a backlight can be attached so
index 3c08f98..b42c1d8 100644 (file)
@@ -2017,7 +2017,7 @@ static const struct display_timing innolux_g070y2_l01_timing = {
 static const struct panel_desc innolux_g070y2_l01 = {
        .timings = &innolux_g070y2_l01_timing,
        .num_timings = 1,
-       .bpc = 6,
+       .bpc = 8,
        .size = {
                .width = 152,
                .height = 91,
index e5a668c..1365c9d 100644 (file)
@@ -1793,8 +1793,6 @@ EXPORT_SYMBOL(input_reset_device);
 
 static int input_inhibit_device(struct input_dev *dev)
 {
-       int ret = 0;
-
        mutex_lock(&dev->mutex);
 
        if (dev->inhibited)
@@ -1816,7 +1814,7 @@ static int input_inhibit_device(struct input_dev *dev)
 
 out:
        mutex_unlock(&dev->mutex);
-       return ret;
+       return 0;
 }
 
 static int input_uninhibit_device(struct input_dev *dev)
index 3b23078..505a032 100644 (file)
@@ -399,4 +399,15 @@ config JOYSTICK_N64
          Say Y here if you want enable support for the four
          built-in controller ports on the Nintendo 64 console.
 
+config JOYSTICK_SENSEHAT
+       tristate "Raspberry Pi Sense HAT joystick"
+       depends on INPUT && I2C
+       select MFD_SIMPLE_MFD_I2C
+       help
+         Say Y here if you want to enable the driver for the
+         the Raspberry Pi Sense HAT.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sensehat_joystick.
+
 endif
index 5174b8a..3937535 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_JOYSTICK_N64)            += n64joy.o
 obj-$(CONFIG_JOYSTICK_PSXPAD_SPI)      += psxpad-spi.o
 obj-$(CONFIG_JOYSTICK_PXRC)            += pxrc.o
 obj-$(CONFIG_JOYSTICK_QWIIC)           += qwiic-joystick.o
+obj-$(CONFIG_JOYSTICK_SENSEHAT)        += sensehat-joystick.o
 obj-$(CONFIG_JOYSTICK_SIDEWINDER)      += sidewinder.o
 obj-$(CONFIG_JOYSTICK_SPACEBALL)       += spaceball.o
 obj-$(CONFIG_JOYSTICK_SPACEORB)                += spaceorb.o
diff --git a/drivers/input/joystick/sensehat-joystick.c b/drivers/input/joystick/sensehat-joystick.c
new file mode 100644 (file)
index 0000000..5ad1fe4
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT joystick driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ * Copyright (C) 2021 Charles Mirabile, Mwesigwa Guma, Joel Savitz
+ *
+ * Original Author: Serge Schneider
+ * Revised for upstream Linux by: Charles Mirabile, Mwesigwa Guma, Joel Savitz
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/property.h>
+
+#define JOYSTICK_SMB_REG 0xf2
+
+struct sensehat_joystick {
+       struct platform_device *pdev;
+       struct input_dev *keys_dev;
+       unsigned long prev_states;
+       struct regmap *regmap;
+};
+
+static const unsigned int keymap[] = {
+       BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT,
+};
+
+static irqreturn_t sensehat_joystick_report(int irq, void *cookie)
+{
+       struct sensehat_joystick *sensehat_joystick = cookie;
+       unsigned long curr_states, changes;
+       unsigned int keys;
+       int error;
+       int i;
+
+       error = regmap_read(sensehat_joystick->regmap, JOYSTICK_SMB_REG, &keys);
+       if (error < 0) {
+               dev_err(&sensehat_joystick->pdev->dev,
+                       "Failed to read joystick state: %d", error);
+               return IRQ_NONE;
+       }
+       curr_states = keys;
+       bitmap_xor(&changes, &curr_states, &sensehat_joystick->prev_states,
+                  ARRAY_SIZE(keymap));
+
+       for_each_set_bit(i, &changes, ARRAY_SIZE(keymap))
+               input_report_key(sensehat_joystick->keys_dev, keymap[i],
+                                curr_states & BIT(i));
+
+       input_sync(sensehat_joystick->keys_dev);
+       sensehat_joystick->prev_states = keys;
+       return IRQ_HANDLED;
+}
+
+static int sensehat_joystick_probe(struct platform_device *pdev)
+{
+       struct sensehat_joystick *sensehat_joystick;
+       int error, i, irq;
+
+       sensehat_joystick = devm_kzalloc(&pdev->dev, sizeof(*sensehat_joystick),
+                                        GFP_KERNEL);
+       if (!sensehat_joystick)
+               return -ENOMEM;
+
+       sensehat_joystick->pdev = pdev;
+
+       sensehat_joystick->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!sensehat_joystick->regmap) {
+               dev_err(&pdev->dev, "unable to get sensehat regmap");
+               return -ENODEV;
+       }
+
+       sensehat_joystick->keys_dev = devm_input_allocate_device(&pdev->dev);
+       if (!sensehat_joystick->keys_dev) {
+               dev_err(&pdev->dev, "Could not allocate input device");
+               return -ENOMEM;
+       }
+
+       sensehat_joystick->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
+       sensehat_joystick->keys_dev->phys = "sensehat-joystick/input0";
+       sensehat_joystick->keys_dev->id.bustype = BUS_I2C;
+
+       __set_bit(EV_KEY, sensehat_joystick->keys_dev->evbit);
+       __set_bit(EV_REP, sensehat_joystick->keys_dev->evbit);
+       for (i = 0; i < ARRAY_SIZE(keymap); i++)
+               __set_bit(keymap[i], sensehat_joystick->keys_dev->keybit);
+
+       error = input_register_device(sensehat_joystick->keys_dev);
+       if (error) {
+               dev_err(&pdev->dev, "Could not register input device");
+               return error;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Could not retrieve interrupt request");
+               return irq;
+       }
+
+       error = devm_request_threaded_irq(&pdev->dev, irq,
+                                         NULL, sensehat_joystick_report,
+                                         IRQF_ONESHOT, "keys",
+                                         sensehat_joystick);
+       if (error) {
+               dev_err(&pdev->dev, "IRQ request failed");
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id sensehat_joystick_device_id[] = {
+       { .compatible = "raspberrypi,sensehat-joystick" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id);
+
+static struct platform_driver sensehat_joystick_driver = {
+       .probe = sensehat_joystick_probe,
+       .driver = {
+               .name = "sensehat-joystick",
+               .of_match_table = sensehat_joystick_device_id,
+       },
+};
+
+module_platform_driver(sensehat_joystick_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
+MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
index 2b771c3..166d602 100644 (file)
@@ -183,8 +183,7 @@ static void bcm_kp_stop(const struct bcm_kp *kp)
        writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
        writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
 
-       if (kp->clk)
-               clk_disable_unprepare(kp->clk);
+       clk_disable_unprepare(kp->clk);
 }
 
 static int bcm_kp_open(struct input_dev *dev)
index 019dd6e..939c886 100644 (file)
@@ -95,8 +95,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
        if (!priv)
                return -ENOMEM;
 
-       priv->syscon =
-               syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1");
+       priv->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
        if (IS_ERR(priv->syscon))
                return PTR_ERR(priv->syscon);
 
index 6534dfc..cc73a14 100644 (file)
@@ -435,10 +435,13 @@ static __maybe_unused int cros_ec_keyb_resume(struct device *dev)
  * but the ckdev->bs_idev will remain NULL when this function exits.
  *
  * @ckdev: The keyboard device
+ * @expect_buttons_switches: Indicates that EC must report button and/or
+ *   switch events
  *
  * Returns 0 if no error or -error upon error.
  */
-static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev)
+static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev,
+                                   bool expect_buttons_switches)
 {
        struct cros_ec_device *ec_dev = ckdev->ec;
        struct device *dev = ckdev->dev;
@@ -465,7 +468,7 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev)
        switches = get_unaligned_le32(&event_data.switches);
 
        if (!buttons && !switches)
-               return 0;
+               return expect_buttons_switches ? -EINVAL : 0;
 
        /*
         * We call the non-matrix buttons/switches 'input1', if present.
@@ -516,7 +519,7 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev)
 }
 
 /**
- * cros_ec_keyb_register_bs - Register matrix keys
+ * cros_ec_keyb_register_matrix - Register matrix keys
  *
  * Handles all the bits of the keyboard driver related to matrix keys.
  *
@@ -648,12 +651,12 @@ static const struct attribute_group cros_ec_keyb_attr_group = {
        .attrs = cros_ec_keyb_attrs,
 };
 
-
 static int cros_ec_keyb_probe(struct platform_device *pdev)
 {
        struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
        struct device *dev = &pdev->dev;
        struct cros_ec_keyb *ckdev;
+       bool buttons_switches_only = device_get_match_data(dev);
        int err;
 
        if (!dev->of_node)
@@ -667,13 +670,16 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
        ckdev->dev = dev;
        dev_set_drvdata(dev, ckdev);
 
-       err = cros_ec_keyb_register_matrix(ckdev);
-       if (err) {
-               dev_err(dev, "cannot register matrix inputs: %d\n", err);
-               return err;
+       if (!buttons_switches_only) {
+               err = cros_ec_keyb_register_matrix(ckdev);
+               if (err) {
+                       dev_err(dev, "cannot register matrix inputs: %d\n",
+                               err);
+                       return err;
+               }
        }
 
-       err = cros_ec_keyb_register_bs(ckdev);
+       err = cros_ec_keyb_register_bs(ckdev, buttons_switches_only);
        if (err) {
                dev_err(dev, "cannot register non-matrix inputs: %d\n", err);
                return err;
@@ -681,7 +687,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
 
        err = devm_device_add_group(dev, &cros_ec_keyb_attr_group);
        if (err) {
-               dev_err(dev, "failed to create attributes. err=%d\n", err);
+               dev_err(dev, "failed to create attributes%d\n", err);
                return err;
        }
 
@@ -710,7 +716,8 @@ static int cros_ec_keyb_remove(struct platform_device *pdev)
 #ifdef CONFIG_OF
 static const struct of_device_id cros_ec_keyb_of_match[] = {
        { .compatible = "google,cros-ec-keyb" },
-       {},
+       { .compatible = "google,cros-ec-keyb-switches", .data = (void *)true },
+       {}
 };
 MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match);
 #endif
index 272a4f1..7a3b066 100644 (file)
@@ -231,7 +231,6 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
        struct ep93xx_keypad *keypad;
        const struct matrix_keymap_data *keymap_data;
        struct input_dev *input_dev;
-       struct resource *res;
        int err;
 
        keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
@@ -250,11 +249,7 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
        if (keypad->irq < 0)
                return keypad->irq;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENXIO;
-
-       keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
+       keypad->mmio_base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(keypad->mmio_base))
                return PTR_ERR(keypad->mmio_base);
 
index d75a8b1..a5dc4ab 100644 (file)
@@ -131,7 +131,7 @@ static void gpio_keys_quiesce_key(void *data)
 
        if (!bdata->gpiod)
                hrtimer_cancel(&bdata->release_timer);
-       if (bdata->debounce_use_hrtimer)
+       else if (bdata->debounce_use_hrtimer)
                hrtimer_cancel(&bdata->debounce_timer);
        else
                cancel_delayed_work_sync(&bdata->work);
index 0dbbddc..2e7c918 100644 (file)
@@ -24,7 +24,6 @@ struct mt6779_keypad {
        struct regmap *regmap;
        struct input_dev *input_dev;
        struct clk *clk;
-       void __iomem *base;
        u32 n_rows;
        u32 n_cols;
        DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS);
@@ -91,6 +90,7 @@ static void mt6779_keypad_clk_disable(void *data)
 static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
 {
        struct mt6779_keypad *keypad;
+       void __iomem *base;
        int irq;
        u32 debounce;
        bool wakeup;
@@ -100,11 +100,11 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
        if (!keypad)
                return -ENOMEM;
 
-       keypad->base = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(keypad->base))
-               return PTR_ERR(keypad->base);
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
 
-       keypad->regmap = devm_regmap_init_mmio(&pdev->dev, keypad->base,
+       keypad->regmap = devm_regmap_init_mmio(&pdev->dev, base,
                                               &mt6779_keypad_regmap_cfg);
        if (IS_ERR(keypad->regmap)) {
                dev_err(&pdev->dev,
index 4a796be..15c15c0 100644 (file)
@@ -14,6 +14,7 @@
  * there are no boards known to use channel 1.
  */
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/pm_wakeup.h>
 #include <linux/regulator/consumer.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 
 #define LRADC_CTRL             0x00
 /* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant
  * @divisor_numerator:         The numerator of lradc Vref internally divisor
  * @divisor_denominator:       The denominator of lradc Vref internally divisor
+ * @has_clock_reset:           If the binding requires a clock and reset
  */
 struct lradc_variant {
        u8 divisor_numerator;
        u8 divisor_denominator;
+       bool has_clock_reset;
 };
 
 static const struct lradc_variant lradc_variant_a10 = {
@@ -74,6 +80,12 @@ static const struct lradc_variant r_lradc_variant_a83t = {
        .divisor_denominator = 4
 };
 
+static const struct lradc_variant lradc_variant_r329 = {
+       .divisor_numerator = 3,
+       .divisor_denominator = 4,
+       .has_clock_reset = true,
+};
+
 struct sun4i_lradc_keymap {
        u32 voltage;
        u32 keycode;
@@ -83,6 +95,8 @@ struct sun4i_lradc_data {
        struct device *dev;
        struct input_dev *input;
        void __iomem *base;
+       struct clk *clk;
+       struct reset_control *reset;
        struct regulator *vref_supply;
        struct sun4i_lradc_keymap *chan0_map;
        const struct lradc_variant *variant;
@@ -140,6 +154,14 @@ static int sun4i_lradc_open(struct input_dev *dev)
        if (error)
                return error;
 
+       error = reset_control_deassert(lradc->reset);
+       if (error)
+               goto err_disable_reg;
+
+       error = clk_prepare_enable(lradc->clk);
+       if (error)
+               goto err_assert_reset;
+
        lradc->vref = regulator_get_voltage(lradc->vref_supply) *
                      lradc->variant->divisor_numerator /
                      lradc->variant->divisor_denominator;
@@ -153,6 +175,13 @@ static int sun4i_lradc_open(struct input_dev *dev)
        writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
 
        return 0;
+
+err_assert_reset:
+       reset_control_assert(lradc->reset);
+err_disable_reg:
+       regulator_disable(lradc->vref_supply);
+
+       return error;
 }
 
 static void sun4i_lradc_close(struct input_dev *dev)
@@ -164,6 +193,8 @@ static void sun4i_lradc_close(struct input_dev *dev)
                SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
        writel(0, lradc->base + LRADC_INTC);
 
+       clk_disable_unprepare(lradc->clk);
+       reset_control_assert(lradc->reset);
        regulator_disable(lradc->vref_supply);
 }
 
@@ -226,8 +257,7 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
 {
        struct sun4i_lradc_data *lradc;
        struct device *dev = &pdev->dev;
-       int i;
-       int error;
+       int error, i, irq;
 
        lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
        if (!lradc)
@@ -243,6 +273,16 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       if (lradc->variant->has_clock_reset) {
+               lradc->clk = devm_clk_get(dev, NULL);
+               if (IS_ERR(lradc->clk))
+                       return PTR_ERR(lradc->clk);
+
+               lradc->reset = devm_reset_control_get_exclusive(dev, NULL);
+               if (IS_ERR(lradc->reset))
+                       return PTR_ERR(lradc->reset);
+       }
+
        lradc->vref_supply = devm_regulator_get(dev, "vref");
        if (IS_ERR(lradc->vref_supply))
                return PTR_ERR(lradc->vref_supply);
@@ -272,8 +312,11 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
        if (IS_ERR(lradc->base))
                return PTR_ERR(lradc->base);
 
-       error = devm_request_irq(dev, platform_get_irq(pdev, 0),
-                                sun4i_lradc_irq, 0,
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       error = devm_request_irq(dev, irq, sun4i_lradc_irq, 0,
                                 "sun4i-a10-lradc-keys", lradc);
        if (error)
                return error;
@@ -282,6 +325,16 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
        if (error)
                return error;
 
+       if (device_property_read_bool(dev, "wakeup-source")) {
+               error = dev_pm_set_wake_irq(dev, irq);
+               if (error)
+                       dev_warn(dev,
+                                "Failed to set IRQ %d as a wake IRQ: %d\n",
+                                irq, error);
+               else
+                       device_set_wakeup_capable(dev, true);
+       }
+
        return 0;
 }
 
@@ -290,6 +343,8 @@ static const struct of_device_id sun4i_lradc_of_match[] = {
                .data = &lradc_variant_a10 },
        { .compatible = "allwinner,sun8i-a83t-r-lradc",
                .data = &r_lradc_variant_a83t },
+       { .compatible = "allwinner,sun50i-r329-lradc",
+               .data = &lradc_variant_r329 },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
index dd5227c..a18ab73 100644 (file)
@@ -762,6 +762,16 @@ config INPUT_IQS626A
          To compile this driver as a module, choose M here: the
          module will be called iqs626a.
 
+config INPUT_IQS7222
+       tristate "Azoteq IQS7222A/B/C capacitive touch controller"
+       depends on I2C
+       help
+         Say Y to enable support for the Azoteq IQS7222A/B/C family
+         of capacitive touch controllers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called iqs7222.
+
 config INPUT_CMA3000
        tristate "VTI CMA3000 Tri-axis accelerometer"
        help
index b92c53a..28dfc44 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_HP_SDC_RTC)              += hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IMS_PCU)            += ims-pcu.o
 obj-$(CONFIG_INPUT_IQS269A)            += iqs269a.o
 obj-$(CONFIG_INPUT_IQS626A)            += iqs626a.o
+obj-$(CONFIG_INPUT_IQS7222)            += iqs7222.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)              += kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c
new file mode 100644 (file)
index 0000000..6b41387
--- /dev/null
@@ -0,0 +1,2446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Azoteq IQS7222A/B/C Capacitive Touch Controller
+ *
+ * Copyright (C) 2022 Jeff LaBundy <jeff@labundy.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define IQS7222_PROD_NUM                       0x00
+#define IQS7222_PROD_NUM_A                     840
+#define IQS7222_PROD_NUM_B                     698
+#define IQS7222_PROD_NUM_C                     863
+
+#define IQS7222_SYS_STATUS                     0x10
+#define IQS7222_SYS_STATUS_RESET               BIT(3)
+#define IQS7222_SYS_STATUS_ATI_ERROR           BIT(1)
+#define IQS7222_SYS_STATUS_ATI_ACTIVE          BIT(0)
+
+#define IQS7222_CHAN_SETUP_0_REF_MODE_MASK     GENMASK(15, 14)
+#define IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW   BIT(15)
+#define IQS7222_CHAN_SETUP_0_REF_MODE_REF      BIT(14)
+#define IQS7222_CHAN_SETUP_0_CHAN_EN           BIT(8)
+
+#define IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK     GENMASK(2, 0)
+#define IQS7222_SLDR_SETUP_2_RES_MASK          GENMASK(15, 8)
+#define IQS7222_SLDR_SETUP_2_RES_SHIFT         8
+#define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK    GENMASK(7, 0)
+#define IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK     GENMASK(9, 0)
+
+#define IQS7222_GPIO_SETUP_0_GPIO_EN           BIT(0)
+
+#define IQS7222_SYS_SETUP                      0xD0
+#define IQS7222_SYS_SETUP_INTF_MODE_MASK       GENMASK(7, 6)
+#define IQS7222_SYS_SETUP_INTF_MODE_TOUCH      BIT(7)
+#define IQS7222_SYS_SETUP_INTF_MODE_EVENT      BIT(6)
+#define IQS7222_SYS_SETUP_PWR_MODE_MASK                GENMASK(5, 4)
+#define IQS7222_SYS_SETUP_PWR_MODE_AUTO                IQS7222_SYS_SETUP_PWR_MODE_MASK
+#define IQS7222_SYS_SETUP_REDO_ATI             BIT(2)
+#define IQS7222_SYS_SETUP_ACK_RESET            BIT(0)
+
+#define IQS7222_EVENT_MASK_ATI                 BIT(12)
+
+#define IQS7222_COMMS_HOLD                     BIT(0)
+#define IQS7222_COMMS_ERROR                    0xEEEE
+#define IQS7222_COMMS_RETRY_MS                 50
+#define IQS7222_COMMS_TIMEOUT_MS               100
+#define IQS7222_RESET_TIMEOUT_MS               250
+#define IQS7222_ATI_TIMEOUT_MS                 2000
+
+#define IQS7222_MAX_COLS_STAT                  8
+#define IQS7222_MAX_COLS_CYCLE                 3
+#define IQS7222_MAX_COLS_GLBL                  3
+#define IQS7222_MAX_COLS_BTN                   3
+#define IQS7222_MAX_COLS_CHAN                  6
+#define IQS7222_MAX_COLS_FILT                  2
+#define IQS7222_MAX_COLS_SLDR                  11
+#define IQS7222_MAX_COLS_GPIO                  3
+#define IQS7222_MAX_COLS_SYS                   13
+
+#define IQS7222_MAX_CHAN                       20
+#define IQS7222_MAX_SLDR                       2
+
+#define IQS7222_NUM_RETRIES                    5
+#define IQS7222_REG_OFFSET                     0x100
+
+enum iqs7222_reg_key_id {
+       IQS7222_REG_KEY_NONE,
+       IQS7222_REG_KEY_PROX,
+       IQS7222_REG_KEY_TOUCH,
+       IQS7222_REG_KEY_DEBOUNCE,
+       IQS7222_REG_KEY_TAP,
+       IQS7222_REG_KEY_AXIAL,
+       IQS7222_REG_KEY_WHEEL,
+       IQS7222_REG_KEY_NO_WHEEL,
+       IQS7222_REG_KEY_RESERVED
+};
+
+enum iqs7222_reg_grp_id {
+       IQS7222_REG_GRP_STAT,
+       IQS7222_REG_GRP_CYCLE,
+       IQS7222_REG_GRP_GLBL,
+       IQS7222_REG_GRP_BTN,
+       IQS7222_REG_GRP_CHAN,
+       IQS7222_REG_GRP_FILT,
+       IQS7222_REG_GRP_SLDR,
+       IQS7222_REG_GRP_GPIO,
+       IQS7222_REG_GRP_SYS,
+       IQS7222_NUM_REG_GRPS
+};
+
+static const char * const iqs7222_reg_grp_names[] = {
+       [IQS7222_REG_GRP_CYCLE] = "cycle",
+       [IQS7222_REG_GRP_CHAN] = "channel",
+       [IQS7222_REG_GRP_SLDR] = "slider",
+       [IQS7222_REG_GRP_GPIO] = "gpio",
+};
+
+static const unsigned int iqs7222_max_cols[] = {
+       [IQS7222_REG_GRP_STAT] = IQS7222_MAX_COLS_STAT,
+       [IQS7222_REG_GRP_CYCLE] = IQS7222_MAX_COLS_CYCLE,
+       [IQS7222_REG_GRP_GLBL] = IQS7222_MAX_COLS_GLBL,
+       [IQS7222_REG_GRP_BTN] = IQS7222_MAX_COLS_BTN,
+       [IQS7222_REG_GRP_CHAN] = IQS7222_MAX_COLS_CHAN,
+       [IQS7222_REG_GRP_FILT] = IQS7222_MAX_COLS_FILT,
+       [IQS7222_REG_GRP_SLDR] = IQS7222_MAX_COLS_SLDR,
+       [IQS7222_REG_GRP_GPIO] = IQS7222_MAX_COLS_GPIO,
+       [IQS7222_REG_GRP_SYS] = IQS7222_MAX_COLS_SYS,
+};
+
+static const unsigned int iqs7222_gpio_links[] = { 2, 5, 6, };
+
+struct iqs7222_event_desc {
+       const char *name;
+       u16 mask;
+       u16 val;
+       u16 enable;
+       enum iqs7222_reg_key_id reg_key;
+};
+
+static const struct iqs7222_event_desc iqs7222_kp_events[] = {
+       {
+               .name = "event-prox",
+               .enable = BIT(0),
+               .reg_key = IQS7222_REG_KEY_PROX,
+       },
+       {
+               .name = "event-touch",
+               .enable = BIT(1),
+               .reg_key = IQS7222_REG_KEY_TOUCH,
+       },
+};
+
+static const struct iqs7222_event_desc iqs7222_sl_events[] = {
+       { .name = "event-press", },
+       {
+               .name = "event-tap",
+               .mask = BIT(0),
+               .val = BIT(0),
+               .enable = BIT(0),
+               .reg_key = IQS7222_REG_KEY_TAP,
+       },
+       {
+               .name = "event-swipe-pos",
+               .mask = BIT(5) | BIT(1),
+               .val = BIT(1),
+               .enable = BIT(1),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+       {
+               .name = "event-swipe-neg",
+               .mask = BIT(5) | BIT(1),
+               .val = BIT(5) | BIT(1),
+               .enable = BIT(1),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+       {
+               .name = "event-flick-pos",
+               .mask = BIT(5) | BIT(2),
+               .val = BIT(2),
+               .enable = BIT(2),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+       {
+               .name = "event-flick-neg",
+               .mask = BIT(5) | BIT(2),
+               .val = BIT(5) | BIT(2),
+               .enable = BIT(2),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+};
+
+struct iqs7222_reg_grp_desc {
+       u16 base;
+       int num_row;
+       int num_col;
+};
+
+struct iqs7222_dev_desc {
+       u16 prod_num;
+       u16 fw_major;
+       u16 fw_minor;
+       u16 sldr_res;
+       u16 touch_link;
+       u16 wheel_enable;
+       int allow_offset;
+       int event_offset;
+       int comms_offset;
+       struct iqs7222_reg_grp_desc reg_grps[IQS7222_NUM_REG_GRPS];
+};
+
+static const struct iqs7222_dev_desc iqs7222_devs[] = {
+       {
+               .prod_num = IQS7222_PROD_NUM_A,
+               .fw_major = 1,
+               .fw_minor = 12,
+               .sldr_res = U8_MAX * 16,
+               .touch_link = 1768,
+               .allow_offset = 9,
+               .event_offset = 10,
+               .comms_offset = 12,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 8,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 7,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8700,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 12,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xA000,
+                               .num_row = 12,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xAC00,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SLDR] = {
+                               .base = 0xB000,
+                               .num_row = 2,
+                               .num_col = 11,
+                       },
+                       [IQS7222_REG_GRP_GPIO] = {
+                               .base = 0xC000,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 13,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_B,
+               .fw_major = 1,
+               .fw_minor = 43,
+               .event_offset = 10,
+               .comms_offset = 11,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 10,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8A00,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 20,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xB000,
+                               .num_row = 20,
+                               .num_col = 4,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xC400,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 13,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_B,
+               .fw_major = 1,
+               .fw_minor = 27,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 10,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8A00,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 20,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xB000,
+                               .num_row = 20,
+                               .num_col = 4,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xC400,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 10,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_C,
+               .fw_major = 2,
+               .fw_minor = 6,
+               .sldr_res = U16_MAX,
+               .touch_link = 1686,
+               .wheel_enable = BIT(3),
+               .event_offset = 9,
+               .comms_offset = 10,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 5,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8500,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 10,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xA000,
+                               .num_row = 10,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xAA00,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SLDR] = {
+                               .base = 0xB000,
+                               .num_row = 2,
+                               .num_col = 10,
+                       },
+                       [IQS7222_REG_GRP_GPIO] = {
+                               .base = 0xC000,
+                               .num_row = 3,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 12,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_C,
+               .fw_major = 1,
+               .fw_minor = 13,
+               .sldr_res = U16_MAX,
+               .touch_link = 1674,
+               .wheel_enable = BIT(3),
+               .event_offset = 9,
+               .comms_offset = 10,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 5,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8500,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 10,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xA000,
+                               .num_row = 10,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xAA00,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SLDR] = {
+                               .base = 0xB000,
+                               .num_row = 2,
+                               .num_col = 10,
+                       },
+                       [IQS7222_REG_GRP_GPIO] = {
+                               .base = 0xC000,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 11,
+                       },
+               },
+       },
+};
+
+struct iqs7222_prop_desc {
+       const char *name;
+       enum iqs7222_reg_grp_id reg_grp;
+       enum iqs7222_reg_key_id reg_key;
+       int reg_offset;
+       int reg_shift;
+       int reg_width;
+       int val_pitch;
+       int val_min;
+       int val_max;
+       bool invert;
+       const char *label;
+};
+
+static const struct iqs7222_prop_desc iqs7222_props[] = {
+       {
+               .name = "azoteq,conv-period",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "conversion period",
+       },
+       {
+               .name = "azoteq,conv-frac",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .label = "conversion frequency fractional divider",
+       },
+       {
+               .name = "azoteq,rx-float-inactive",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 6,
+               .reg_width = 1,
+               .invert = true,
+       },
+       {
+               .name = "azoteq,dead-time-enable",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 5,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,tx-freq-fosc",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 4,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,vbias-enable",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 3,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,sense-mode",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 3,
+               .val_max = 3,
+               .label = "sensing mode",
+       },
+       {
+               .name = "azoteq,iref-enable",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 2,
+               .reg_shift = 10,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,iref-level",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 2,
+               .reg_shift = 4,
+               .reg_width = 4,
+               .label = "current reference level",
+       },
+       {
+               .name = "azoteq,iref-trim",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 4,
+               .label = "current reference trim",
+       },
+       {
+               .name = "azoteq,rf-filt-enable",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 0,
+               .reg_shift = 15,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,max-counts",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 0,
+               .reg_shift = 13,
+               .reg_width = 2,
+               .label = "maximum counts",
+       },
+       {
+               .name = "azoteq,auto-mode",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 0,
+               .reg_shift = 2,
+               .reg_width = 2,
+               .label = "number of conversions",
+       },
+       {
+               .name = "azoteq,ati-frac-div-fine",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 1,
+               .reg_shift = 9,
+               .reg_width = 5,
+               .label = "ATI fine fractional divider",
+       },
+       {
+               .name = "azoteq,ati-frac-div-coarse",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 5,
+               .label = "ATI coarse fractional divider",
+       },
+       {
+               .name = "azoteq,ati-comp-select",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 10,
+               .label = "ATI compensation selection",
+       },
+       {
+               .name = "azoteq,ati-band",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 12,
+               .reg_width = 2,
+               .label = "ATI band",
+       },
+       {
+               .name = "azoteq,global-halt",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 11,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,invert-enable",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 10,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,dual-direction",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 9,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,samp-cap-double",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 3,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,vref-half",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 2,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,proj-bias",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 2,
+               .label = "projected bias current",
+       },
+       {
+               .name = "azoteq,ati-target",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 1,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .val_pitch = 8,
+               .label = "ATI target",
+       },
+       {
+               .name = "azoteq,ati-base",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 1,
+               .reg_shift = 3,
+               .reg_width = 5,
+               .val_pitch = 16,
+               .label = "ATI base",
+       },
+       {
+               .name = "azoteq,ati-mode",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 3,
+               .val_max = 5,
+               .label = "ATI mode",
+       },
+       {
+               .name = "azoteq,ati-frac-div-fine",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 2,
+               .reg_shift = 9,
+               .reg_width = 5,
+               .label = "ATI fine fractional divider",
+       },
+       {
+               .name = "azoteq,ati-frac-mult-coarse",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 2,
+               .reg_shift = 5,
+               .reg_width = 4,
+               .label = "ATI coarse fractional multiplier",
+       },
+       {
+               .name = "azoteq,ati-frac-div-coarse",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 5,
+               .label = "ATI coarse fractional divider",
+       },
+       {
+               .name = "azoteq,ati-comp-div",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 3,
+               .reg_shift = 11,
+               .reg_width = 5,
+               .label = "ATI compensation divider",
+       },
+       {
+               .name = "azoteq,ati-comp-select",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 3,
+               .reg_shift = 0,
+               .reg_width = 10,
+               .label = "ATI compensation selection",
+       },
+       {
+               .name = "azoteq,debounce-exit",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_DEBOUNCE,
+               .reg_offset = 0,
+               .reg_shift = 12,
+               .reg_width = 4,
+               .label = "debounce exit factor",
+       },
+       {
+               .name = "azoteq,debounce-enter",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_DEBOUNCE,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 4,
+               .label = "debounce entrance factor",
+       },
+       {
+               .name = "azoteq,thresh",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_PROX,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .val_max = 127,
+               .label = "threshold",
+       },
+       {
+               .name = "azoteq,thresh",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_TOUCH,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .label = "threshold",
+       },
+       {
+               .name = "azoteq,hyst",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_TOUCH,
+               .reg_offset = 1,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "hysteresis",
+       },
+       {
+               .name = "azoteq,lta-beta-lp",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 12,
+               .reg_width = 4,
+               .label = "low-power mode long-term average beta",
+       },
+       {
+               .name = "azoteq,lta-beta-np",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 4,
+               .label = "normal-power mode long-term average beta",
+       },
+       {
+               .name = "azoteq,counts-beta-lp",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 4,
+               .reg_width = 4,
+               .label = "low-power mode counts beta",
+       },
+       {
+               .name = "azoteq,counts-beta-np",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 4,
+               .label = "normal-power mode counts beta",
+       },
+       {
+               .name = "azoteq,lta-fast-beta-lp",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 1,
+               .reg_shift = 4,
+               .reg_width = 4,
+               .label = "low-power mode long-term average fast beta",
+       },
+       {
+               .name = "azoteq,lta-fast-beta-np",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 4,
+               .label = "normal-power mode long-term average fast beta",
+       },
+       {
+               .name = "azoteq,lower-cal",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "lower calibration",
+       },
+       {
+               .name = "azoteq,static-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_NO_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 6,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,bottom-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_NO_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 3,
+               .reg_width = 3,
+               .label = "bottom beta",
+       },
+       {
+               .name = "azoteq,static-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 7,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,bottom-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 4,
+               .reg_width = 3,
+               .label = "bottom beta",
+       },
+       {
+               .name = "azoteq,bottom-speed",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_offset = 1,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "bottom speed",
+       },
+       {
+               .name = "azoteq,upper-cal",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .label = "upper calibration",
+       },
+       {
+               .name = "azoteq,gesture-max-ms",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_TAP,
+               .reg_offset = 9,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .val_pitch = 4,
+               .label = "maximum gesture time",
+       },
+       {
+               .name = "azoteq,gesture-min-ms",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_TAP,
+               .reg_offset = 9,
+               .reg_shift = 3,
+               .reg_width = 5,
+               .val_pitch = 4,
+               .label = "minimum gesture time",
+       },
+       {
+               .name = "azoteq,gesture-dist",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+               .reg_offset = 10,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .val_pitch = 16,
+               .label = "gesture distance",
+       },
+       {
+               .name = "azoteq,gesture-max-ms",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+               .reg_offset = 10,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .val_pitch = 4,
+               .label = "maximum gesture time",
+       },
+       {
+               .name = "drive-open-drain",
+               .reg_grp = IQS7222_REG_GRP_GPIO,
+               .reg_offset = 0,
+               .reg_shift = 1,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,timeout-ati-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_pitch = 500,
+               .label = "ATI error timeout",
+       },
+       {
+               .name = "azoteq,rate-ati-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "ATI report rate",
+       },
+       {
+               .name = "azoteq,timeout-np-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 3,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "normal-power mode timeout",
+       },
+       {
+               .name = "azoteq,rate-np-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 4,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_max = 3000,
+               .label = "normal-power mode report rate",
+       },
+       {
+               .name = "azoteq,timeout-lp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 5,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "low-power mode timeout",
+       },
+       {
+               .name = "azoteq,rate-lp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 6,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_max = 3000,
+               .label = "low-power mode report rate",
+       },
+       {
+               .name = "azoteq,timeout-ulp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 7,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "ultra-low-power mode timeout",
+       },
+       {
+               .name = "azoteq,rate-ulp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 8,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_max = 3000,
+               .label = "ultra-low-power mode report rate",
+       },
+};
+
+struct iqs7222_private {
+       const struct iqs7222_dev_desc *dev_desc;
+       struct gpio_desc *reset_gpio;
+       struct gpio_desc *irq_gpio;
+       struct i2c_client *client;
+       struct input_dev *keypad;
+       unsigned int kp_type[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
+       unsigned int kp_code[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
+       unsigned int sl_code[IQS7222_MAX_SLDR][ARRAY_SIZE(iqs7222_sl_events)];
+       unsigned int sl_axis[IQS7222_MAX_SLDR];
+       u16 cycle_setup[IQS7222_MAX_CHAN / 2][IQS7222_MAX_COLS_CYCLE];
+       u16 glbl_setup[IQS7222_MAX_COLS_GLBL];
+       u16 btn_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_BTN];
+       u16 chan_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_CHAN];
+       u16 filt_setup[IQS7222_MAX_COLS_FILT];
+       u16 sldr_setup[IQS7222_MAX_SLDR][IQS7222_MAX_COLS_SLDR];
+       u16 gpio_setup[ARRAY_SIZE(iqs7222_gpio_links)][IQS7222_MAX_COLS_GPIO];
+       u16 sys_setup[IQS7222_MAX_COLS_SYS];
+};
+
+static u16 *iqs7222_setup(struct iqs7222_private *iqs7222,
+                         enum iqs7222_reg_grp_id reg_grp, int row)
+{
+       switch (reg_grp) {
+       case IQS7222_REG_GRP_CYCLE:
+               return iqs7222->cycle_setup[row];
+
+       case IQS7222_REG_GRP_GLBL:
+               return iqs7222->glbl_setup;
+
+       case IQS7222_REG_GRP_BTN:
+               return iqs7222->btn_setup[row];
+
+       case IQS7222_REG_GRP_CHAN:
+               return iqs7222->chan_setup[row];
+
+       case IQS7222_REG_GRP_FILT:
+               return iqs7222->filt_setup;
+
+       case IQS7222_REG_GRP_SLDR:
+               return iqs7222->sldr_setup[row];
+
+       case IQS7222_REG_GRP_GPIO:
+               return iqs7222->gpio_setup[row];
+
+       case IQS7222_REG_GRP_SYS:
+               return iqs7222->sys_setup;
+
+       default:
+               return NULL;
+       }
+}
+
+static int iqs7222_irq_poll(struct iqs7222_private *iqs7222, u16 timeout_ms)
+{
+       ktime_t irq_timeout = ktime_add_ms(ktime_get(), timeout_ms);
+       int ret;
+
+       do {
+               usleep_range(1000, 1100);
+
+               ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       return 0;
+       } while (ktime_compare(ktime_get(), irq_timeout) < 0);
+
+       return -EBUSY;
+}
+
+static int iqs7222_hard_reset(struct iqs7222_private *iqs7222)
+{
+       struct i2c_client *client = iqs7222->client;
+       int error;
+
+       if (!iqs7222->reset_gpio)
+               return 0;
+
+       gpiod_set_value_cansleep(iqs7222->reset_gpio, 1);
+       usleep_range(1000, 1100);
+
+       gpiod_set_value_cansleep(iqs7222->reset_gpio, 0);
+
+       error = iqs7222_irq_poll(iqs7222, IQS7222_RESET_TIMEOUT_MS);
+       if (error)
+               dev_err(&client->dev, "Failed to reset device: %d\n", error);
+
+       return error;
+}
+
+static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
+{
+       u8 msg_buf[] = { 0xFF, 0x00, };
+       int ret;
+
+       /*
+        * The device cannot communicate until it asserts its interrupt (RDY)
+        * pin. Attempts to do so while RDY is deasserted return an ACK; how-
+        * ever all write data is ignored, and all read data returns 0xEE.
+        *
+        * Unsolicited communication must be preceded by a special force com-
+        * munication command, after which the device eventually asserts its
+        * RDY pin and agrees to communicate.
+        *
+        * Regardless of whether communication is forced or the result of an
+        * interrupt, the device automatically deasserts its RDY pin once it
+        * detects an I2C stop condition, or a timeout expires.
+        */
+       ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
+       if (ret < 0)
+               return ret;
+       else if (ret > 0)
+               return 0;
+
+       ret = i2c_master_send(iqs7222->client, msg_buf, sizeof(msg_buf));
+       if (ret < (int)sizeof(msg_buf)) {
+               if (ret >= 0)
+                       ret = -EIO;
+
+               /*
+                * The datasheet states that the host must wait to retry any
+                * failed attempt to communicate over I2C.
+                */
+               msleep(IQS7222_COMMS_RETRY_MS);
+               return ret;
+       }
+
+       return iqs7222_irq_poll(iqs7222, IQS7222_COMMS_TIMEOUT_MS);
+}
+
+static int iqs7222_read_burst(struct iqs7222_private *iqs7222,
+                             u16 reg, void *val, u16 num_val)
+{
+       u8 reg_buf[sizeof(__be16)];
+       int ret, i;
+       struct i2c_client *client = iqs7222->client;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = client->addr,
+                       .flags = 0,
+                       .len = reg > U8_MAX ? sizeof(reg) : sizeof(u8),
+                       .buf = reg_buf,
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = num_val * sizeof(__le16),
+                       .buf = (u8 *)val,
+               },
+       };
+
+       if (reg > U8_MAX)
+               put_unaligned_be16(reg, reg_buf);
+       else
+               *reg_buf = (u8)reg;
+
+       /*
+        * The following loop protects against an edge case in which the RDY
+        * pin is automatically deasserted just as the read is initiated. In
+        * that case, the read must be retried using forced communication.
+        */
+       for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+               ret = iqs7222_force_comms(iqs7222);
+               if (ret < 0)
+                       continue;
+
+               ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret < (int)ARRAY_SIZE(msg)) {
+                       if (ret >= 0)
+                               ret = -EIO;
+
+                       msleep(IQS7222_COMMS_RETRY_MS);
+                       continue;
+               }
+
+               if (get_unaligned_le16(msg[1].buf) == IQS7222_COMMS_ERROR) {
+                       ret = -ENODATA;
+                       continue;
+               }
+
+               ret = 0;
+               break;
+       }
+
+       /*
+        * The following delay ensures the device has deasserted the RDY pin
+        * following the I2C stop condition.
+        */
+       usleep_range(50, 100);
+
+       if (ret < 0)
+               dev_err(&client->dev,
+                       "Failed to read from address 0x%04X: %d\n", reg, ret);
+
+       return ret;
+}
+
+static int iqs7222_read_word(struct iqs7222_private *iqs7222, u16 reg, u16 *val)
+{
+       __le16 val_buf;
+       int error;
+
+       error = iqs7222_read_burst(iqs7222, reg, &val_buf, 1);
+       if (error)
+               return error;
+
+       *val = le16_to_cpu(val_buf);
+
+       return 0;
+}
+
+static int iqs7222_write_burst(struct iqs7222_private *iqs7222,
+                              u16 reg, const void *val, u16 num_val)
+{
+       int reg_len = reg > U8_MAX ? sizeof(reg) : sizeof(u8);
+       int val_len = num_val * sizeof(__le16);
+       int msg_len = reg_len + val_len;
+       int ret, i;
+       struct i2c_client *client = iqs7222->client;
+       u8 *msg_buf;
+
+       msg_buf = kzalloc(msg_len, GFP_KERNEL);
+       if (!msg_buf)
+               return -ENOMEM;
+
+       if (reg > U8_MAX)
+               put_unaligned_be16(reg, msg_buf);
+       else
+               *msg_buf = (u8)reg;
+
+       memcpy(msg_buf + reg_len, val, val_len);
+
+       /*
+        * The following loop protects against an edge case in which the RDY
+        * pin is automatically asserted just before the force communication
+        * command is sent.
+        *
+        * In that case, the subsequent I2C stop condition tricks the device
+        * into preemptively deasserting the RDY pin and the command must be
+        * sent again.
+        */
+       for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+               ret = iqs7222_force_comms(iqs7222);
+               if (ret < 0)
+                       continue;
+
+               ret = i2c_master_send(client, msg_buf, msg_len);
+               if (ret < msg_len) {
+                       if (ret >= 0)
+                               ret = -EIO;
+
+                       msleep(IQS7222_COMMS_RETRY_MS);
+                       continue;
+               }
+
+               ret = 0;
+               break;
+       }
+
+       kfree(msg_buf);
+
+       usleep_range(50, 100);
+
+       if (ret < 0)
+               dev_err(&client->dev,
+                       "Failed to write to address 0x%04X: %d\n", reg, ret);
+
+       return ret;
+}
+
+static int iqs7222_write_word(struct iqs7222_private *iqs7222, u16 reg, u16 val)
+{
+       __le16 val_buf = cpu_to_le16(val);
+
+       return iqs7222_write_burst(iqs7222, reg, &val_buf, 1);
+}
+
+static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222)
+{
+       struct i2c_client *client = iqs7222->client;
+       ktime_t ati_timeout;
+       u16 sys_status = 0;
+       u16 sys_setup = iqs7222->sys_setup[0] & ~IQS7222_SYS_SETUP_ACK_RESET;
+       int error, i;
+
+       for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+               /*
+                * Trigger ATI from streaming and normal-power modes so that
+                * the RDY pin continues to be asserted during ATI.
+                */
+               error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+                                          sys_setup |
+                                          IQS7222_SYS_SETUP_REDO_ATI);
+               if (error)
+                       return error;
+
+               ati_timeout = ktime_add_ms(ktime_get(), IQS7222_ATI_TIMEOUT_MS);
+
+               do {
+                       error = iqs7222_irq_poll(iqs7222,
+                                                IQS7222_COMMS_TIMEOUT_MS);
+                       if (error)
+                               continue;
+
+                       error = iqs7222_read_word(iqs7222, IQS7222_SYS_STATUS,
+                                                 &sys_status);
+                       if (error)
+                               return error;
+
+                       if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE)
+                               continue;
+
+                       if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR)
+                               break;
+
+                       /*
+                        * Use stream-in-touch mode if either slider reports
+                        * absolute position.
+                        */
+                       sys_setup |= test_bit(EV_ABS, iqs7222->keypad->evbit)
+                                  ? IQS7222_SYS_SETUP_INTF_MODE_TOUCH
+                                  : IQS7222_SYS_SETUP_INTF_MODE_EVENT;
+                       sys_setup |= IQS7222_SYS_SETUP_PWR_MODE_AUTO;
+
+                       return iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+                                                 sys_setup);
+               } while (ktime_compare(ktime_get(), ati_timeout) < 0);
+
+               dev_err(&client->dev,
+                       "ATI attempt %d of %d failed with status 0x%02X, %s\n",
+                       i + 1, IQS7222_NUM_RETRIES, (u8)sys_status,
+                       i < IQS7222_NUM_RETRIES ? "retrying..." : "stopping");
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       int comms_offset = dev_desc->comms_offset;
+       int error, i, j, k;
+
+       /*
+        * Take advantage of the stop-bit disable function, if available, to
+        * save the trouble of having to reopen a communication window after
+        * each burst read or write.
+        */
+       if (comms_offset) {
+               u16 comms_setup;
+
+               error = iqs7222_read_word(iqs7222,
+                                         IQS7222_SYS_SETUP + comms_offset,
+                                         &comms_setup);
+               if (error)
+                       return error;
+
+               error = iqs7222_write_word(iqs7222,
+                                          IQS7222_SYS_SETUP + comms_offset,
+                                          comms_setup | IQS7222_COMMS_HOLD);
+               if (error)
+                       return error;
+       }
+
+       for (i = 0; i < IQS7222_NUM_REG_GRPS; i++) {
+               int num_row = dev_desc->reg_grps[i].num_row;
+               int num_col = dev_desc->reg_grps[i].num_col;
+               u16 reg = dev_desc->reg_grps[i].base;
+               __le16 *val_buf;
+               u16 *val;
+
+               if (!num_col)
+                       continue;
+
+               val = iqs7222_setup(iqs7222, i, 0);
+               if (!val)
+                       continue;
+
+               val_buf = kcalloc(num_col, sizeof(__le16), GFP_KERNEL);
+               if (!val_buf)
+                       return -ENOMEM;
+
+               for (j = 0; j < num_row; j++) {
+                       switch (dir) {
+                       case READ:
+                               error = iqs7222_read_burst(iqs7222, reg,
+                                                          val_buf, num_col);
+                               for (k = 0; k < num_col; k++)
+                                       val[k] = le16_to_cpu(val_buf[k]);
+                               break;
+
+                       case WRITE:
+                               for (k = 0; k < num_col; k++)
+                                       val_buf[k] = cpu_to_le16(val[k]);
+                               error = iqs7222_write_burst(iqs7222, reg,
+                                                           val_buf, num_col);
+                               break;
+
+                       default:
+                               error = -EINVAL;
+                       }
+
+                       if (error)
+                               break;
+
+                       reg += IQS7222_REG_OFFSET;
+                       val += iqs7222_max_cols[i];
+               }
+
+               kfree(val_buf);
+
+               if (error)
+                       return error;
+       }
+
+       if (comms_offset) {
+               u16 comms_setup;
+
+               error = iqs7222_read_word(iqs7222,
+                                         IQS7222_SYS_SETUP + comms_offset,
+                                         &comms_setup);
+               if (error)
+                       return error;
+
+               error = iqs7222_write_word(iqs7222,
+                                          IQS7222_SYS_SETUP + comms_offset,
+                                          comms_setup & ~IQS7222_COMMS_HOLD);
+               if (error)
+                       return error;
+       }
+
+       if (dir == READ)
+               return 0;
+
+       return iqs7222_ati_trigger(iqs7222);
+}
+
+static int iqs7222_dev_info(struct iqs7222_private *iqs7222)
+{
+       struct i2c_client *client = iqs7222->client;
+       bool prod_num_valid = false;
+       __le16 dev_id[3];
+       int error, i;
+
+       error = iqs7222_read_burst(iqs7222, IQS7222_PROD_NUM, dev_id,
+                                  ARRAY_SIZE(dev_id));
+       if (error)
+               return error;
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_devs); i++) {
+               if (le16_to_cpu(dev_id[0]) != iqs7222_devs[i].prod_num)
+                       continue;
+
+               prod_num_valid = true;
+
+               if (le16_to_cpu(dev_id[1]) < iqs7222_devs[i].fw_major)
+                       continue;
+
+               if (le16_to_cpu(dev_id[2]) < iqs7222_devs[i].fw_minor)
+                       continue;
+
+               iqs7222->dev_desc = &iqs7222_devs[i];
+               return 0;
+       }
+
+       if (prod_num_valid)
+               dev_err(&client->dev, "Unsupported firmware revision: %u.%u\n",
+                       le16_to_cpu(dev_id[1]), le16_to_cpu(dev_id[2]));
+       else
+               dev_err(&client->dev, "Unrecognized product number: %u\n",
+                       le16_to_cpu(dev_id[0]));
+
+       return -EINVAL;
+}
+
+static int iqs7222_gpio_select(struct iqs7222_private *iqs7222,
+                              struct fwnode_handle *child_node,
+                              int child_enable, u16 child_link)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       int num_gpio = dev_desc->reg_grps[IQS7222_REG_GRP_GPIO].num_row;
+       int error, count, i;
+       unsigned int gpio_sel[ARRAY_SIZE(iqs7222_gpio_links)];
+
+       if (!num_gpio)
+               return 0;
+
+       if (!fwnode_property_present(child_node, "azoteq,gpio-select"))
+               return 0;
+
+       count = fwnode_property_count_u32(child_node, "azoteq,gpio-select");
+       if (count > num_gpio) {
+               dev_err(&client->dev, "Invalid number of %s GPIOs\n",
+                       fwnode_get_name(child_node));
+               return -EINVAL;
+       } else if (count < 0) {
+               dev_err(&client->dev, "Failed to count %s GPIOs: %d\n",
+                       fwnode_get_name(child_node), count);
+               return count;
+       }
+
+       error = fwnode_property_read_u32_array(child_node,
+                                              "azoteq,gpio-select",
+                                              gpio_sel, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read %s GPIOs: %d\n",
+                       fwnode_get_name(child_node), error);
+               return error;
+       }
+
+       for (i = 0; i < count; i++) {
+               u16 *gpio_setup;
+
+               if (gpio_sel[i] >= num_gpio) {
+                       dev_err(&client->dev, "Invalid %s GPIO: %u\n",
+                               fwnode_get_name(child_node), gpio_sel[i]);
+                       return -EINVAL;
+               }
+
+               gpio_setup = iqs7222->gpio_setup[gpio_sel[i]];
+
+               if (gpio_setup[2] && child_link != gpio_setup[2]) {
+                       dev_err(&client->dev,
+                               "Conflicting GPIO %u event types\n",
+                               gpio_sel[i]);
+                       return -EINVAL;
+               }
+
+               gpio_setup[0] |= IQS7222_GPIO_SETUP_0_GPIO_EN;
+               gpio_setup[1] |= child_enable;
+               gpio_setup[2] = child_link;
+       }
+
+       return 0;
+}
+
+static int iqs7222_parse_props(struct iqs7222_private *iqs7222,
+                              struct fwnode_handle **child_node,
+                              int child_index,
+                              enum iqs7222_reg_grp_id reg_grp,
+                              enum iqs7222_reg_key_id reg_key)
+{
+       u16 *setup = iqs7222_setup(iqs7222, reg_grp, child_index);
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *reg_grp_node;
+       char reg_grp_name[16];
+       int i;
+
+       switch (reg_grp) {
+       case IQS7222_REG_GRP_CYCLE:
+       case IQS7222_REG_GRP_CHAN:
+       case IQS7222_REG_GRP_SLDR:
+       case IQS7222_REG_GRP_GPIO:
+       case IQS7222_REG_GRP_BTN:
+               /*
+                * These groups derive a child node and return it to the caller
+                * for additional group-specific processing. In some cases, the
+                * child node may have already been derived.
+                */
+               reg_grp_node = *child_node;
+               if (reg_grp_node)
+                       break;
+
+               snprintf(reg_grp_name, sizeof(reg_grp_name), "%s-%d",
+                        iqs7222_reg_grp_names[reg_grp], child_index);
+
+               reg_grp_node = device_get_named_child_node(&client->dev,
+                                                          reg_grp_name);
+               if (!reg_grp_node)
+                       return 0;
+
+               *child_node = reg_grp_node;
+               break;
+
+       case IQS7222_REG_GRP_GLBL:
+       case IQS7222_REG_GRP_FILT:
+       case IQS7222_REG_GRP_SYS:
+               /*
+                * These groups are not organized beneath a child node, nor are
+                * they subject to any additional processing by the caller.
+                */
+               reg_grp_node = dev_fwnode(&client->dev);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_props); i++) {
+               const char *name = iqs7222_props[i].name;
+               int reg_offset = iqs7222_props[i].reg_offset;
+               int reg_shift = iqs7222_props[i].reg_shift;
+               int reg_width = iqs7222_props[i].reg_width;
+               int val_pitch = iqs7222_props[i].val_pitch ? : 1;
+               int val_min = iqs7222_props[i].val_min;
+               int val_max = iqs7222_props[i].val_max;
+               bool invert = iqs7222_props[i].invert;
+               const char *label = iqs7222_props[i].label ? : name;
+               unsigned int val;
+               int error;
+
+               if (iqs7222_props[i].reg_grp != reg_grp ||
+                   iqs7222_props[i].reg_key != reg_key)
+                       continue;
+
+               /*
+                * Boolean register fields are one bit wide; they are forcibly
+                * reset to provide a means to undo changes by a bootloader if
+                * necessary.
+                *
+                * Scalar fields, on the other hand, are left untouched unless
+                * their corresponding properties are present.
+                */
+               if (reg_width == 1) {
+                       if (invert)
+                               setup[reg_offset] |= BIT(reg_shift);
+                       else
+                               setup[reg_offset] &= ~BIT(reg_shift);
+               }
+
+               if (!fwnode_property_present(reg_grp_node, name))
+                       continue;
+
+               if (reg_width == 1) {
+                       if (invert)
+                               setup[reg_offset] &= ~BIT(reg_shift);
+                       else
+                               setup[reg_offset] |= BIT(reg_shift);
+
+                       continue;
+               }
+
+               error = fwnode_property_read_u32(reg_grp_node, name, &val);
+               if (error) {
+                       dev_err(&client->dev, "Failed to read %s %s: %d\n",
+                               fwnode_get_name(reg_grp_node), label, error);
+                       return error;
+               }
+
+               if (!val_max)
+                       val_max = GENMASK(reg_width - 1, 0) * val_pitch;
+
+               if (val < val_min || val > val_max) {
+                       dev_err(&client->dev, "Invalid %s %s: %u\n",
+                               fwnode_get_name(reg_grp_node), label, val);
+                       return -EINVAL;
+               }
+
+               setup[reg_offset] &= ~GENMASK(reg_shift + reg_width - 1,
+                                             reg_shift);
+               setup[reg_offset] |= (val / val_pitch << reg_shift);
+       }
+
+       return 0;
+}
+
+static int iqs7222_parse_cycle(struct iqs7222_private *iqs7222, int cycle_index)
+{
+       u16 *cycle_setup = iqs7222->cycle_setup[cycle_index];
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *cycle_node = NULL;
+       unsigned int pins[9];
+       int error, count, i;
+
+       /*
+        * Each channel shares a cycle with one other channel; the mapping of
+        * channels to cycles is fixed. Properties defined for a cycle impact
+        * both channels tied to the cycle.
+        */
+       error = iqs7222_parse_props(iqs7222, &cycle_node, cycle_index,
+                                   IQS7222_REG_GRP_CYCLE,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       if (!cycle_node)
+               return 0;
+
+       /*
+        * Unlike channels which are restricted to a select range of CRx pins
+        * based on channel number, any cycle can claim any of the device's 9
+        * CTx pins (CTx0-8).
+        */
+       if (!fwnode_property_present(cycle_node, "azoteq,tx-enable"))
+               return 0;
+
+       count = fwnode_property_count_u32(cycle_node, "azoteq,tx-enable");
+       if (count < 0) {
+               dev_err(&client->dev, "Failed to count %s CTx pins: %d\n",
+                       fwnode_get_name(cycle_node), count);
+               return count;
+       } else if (count > ARRAY_SIZE(pins)) {
+               dev_err(&client->dev, "Invalid number of %s CTx pins\n",
+                       fwnode_get_name(cycle_node));
+               return -EINVAL;
+       }
+
+       error = fwnode_property_read_u32_array(cycle_node, "azoteq,tx-enable",
+                                              pins, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read %s CTx pins: %d\n",
+                       fwnode_get_name(cycle_node), error);
+               return error;
+       }
+
+       cycle_setup[1] &= ~GENMASK(7 + ARRAY_SIZE(pins) - 1, 7);
+
+       for (i = 0; i < count; i++) {
+               if (pins[i] > 8) {
+                       dev_err(&client->dev, "Invalid %s CTx pin: %u\n",
+                               fwnode_get_name(cycle_node), pins[i]);
+                       return -EINVAL;
+               }
+
+               cycle_setup[1] |= BIT(pins[i] + 7);
+       }
+
+       return 0;
+}
+
+static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, int chan_index)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *chan_node = NULL;
+       int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+       int ext_chan = rounddown(num_chan, 10);
+       int error, i;
+       u16 *chan_setup = iqs7222->chan_setup[chan_index];
+       u16 *sys_setup = iqs7222->sys_setup;
+       unsigned int val;
+
+       error = iqs7222_parse_props(iqs7222, &chan_node, chan_index,
+                                   IQS7222_REG_GRP_CHAN,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       if (!chan_node)
+               return 0;
+
+       if (dev_desc->allow_offset) {
+               sys_setup[dev_desc->allow_offset] |= BIT(chan_index);
+               if (fwnode_property_present(chan_node, "azoteq,ulp-allow"))
+                       sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
+       }
+
+       chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
+
+       /*
+        * The reference channel function allows for differential measurements
+        * and is only available in the case of IQS7222A or IQS7222C.
+        */
+       if (dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_col > 4 &&
+           fwnode_property_present(chan_node, "azoteq,ref-select")) {
+               u16 *ref_setup;
+
+               error = fwnode_property_read_u32(chan_node, "azoteq,ref-select",
+                                                &val);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to read %s reference channel: %d\n",
+                               fwnode_get_name(chan_node), error);
+                       return error;
+               }
+
+               if (val >= ext_chan) {
+                       dev_err(&client->dev,
+                               "Invalid %s reference channel: %u\n",
+                               fwnode_get_name(chan_node), val);
+                       return -EINVAL;
+               }
+
+               ref_setup = iqs7222->chan_setup[val];
+
+               /*
+                * Configure the current channel as a follower of the selected
+                * reference channel.
+                */
+               chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW;
+               chan_setup[4] = val * 42 + 1048;
+
+               if (!fwnode_property_read_u32(chan_node, "azoteq,ref-weight",
+                                             &val)) {
+                       if (val > U16_MAX) {
+                               dev_err(&client->dev,
+                                       "Invalid %s reference weight: %u\n",
+                                       fwnode_get_name(chan_node), val);
+                               return -EINVAL;
+                       }
+
+                       chan_setup[5] = val;
+               }
+
+               /*
+                * Configure the selected channel as a reference channel which
+                * serves the current channel.
+                */
+               ref_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF;
+               ref_setup[5] |= BIT(chan_index);
+
+               ref_setup[4] = dev_desc->touch_link;
+               if (fwnode_property_present(chan_node, "azoteq,use-prox"))
+                       ref_setup[4] -= 2;
+       }
+
+       if (fwnode_property_present(chan_node, "azoteq,rx-enable")) {
+               /*
+                * Each channel can claim up to 4 CRx pins. The first half of
+                * the channels can use CRx0-3, while the second half can use
+                * CRx4-7.
+                */
+               unsigned int pins[4];
+               int count;
+
+               count = fwnode_property_count_u32(chan_node,
+                                                 "azoteq,rx-enable");
+               if (count < 0) {
+                       dev_err(&client->dev,
+                               "Failed to count %s CRx pins: %d\n",
+                               fwnode_get_name(chan_node), count);
+                       return count;
+               } else if (count > ARRAY_SIZE(pins)) {
+                       dev_err(&client->dev,
+                               "Invalid number of %s CRx pins\n",
+                               fwnode_get_name(chan_node));
+                       return -EINVAL;
+               }
+
+               error = fwnode_property_read_u32_array(chan_node,
+                                                      "azoteq,rx-enable",
+                                                      pins, count);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to read %s CRx pins: %d\n",
+                               fwnode_get_name(chan_node), error);
+                       return error;
+               }
+
+               chan_setup[0] &= ~GENMASK(4 + ARRAY_SIZE(pins) - 1, 4);
+
+               for (i = 0; i < count; i++) {
+                       int min_crx = chan_index < ext_chan / 2 ? 0 : 4;
+
+                       if (pins[i] < min_crx || pins[i] > min_crx + 3) {
+                               dev_err(&client->dev,
+                                       "Invalid %s CRx pin: %u\n",
+                                       fwnode_get_name(chan_node), pins[i]);
+                               return -EINVAL;
+                       }
+
+                       chan_setup[0] |= BIT(pins[i] + 4 - min_crx);
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_kp_events); i++) {
+               const char *event_name = iqs7222_kp_events[i].name;
+               u16 event_enable = iqs7222_kp_events[i].enable;
+               struct fwnode_handle *event_node;
+
+               event_node = fwnode_get_named_child_node(chan_node, event_name);
+               if (!event_node)
+                       continue;
+
+               error = iqs7222_parse_props(iqs7222, &event_node, chan_index,
+                                           IQS7222_REG_GRP_BTN,
+                                           iqs7222_kp_events[i].reg_key);
+               if (error)
+                       return error;
+
+               error = iqs7222_gpio_select(iqs7222, event_node,
+                                           BIT(chan_index),
+                                           dev_desc->touch_link - (i ? 0 : 2));
+               if (error)
+                       return error;
+
+               if (!fwnode_property_read_u32(event_node,
+                                             "azoteq,timeout-press-ms",
+                                             &val)) {
+                       /*
+                        * The IQS7222B employs a global pair of press timeout
+                        * registers as opposed to channel-specific registers.
+                        */
+                       u16 *setup = dev_desc->reg_grps
+                                    [IQS7222_REG_GRP_BTN].num_col > 2 ?
+                                    &iqs7222->btn_setup[chan_index][2] :
+                                    &sys_setup[9];
+
+                       if (val > U8_MAX * 500) {
+                               dev_err(&client->dev,
+                                       "Invalid %s press timeout: %u\n",
+                                       fwnode_get_name(chan_node), val);
+                               return -EINVAL;
+                       }
+
+                       *setup &= ~(U8_MAX << i * 8);
+                       *setup |= (val / 500 << i * 8);
+               }
+
+               error = fwnode_property_read_u32(event_node, "linux,code",
+                                                &val);
+               if (error) {
+                       dev_err(&client->dev, "Failed to read %s code: %d\n",
+                               fwnode_get_name(chan_node), error);
+                       return error;
+               }
+
+               iqs7222->kp_code[chan_index][i] = val;
+               iqs7222->kp_type[chan_index][i] = EV_KEY;
+
+               if (fwnode_property_present(event_node, "linux,input-type")) {
+                       error = fwnode_property_read_u32(event_node,
+                                                        "linux,input-type",
+                                                        &val);
+                       if (error) {
+                               dev_err(&client->dev,
+                                       "Failed to read %s input type: %d\n",
+                                       fwnode_get_name(chan_node), error);
+                               return error;
+                       }
+
+                       if (val != EV_KEY && val != EV_SW) {
+                               dev_err(&client->dev,
+                                       "Invalid %s input type: %u\n",
+                                       fwnode_get_name(chan_node), val);
+                               return -EINVAL;
+                       }
+
+                       iqs7222->kp_type[chan_index][i] = val;
+               }
+
+               /*
+                * Reference channels can opt out of event reporting by using
+                * KEY_RESERVED in place of a true key or switch code.
+                */
+               if (iqs7222->kp_type[chan_index][i] == EV_KEY &&
+                   iqs7222->kp_code[chan_index][i] == KEY_RESERVED)
+                       continue;
+
+               input_set_capability(iqs7222->keypad,
+                                    iqs7222->kp_type[chan_index][i],
+                                    iqs7222->kp_code[chan_index][i]);
+
+               if (!dev_desc->event_offset)
+                       continue;
+
+               sys_setup[dev_desc->event_offset] |= event_enable;
+       }
+
+       /*
+        * The following call handles a special pair of properties that apply
+        * to a channel node, but reside within the button (event) group.
+        */
+       return iqs7222_parse_props(iqs7222, &chan_node, chan_index,
+                                  IQS7222_REG_GRP_BTN,
+                                  IQS7222_REG_KEY_DEBOUNCE);
+}
+
+static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *sldr_node = NULL;
+       int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+       int ext_chan = rounddown(num_chan, 10);
+       int count, error, reg_offset, i;
+       u16 *sldr_setup = iqs7222->sldr_setup[sldr_index];
+       u16 *sys_setup = iqs7222->sys_setup;
+       unsigned int chan_sel[4], val;
+
+       error = iqs7222_parse_props(iqs7222, &sldr_node, sldr_index,
+                                   IQS7222_REG_GRP_SLDR,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       if (!sldr_node)
+               return 0;
+
+       /*
+        * Each slider can be spread across 3 to 4 channels. It is possible to
+        * select only 2 channels, but doing so prevents the slider from using
+        * the specified resolution.
+        */
+       count = fwnode_property_count_u32(sldr_node, "azoteq,channel-select");
+       if (count < 0) {
+               dev_err(&client->dev, "Failed to count %s channels: %d\n",
+                       fwnode_get_name(sldr_node), count);
+               return count;
+       } else if (count < 3 || count > ARRAY_SIZE(chan_sel)) {
+               dev_err(&client->dev, "Invalid number of %s channels\n",
+                       fwnode_get_name(sldr_node));
+               return -EINVAL;
+       }
+
+       error = fwnode_property_read_u32_array(sldr_node,
+                                              "azoteq,channel-select",
+                                              chan_sel, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read %s channels: %d\n",
+                       fwnode_get_name(sldr_node), error);
+               return error;
+       }
+
+       /*
+        * Resolution and top speed, if small enough, are packed into a single
+        * register. Otherwise, each occupies its own register and the rest of
+        * the slider-related register addresses are offset by one.
+        */
+       reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1;
+
+       sldr_setup[0] |= count;
+       sldr_setup[3 + reg_offset] &= ~IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK;
+
+       for (i = 0; i < ARRAY_SIZE(chan_sel); i++) {
+               sldr_setup[5 + reg_offset + i] = 0;
+               if (i >= count)
+                       continue;
+
+               if (chan_sel[i] >= ext_chan) {
+                       dev_err(&client->dev, "Invalid %s channel: %u\n",
+                               fwnode_get_name(sldr_node), chan_sel[i]);
+                       return -EINVAL;
+               }
+
+               /*
+                * The following fields indicate which channels participate in
+                * the slider, as well as each channel's relative placement.
+                */
+               sldr_setup[3 + reg_offset] |= BIT(chan_sel[i]);
+               sldr_setup[5 + reg_offset + i] = chan_sel[i] * 42 + 1080;
+       }
+
+       sldr_setup[4 + reg_offset] = dev_desc->touch_link;
+       if (fwnode_property_present(sldr_node, "azoteq,use-prox"))
+               sldr_setup[4 + reg_offset] -= 2;
+
+       if (!fwnode_property_read_u32(sldr_node, "azoteq,slider-size", &val)) {
+               if (!val || val > dev_desc->sldr_res) {
+                       dev_err(&client->dev, "Invalid %s size: %u\n",
+                               fwnode_get_name(sldr_node), val);
+                       return -EINVAL;
+               }
+
+               if (reg_offset) {
+                       sldr_setup[3] = val;
+               } else {
+                       sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_RES_MASK;
+                       sldr_setup[2] |= (val / 16 <<
+                                         IQS7222_SLDR_SETUP_2_RES_SHIFT);
+               }
+       }
+
+       if (!fwnode_property_read_u32(sldr_node, "azoteq,top-speed", &val)) {
+               if (val > (reg_offset ? U16_MAX : U8_MAX * 4)) {
+                       dev_err(&client->dev, "Invalid %s top speed: %u\n",
+                               fwnode_get_name(sldr_node), val);
+                       return -EINVAL;
+               }
+
+               if (reg_offset) {
+                       sldr_setup[2] = val;
+               } else {
+                       sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK;
+                       sldr_setup[2] |= (val / 4);
+               }
+       }
+
+       if (!fwnode_property_read_u32(sldr_node, "linux,axis", &val)) {
+               u16 sldr_max = sldr_setup[3] - 1;
+
+               if (!reg_offset) {
+                       sldr_max = sldr_setup[2];
+
+                       sldr_max &= IQS7222_SLDR_SETUP_2_RES_MASK;
+                       sldr_max >>= IQS7222_SLDR_SETUP_2_RES_SHIFT;
+
+                       sldr_max = sldr_max * 16 - 1;
+               }
+
+               input_set_abs_params(iqs7222->keypad, val, 0, sldr_max, 0, 0);
+               iqs7222->sl_axis[sldr_index] = val;
+       }
+
+       if (dev_desc->wheel_enable) {
+               sldr_setup[0] &= ~dev_desc->wheel_enable;
+               if (iqs7222->sl_axis[sldr_index] == ABS_WHEEL)
+                       sldr_setup[0] |= dev_desc->wheel_enable;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) {
+               const char *event_name = iqs7222_sl_events[i].name;
+               struct fwnode_handle *event_node;
+
+               /*
+                * The absence of a register offset means the remaining fields
+                * in the group represent gesture settings.
+                */
+               if (iqs7222_sl_events[i].enable && !reg_offset)
+                       sldr_setup[9] &= ~iqs7222_sl_events[i].enable;
+
+               event_node = fwnode_get_named_child_node(sldr_node, event_name);
+               if (!event_node)
+                       continue;
+
+               error = iqs7222_parse_props(iqs7222, &event_node, sldr_index,
+                                           IQS7222_REG_GRP_SLDR,
+                                           reg_offset ?
+                                           IQS7222_REG_KEY_RESERVED :
+                                           iqs7222_sl_events[i].reg_key);
+               if (error)
+                       return error;
+
+               error = fwnode_property_read_u32(event_node, "linux,code",
+                                                &val);
+               if (error) {
+                       dev_err(&client->dev, "Failed to read %s code: %d\n",
+                               fwnode_get_name(sldr_node), error);
+                       return error;
+               }
+
+               iqs7222->sl_code[sldr_index][i] = val;
+               input_set_capability(iqs7222->keypad, EV_KEY, val);
+
+               /*
+                * The press/release event is determined based on whether the
+                * coordinate field reports 0xFFFF and has no explicit enable
+                * control.
+                */
+               if (!iqs7222_sl_events[i].enable || reg_offset)
+                       continue;
+
+               sldr_setup[9] |= iqs7222_sl_events[i].enable;
+
+               error = iqs7222_gpio_select(iqs7222, event_node,
+                                           iqs7222_sl_events[i].enable,
+                                           1568 + sldr_index * 30);
+               if (error)
+                       return error;
+
+               if (!dev_desc->event_offset)
+                       continue;
+
+               sys_setup[dev_desc->event_offset] |= BIT(10 + sldr_index);
+       }
+
+       /*
+        * The following call handles a special pair of properties that shift
+        * to make room for a wheel enable control in the case of IQS7222C.
+        */
+       return iqs7222_parse_props(iqs7222, &sldr_node, sldr_index,
+                                  IQS7222_REG_GRP_SLDR,
+                                  dev_desc->wheel_enable ?
+                                  IQS7222_REG_KEY_WHEEL :
+                                  IQS7222_REG_KEY_NO_WHEEL);
+}
+
+static int iqs7222_parse_all(struct iqs7222_private *iqs7222)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       const struct iqs7222_reg_grp_desc *reg_grps = dev_desc->reg_grps;
+       u16 *sys_setup = iqs7222->sys_setup;
+       int error, i;
+
+       if (dev_desc->event_offset)
+               sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_CYCLE].num_row; i++) {
+               error = iqs7222_parse_cycle(iqs7222, i);
+               if (error)
+                       return error;
+       }
+
+       error = iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_GLBL,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_GPIO].num_row; i++) {
+               struct fwnode_handle *gpio_node = NULL;
+               u16 *gpio_setup = iqs7222->gpio_setup[i];
+               int j;
+
+               gpio_setup[0] &= ~IQS7222_GPIO_SETUP_0_GPIO_EN;
+               gpio_setup[1] = 0;
+               gpio_setup[2] = 0;
+
+               error = iqs7222_parse_props(iqs7222, &gpio_node, i,
+                                           IQS7222_REG_GRP_GPIO,
+                                           IQS7222_REG_KEY_NONE);
+               if (error)
+                       return error;
+
+               if (reg_grps[IQS7222_REG_GRP_GPIO].num_row == 1)
+                       continue;
+
+               /*
+                * The IQS7222C exposes multiple GPIO and must be informed
+                * as to which GPIO this group represents.
+                */
+               for (j = 0; j < ARRAY_SIZE(iqs7222_gpio_links); j++)
+                       gpio_setup[0] &= ~BIT(iqs7222_gpio_links[j]);
+
+               gpio_setup[0] |= BIT(iqs7222_gpio_links[i]);
+       }
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_CHAN].num_row; i++) {
+               u16 *chan_setup = iqs7222->chan_setup[i];
+
+               chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_REF_MODE_MASK;
+               chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_CHAN_EN;
+
+               chan_setup[5] = 0;
+       }
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_CHAN].num_row; i++) {
+               error = iqs7222_parse_chan(iqs7222, i);
+               if (error)
+                       return error;
+       }
+
+       error = iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_FILT,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
+               u16 *sldr_setup = iqs7222->sldr_setup[i];
+
+               sldr_setup[0] &= ~IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK;
+
+               error = iqs7222_parse_sldr(iqs7222, i);
+               if (error)
+                       return error;
+       }
+
+       sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK;
+       sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK;
+
+       sys_setup[0] |= IQS7222_SYS_SETUP_ACK_RESET;
+
+       return iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_SYS,
+                                  IQS7222_REG_KEY_NONE);
+}
+
+static int iqs7222_report(struct iqs7222_private *iqs7222)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+       int num_stat = dev_desc->reg_grps[IQS7222_REG_GRP_STAT].num_col;
+       int error, i, j;
+       __le16 status[IQS7222_MAX_COLS_STAT];
+
+       error = iqs7222_read_burst(iqs7222, IQS7222_SYS_STATUS, status,
+                                  num_stat);
+       if (error)
+               return error;
+
+       if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_RESET) {
+               dev_err(&client->dev, "Unexpected device reset\n");
+               return iqs7222_dev_init(iqs7222, WRITE);
+       }
+
+       if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ERROR) {
+               dev_err(&client->dev, "Unexpected ATI error\n");
+               return iqs7222_ati_trigger(iqs7222);
+       }
+
+       if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ACTIVE)
+               return 0;
+
+       for (i = 0; i < num_chan; i++) {
+               u16 *chan_setup = iqs7222->chan_setup[i];
+
+               if (!(chan_setup[0] & IQS7222_CHAN_SETUP_0_CHAN_EN))
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(iqs7222_kp_events); j++) {
+                       /*
+                        * Proximity state begins at offset 2 and spills into
+                        * offset 3 for devices with more than 16 channels.
+                        *
+                        * Touch state begins at the first offset immediately
+                        * following proximity state.
+                        */
+                       int k = 2 + j * (num_chan > 16 ? 2 : 1);
+                       u16 state = le16_to_cpu(status[k + i / 16]);
+
+                       input_event(iqs7222->keypad,
+                                   iqs7222->kp_type[i][j],
+                                   iqs7222->kp_code[i][j],
+                                   !!(state & BIT(i % 16)));
+               }
+       }
+
+       for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
+               u16 *sldr_setup = iqs7222->sldr_setup[i];
+               u16 sldr_pos = le16_to_cpu(status[4 + i]);
+               u16 state = le16_to_cpu(status[6 + i]);
+
+               if (!(sldr_setup[0] & IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK))
+                       continue;
+
+               if (sldr_pos < dev_desc->sldr_res)
+                       input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i],
+                                        sldr_pos);
+
+               for (j = 0; j < ARRAY_SIZE(iqs7222_sl_events); j++) {
+                       u16 mask = iqs7222_sl_events[j].mask;
+                       u16 val = iqs7222_sl_events[j].val;
+
+                       if (!iqs7222_sl_events[j].enable) {
+                               input_report_key(iqs7222->keypad,
+                                                iqs7222->sl_code[i][j],
+                                                sldr_pos < dev_desc->sldr_res);
+                               continue;
+                       }
+
+                       /*
+                        * The remaining offsets represent gesture state, and
+                        * are discarded in the case of IQS7222C because only
+                        * absolute position is reported.
+                        */
+                       if (num_stat < IQS7222_MAX_COLS_STAT)
+                               continue;
+
+                       input_report_key(iqs7222->keypad,
+                                        iqs7222->sl_code[i][j],
+                                        (state & mask) == val);
+               }
+       }
+
+       input_sync(iqs7222->keypad);
+
+       return 0;
+}
+
+static irqreturn_t iqs7222_irq(int irq, void *context)
+{
+       struct iqs7222_private *iqs7222 = context;
+
+       return iqs7222_report(iqs7222) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int iqs7222_probe(struct i2c_client *client)
+{
+       struct iqs7222_private *iqs7222;
+       unsigned long irq_flags;
+       int error, irq;
+
+       iqs7222 = devm_kzalloc(&client->dev, sizeof(*iqs7222), GFP_KERNEL);
+       if (!iqs7222)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, iqs7222);
+       iqs7222->client = client;
+
+       iqs7222->keypad = devm_input_allocate_device(&client->dev);
+       if (!iqs7222->keypad)
+               return -ENOMEM;
+
+       iqs7222->keypad->name = client->name;
+       iqs7222->keypad->id.bustype = BUS_I2C;
+
+       /*
+        * The RDY pin behaves as an interrupt, but must also be polled ahead
+        * of unsolicited I2C communication. As such, it is first opened as a
+        * GPIO and then passed to gpiod_to_irq() to register the interrupt.
+        */
+       iqs7222->irq_gpio = devm_gpiod_get(&client->dev, "irq", GPIOD_IN);
+       if (IS_ERR(iqs7222->irq_gpio)) {
+               error = PTR_ERR(iqs7222->irq_gpio);
+               dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
+                       error);
+               return error;
+       }
+
+       iqs7222->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+                                                     GPIOD_OUT_HIGH);
+       if (IS_ERR(iqs7222->reset_gpio)) {
+               error = PTR_ERR(iqs7222->reset_gpio);
+               dev_err(&client->dev, "Failed to request reset GPIO: %d\n",
+                       error);
+               return error;
+       }
+
+       error = iqs7222_hard_reset(iqs7222);
+       if (error)
+               return error;
+
+       error = iqs7222_dev_info(iqs7222);
+       if (error)
+               return error;
+
+       error = iqs7222_dev_init(iqs7222, READ);
+       if (error)
+               return error;
+
+       error = iqs7222_parse_all(iqs7222);
+       if (error)
+               return error;
+
+       error = iqs7222_dev_init(iqs7222, WRITE);
+       if (error)
+               return error;
+
+       error = iqs7222_report(iqs7222);
+       if (error)
+               return error;
+
+       error = input_register_device(iqs7222->keypad);
+       if (error) {
+               dev_err(&client->dev, "Failed to register device: %d\n", error);
+               return error;
+       }
+
+       irq = gpiod_to_irq(iqs7222->irq_gpio);
+       if (irq < 0)
+               return irq;
+
+       irq_flags = gpiod_is_active_low(iqs7222->irq_gpio) ? IRQF_TRIGGER_LOW
+                                                          : IRQF_TRIGGER_HIGH;
+       irq_flags |= IRQF_ONESHOT;
+
+       error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7222_irq,
+                                         irq_flags, client->name, iqs7222);
+       if (error)
+               dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+
+       return error;
+}
+
+static const struct of_device_id iqs7222_of_match[] = {
+       { .compatible = "azoteq,iqs7222a" },
+       { .compatible = "azoteq,iqs7222b" },
+       { .compatible = "azoteq,iqs7222c" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, iqs7222_of_match);
+
+static struct i2c_driver iqs7222_i2c_driver = {
+       .driver = {
+               .name = "iqs7222",
+               .of_match_table = iqs7222_of_match,
+       },
+       .probe_new = iqs7222_probe,
+};
+module_i2c_driver(iqs7222_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS7222A/B/C Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
index 89af524..549df01 100644 (file)
@@ -9,9 +9,11 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/reboot.h>
 
 #define PON_REV2                       0x01
 
+#define PON_SUBTYPE                    0x05
+
+#define PON_SUBTYPE_PRIMARY            0x01
+#define PON_SUBTYPE_SECONDARY          0x02
+#define PON_SUBTYPE_1REG               0x03
+#define PON_SUBTYPE_GEN2_PRIMARY       0x04
+#define PON_SUBTYPE_GEN2_SECONDARY     0x05
+#define PON_SUBTYPE_GEN3_PBS           0x08
+#define PON_SUBTYPE_GEN3_HLOS          0x09
+
 #define PON_RT_STS                     0x10
 #define  PON_KPDPWR_N_SET              BIT(0)
 #define  PON_RESIN_N_SET               BIT(1)
@@ -45,6 +57,7 @@ struct pm8941_data {
        unsigned int    status_bit;
        bool            supports_ps_hold_poff_config;
        bool            supports_debounce_config;
+       bool            has_pon_pbs;
        const char      *name;
        const char      *phys;
 };
@@ -53,13 +66,18 @@ struct pm8941_pwrkey {
        struct device *dev;
        int irq;
        u32 baseaddr;
+       u32 pon_pbs_baseaddr;
        struct regmap *regmap;
        struct input_dev *input;
 
        unsigned int revision;
+       unsigned int subtype;
        struct notifier_block reboot_notifier;
 
        u32 code;
+       u32 sw_debounce_time_us;
+       ktime_t sw_debounce_end_time;
+       bool last_status;
        const struct pm8941_data *data;
 };
 
@@ -129,20 +147,76 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
 {
        struct pm8941_pwrkey *pwrkey = _data;
        unsigned int sts;
-       int error;
+       int err;
+
+       if (pwrkey->sw_debounce_time_us) {
+               if (ktime_before(ktime_get(), pwrkey->sw_debounce_end_time)) {
+                       dev_dbg(pwrkey->dev,
+                               "ignoring key event received before debounce end %llu us\n",
+                               pwrkey->sw_debounce_end_time);
+                       return IRQ_HANDLED;
+               }
+       }
 
-       error = regmap_read(pwrkey->regmap,
-                           pwrkey->baseaddr + PON_RT_STS, &sts);
-       if (error)
+       err = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_RT_STS, &sts);
+       if (err)
                return IRQ_HANDLED;
 
-       input_report_key(pwrkey->input, pwrkey->code,
-                        sts & pwrkey->data->status_bit);
+       sts &= pwrkey->data->status_bit;
+
+       if (pwrkey->sw_debounce_time_us && !sts)
+               pwrkey->sw_debounce_end_time = ktime_add_us(ktime_get(),
+                                               pwrkey->sw_debounce_time_us);
+
+       /*
+        * Simulate a press event in case a release event occurred without a
+        * corresponding press event.
+        */
+       if (!pwrkey->last_status && !sts) {
+               input_report_key(pwrkey->input, pwrkey->code, 1);
+               input_sync(pwrkey->input);
+       }
+       pwrkey->last_status = sts;
+
+       input_report_key(pwrkey->input, pwrkey->code, sts);
        input_sync(pwrkey->input);
 
        return IRQ_HANDLED;
 }
 
+static int pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey *pwrkey)
+{
+       unsigned int val, addr, mask;
+       int error;
+
+       if (pwrkey->data->has_pon_pbs && !pwrkey->pon_pbs_baseaddr) {
+               dev_err(pwrkey->dev,
+                       "PON_PBS address missing, can't read HW debounce time\n");
+               return 0;
+       }
+
+       if (pwrkey->pon_pbs_baseaddr)
+               addr = pwrkey->pon_pbs_baseaddr + PON_DBC_CTL;
+       else
+               addr = pwrkey->baseaddr + PON_DBC_CTL;
+       error = regmap_read(pwrkey->regmap, addr, &val);
+       if (error)
+               return error;
+
+       if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
+               mask = 0xf;
+       else
+               mask = 0x7;
+
+       pwrkey->sw_debounce_time_us =
+               2 * USEC_PER_SEC / (1 << (mask - (val & mask)));
+
+       dev_dbg(pwrkey->dev, "SW debounce time = %u us\n",
+               pwrkey->sw_debounce_time_us);
+
+       return 0;
+}
+
 static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
 {
        struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -171,6 +245,8 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
        struct pm8941_pwrkey *pwrkey;
        bool pull_up;
        struct device *parent;
+       struct device_node *regmap_node;
+       const __be32 *addr;
        u32 req_delay;
        int error;
 
@@ -192,8 +268,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
        pwrkey->data = of_device_get_match_data(&pdev->dev);
 
        parent = pdev->dev.parent;
+       regmap_node = pdev->dev.of_node;
        pwrkey->regmap = dev_get_regmap(parent, NULL);
        if (!pwrkey->regmap) {
+               regmap_node = parent->of_node;
                /*
                 * We failed to get regmap for parent. Let's see if we are
                 * a child of pon node and read regmap and reg from its
@@ -204,15 +282,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "failed to locate regmap\n");
                        return -ENODEV;
                }
+       }
 
-               error = of_property_read_u32(parent->of_node,
-                                            "reg", &pwrkey->baseaddr);
-       } else {
-               error = of_property_read_u32(pdev->dev.of_node, "reg",
-                                            &pwrkey->baseaddr);
+       addr = of_get_address(regmap_node, 0, NULL, NULL);
+       if (!addr) {
+               dev_err(&pdev->dev, "reg property missing\n");
+               return -EINVAL;
+       }
+       pwrkey->baseaddr = be32_to_cpup(addr);
+
+       if (pwrkey->data->has_pon_pbs) {
+               /* PON_PBS base address is optional */
+               addr = of_get_address(regmap_node, 1, NULL, NULL);
+               if (addr)
+                       pwrkey->pon_pbs_baseaddr = be32_to_cpup(addr);
        }
-       if (error)
-               return error;
 
        pwrkey->irq = platform_get_irq(pdev, 0);
        if (pwrkey->irq < 0)
@@ -221,7 +305,14 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
        error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
                            &pwrkey->revision);
        if (error) {
-               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+               dev_err(&pdev->dev, "failed to read revision: %d\n", error);
+               return error;
+       }
+
+       error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_SUBTYPE,
+                           &pwrkey->subtype);
+       if (error) {
+               dev_err(&pdev->dev, "failed to read subtype: %d\n", error);
                return error;
        }
 
@@ -259,6 +350,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
                }
        }
 
+       error = pm8941_pwrkey_sw_debounce_init(pwrkey);
+       if (error)
+               return error;
+
        if (pwrkey->data->pull_up_bit) {
                error = regmap_update_bits(pwrkey->regmap,
                                           pwrkey->baseaddr + PON_PULL_CTL,
@@ -320,6 +415,7 @@ static const struct pm8941_data pwrkey_data = {
        .phys = "pm8941_pwrkey/input0",
        .supports_ps_hold_poff_config = true,
        .supports_debounce_config = true,
+       .has_pon_pbs = false,
 };
 
 static const struct pm8941_data resin_data = {
@@ -329,6 +425,7 @@ static const struct pm8941_data resin_data = {
        .phys = "pm8941_resin/input0",
        .supports_ps_hold_poff_config = true,
        .supports_debounce_config = true,
+       .has_pon_pbs = false,
 };
 
 static const struct pm8941_data pon_gen3_pwrkey_data = {
@@ -337,6 +434,7 @@ static const struct pm8941_data pon_gen3_pwrkey_data = {
        .phys = "pmic_pwrkey/input0",
        .supports_ps_hold_poff_config = false,
        .supports_debounce_config = false,
+       .has_pon_pbs = true,
 };
 
 static const struct pm8941_data pon_gen3_resin_data = {
@@ -345,6 +443,7 @@ static const struct pm8941_data pon_gen3_resin_data = {
        .phys = "pmic_resin/input0",
        .supports_ps_hold_poff_config = false,
        .supports_debounce_config = false,
+       .has_pon_pbs = true,
 };
 
 static const struct of_device_id pm8941_pwr_key_id_table[] = {
index fe43e55..cdcb773 100644 (file)
@@ -205,6 +205,7 @@ static int bbc_beep_probe(struct platform_device *op)
 
        info = &state->u.bbc;
        info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
+       of_node_put(dp);
        if (!info->clock_freq)
                goto out_free;
 
index 5f86800..d272f1e 100644 (file)
@@ -696,7 +696,7 @@ int cypress_init(struct psmouse *psmouse)
 err_exit:
        /*
         * Reset Cypress Trackpad as a standard mouse. Then
-        * let psmouse driver commmunicating with it as default PS2 mouse.
+        * let psmouse driver communicating with it as default PS2 mouse.
         */
        cypress_reset(psmouse);
 
index 164f6c7..2a2459b 100644 (file)
@@ -26,6 +26,8 @@ struct psmouse_smbus_dev {
 static LIST_HEAD(psmouse_smbus_list);
 static DEFINE_MUTEX(psmouse_smbus_mutex);
 
+static struct workqueue_struct *psmouse_smbus_wq;
+
 static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
 {
        struct psmouse_smbus_dev *smbdev;
@@ -161,7 +163,7 @@ static void psmouse_smbus_schedule_remove(struct i2c_client *client)
                INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
                rwork->client = client;
 
-               schedule_work(&rwork->work);
+               queue_work(psmouse_smbus_wq, &rwork->work);
        }
 }
 
@@ -305,9 +307,14 @@ int __init psmouse_smbus_module_init(void)
 {
        int error;
 
+       psmouse_smbus_wq = alloc_workqueue("psmouse-smbus", 0, 0);
+       if (!psmouse_smbus_wq)
+               return -ENOMEM;
+
        error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
        if (error) {
                pr_err("failed to register i2c bus notifier: %d\n", error);
+               destroy_workqueue(psmouse_smbus_wq);
                return error;
        }
 
@@ -317,5 +324,5 @@ int __init psmouse_smbus_module_init(void)
 void psmouse_smbus_module_exit(void)
 {
        bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
-       flush_scheduled_work();
+       destroy_workqueue(psmouse_smbus_wq);
 }
index 42443ff..ea9eff7 100644 (file)
@@ -366,6 +366,19 @@ int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
 }
 
 /**
+ * vmmouse_reset - Disable vmmouse and reset
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to disable vmmouse mode before enter suspend.
+ */
+static void vmmouse_reset(struct psmouse *psmouse)
+{
+       vmmouse_disable(psmouse);
+       psmouse_reset(psmouse);
+}
+
+/**
  * vmmouse_disconnect - Take down vmmouse driver
  *
  * @psmouse: Pointer to the psmouse struct
@@ -472,6 +485,7 @@ int vmmouse_init(struct psmouse *psmouse)
        psmouse->protocol_handler = vmmouse_process_byte;
        psmouse->disconnect = vmmouse_disconnect;
        psmouse->reconnect = vmmouse_reconnect;
+       psmouse->cleanup = vmmouse_reset;
 
        return 0;
 
index 93b328c..c5ce907 100644 (file)
@@ -733,7 +733,6 @@ remove_v4l2:
        v4l2_device_unregister(&f54->v4l2);
 remove_wq:
        cancel_delayed_work_sync(&f54->work);
-       flush_workqueue(f54->workqueue);
        destroy_workqueue(f54->workqueue);
        return ret;
 }
index 1581f6e..24ec484 100644 (file)
@@ -931,8 +931,7 @@ aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
        }
        msleep(aiptek->curSetting.programmableDelay);
 
-       if ((ret =
-            aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+       if (aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf) != sizeof_buf) {
                dev_dbg(&aiptek->intf->dev,
                        "aiptek_query failed: returned 0x%02x 0x%02x 0x%02x\n",
                        buf[0], buf[1], buf[2]);
index 72e0b76..c175d44 100644 (file)
@@ -337,13 +337,15 @@ static int stmfts_input_open(struct input_dev *dev)
        struct stmfts_data *sdata = input_get_drvdata(dev);
        int err;
 
-       err = pm_runtime_get_sync(&sdata->client->dev);
-       if (err < 0)
-               goto out;
+       err = pm_runtime_resume_and_get(&sdata->client->dev);
+       if (err)
+               return err;
 
        err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON);
-       if (err)
-               goto out;
+       if (err) {
+               pm_runtime_put_sync(&sdata->client->dev);
+               return err;
+       }
 
        mutex_lock(&sdata->mutex);
        sdata->running = true;
@@ -366,9 +368,7 @@ static int stmfts_input_open(struct input_dev *dev)
                                 "failed to enable touchkey\n");
        }
 
-out:
-       pm_runtime_put_noidle(&sdata->client->dev);
-       return err;
+       return 0;
 }
 
 static void stmfts_input_close(struct input_dev *dev)
index 866767b..b0a7dee 100644 (file)
@@ -124,12 +124,23 @@ static const struct of_device_id ksz8795_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, ksz8795_dt_ids);
 
+static const struct spi_device_id ksz8795_spi_ids[] = {
+       { "ksz8765" },
+       { "ksz8794" },
+       { "ksz8795" },
+       { "ksz8863" },
+       { "ksz8873" },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, ksz8795_spi_ids);
+
 static struct spi_driver ksz8795_spi_driver = {
        .driver = {
                .name   = "ksz8795-switch",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(ksz8795_dt_ids),
        },
+       .id_table = ksz8795_spi_ids,
        .probe  = ksz8795_spi_probe,
        .remove = ksz8795_spi_remove,
        .shutdown = ksz8795_spi_shutdown,
index e3cb0e6..43addea 100644 (file)
@@ -98,12 +98,24 @@ static const struct of_device_id ksz9477_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
 
+static const struct spi_device_id ksz9477_spi_ids[] = {
+       { "ksz9477" },
+       { "ksz9897" },
+       { "ksz9893" },
+       { "ksz9563" },
+       { "ksz8563" },
+       { "ksz9567" },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, ksz9477_spi_ids);
+
 static struct spi_driver ksz9477_spi_driver = {
        .driver = {
                .name   = "ksz9477-switch",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(ksz9477_dt_ids),
        },
+       .id_table = ksz9477_spi_ids,
        .probe  = ksz9477_spi_probe,
        .remove = ksz9477_spi_remove,
        .shutdown = ksz9477_spi_shutdown,
index 4ad3fc7..a89b93c 100644 (file)
@@ -1181,8 +1181,11 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
        alx->hw.mtu = mtu;
        alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
        netdev_update_features(netdev);
-       if (netif_running(netdev))
+       if (netif_running(netdev)) {
+               mutex_lock(&alx->mtx);
                alx_reinit(alx);
+               mutex_unlock(&alx->mtx);
+       }
        return 0;
 }
 
index a19dd67..2209d99 100644 (file)
@@ -2533,6 +2533,4 @@ void bnx2x_register_phc(struct bnx2x *bp);
  * Meant for implicit re-load flows.
  */
 int bnx2x_vlan_reconfigure_vid(struct bnx2x *bp);
-int bnx2x_init_firmware(struct bnx2x *bp);
-void bnx2x_release_firmware(struct bnx2x *bp);
 #endif /* bnx2x.h */
index 8d36ebb..5729a5a 100644 (file)
@@ -2364,24 +2364,30 @@ int bnx2x_compare_fw_ver(struct bnx2x *bp, u32 load_code, bool print_err)
        /* is another pf loaded on this engine? */
        if (load_code != FW_MSG_CODE_DRV_LOAD_COMMON_CHIP &&
            load_code != FW_MSG_CODE_DRV_LOAD_COMMON) {
-               /* build my FW version dword */
-               u32 my_fw = (bp->fw_major) + (bp->fw_minor << 8) +
-                               (bp->fw_rev << 16) + (bp->fw_eng << 24);
+               u8 loaded_fw_major, loaded_fw_minor, loaded_fw_rev, loaded_fw_eng;
+               u32 loaded_fw;
 
                /* read loaded FW from chip */
-               u32 loaded_fw = REG_RD(bp, XSEM_REG_PRAM);
+               loaded_fw = REG_RD(bp, XSEM_REG_PRAM);
 
-               DP(BNX2X_MSG_SP, "loaded fw %x, my fw %x\n",
-                  loaded_fw, my_fw);
+               loaded_fw_major = loaded_fw & 0xff;
+               loaded_fw_minor = (loaded_fw >> 8) & 0xff;
+               loaded_fw_rev = (loaded_fw >> 16) & 0xff;
+               loaded_fw_eng = (loaded_fw >> 24) & 0xff;
+
+               DP(BNX2X_MSG_SP, "loaded fw 0x%x major 0x%x minor 0x%x rev 0x%x eng 0x%x\n",
+                  loaded_fw, loaded_fw_major, loaded_fw_minor, loaded_fw_rev, loaded_fw_eng);
 
                /* abort nic load if version mismatch */
-               if (my_fw != loaded_fw) {
+               if (loaded_fw_major != BCM_5710_FW_MAJOR_VERSION ||
+                   loaded_fw_minor != BCM_5710_FW_MINOR_VERSION ||
+                   loaded_fw_eng != BCM_5710_FW_ENGINEERING_VERSION ||
+                   loaded_fw_rev < BCM_5710_FW_REVISION_VERSION_V15) {
                        if (print_err)
-                               BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. Aborting\n",
-                                         loaded_fw, my_fw);
+                               BNX2X_ERR("loaded FW incompatible. Aborting\n");
                        else
-                               BNX2X_DEV_INFO("bnx2x with FW %x was already loaded which mismatches my %x FW, possibly due to MF UNDI\n",
-                                              loaded_fw, my_fw);
+                               BNX2X_DEV_INFO("loaded FW incompatible, possibly due to MF UNDI\n");
+
                        return -EBUSY;
                }
        }
index eedb48d..c19b072 100644 (file)
@@ -12319,15 +12319,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 
        bnx2x_read_fwinfo(bp);
 
-       if (IS_PF(bp)) {
-               rc = bnx2x_init_firmware(bp);
-
-               if (rc) {
-                       bnx2x_free_mem_bp(bp);
-                       return rc;
-               }
-       }
-
        func = BP_FUNC(bp);
 
        /* need to reset chip if undi was active */
@@ -12340,7 +12331,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 
                rc = bnx2x_prev_unload(bp);
                if (rc) {
-                       bnx2x_release_firmware(bp);
                        bnx2x_free_mem_bp(bp);
                        return rc;
                }
@@ -13409,7 +13399,7 @@ do {                                                                    \
             (u8 *)bp->arr, len);                                       \
 } while (0)
 
-int bnx2x_init_firmware(struct bnx2x *bp)
+static int bnx2x_init_firmware(struct bnx2x *bp)
 {
        const char *fw_file_name, *fw_file_name_v15;
        struct bnx2x_fw_file_hdr *fw_hdr;
@@ -13509,7 +13499,7 @@ request_firmware_exit:
        return rc;
 }
 
-void bnx2x_release_firmware(struct bnx2x *bp)
+static void bnx2x_release_firmware(struct bnx2x *bp)
 {
        kfree(bp->init_ops_offsets);
        kfree(bp->init_ops);
@@ -14026,7 +14016,6 @@ static int bnx2x_init_one(struct pci_dev *pdev,
        return 0;
 
 init_one_freemem:
-       bnx2x_release_firmware(bp);
        bnx2x_free_mem_bp(bp);
 
 init_one_exit:
index 87f1056..2da804f 100644 (file)
@@ -2287,8 +2287,10 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
                dma_length_status = status->length_status;
                if (dev->features & NETIF_F_RXCSUM) {
                        rx_csum = (__force __be16)(status->rx_csum & 0xffff);
-                       skb->csum = (__force __wsum)ntohs(rx_csum);
-                       skb->ip_summed = CHECKSUM_COMPLETE;
+                       if (rx_csum) {
+                               skb->csum = (__force __wsum)ntohs(rx_csum);
+                               skb->ip_summed = CHECKSUM_COMPLETE;
+                       }
                }
 
                /* DMA flags and length are still valid no matter how
index 8e644e9..0e178a0 100644 (file)
@@ -2541,6 +2541,13 @@ restart_watchdog:
                queue_delayed_work(iavf_wq, &adapter->watchdog_task, HZ * 2);
 }
 
+/**
+ * iavf_disable_vf - disable VF
+ * @adapter: board private structure
+ *
+ * Set communication failed flag and free all resources.
+ * NOTE: This function is expected to be called with crit_lock being held.
+ **/
 static void iavf_disable_vf(struct iavf_adapter *adapter)
 {
        struct iavf_mac_filter *f, *ftmp;
@@ -2595,7 +2602,6 @@ static void iavf_disable_vf(struct iavf_adapter *adapter)
        memset(adapter->vf_res, 0, IAVF_VIRTCHNL_VF_RESOURCE_SIZE);
        iavf_shutdown_adminq(&adapter->hw);
        adapter->netdev->flags &= ~IFF_UP;
-       mutex_unlock(&adapter->crit_lock);
        adapter->flags &= ~IAVF_FLAG_RESET_PENDING;
        iavf_change_state(adapter, __IAVF_DOWN);
        wake_up(&adapter->down_waitqueue);
@@ -4614,6 +4620,13 @@ static void iavf_remove(struct pci_dev *pdev)
        struct iavf_hw *hw = &adapter->hw;
        int err;
 
+       /* When reboot/shutdown is in progress no need to do anything
+        * as the adapter is already REMOVE state that was set during
+        * iavf_shutdown() callback.
+        */
+       if (adapter->state == __IAVF_REMOVE)
+               return;
+
        set_bit(__IAVF_IN_REMOVE_TASK, &adapter->crit_section);
        /* Wait until port initialization is complete.
         * There are flows where register/unregister netdev may race.
index 493942e..b7e8744 100644 (file)
@@ -4880,7 +4880,6 @@ static void ice_remove(struct pci_dev *pdev)
        ice_devlink_unregister_params(pf);
        set_bit(ICE_DOWN, pf->state);
 
-       mutex_destroy(&(&pf->hw)->fdir_fltr_lock);
        ice_deinit_lag(pf);
        if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
                ice_ptp_release(pf);
@@ -4888,6 +4887,7 @@ static void ice_remove(struct pci_dev *pdev)
                ice_remove_arfs(pf);
        ice_setup_mc_magic_wake(pf);
        ice_vsi_release_all(pf);
+       mutex_destroy(&(&pf->hw)->fdir_fltr_lock);
        ice_set_wake(pf);
        ice_free_irq_msix_misc(pf);
        ice_for_each_vsi(pf, i) {
@@ -5962,8 +5962,9 @@ ice_update_vsi_tx_ring_stats(struct ice_vsi *vsi,
                u64 pkts = 0, bytes = 0;
 
                ring = READ_ONCE(rings[i]);
-               if (ring)
-                       ice_fetch_u64_stats_per_ring(&ring->syncp, ring->stats, &pkts, &bytes);
+               if (!ring)
+                       continue;
+               ice_fetch_u64_stats_per_ring(&ring->syncp, ring->stats, &pkts, &bytes);
                vsi_stats->tx_packets += pkts;
                vsi_stats->tx_bytes += bytes;
                vsi->tx_restart += ring->tx_stats.restart_q;
index 9498588..fdb4d7e 100644 (file)
@@ -60,6 +60,12 @@ static int ocelot_chain_to_block(int chain, bool ingress)
  */
 static int ocelot_chain_to_lookup(int chain)
 {
+       /* Backwards compatibility with older, single-chain tc-flower
+        * offload support in Ocelot
+        */
+       if (chain == 0)
+               return 0;
+
        return (chain / VCAP_LOOKUP) % 10;
 }
 
@@ -68,7 +74,15 @@ static int ocelot_chain_to_lookup(int chain)
  */
 static int ocelot_chain_to_pag(int chain)
 {
-       int lookup = ocelot_chain_to_lookup(chain);
+       int lookup;
+
+       /* Backwards compatibility with older, single-chain tc-flower
+        * offload support in Ocelot
+        */
+       if (chain == 0)
+               return 0;
+
+       lookup = ocelot_chain_to_lookup(chain);
 
        /* calculate PAG value as chain index relative to the first PAG */
        return chain - VCAP_IS2_CHAIN(lookup, 0);
index 3646469..fde1c49 100644 (file)
@@ -1587,6 +1587,9 @@ static void netvsc_get_ethtool_stats(struct net_device *dev,
        pcpu_sum = kvmalloc_array(num_possible_cpus(),
                                  sizeof(struct netvsc_ethtool_pcpu_stats),
                                  GFP_KERNEL);
+       if (!pcpu_sum)
+               return;
+
        netvsc_get_pcpu_stats(dev, pcpu_sum);
        for_each_present_cpu(cpu) {
                struct netvsc_ethtool_pcpu_stats *this_sum = &pcpu_sum[cpu];
index 7d2abaf..64fb76c 100644 (file)
@@ -187,6 +187,13 @@ static const struct regmap_config mscc_miim_regmap_config = {
        .reg_stride     = 4,
 };
 
+static const struct regmap_config mscc_miim_phy_regmap_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+       .name           = "phy",
+};
+
 int mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name,
                    struct regmap *mii_regmap, int status_offset)
 {
@@ -250,7 +257,7 @@ static int mscc_miim_probe(struct platform_device *pdev)
                }
 
                phy_regmap = devm_regmap_init_mmio(&pdev->dev, phy_regs,
-                                                  &mscc_miim_regmap_config);
+                                                  &mscc_miim_phy_regmap_config);
                if (IS_ERR(phy_regmap)) {
                        dev_err(&pdev->dev, "Unable to create phy register regmap\n");
                        return PTR_ERR(phy_regmap);
index 2429db6..2702faf 100644 (file)
@@ -1687,8 +1687,8 @@ static int marvell_suspend(struct phy_device *phydev)
        int err;
 
        /* Suspend the fiber mode first */
-       if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
-                              phydev->supported)) {
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                             phydev->supported)) {
                err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
@@ -1722,8 +1722,8 @@ static int marvell_resume(struct phy_device *phydev)
        int err;
 
        /* Resume the fiber mode first */
-       if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
-                              phydev->supported)) {
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                             phydev->supported)) {
                err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
index ebfeeb3..7e3017e 100644 (file)
@@ -2685,3 +2685,6 @@ MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
 MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
 MODULE_AUTHOR("Nagaraju Lakkaraju");
 MODULE_LICENSE("Dual MIT/GPL");
+
+MODULE_FIRMWARE(MSCC_VSC8584_REVB_INT8051_FW);
+MODULE_FIRMWARE(MSCC_VSC8574_REVB_INT8051_FW);
index 62c453a..7c1c265 100644 (file)
@@ -2611,36 +2611,9 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
                ath10k_mac_handle_beacon(ar, skb);
 
        if (ieee80211_is_beacon(hdr->frame_control) ||
-           ieee80211_is_probe_resp(hdr->frame_control)) {
-               struct ieee80211_mgmt *mgmt = (void *)skb->data;
-               enum cfg80211_bss_frame_type ftype;
-               u8 *ies;
-               int ies_ch;
-
+           ieee80211_is_probe_resp(hdr->frame_control))
                status->boottime_ns = ktime_get_boottime_ns();
 
-               if (!ar->scan_channel)
-                       goto drop;
-
-               ies = mgmt->u.beacon.variable;
-
-               if (ieee80211_is_beacon(mgmt->frame_control))
-                       ftype = CFG80211_BSS_FTYPE_BEACON;
-               else
-                       ftype = CFG80211_BSS_FTYPE_PRESP;
-
-               ies_ch = cfg80211_get_ies_channel_number(mgmt->u.beacon.variable,
-                                                        skb_tail_pointer(skb) - ies,
-                                                        sband->band, ftype);
-
-               if (ies_ch > 0 && ies_ch != channel) {
-                       ath10k_dbg(ar, ATH10K_DBG_MGMT,
-                                  "channel mismatched ds channel %d scan channel %d\n",
-                                  ies_ch, channel);
-                       goto drop;
-               }
-       }
-
        ath10k_dbg(ar, ATH10K_DBG_MGMT,
                   "event mgmt rx skb %pK len %d ftype %02x stype %02x\n",
                   skb, skb->len,
@@ -2654,10 +2627,6 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        ieee80211_rx_ni(ar->hw, skb);
 
        return 0;
-
-drop:
-       dev_kfree_skb(skb);
-       return 0;
 }
 
 static int freq_to_idx(struct ath10k *ar, int freq)
index 091a0ca..496d775 100644 (file)
@@ -1233,44 +1233,6 @@ static ssize_t nvmet_subsys_attr_model_store(struct config_item *item,
 }
 CONFIGFS_ATTR(nvmet_subsys_, attr_model);
 
-static ssize_t nvmet_subsys_attr_discovery_nqn_show(struct config_item *item,
-                       char *page)
-{
-       return snprintf(page, PAGE_SIZE, "%s\n",
-                       nvmet_disc_subsys->subsysnqn);
-}
-
-static ssize_t nvmet_subsys_attr_discovery_nqn_store(struct config_item *item,
-                       const char *page, size_t count)
-{
-       struct nvmet_subsys *subsys = to_subsys(item);
-       char *subsysnqn;
-       int len;
-
-       len = strcspn(page, "\n");
-       if (!len)
-               return -EINVAL;
-
-       subsysnqn = kmemdup_nul(page, len, GFP_KERNEL);
-       if (!subsysnqn)
-               return -ENOMEM;
-
-       /*
-        * The discovery NQN must be different from subsystem NQN.
-        */
-       if (!strcmp(subsysnqn, subsys->subsysnqn)) {
-               kfree(subsysnqn);
-               return -EBUSY;
-       }
-       down_write(&nvmet_config_sem);
-       kfree(nvmet_disc_subsys->subsysnqn);
-       nvmet_disc_subsys->subsysnqn = subsysnqn;
-       up_write(&nvmet_config_sem);
-
-       return count;
-}
-CONFIGFS_ATTR(nvmet_subsys_, attr_discovery_nqn);
-
 #ifdef CONFIG_BLK_DEV_INTEGRITY
 static ssize_t nvmet_subsys_attr_pi_enable_show(struct config_item *item,
                                                char *page)
@@ -1300,7 +1262,6 @@ static struct configfs_attribute *nvmet_subsys_attrs[] = {
        &nvmet_subsys_attr_attr_cntlid_min,
        &nvmet_subsys_attr_attr_cntlid_max,
        &nvmet_subsys_attr_attr_model,
-       &nvmet_subsys_attr_attr_discovery_nqn,
 #ifdef CONFIG_BLK_DEV_INTEGRITY
        &nvmet_subsys_attr_attr_pi_enable,
 #endif
index 5119c68..626caf6 100644 (file)
@@ -1493,8 +1493,7 @@ static struct nvmet_subsys *nvmet_find_get_subsys(struct nvmet_port *port,
        if (!port)
                return NULL;
 
-       if (!strcmp(NVME_DISC_SUBSYS_NAME, subsysnqn) ||
-           !strcmp(nvmet_disc_subsys->subsysnqn, subsysnqn)) {
+       if (!strcmp(NVME_DISC_SUBSYS_NAME, subsysnqn)) {
                if (!kref_get_unless_zero(&nvmet_disc_subsys->ref))
                        return NULL;
                return nvmet_disc_subsys;
index 88c549f..40a52fe 100644 (file)
@@ -986,8 +986,6 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
        CMD_SP(sc) = NULL;
        CMD_FLAGS(sc) |= FNIC_IO_DONE;
 
-       spin_unlock_irqrestore(io_lock, flags);
-
        if (hdr_status != FCPIO_SUCCESS) {
                atomic64_inc(&fnic_stats->io_stats.io_failures);
                shost_printk(KERN_ERR, fnic->lport->host, "hdr status = %s\n",
@@ -996,8 +994,6 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 
        fnic_release_ioreq_buf(fnic, io_req, sc);
 
-       mempool_free(io_req, fnic->io_req_pool);
-
        cmd_trace = ((u64)hdr_status << 56) |
                  (u64)icmnd_cmpl->scsi_status << 48 |
                  (u64)icmnd_cmpl->flags << 40 | (u64)sc->cmnd[0] << 32 |
@@ -1021,6 +1017,12 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
        } else
                fnic->lport->host_stats.fcp_control_requests++;
 
+       /* Call SCSI completion function to complete the IO */
+       scsi_done(sc);
+       spin_unlock_irqrestore(io_lock, flags);
+
+       mempool_free(io_req, fnic->io_req_pool);
+
        atomic64_dec(&fnic_stats->io_stats.active_ios);
        if (atomic64_read(&fnic->io_cmpl_skip))
                atomic64_dec(&fnic->io_cmpl_skip);
@@ -1049,9 +1051,6 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
                if(io_duration_time > atomic64_read(&fnic_stats->io_stats.current_max_io_time))
                        atomic64_set(&fnic_stats->io_stats.current_max_io_time, io_duration_time);
        }
-
-       /* Call SCSI completion function to complete the IO */
-       scsi_done(sc);
 }
 
 /* fnic_fcpio_itmf_cmpl_handler
index 511726f..76229b8 100644 (file)
@@ -2011,9 +2011,10 @@ mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc, u8 poll)
                                enable_irq(reply_q->os_irq);
                        }
                }
+
+               if (poll)
+                       _base_process_reply_queue(reply_q);
        }
-       if (poll)
-               _base_process_reply_queue(reply_q);
 }
 
 /**
index 73f419a..4bb6d30 100644 (file)
@@ -1919,6 +1919,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
        struct usbtmc_ctrlrequest request;
        u8 *buffer = NULL;
        int rv;
+       unsigned int is_in, pipe;
        unsigned long res;
 
        res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest));
@@ -1928,12 +1929,14 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
        if (request.req.wLength > USBTMC_BUFSIZE)
                return -EMSGSIZE;
 
+       is_in = request.req.bRequestType & USB_DIR_IN;
+
        if (request.req.wLength) {
                buffer = kmalloc(request.req.wLength, GFP_KERNEL);
                if (!buffer)
                        return -ENOMEM;
 
-               if ((request.req.bRequestType & USB_DIR_IN) == 0) {
+               if (!is_in) {
                        /* Send control data to device */
                        res = copy_from_user(buffer, request.data,
                                             request.req.wLength);
@@ -1944,8 +1947,12 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
                }
        }
 
+       if (is_in)
+               pipe = usb_rcvctrlpipe(data->usb_dev, 0);
+       else
+               pipe = usb_sndctrlpipe(data->usb_dev, 0);
        rv = usb_control_msg(data->usb_dev,
-                       usb_rcvctrlpipe(data->usb_dev, 0),
+                       pipe,
                        request.req.bRequest,
                        request.req.bRequestType,
                        request.req.wValue,
@@ -1957,7 +1964,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
                goto exit;
        }
 
-       if (rv && (request.req.bRequestType & USB_DIR_IN)) {
+       if (rv && is_in) {
                /* Read control data from device */
                res = copy_to_user(request.data, buffer, rv);
                if (res)
index 00b3f6b..713efd9 100644 (file)
@@ -640,6 +640,7 @@ static int rndis_set_response(struct rndis_params *params,
        BufLength = le32_to_cpu(buf->InformationBufferLength);
        BufOffset = le32_to_cpu(buf->InformationBufferOffset);
        if ((BufLength > RNDIS_MAX_TOTAL_SIZE) ||
+           (BufOffset > RNDIS_MAX_TOTAL_SIZE) ||
            (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE))
                    return -EINVAL;
 
index 568534a..c109b06 100644 (file)
@@ -1436,7 +1436,6 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
        usb_gadget_udc_stop(udc);
 
        udc->driver = NULL;
-       udc->dev.driver = NULL;
        udc->gadget->dev.driver = NULL;
 }
 
@@ -1498,7 +1497,6 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
                        driver->function);
 
        udc->driver = driver;
-       udc->dev.driver = &driver->driver;
        udc->gadget->dev.driver = &driver->driver;
 
        usb_gadget_udc_set_speed(udc, driver->max_speed);
@@ -1521,7 +1519,6 @@ err1:
                dev_err(&udc->dev, "failed to start %s: %d\n",
                        udc->driver->function, ret);
        udc->driver = NULL;
-       udc->dev.driver = NULL;
        udc->gadget->dev.driver = NULL;
        return ret;
 }
index 7d4d071..d2b7e61 100644 (file)
@@ -327,7 +327,6 @@ static int omap2430_probe(struct platform_device *pdev)
        musb->dev.parent                = &pdev->dev;
        musb->dev.dma_mask              = &omap2430_dmamask;
        musb->dev.coherent_dma_mask     = omap2430_dmamask;
-       device_set_of_node_from_dev(&musb->dev, &pdev->dev);
 
        glue->dev                       = &pdev->dev;
        glue->musb                      = musb;
index 082380c..1768362 100644 (file)
@@ -1170,7 +1170,9 @@ ssize_t vhost_chr_write_iter(struct vhost_dev *dev,
                goto done;
        }
 
-       if (msg.size == 0) {
+       if ((msg.type == VHOST_IOTLB_UPDATE ||
+            msg.type == VHOST_IOTLB_INVALIDATE) &&
+            msg.size == 0) {
                ret = -EINVAL;
                goto done;
        }
index 37f0b42..e6c9d41 100644 (file)
@@ -753,7 +753,8 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file)
 
        /* Iterating over all connections for all CIDs to find orphans is
         * inefficient.  Room for improvement here. */
-       vsock_for_each_connected_socket(vhost_vsock_reset_orphans);
+       vsock_for_each_connected_socket(&vhost_transport.transport,
+                                       vhost_vsock_reset_orphans);
 
        /* Don't check the owner, because we are in the release path, so we
         * need to stop the vsock device in any case.
index 053cb44..d3020ab 100644 (file)
@@ -3924,7 +3924,8 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
 
        /* only send once per connect */
        spin_lock(&cifs_tcp_ses_lock);
-       if (server->tcpStatus != CifsNeedSessSetup) {
+       if ((server->tcpStatus != CifsNeedSessSetup) &&
+           (ses->status == CifsGood)) {
                spin_unlock(&cifs_tcp_ses_lock);
                return 0;
        }
index 2772dec..8bde30f 100644 (file)
@@ -1105,17 +1105,6 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
                goto read_super_error;
        }
 
-       root = d_make_root(inode);
-       if (!root) {
-               status = -ENOMEM;
-               mlog_errno(status);
-               goto read_super_error;
-       }
-
-       sb->s_root = root;
-
-       ocfs2_complete_mount_recovery(osb);
-
        osb->osb_dev_kset = kset_create_and_add(sb->s_id, NULL,
                                                &ocfs2_kset->kobj);
        if (!osb->osb_dev_kset) {
@@ -1133,6 +1122,17 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
                goto read_super_error;
        }
 
+       root = d_make_root(inode);
+       if (!root) {
+               status = -ENOMEM;
+               mlog_errno(status);
+               goto read_super_error;
+       }
+
+       sb->s_root = root;
+
+       ocfs2_complete_mount_recovery(osb);
+
        if (ocfs2_mount_local(osb))
                snprintf(nodestr, sizeof(nodestr), "local");
        else
index b712217..1ed5244 100644 (file)
@@ -52,6 +52,7 @@ static inline bool dev_is_mac_header_xmit(const struct net_device *dev)
        case ARPHRD_VOID:
        case ARPHRD_NONE:
        case ARPHRD_RAWIP:
+       case ARPHRD_PIMREG:
                return false;
        default:
                return true;
index ab20767..f742e50 100644 (file)
@@ -205,7 +205,8 @@ struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr);
 struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
                                         struct sockaddr_vm *dst);
 void vsock_remove_sock(struct vsock_sock *vsk);
-void vsock_for_each_connected_socket(void (*fn)(struct sock *sk));
+void vsock_for_each_connected_socket(struct vsock_transport *transport,
+                                    void (*fn)(struct sock *sk));
 int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk);
 bool vsock_find_cid(unsigned int cid);
 
index 8731d5b..b08b709 100644 (file)
@@ -97,7 +97,6 @@ struct nf_conn {
        unsigned long status;
 
        u16             cpu;
-       u16             local_origin:1;
        possible_net_t ct_net;
 
 #if IS_ENABLED(CONFIG_NF_NAT)
index 07df6d9..e8db8d9 100644 (file)
@@ -16,6 +16,7 @@ CONFIG_SYMBOLIC_ERRNAME=y
 #
 # Compile-time checks and compiler options
 #
+CONFIG_DEBUG_INFO=y
 CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
 CONFIG_DEBUG_SECTION_MISMATCH=y
 CONFIG_FRAME_WARN=2048
index 8d41042..ee67164 100644 (file)
@@ -478,7 +478,7 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
                 * __read_swap_cache_async(), which has set SWAP_HAS_CACHE
                 * in swap_map, but not yet added its page to swap cache.
                 */
-               cond_resched();
+               schedule_timeout_uninterruptible(1);
        }
 
        /*
index 074e4a6..88e2808 100644 (file)
@@ -1436,6 +1436,7 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
                const char *user_protocol;
 
                master = of_find_net_device_by_node(ethernet);
+               of_node_put(ethernet);
                if (!master)
                        return -EPROBE_DEFER;
 
index b0ffbcd..55d604c 100644 (file)
@@ -812,8 +812,7 @@ int esp6_input_done2(struct sk_buff *skb, int err)
                struct tcphdr *th;
 
                offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
-
-               if (offset < 0) {
+               if (offset == -1) {
                        err = -EINVAL;
                        goto out;
                }
index 4788f6b..1948326 100644 (file)
@@ -1476,8 +1476,8 @@ static int __ip6_append_data(struct sock *sk,
                      sizeof(struct frag_hdr) : 0) +
                     rt->rt6i_nfheader_len;
 
-       if (mtu < fragheaderlen ||
-           ((mtu - fragheaderlen) & ~7) + fragheaderlen < sizeof(struct frag_hdr))
+       if (mtu <= fragheaderlen ||
+           ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
                goto emsgsize;
 
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
index 9bf52a0..fd51db3 100644 (file)
@@ -1699,7 +1699,7 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sad
 
        xfrm_probe_algs();
 
-       supp_skb = compose_sadb_supported(hdr, GFP_KERNEL);
+       supp_skb = compose_sadb_supported(hdr, GFP_KERNEL | __GFP_ZERO);
        if (!supp_skb) {
                if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
                        pfk->registered &= ~(1<<hdr->sadb_msg_satype);
index d6aa5b4..bf1e17c 100644 (file)
@@ -1748,9 +1748,6 @@ resolve_normal_ct(struct nf_conn *tmpl,
                        return 0;
                if (IS_ERR(h))
                        return PTR_ERR(h);
-
-               ct = nf_ct_tuplehash_to_ctrack(h);
-               ct->local_origin = state->hook == NF_INET_LOCAL_OUT;
        }
        ct = nf_ct_tuplehash_to_ctrack(h);
 
index 2d06a66..ffcf652 100644 (file)
@@ -494,38 +494,6 @@ another_round:
        goto another_round;
 }
 
-static bool tuple_force_port_remap(const struct nf_conntrack_tuple *tuple)
-{
-       u16 sp, dp;
-
-       switch (tuple->dst.protonum) {
-       case IPPROTO_TCP:
-               sp = ntohs(tuple->src.u.tcp.port);
-               dp = ntohs(tuple->dst.u.tcp.port);
-               break;
-       case IPPROTO_UDP:
-       case IPPROTO_UDPLITE:
-               sp = ntohs(tuple->src.u.udp.port);
-               dp = ntohs(tuple->dst.u.udp.port);
-               break;
-       default:
-               return false;
-       }
-
-       /* IANA: System port range: 1-1023,
-        *         user port range: 1024-49151,
-        *      private port range: 49152-65535.
-        *
-        * Linux default ephemeral port range is 32768-60999.
-        *
-        * Enforce port remapping if sport is significantly lower
-        * than dport to prevent NAT port shadowing, i.e.
-        * accidental match of 'new' inbound connection vs.
-        * existing outbound one.
-        */
-       return sp < 16384 && dp >= 32768;
-}
-
 /* Manipulate the tuple into the range given. For NF_INET_POST_ROUTING,
  * we change the source to map into the range. For NF_INET_PRE_ROUTING
  * and NF_INET_LOCAL_OUT, we change the destination to map into the
@@ -539,17 +507,11 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
                 struct nf_conn *ct,
                 enum nf_nat_manip_type maniptype)
 {
-       bool random_port = range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL;
        const struct nf_conntrack_zone *zone;
        struct net *net = nf_ct_net(ct);
 
        zone = nf_ct_zone(ct);
 
-       if (maniptype == NF_NAT_MANIP_SRC &&
-           !random_port &&
-           !ct->local_origin)
-               random_port = tuple_force_port_remap(orig_tuple);
-
        /* 1) If this srcip/proto/src-proto-part is currently mapped,
         * and that same mapping gives a unique tuple within the given
         * range, use that.
@@ -558,7 +520,8 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
         * So far, we don't do local source mappings, so multiple
         * manips not an issue.
         */
-       if (maniptype == NF_NAT_MANIP_SRC && !random_port) {
+       if (maniptype == NF_NAT_MANIP_SRC &&
+           !(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
                /* try the original tuple first */
                if (in_range(orig_tuple, range)) {
                        if (!nf_nat_used_tuple(orig_tuple, ct)) {
@@ -582,7 +545,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
         */
 
        /* Only bother mapping if it's not already in range and unique */
-       if (!random_port) {
+       if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
                if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
                        if (!(range->flags & NF_NAT_RANGE_PROTO_OFFSET) &&
                            l4proto_in_range(tuple, maniptype,
index c86748b..d71a33a 100644 (file)
@@ -8260,6 +8260,12 @@ void nf_tables_trans_destroy_flush_work(void)
 }
 EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);
 
+static bool nft_expr_reduce(struct nft_regs_track *track,
+                           const struct nft_expr *expr)
+{
+       return false;
+}
+
 static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)
 {
        const struct nft_expr *expr, *last;
@@ -8307,8 +8313,7 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha
                nft_rule_for_each_expr(expr, last, rule) {
                        track.cur = expr;
 
-                       if (expr->ops->reduce &&
-                           expr->ops->reduce(&track, expr)) {
+                       if (nft_expr_reduce(&track, expr)) {
                                expr = track.cur;
                                continue;
                        }
index ab87f22..a7273af 100644 (file)
@@ -2317,8 +2317,11 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
                                        copy_skb = skb_get(skb);
                                        skb_head = skb->data;
                                }
-                               if (copy_skb)
+                               if (copy_skb) {
+                                       memset(&PACKET_SKB_CB(copy_skb)->sa.ll, 0,
+                                              sizeof(PACKET_SKB_CB(copy_skb)->sa.ll));
                                        skb_set_owner_r(copy_skb, sk);
+                               }
                        }
                        snaplen = po->rx_ring.frame_size - macoff;
                        if ((int)snaplen < 0) {
@@ -3462,6 +3465,8 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
        sock_recv_ts_and_drops(msg, sk, skb);
 
        if (msg->msg_name) {
+               const size_t max_len = min(sizeof(skb->cb),
+                                          sizeof(struct sockaddr_storage));
                int copy_len;
 
                /* If the address length field is there to be filled
@@ -3484,6 +3489,10 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
                                msg->msg_namelen = sizeof(struct sockaddr_ll);
                        }
                }
+               if (WARN_ON_ONCE(copy_len > max_len)) {
+                       copy_len = max_len;
+                       msg->msg_namelen = copy_len;
+               }
                memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa, copy_len);
        }
 
index 38baeb1..f04abf6 100644 (file)
@@ -334,7 +334,8 @@ void vsock_remove_sock(struct vsock_sock *vsk)
 }
 EXPORT_SYMBOL_GPL(vsock_remove_sock);
 
-void vsock_for_each_connected_socket(void (*fn)(struct sock *sk))
+void vsock_for_each_connected_socket(struct vsock_transport *transport,
+                                    void (*fn)(struct sock *sk))
 {
        int i;
 
@@ -343,8 +344,12 @@ void vsock_for_each_connected_socket(void (*fn)(struct sock *sk))
        for (i = 0; i < ARRAY_SIZE(vsock_connected_table); i++) {
                struct vsock_sock *vsk;
                list_for_each_entry(vsk, &vsock_connected_table[i],
-                                   connected_table)
+                                   connected_table) {
+                       if (vsk->transport != transport)
+                               continue;
+
                        fn(sk_vsock(vsk));
+               }
        }
 
        spin_unlock_bh(&vsock_table_lock);
index fb3302f..5afc194 100644 (file)
@@ -24,6 +24,7 @@
 static struct workqueue_struct *virtio_vsock_workqueue;
 static struct virtio_vsock __rcu *the_virtio_vsock;
 static DEFINE_MUTEX(the_virtio_vsock_mutex); /* protects the_virtio_vsock */
+static struct virtio_transport virtio_transport; /* forward declaration */
 
 struct virtio_vsock {
        struct virtio_device *vdev;
@@ -384,7 +385,8 @@ static void virtio_vsock_event_handle(struct virtio_vsock *vsock,
        switch (le32_to_cpu(event->id)) {
        case VIRTIO_VSOCK_EVENT_TRANSPORT_RESET:
                virtio_vsock_update_guest_cid(vsock);
-               vsock_for_each_connected_socket(virtio_vsock_reset_sock);
+               vsock_for_each_connected_socket(&virtio_transport.transport,
+                                               virtio_vsock_reset_sock);
                break;
        }
 }
@@ -662,7 +664,8 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
        synchronize_rcu();
 
        /* Reset all connected sockets when the device disappear */
-       vsock_for_each_connected_socket(virtio_vsock_reset_sock);
+       vsock_for_each_connected_socket(&virtio_transport.transport,
+                                       virtio_vsock_reset_sock);
 
        /* Stop all work handlers to make sure no one is accessing the device,
         * so we can safely call virtio_reset_device().
index 7aef34e..b17dc97 100644 (file)
@@ -75,6 +75,8 @@ static u32 vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID;
 
 static int PROTOCOL_OVERRIDE = -1;
 
+static struct vsock_transport vmci_transport; /* forward declaration */
+
 /* Helper function to convert from a VMCI error code to a VSock error code. */
 
 static s32 vmci_transport_error_to_vsock_error(s32 vmci_error)
@@ -882,7 +884,8 @@ static void vmci_transport_qp_resumed_cb(u32 sub_id,
                                         const struct vmci_event_data *e_data,
                                         void *client_data)
 {
-       vsock_for_each_connected_socket(vmci_transport_handle_detach);
+       vsock_for_each_connected_socket(&vmci_transport,
+                                       vmci_transport_handle_detach);
 }
 
 static void vmci_transport_recv_pkt_work(struct work_struct *work)
index f924246..8d9b559 100644 (file)
@@ -29,7 +29,7 @@ struct evsel *arch_evlist__leader(struct list_head *list)
 
        __evlist__for_each_entry(list, evsel) {
                if (evsel->pmu_name && !strcmp(evsel->pmu_name, "cpu") &&
-                       evsel->name && strstr(evsel->name, "slots"))
+                       evsel->name && strcasestr(evsel->name, "slots"))
                        return evsel;
        }
        return first;
index eaad04e..41a66a4 100644 (file)
@@ -346,7 +346,7 @@ struct evlist_cpu_iterator evlist__cpu_begin(struct evlist *evlist, struct affin
 {
        struct evlist_cpu_iterator itr = {
                .container = evlist,
-               .evsel = evlist__first(evlist),
+               .evsel = NULL,
                .cpu_map_idx = 0,
                .evlist_cpu_map_idx = 0,
                .evlist_cpu_map_nr = perf_cpu_map__nr(evlist->core.all_cpus),
@@ -354,16 +354,22 @@ struct evlist_cpu_iterator evlist__cpu_begin(struct evlist *evlist, struct affin
                .affinity = affinity,
        };
 
-       if (itr.affinity) {
-               itr.cpu = perf_cpu_map__cpu(evlist->core.all_cpus, 0);
-               affinity__set(itr.affinity, itr.cpu.cpu);
-               itr.cpu_map_idx = perf_cpu_map__idx(itr.evsel->core.cpus, itr.cpu);
-               /*
-                * If this CPU isn't in the evsel's cpu map then advance through
-                * the list.
-                */
-               if (itr.cpu_map_idx == -1)
-                       evlist_cpu_iterator__next(&itr);
+       if (evlist__empty(evlist)) {
+               /* Ensure the empty list doesn't iterate. */
+               itr.evlist_cpu_map_idx = itr.evlist_cpu_map_nr;
+       } else {
+               itr.evsel = evlist__first(evlist);
+               if (itr.affinity) {
+                       itr.cpu = perf_cpu_map__cpu(evlist->core.all_cpus, 0);
+                       affinity__set(itr.affinity, itr.cpu.cpu);
+                       itr.cpu_map_idx = perf_cpu_map__idx(itr.evsel->core.cpus, itr.cpu);
+                       /*
+                        * If this CPU isn't in the evsel's cpu map then advance
+                        * through the list.
+                        */
+                       if (itr.cpu_map_idx == -1)
+                               evlist_cpu_iterator__next(&itr);
+               }
        }
        return itr;
 }
index b2ed314..dfde9ea 100644 (file)
@@ -231,7 +231,7 @@ void symbols__fixup_end(struct rb_root_cached *symbols)
                prev = curr;
                curr = rb_entry(nd, struct symbol, rb_node);
 
-               if (prev->end == prev->start && prev->end != curr->start)
+               if (prev->end == prev->start || prev->end != curr->start)
                        arch__symbols__fixup_end(prev, curr);
        }
 
index 79fe627..eb8543b 100755 (executable)
@@ -880,9 +880,8 @@ EOF
                return $ksft_skip
        fi
 
-       # test default behaviour. Packet from ns1 to ns0 is not redirected
-       # due to automatic port translation.
-       test_port_shadow "default" "ROUTER"
+       # test default behaviour. Packet from ns1 to ns0 is redirected to ns2.
+       test_port_shadow "default" "CLIENT"
 
        # test packet filter based mitigation: prevent forwarding of
        # packets claiming to come from the service port.
index 1607322..a14b5b8 100644 (file)
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for vm selftests
 
+LOCAL_HDRS += $(selfdir)/vm/local_config.h $(top_srcdir)/mm/gup_test.h
+
 include local_config.mk
 
 uname_M := $(shell uname -m 2>/dev/null || echo not)
@@ -140,10 +142,6 @@ endif
 
 $(OUTPUT)/mlock-random-test $(OUTPUT)/memfd_secret: LDLIBS += -lcap
 
-$(OUTPUT)/gup_test: ../../../../mm/gup_test.h
-
-$(OUTPUT)/hmm-tests: local_config.h
-
 # HMM_EXTRA_LIBS may get set in local_config.mk, or it may be left empty.
 $(OUTPUT)/hmm-tests: LDLIBS += $(HMM_EXTRA_LIBS)