Input updates for 6.1 merge window:

- a new driver for IBM Operational Panel
 
 - a new driver for PinePhone keyboards
 
 - RT5120 PMIC power key support
 
 - various enhancements and support for new models in xpad (Xbox) driver
 
 - a new compatible ID for Elan touchscreen driver
 
 - rework of adp5588-keys driver to support configuring via device
   properties (OF, ACPI, etc) instead of platform data, and proper support
   of optional gpiochip functionality (and removal of gpio-adp5588 driver)
 
 - improvements to firmware update handling in Synaptics RMI4 driver
 
 - support for double key matrix in mt6779-keypad
 
 - support for polled mode in adc-joystick driver
 
 - other assorted driver fixes, cleanups and improvements.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQST2eWILY88ieB2DOtAj56VGEWXnAUCY0O9YwAKCRBAj56VGEWX
 nICKAQD6/pS5tIWFCyYrM/A0xbkYuPWtmXqJTSGANlevU7nypwEAqaH2tYSDAdxM
 NylDCPvvduHVmA2ewXgvIlUzjCLr6As=
 =cUmW
 -----END PGP SIGNATURE-----

Merge tag 'input-for-v6.1-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:

 - a new driver for IBM Operational Panel

 - a new driver for PinePhone keyboards

 - RT5120 PMIC power key support

 - various enhancements and support for new models in xpad (Xbox) driver

 - a new compatible ID for Elan touchscreen driver

 - rework of adp5588-keys driver to support configuring via device
   properties (OF, ACPI, etc) instead of platform data, and proper
   support of optional gpiochip functionality (and removal of
   gpio-adp5588 driver)

 - improvements to firmware update handling in Synaptics RMI4 driver

 - support for double key matrix in mt6779-keypad

 - support for polled mode in adc-joystick driver

 - other assorted driver fixes, cleanups and improvements

* tag 'input-for-v6.1-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (90 commits)
  Input: i8042 - fix refount leak on sparc
  Input: i8042 - add LoongArch support in i8042-acpipnpio.h
  Input: i8042 - rename i8042-x86ia64io.h to i8042-acpipnpio.h
  Input: pinephone-keyboard - support the proxied I2C bus
  Input: pinephone-keyboard - add PinePhone keyboard driver
  dt-bindings: input: Add the PinePhone keyboard binding
  dt-bindings: input: Convert hid-over-i2c to DT schema
  input: drop empty comment blocks
  Input: xpad - add X-Box Adaptive Profile button
  Input: add ABS_PROFILE to uapi and documentation
  Input: xpad - add X-Box Adaptive XBox button
  Input: xpad - add X-Box Adaptive support
  Input: ims-pcu - fix spelling mistake "BOOLTLOADER" -> "BOOTLOADER"
  Input: ibm-panel - add missing MODULE_DEVICE_TABLE
  Input: icn8505 - utilize acpi_get_subsystem_id()
  Input: xpad - decipher xpadone packages with GIP defines
  Input: xpad - refactor using BIT() macro
  Input: synaptics-rmi4 - convert to use sysfs_emit() APIs
  Input: twl4030-pwrbutton - add missing of.h include
  Input: applespi - replace zero-length array with DECLARE_FLEX_ARRAY() helper
  ...
This commit is contained in:
Linus Torvalds 2022-10-11 10:53:25 -07:00
commit 694b37a5dd
138 changed files with 2654 additions and 1947 deletions

View File

@ -14,6 +14,9 @@ description: >
Bindings for joystick devices connected to ADC controllers supporting Bindings for joystick devices connected to ADC controllers supporting
the Industrial I/O subsystem. the Industrial I/O subsystem.
allOf:
- $ref: input.yaml#
properties: properties:
compatible: compatible:
const: adc-joystick const: adc-joystick
@ -28,6 +31,8 @@ properties:
https://github.com/devicetree-org/dt-schema/blob/master/schemas/iio/iio-consumer.yaml https://github.com/devicetree-org/dt-schema/blob/master/schemas/iio/iio-consumer.yaml
for details. for details.
poll-interval: true
'#address-cells': '#address-cells':
const: 1 const: 1

View File

@ -0,0 +1,111 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/adi,adp5588.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADP5588 Keypad Controller
maintainers:
- Nuno Sá <nuno.sa@analog.com>
description: |
Analog Devices Mobile I/O Expander and QWERTY Keypad Controller
https://www.analog.com/media/en/technical-documentation/data-sheets/ADP5588.pdf
allOf:
- $ref: matrix-keymap.yaml#
- $ref: input.yaml#
properties:
compatible:
enum:
- adi,adp5587
- adi,adp5588
reg:
maxItems: 1
vcc-supply:
description: Supply Voltage Input
reset-gpios:
description:
If specified, it will be asserted during driver probe. As the line is
active low, it should be marked GPIO_ACTIVE_LOW.
maxItems: 1
interrupts:
maxItems: 1
gpio-controller:
description:
This property applies if either keypad,num-rows lower than 8 or
keypad,num-columns lower than 10.
'#gpio-cells':
const: 2
interrupt-controller:
description:
This property applies if either keypad,num-rows lower than 8 or
keypad,num-columns lower than 10.
'#interrupt-cells':
const: 2
adi,unlock-keys:
description:
Specifies a maximum of 2 keys that can be used to unlock the keypad.
If this property is set, the keyboard will be locked and only unlocked
after these keys are pressed. If only one key is set, a double click is
needed to unlock the keypad. The value of this property cannot be bigger
or equal than keypad,num-rows * keypad,num-columns.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 2
required:
- compatible
- reg
- interrupts
- keypad,num-rows
- keypad,num-columns
- linux,keymap
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
keys@34 {
compatible = "adi,adp5588";
reg = <0x34>;
vcc-supply = <&vcc>;
interrupts = <21 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
reset-gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
keypad,num-rows = <1>;
keypad,num-columns = <9>;
linux,keymap = <
MATRIX_KEY(0x00, 0x00, KEY_1)
MATRIX_KEY(0x00, 0x01, KEY_2)
MATRIX_KEY(0x00, 0x02, KEY_3)
MATRIX_KEY(0x00, 0x03, KEY_4)
MATRIX_KEY(0x00, 0x04, KEY_5)
MATRIX_KEY(0x00, 0x05, KEY_6)
MATRIX_KEY(0x00, 0x06, KEY_7)
MATRIX_KEY(0x00, 0x07, KEY_8)
MATRIX_KEY(0x00, 0x08, KEY_9)
>;
};
};
...

View File

@ -1,46 +0,0 @@
* HID over I2C Device-Tree bindings
HID over I2C provides support for various Human Interface Devices over the
I2C bus. These devices can be for example touchpads, keyboards, touch screens
or sensors.
The specification has been written by Microsoft and is currently available here:
http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
If this binding is used, the kernel module i2c-hid will handle the communication
with the device and the generic hid core layer will handle the protocol.
Required properties:
- compatible: must be "hid-over-i2c"
- reg: i2c slave address
- hid-descr-addr: HID descriptor address
- interrupts: interrupt line
Additional optional properties:
Some devices may support additional optional properties to help with, e.g.,
power sequencing. The following properties can be supported by one or more
device-specific compatible properties, which should be used in addition to the
"hid-over-i2c" string.
- compatible:
* "wacom,w9013" (Wacom W9013 digitizer). Supports:
- vdd-supply (3.3V)
- vddl-supply (1.8V)
- post-power-on-delay-ms
- vdd-supply: phandle of the regulator that provides the supply voltage.
- post-power-on-delay-ms: time required by the device after enabling its regulators
or powering it on, before it is ready for communication.
- touchscreen-inverted-x: See touchscreen.txt
- touchscreen-inverted-y: See touchscreen.txt
Example:
i2c-hid-dev@2c {
compatible = "hid-over-i2c";
reg = <0x2c>;
hid-descr-addr = <0x0020>;
interrupt-parent = <&gpx3>;
interrupts = <3 2>;
};

View File

@ -0,0 +1,83 @@
# SPDX-License-Identifier: GPL-2.0-only
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/hid-over-i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: HID over I2C Devices
maintainers:
- Benjamin Tissoires <benjamin.tissoires@redhat.com>
- Jiri Kosina <jkosina@suse.cz>
description: |+
HID over I2C provides support for various Human Interface Devices over the
I2C bus. These devices can be for example touchpads, keyboards, touch screens
or sensors.
The specification has been written by Microsoft and is currently available here:
https://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
If this binding is used, the kernel module i2c-hid will handle the communication
with the device and the generic hid core layer will handle the protocol.
allOf:
- $ref: /schemas/input/touchscreen/touchscreen.yaml#
properties:
compatible:
oneOf:
- items:
- enum:
- wacom,w9013
- const: hid-over-i2c
- description: Just "hid-over-i2c" alone is allowed, but not recommended.
const: hid-over-i2c
reg:
maxItems: 1
interrupts:
maxItems: 1
hid-descr-addr:
description: HID descriptor address
$ref: /schemas/types.yaml#/definitions/uint32
post-power-on-delay-ms:
description: Time required by the device after enabling its regulators
or powering it on, before it is ready for communication.
touchscreen-inverted-x: true
touchscreen-inverted-y: true
vdd-supply:
description: 3.3V supply
vddl-supply:
description: 1.8V supply
wakeup-source: true
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
hid@2c {
compatible = "hid-over-i2c";
reg = <0x2c>;
hid-descr-addr = <0x0020>;
interrupts = <3 2>;
};
};
...

View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/ibm,op-panel.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IBM Operation Panel
maintainers:
- Eddie James <eajames@linux.ibm.com>
allOf:
- $ref: input.yaml#
description: |
The IBM Operation Panel provides a simple interface to control the connected
server. It has a display and three buttons: two directional arrows and one
'Enter' button.
properties:
compatible:
const: ibm,op-panel
reg:
maxItems: 1
linux,keycodes:
minItems: 1
maxItems: 3
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/i2c/i2c.h>
#include <dt-bindings/input/input.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
ibm-op-panel@62 {
compatible = "ibm,op-panel";
reg = <(0x62 | I2C_OWN_SLAVE_ADDRESS)>;
linux,keycodes = <KEY_UP>, <KEY_DOWN>, <KEY_ENTER>;
};
};

View File

@ -49,6 +49,12 @@ properties:
maximum: 256 maximum: 256
default: 16 default: 16
mediatek,keys-per-group:
description: each (row, column) group has multiple keys
$ref: /schemas/types.yaml#/definitions/uint32
default: 1
maximum: 2
required: required:
- compatible - compatible
- reg - reg
@ -56,7 +62,7 @@ required:
- clocks - clocks
- clock-names - clock-names
additionalProperties: false unevaluatedProperties: false
examples: examples:
- | - |

View File

@ -0,0 +1,114 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/mediatek,pmic-keys.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek PMIC Keys
maintainers:
- Chen Zhong <chen.zhong@mediatek.com>
allOf:
- $ref: input.yaml#
description: |
There are two key functions provided by MT6397, MT6323 and other MediaTek
PMICs: pwrkey and homekey.
The key functions are defined as the subnode of the function node provided
by the PMIC that is defined as a Multi-Function Device (MFD).
For MediaTek MT6323/MT6397 PMIC bindings see
Documentation/devicetree/bindings/mfd/mt6397.txt
properties:
compatible:
enum:
- mediatek,mt6323-keys
- mediatek,mt6331-keys
- mediatek,mt6358-keys
- mediatek,mt6397-keys
power-off-time-sec: true
mediatek,long-press-mode:
description: |
Key long-press force shutdown setting
0 - disabled
1 - pwrkey
2 - pwrkey+homekey
$ref: /schemas/types.yaml#/definitions/uint32
default: 0
maximum: 2
patternProperties:
"^((power|home)|(key-[a-z0-9-]+|[a-z0-9-]+-key))$":
$ref: input.yaml#
properties:
interrupts:
minItems: 1
items:
- description: Key press interrupt
- description: Key release interrupt
interrupt-names: true
linux-keycodes:
maxItems: 1
wakeup-source: true
required:
- linux,keycodes
if:
properties:
interrupt-names:
contains:
const: powerkey
then:
properties:
interrupt-names:
minItems: 1
items:
- const: powerkey
- const: powerkey_r
else:
properties:
interrupt-names:
minItems: 1
items:
- const: homekey
- const: homekey_r
unevaluatedProperties: false
required:
- compatible
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
pmic {
compatible = "mediatek,mt6397";
keys {
compatible = "mediatek,mt6397-keys";
mediatek,long-press-mode = <1>;
power-off-time-sec = <0>;
key-power {
linux,keycodes = <KEY_POWER>;
wakeup-source;
};
key-home {
linux,keycodes = <KEY_VOLUMEDOWN>;
};
};
};

View File

@ -1,46 +0,0 @@
MediaTek MT6397/MT6323 PMIC Keys Device Driver
There are two key functions provided by MT6397/MT6323 PMIC, pwrkey
and homekey. The key functions are defined as the subnode of the function
node provided by MT6397/MT6323 PMIC that is being defined as one kind
of Muti-Function Device (MFD)
For MT6397/MT6323 MFD bindings see:
Documentation/devicetree/bindings/mfd/mt6397.txt
Required properties:
- compatible: Should be one of:
- "mediatek,mt6397-keys"
- "mediatek,mt6323-keys"
- "mediatek,mt6358-keys"
- linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
Optional Properties:
- wakeup-source: See Documentation/devicetree/bindings/power/wakeup-source.txt
- mediatek,long-press-mode: Long press key shutdown setting, 1 for
pwrkey only, 2 for pwrkey/homekey together, others for disabled.
- power-off-time-sec: See Documentation/devicetree/bindings/input/input.yaml
Example:
pmic: mt6397 {
compatible = "mediatek,mt6397";
...
mt6397keys: mt6397keys {
compatible = "mediatek,mt6397-keys";
mediatek,long-press-mode = <1>;
power-off-time-sec = <0>;
power {
linux,keycodes = <116>;
wakeup-source;
};
home {
linux,keycodes = <114>;
};
};
};

View File

@ -0,0 +1,66 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/pine64,pinephone-keyboard.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Pine64 PinePhone keyboard device tree bindings
maintainers:
- Samuel Holland <samuel@sholland.org>
description:
A keyboard accessory is available for the Pine64 PinePhone and PinePhone Pro.
It connects via I2C, providing a raw scan matrix, a flashing interface, and a
subordinate I2C bus for communication with a battery charger IC.
properties:
compatible:
const: pine64,pinephone-keyboard
reg:
const: 0x15
interrupts:
maxItems: 1
vbat-supply:
description: Supply for the keyboard MCU
wakeup-source: true
i2c:
$ref: /schemas/i2c/i2c-controller.yaml#
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
keyboard@15 {
compatible = "pine64,pinephone-keyboard";
reg = <0x15>;
interrupt-parent = <&r_pio>;
interrupts = <0 12 IRQ_TYPE_EDGE_FALLING>; /* PL12 */
i2c {
#address-cells = <1>;
#size-cells = <0>;
charger@75 {
reg = <0x75>;
};
};
};
};

View File

@ -1,23 +0,0 @@
Qualcomm PM8xxx PMIC Vibrator
PROPERTIES
- compatible:
Usage: required
Value type: <string>
Definition: must be one of:
"qcom,pm8058-vib"
"qcom,pm8916-vib"
"qcom,pm8921-vib"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: address of vibration control register
EXAMPLE
vibrator@4a {
compatible = "qcom,pm8058-vib";
reg = <0x4a>;
};

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/qcom,pm8xxx-vib.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm PM8xxx PMIC Vibrator
maintainers:
- Bjorn Andersson <andersson@kernel.org>
properties:
compatible:
enum:
- qcom,pm8058-vib
- qcom,pm8916-vib
- qcom,pm8921-vib
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
pmic {
#address-cells = <1>;
#size-cells = <0>;
vibrator@4a {
compatible = "qcom,pm8058-vib";
reg = <0x4a>;
};
};

View File

@ -17,10 +17,10 @@ Example:
auo_pixcir_ts@5c { auo_pixcir_ts@5c {
compatible = "auo,auo_pixcir_ts"; compatible = "auo,auo_pixcir_ts";
reg = <0x5c>; reg = <0x5c>;
interrupts = <2 0>; interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
gpios = <&gpf 2 0 2>, /* INT */ gpios = <&gpf 2 0 GPIO_LEVEL_HIGH>, /* INT */
<&gpf 5 1 0>; /* RST */ <&gpf 5 1 GPIO_LEVEL_LOW>; /* RST */
x-size = <800>; x-size = <800>;
y-size = <600>; y-size = <600>;

View File

@ -3,15 +3,16 @@
Required Properties: Required Properties:
- compatible must be toradex,vf50-touchscreen - compatible must be toradex,vf50-touchscreen
- io-channels: adc channels being used by the Colibri VF50 module - io-channels: adc channels being used by the Colibri VF50 module
IIO ADC for Y-, X-, Y+, X+ connections
- xp-gpios: FET gate driver for input of X+ - xp-gpios: FET gate driver for input of X+
- xm-gpios: FET gate driver for input of X- - xm-gpios: FET gate driver for input of X-
- yp-gpios: FET gate driver for input of Y+ - yp-gpios: FET gate driver for input of Y+
- ym-gpios: FET gate driver for input of Y- - ym-gpios: FET gate driver for input of Y-
- interrupts: pen irq interrupt for touch detection - interrupts: pen irq interrupt for touch detection, signal from X plate
- pinctrl-names: "idle", "default", "gpios" - pinctrl-names: "idle", "default"
- pinctrl-0: pinctrl node for pen/touch detection state pinmux - pinctrl-0: pinctrl node for pen/touch detection, pinctrl must provide
pull-up resistor on X+, X-.
- pinctrl-1: pinctrl node for X/Y and pressure measurement (ADC) state pinmux - pinctrl-1: pinctrl node for X/Y and pressure measurement (ADC) state pinmux
- pinctrl-2: pinctrl node for gpios functioning as FET gate drivers
- vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values - vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values
Example: Example:
@ -26,9 +27,8 @@ Example:
ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio0>; interrupt-parent = <&gpio0>;
interrupts = <8 IRQ_TYPE_LEVEL_LOW>; interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "idle","default","gpios"; pinctrl-names = "idle","default";
pinctrl-0 = <&pinctrl_touchctrl_idle>; pinctrl-0 = <&pinctrl_touchctrl_idle>, <&pinctrl_touchctrl_gpios>;
pinctrl-1 = <&pinctrl_touchctrl_default>; pinctrl-1 = <&pinctrl_touchctrl_default>, <&pinctrl_touchctrl_gpios>;
pinctrl-2 = <&pinctrl_touchctrl_gpios>;
vf50-ts-min-pressure = <200>; vf50-ts-min-pressure = <200>;
}; };

View File

@ -14,9 +14,13 @@ allOf:
properties: properties:
compatible: compatible:
enum: oneOf:
- elan,ektf3624 - enum:
- elan,ekth3500 - elan,ektf3624
- elan,ekth3500
- items:
- const: elan,ekth3915
- const: elan,ekth3500
reg: reg:
maxItems: 1 maxItems: 1

View File

@ -54,8 +54,7 @@ Optional properties common with MFD (deprecated):
1 -> 3.25 MHz 1 -> 3.25 MHz
2 || 3 -> 6.5 MHz 2 || 3 -> 6.5 MHz
Node name must be stmpe_touchscreen and should be child node of stmpe node to Node should be child node of stmpe node to which it belongs.
which it belongs.
Note that common ADC settings of stmpe_touchscreen (child) will take precedence Note that common ADC settings of stmpe_touchscreen (child) will take precedence
over the settings done in MFD. over the settings done in MFD.

View File

@ -235,6 +235,12 @@ A few EV_ABS codes have special meanings:
BTN_TOOL_<name> signals the type of tool that is currently detected by the BTN_TOOL_<name> signals the type of tool that is currently detected by the
hardware and is otherwise independent of ABS_DISTANCE and/or BTN_TOUCH. hardware and is otherwise independent of ABS_DISTANCE and/or BTN_TOUCH.
* ABS_PROFILE:
- Used to describe the state of a multi-value profile switch. An event is
emitted only when the selected profile changes, indicating the newly
selected profile value.
* ABS_MT_<name>: * ABS_MT_<name>:
- Used to describe multitouch input events. Please see - Used to describe multitouch input events. Please see

View File

@ -189,3 +189,9 @@ Gamepads report the following events:
- Rumble: - Rumble:
Rumble is advertised as FF_RUMBLE. Rumble is advertised as FF_RUMBLE.
- Profile:
Some pads provide a multi-value profile selection switch. An example is the
XBox Adaptive and the XBox Elite 2 controllers. When the active profile is
switched, its newly selected value is emitted as an ABS_PROFILE event.

View File

@ -554,7 +554,7 @@ M: Michael Hennerich <michael.hennerich@analog.com>
S: Supported S: Supported
W: http://wiki.analog.com/ADP5588 W: http://wiki.analog.com/ADP5588
W: https://ez.analog.com/linux-software-drivers W: https://ez.analog.com/linux-software-drivers
F: drivers/gpio/gpio-adp5588.c F: Documentation/devicetree/bindings/input/adi,adp5588.yaml
F: drivers/input/keyboard/adp5588-keys.c F: drivers/input/keyboard/adp5588-keys.c
ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863) ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863)
@ -9713,6 +9713,13 @@ S: Orphan
F: Documentation/ia64/ F: Documentation/ia64/
F: arch/ia64/ F: arch/ia64/
IBM Operation Panel Input Driver
M: Eddie James <eajames@linux.ibm.com>
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/ibm,op-panel.yaml
F: drivers/input/misc/ibm-panel.c
IBM Power 842 compression accelerator IBM Power 842 compression accelerator
M: Haren Myneni <haren@us.ibm.com> M: Haren Myneni <haren@us.ibm.com>
S: Supported S: Supported
@ -12925,6 +12932,12 @@ S: Supported
F: Documentation/devicetree/bindings/media/mediatek-jpeg-*.yaml F: Documentation/devicetree/bindings/media/mediatek-jpeg-*.yaml
F: drivers/media/platform/mediatek/jpeg/ F: drivers/media/platform/mediatek/jpeg/
MEDIATEK KEYPAD DRIVER
M: Mattijs Korpershoek <mkorpershoek@baylibre.com>
S: Supported
F: Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml
F: drivers/input/keyboard/mt6779-keypad.c
MEDIATEK MDP DRIVER MEDIATEK MDP DRIVER
M: Minghsiu Tsai <minghsiu.tsai@mediatek.com> M: Minghsiu Tsai <minghsiu.tsai@mediatek.com>
M: Houlong Wei <houlong.wei@mediatek.com> M: Houlong Wei <houlong.wei@mediatek.com>
@ -16337,6 +16350,12 @@ F: Documentation/devicetree/bindings/pinctrl/sunplus,*
F: drivers/pinctrl/sunplus/ F: drivers/pinctrl/sunplus/
F: include/dt-bindings/pinctrl/sppctl*.h F: include/dt-bindings/pinctrl/sppctl*.h
PINE64 PINEPHONE KEYBOARD DRIVER
M: Samuel Holland <samuel@sholland.org>
S: Supported
F: Documentation/devicetree/bindings/input/pine64,pinephone-keyboard.yaml
F: drivers/input/keyboard/pinephone-keyboard.c
PKTCDVD DRIVER PKTCDVD DRIVER
M: linux-block@vger.kernel.org M: linux-block@vger.kernel.org
S: Orphan S: Orphan

View File

@ -990,20 +990,6 @@ endmenu
menu "I2C GPIO expanders" menu "I2C GPIO expanders"
depends on I2C depends on I2C
config GPIO_ADP5588
tristate "ADP5588 I2C GPIO expander"
help
This option enables support for 18 GPIOs found
on Analog Devices ADP5588 GPIO Expanders.
config GPIO_ADP5588_IRQ
bool "Interrupt controller support for ADP5588"
depends on GPIO_ADP5588=y
select GPIOLIB_IRQCHIP
help
Say yes here to enable the adp5588 to be used as an interrupt
controller. It requires the driver to be built in the kernel.
config GPIO_ADNP config GPIO_ADNP
tristate "Avionic Design N-bit GPIO expander" tristate "Avionic Design N-bit GPIO expander"
depends on OF_GPIO depends on OF_GPIO

View File

@ -25,7 +25,6 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o

View File

@ -1,446 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* GPIO Chip driver for Analog Devices
* ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
*
* Copyright 2009-2010 Analog Devices Inc.
*/
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_data/adp5588.h>
/*
* Early pre 4.0 Silicon required to delay readout by at least 25ms,
* since the Event Counter Register updated 25ms after the interrupt
* asserted.
*/
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
struct adp5588_gpio {
struct i2c_client *client;
struct gpio_chip gpio_chip;
struct mutex lock; /* protect cached dir, dat_out */
/* protect serialized access to the interrupt controller bus */
struct mutex irq_lock;
uint8_t dat_out[3];
uint8_t dir[3];
uint8_t int_lvl_low[3];
uint8_t int_lvl_high[3];
uint8_t int_en[3];
uint8_t irq_mask[3];
uint8_t int_input_en[3];
};
static int adp5588_gpio_read(struct i2c_client *client, u8 reg)
{
int ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
dev_err(&client->dev, "Read Error\n");
return ret;
}
static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val)
{
int ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0)
dev_err(&client->dev, "Write Error\n");
return ret;
}
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
{
struct adp5588_gpio *dev = gpiochip_get_data(chip);
unsigned bank = ADP5588_BANK(off);
unsigned bit = ADP5588_BIT(off);
int val;
mutex_lock(&dev->lock);
if (dev->dir[bank] & bit)
val = dev->dat_out[bank];
else
val = adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + bank);
mutex_unlock(&dev->lock);
return !!(val & bit);
}
static void adp5588_gpio_set_value(struct gpio_chip *chip,
unsigned off, int val)
{
unsigned bank, bit;
struct adp5588_gpio *dev = gpiochip_get_data(chip);
bank = ADP5588_BANK(off);
bit = ADP5588_BIT(off);
mutex_lock(&dev->lock);
if (val)
dev->dat_out[bank] |= bit;
else
dev->dat_out[bank] &= ~bit;
adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
dev->dat_out[bank]);
mutex_unlock(&dev->lock);
}
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
{
int ret;
unsigned bank;
struct adp5588_gpio *dev = gpiochip_get_data(chip);
bank = ADP5588_BANK(off);
mutex_lock(&dev->lock);
dev->dir[bank] &= ~ADP5588_BIT(off);
ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]);
mutex_unlock(&dev->lock);
return ret;
}
static int adp5588_gpio_direction_output(struct gpio_chip *chip,
unsigned off, int val)
{
int ret;
unsigned bank, bit;
struct adp5588_gpio *dev = gpiochip_get_data(chip);
bank = ADP5588_BANK(off);
bit = ADP5588_BIT(off);
mutex_lock(&dev->lock);
dev->dir[bank] |= bit;
if (val)
dev->dat_out[bank] |= bit;
else
dev->dat_out[bank] &= ~bit;
ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
dev->dat_out[bank]);
ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank,
dev->dir[bank]);
mutex_unlock(&dev->lock);
return ret;
}
#ifdef CONFIG_GPIO_ADP5588_IRQ
static void adp5588_irq_bus_lock(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_gpio *dev = gpiochip_get_data(gc);
mutex_lock(&dev->irq_lock);
}
/*
* genirq core code can issue chip->mask/unmask from atomic context.
* This doesn't work for slow busses where an access needs to sleep.
* bus_sync_unlock() is therefore called outside the atomic context,
* syncs the current irq mask state with the slow external controller
* and unlocks the bus.
*/
static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_gpio *dev = gpiochip_get_data(gc);
int i;
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
if (dev->int_input_en[i]) {
mutex_lock(&dev->lock);
dev->dir[i] &= ~dev->int_input_en[i];
dev->int_input_en[i] = 0;
adp5588_gpio_write(dev->client, GPIO_DIR1 + i,
dev->dir[i]);
mutex_unlock(&dev->lock);
}
if (dev->int_en[i] ^ dev->irq_mask[i]) {
dev->int_en[i] = dev->irq_mask[i];
adp5588_gpio_write(dev->client, GPI_EM1 + i,
dev->int_en[i]);
}
}
mutex_unlock(&dev->irq_lock);
}
static void adp5588_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_gpio *dev = gpiochip_get_data(gc);
dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq);
}
static void adp5588_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_gpio *dev = gpiochip_get_data(gc);
dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq);
}
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_gpio *dev = gpiochip_get_data(gc);
uint16_t gpio = d->hwirq;
unsigned bank, bit;
bank = ADP5588_BANK(gpio);
bit = ADP5588_BIT(gpio);
dev->int_lvl_low[bank] &= ~bit;
dev->int_lvl_high[bank] &= ~bit;
if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH)
dev->int_lvl_high[bank] |= bit;
if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW)
dev->int_lvl_low[bank] |= bit;
dev->int_input_en[bank] |= bit;
return 0;
}
static struct irq_chip adp5588_irq_chip = {
.name = "adp5588",
.irq_mask = adp5588_irq_mask,
.irq_unmask = adp5588_irq_unmask,
.irq_bus_lock = adp5588_irq_bus_lock,
.irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
.irq_set_type = adp5588_irq_set_type,
};
static irqreturn_t adp5588_irq_handler(int irq, void *devid)
{
struct adp5588_gpio *dev = devid;
int status = adp5588_gpio_read(dev->client, INT_STAT);
if (status & ADP5588_KE_INT) {
int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT);
if (ev_cnt > 0) {
int i;
for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) {
int key = adp5588_gpio_read(dev->client,
Key_EVENTA + i);
/* GPIN events begin at 97,
* bit 7 indicates logic level
*/
int gpio = (key & 0x7f) - 97;
int lvl = key & (1 << 7);
int bank = ADP5588_BANK(gpio);
int bit = ADP5588_BIT(gpio);
if ((lvl && dev->int_lvl_high[bank] & bit) ||
(!lvl && dev->int_lvl_low[bank] & bit))
handle_nested_irq(irq_find_mapping(
dev->gpio_chip.irq.domain, gpio));
}
}
}
adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */
return IRQ_HANDLED;
}
static int adp5588_irq_init_hw(struct gpio_chip *gc)
{
struct adp5588_gpio *dev = gpiochip_get_data(gc);
/* Enable IRQs after registering chip */
adp5588_gpio_write(dev->client, CFG,
ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN);
return 0;
}
static int adp5588_irq_setup(struct adp5588_gpio *dev)
{
struct i2c_client *client = dev->client;
int ret;
struct adp5588_gpio_platform_data *pdata =
dev_get_platdata(&client->dev);
struct gpio_irq_chip *girq;
adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
mutex_init(&dev->irq_lock);
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, adp5588_irq_handler, IRQF_ONESHOT
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
dev_name(&client->dev), dev);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
return ret;
}
/* This will be registered in the call to devm_gpiochip_add_data() */
girq = &dev->gpio_chip.irq;
girq->chip = &adp5588_irq_chip;
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->first = pdata ? pdata->irq_base : 0;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_simple_irq;
girq->init_hw = adp5588_irq_init_hw;
girq->threaded = true;
return 0;
}
#else
static int adp5588_irq_setup(struct adp5588_gpio *dev)
{
struct i2c_client *client = dev->client;
dev_warn(&client->dev, "interrupt support not compiled in\n");
return 0;
}
#endif /* CONFIG_GPIO_ADP5588_IRQ */
static int adp5588_gpio_probe(struct i2c_client *client)
{
struct adp5588_gpio_platform_data *pdata =
dev_get_platdata(&client->dev);
struct adp5588_gpio *dev;
struct gpio_chip *gc;
int ret, i, revid;
unsigned int pullup_dis_mask = 0;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
return -EIO;
}
dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->client = client;
gc = &dev->gpio_chip;
gc->direction_input = adp5588_gpio_direction_input;
gc->direction_output = adp5588_gpio_direction_output;
gc->get = adp5588_gpio_get_value;
gc->set = adp5588_gpio_set_value;
gc->can_sleep = true;
gc->base = -1;
gc->parent = &client->dev;
if (pdata) {
gc->base = pdata->gpio_start;
gc->names = pdata->names;
pullup_dis_mask = pdata->pullup_dis_mask;
}
gc->ngpio = ADP5588_MAXGPIO;
gc->label = client->name;
gc->owner = THIS_MODULE;
mutex_init(&dev->lock);
ret = adp5588_gpio_read(dev->client, DEV_ID);
if (ret < 0)
return ret;
revid = ret & ADP5588_DEVICE_ID_MASK;
for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i);
dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i);
ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0);
ret |= adp5588_gpio_write(client, GPIO_PULL1 + i,
(pullup_dis_mask >> (8 * i)) & 0xFF);
ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0);
if (ret)
return ret;
}
if (client->irq) {
if (WA_DELAYED_READOUT_REVID(revid)) {
dev_warn(&client->dev, "GPIO int not supported\n");
} else {
ret = adp5588_irq_setup(dev);
if (ret)
return ret;
}
}
ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev);
if (ret)
return ret;
i2c_set_clientdata(client, dev);
return 0;
}
static void adp5588_gpio_remove(struct i2c_client *client)
{
struct adp5588_gpio *dev = i2c_get_clientdata(client);
if (dev->client->irq)
free_irq(dev->client->irq, dev);
}
static const struct i2c_device_id adp5588_gpio_id[] = {
{ "adp5588-gpio" },
{}
};
MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id);
static const struct of_device_id adp5588_gpio_of_id[] = {
{ .compatible = "adi,adp5588-gpio" },
{}
};
MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id);
static struct i2c_driver adp5588_gpio_driver = {
.driver = {
.name = "adp5588-gpio",
.of_match_table = adp5588_gpio_of_id,
},
.probe_new = adp5588_gpio_probe,
.remove = adp5588_gpio_remove,
.id_table = adp5588_gpio_id,
};
module_i2c_driver(adp5588_gpio_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
MODULE_LICENSE("GPL");

View File

@ -1014,7 +1014,8 @@ static const char *absolutes[ABS_CNT] = {
[ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure", [ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure",
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt", [ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
[ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth", [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth",
[ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc", [ABS_VOLUME] = "Volume", [ABS_PROFILE] = "Profile",
[ABS_MISC] = "Misc",
[ABS_MT_TOUCH_MAJOR] = "MTMajor", [ABS_MT_TOUCH_MAJOR] = "MTMajor",
[ABS_MT_TOUCH_MINOR] = "MTMinor", [ABS_MT_TOUCH_MINOR] = "MTMinor",
[ABS_MT_WIDTH_MAJOR] = "MTMajorW", [ABS_MT_WIDTH_MAJOR] = "MTMajorW",

View File

@ -6,9 +6,6 @@
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru> * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
*/ */
/*
*/
/* #define DEBUG */ /* #define DEBUG */
#include <linux/input.h> #include <linux/input.h>

View File

@ -6,9 +6,6 @@
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru> * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
*/ */
/*
*/
/* #define DEBUG */ /* #define DEBUG */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

View File

@ -7,9 +7,6 @@
* EMU10k1 - SB Live / Audigy - gameport driver for Linux * EMU10k1 - SB Live / Audigy - gameport driver for Linux
*/ */
/*
*/
#include <asm/io.h> #include <asm/io.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -7,9 +7,6 @@
* PDPI Lightning 4 gamecard driver for Linux. * PDPI Lightning 4 gamecard driver for Linux.
*/ */
/*
*/
#include <asm/io.h> #include <asm/io.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/errno.h> #include <linux/errno.h>

View File

@ -8,9 +8,6 @@
* NS558 based standard IBM game port driver for Linux * NS558 based standard IBM game port driver for Linux
*/ */
/*
*/
#include <asm/io.h> #include <asm/io.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -746,7 +746,7 @@ static void joydev_cleanup(struct joydev *joydev)
} }
/* /*
* These codes are copied from from hid-ids.h, unfortunately there is no common * These codes are copied from hid-ids.h, unfortunately there is no common
* usb_ids/bt_ids.h header. * usb_ids/bt_ids.h header.
*/ */
#define USB_VENDOR_ID_SONY 0x054c #define USB_VENDOR_ID_SONY 0x054c

View File

@ -7,9 +7,6 @@
* FP-Gaming Assassin 3D joystick driver for Linux * FP-Gaming Assassin 3D joystick driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -26,8 +26,23 @@ struct adc_joystick {
struct adc_joystick_axis *axes; struct adc_joystick_axis *axes;
struct iio_channel *chans; struct iio_channel *chans;
int num_chans; int num_chans;
bool polled;
}; };
static void adc_joystick_poll(struct input_dev *input)
{
struct adc_joystick *joy = input_get_drvdata(input);
int i, val, ret;
for (i = 0; i < joy->num_chans; i++) {
ret = iio_read_channel_raw(&joy->chans[i], &val);
if (ret < 0)
return;
input_report_abs(input, joy->axes[i].code, val);
}
input_sync(input);
}
static int adc_joystick_handle(const void *data, void *private) static int adc_joystick_handle(const void *data, void *private)
{ {
struct adc_joystick *joy = private; struct adc_joystick *joy = private;
@ -179,6 +194,7 @@ static int adc_joystick_probe(struct platform_device *pdev)
int error; int error;
int bits; int bits;
int i; int i;
unsigned int poll_interval;
joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL); joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
if (!joy) if (!joy)
@ -192,8 +208,25 @@ static int adc_joystick_probe(struct platform_device *pdev)
return error; return error;
} }
/* Count how many channels we got. NULL terminated. */ error = device_property_read_u32(dev, "poll-interval", &poll_interval);
if (error) {
/* -EINVAL means the property is absent. */
if (error != -EINVAL)
return error;
} else if (poll_interval == 0) {
dev_err(dev, "Unable to get poll-interval\n");
return -EINVAL;
} else {
joy->polled = true;
}
/*
* Count how many channels we got. NULL terminated.
* Do not check the storage size if using polling.
*/
for (i = 0; joy->chans[i].indio_dev; i++) { for (i = 0; joy->chans[i].indio_dev; i++) {
if (joy->polled)
continue;
bits = joy->chans[i].channel->scan_type.storagebits; bits = joy->chans[i].channel->scan_type.storagebits;
if (!bits || bits > 16) { if (!bits || bits > 16) {
dev_err(dev, "Unsupported channel storage size\n"); dev_err(dev, "Unsupported channel storage size\n");
@ -215,23 +248,31 @@ static int adc_joystick_probe(struct platform_device *pdev)
joy->input = input; joy->input = input;
input->name = pdev->name; input->name = pdev->name;
input->id.bustype = BUS_HOST; input->id.bustype = BUS_HOST;
input->open = adc_joystick_open;
input->close = adc_joystick_close;
error = adc_joystick_set_axes(dev, joy); error = adc_joystick_set_axes(dev, joy);
if (error) if (error)
return error; return error;
joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy); if (joy->polled) {
if (IS_ERR(joy->buffer)) { input_setup_polling(input, adc_joystick_poll);
dev_err(dev, "Unable to allocate callback buffer\n"); input_set_poll_interval(input, poll_interval);
return PTR_ERR(joy->buffer); } else {
} input->open = adc_joystick_open;
input->close = adc_joystick_close;
error = devm_add_action_or_reset(dev, adc_joystick_cleanup, joy->buffer); joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle,
if (error) { joy);
dev_err(dev, "Unable to add action\n"); if (IS_ERR(joy->buffer)) {
return error; dev_err(dev, "Unable to allocate callback buffer\n");
return PTR_ERR(joy->buffer);
}
error = devm_add_action_or_reset(dev, adc_joystick_cleanup,
joy->buffer);
if (error) {
dev_err(dev, "Unable to add action\n");
return error;
}
} }
input_set_drvdata(input, joy); input_set_drvdata(input, joy);

View File

@ -7,9 +7,6 @@
* Logitech ADI joystick family driver for Linux * Logitech ADI joystick family driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -7,9 +7,6 @@
* Driver for Amiga joysticks for Linux/m68k * Driver for Amiga joysticks for Linux/m68k
*/ */
/*
*/
#include <linux/types.h> #include <linux/types.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>

View File

@ -7,9 +7,6 @@
* Analog joystick and gamepad driver for Linux * Analog joystick and gamepad driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -7,9 +7,6 @@
* Creative Labs Blaster GamePad Cobra driver for Linux * Creative Labs Blaster GamePad Cobra driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -10,9 +10,6 @@
* Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/delay.h> #include <linux/delay.h>

View File

@ -11,9 +11,6 @@
* Raphael Assenat * Raphael Assenat
*/ */
/*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>

View File

@ -7,9 +7,6 @@
* Genius Flight 2000 joystick driver for Linux * Genius Flight 2000 joystick driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -7,9 +7,6 @@
* Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux * Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -7,9 +7,6 @@
* Guillemot Digital Interface Protocol driver for Linux * Guillemot Digital Interface Protocol driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -10,9 +10,6 @@
* InterAct digital gamepad/joystick driver for Linux * InterAct digital gamepad/joystick driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -8,9 +8,6 @@
* out of the joystick port into the syslog ... * out of the joystick port into the syslog ...
*/ */
/*
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/gameport.h> #include <linux/gameport.h>
#include <linux/kernel.h> #include <linux/kernel.h>

View File

@ -7,9 +7,6 @@
* Magellan and Space Mouse 6dof controller driver for Linux * Magellan and Space Mouse 6dof controller driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -7,9 +7,6 @@
* Microsoft SideWinder joystick family driver for Linux * Microsoft SideWinder joystick family driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -11,9 +11,6 @@
* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux * SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -10,9 +10,6 @@
* SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux * SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -8,9 +8,6 @@
* Gravis Stinger gamepad driver for Linux * Gravis Stinger gamepad driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -10,9 +10,6 @@
* ThrustMaster DirectConnect (BSP) joystick family driver for Linux * ThrustMaster DirectConnect (BSP) joystick family driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -10,9 +10,6 @@
* TurboGraFX parallel port interface driver for Linux. * TurboGraFX parallel port interface driver for Linux.
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/parport.h> #include <linux/parport.h>
#include <linux/input.h> #include <linux/input.h>

View File

@ -32,9 +32,6 @@
* Arndt Schoenewald <arndt@quelltext.com> * Arndt Schoenewald <arndt@quelltext.com>
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -7,9 +7,6 @@
* Logitech WingMan Warrior joystick driver for Linux * Logitech WingMan Warrior joystick driver for Linux
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -61,6 +61,7 @@
* Later changes can be tracked in SCM. * Later changes can be tracked in SCM.
*/ */
#include <linux/bits.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
@ -80,6 +81,9 @@
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1) #define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
#define MAP_STICKS_TO_NULL (1 << 2) #define MAP_STICKS_TO_NULL (1 << 2)
#define MAP_SELECT_BUTTON (1 << 3) #define MAP_SELECT_BUTTON (1 << 3)
#define MAP_PADDLES (1 << 4)
#define MAP_PROFILE_BUTTON (1 << 5)
#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \ #define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \
MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL) MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
@ -89,6 +93,17 @@
#define XTYPE_XBOXONE 3 #define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4 #define XTYPE_UNKNOWN 4
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
#define XPAD360W_POWEROFF_TIMEOUT 5
#define PKT_XB 0
#define PKT_XBE1 1
#define PKT_XBE2_FW_OLD 2
#define PKT_XBE2_FW_5_EARLY 3
#define PKT_XBE2_FW_5_11 4
static bool dpad_to_buttons; static bool dpad_to_buttons;
module_param(dpad_to_buttons, bool, S_IRUGO); module_param(dpad_to_buttons, bool, S_IRUGO);
MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
@ -111,8 +126,11 @@ static const struct xpad_device {
char *name; char *name;
u8 mapping; u8 mapping;
u8 xtype; u8 xtype;
u8 packet_type;
} xpad_device[] = { } xpad_device[] = {
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 }, { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
{ 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
{ 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
@ -128,9 +146,11 @@ static const struct xpad_device {
{ 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE },
{ 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE }, { 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
{ 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
@ -244,6 +264,7 @@ static const struct xpad_device {
{ 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE }, { 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE },
{ 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x0f0d, 0x00c5, "Hori Fighting Commander ONE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX }, { 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX },
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
@ -260,6 +281,7 @@ static const struct xpad_device {
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 }, { 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
{ 0x146b, 0x0604, "Bigben Interactive DAIJA Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE }, { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
@ -325,6 +347,7 @@ static const struct xpad_device {
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE }, { 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
@ -334,6 +357,14 @@ static const struct xpad_device {
{ 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
{ 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
{ 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
{ 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1220, "Wooting Two HE", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1300, "Wooting 60HE (AVR)", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1310, "Wooting 60HE (ARM)", 0, XTYPE_XBOX360 },
{ 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 }, { 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 },
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX }, { 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
@ -390,6 +421,13 @@ static const signed short xpad_abs_triggers[] = {
-1 -1
}; };
/* used when the controller has extra paddle buttons */
static const signed short xpad_btn_paddles[] = {
BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6, /* paddle upper right, lower right */
BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8, /* paddle upper left, lower left */
-1 /* terminating entry */
};
/* /*
* Xbox 360 has a vendor-specific class, so we cannot match it with only * Xbox 360 has a vendor-specific class, so we cannot match it with only
* USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
@ -419,6 +457,7 @@ static const signed short xpad_abs_triggers[] = {
static const struct usb_device_id xpad_table[] = { static const struct usb_device_id xpad_table[] = {
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */ XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */ XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
@ -429,6 +468,7 @@ static const struct usb_device_id xpad_table[] = {
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */ XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */
XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */ XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
@ -450,8 +490,12 @@ static const struct usb_device_id xpad_table[] = {
XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */ XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */ XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */
XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */
XPAD_XBOXONE_VENDOR(0x2e24), /* Hyperkin Duke X-Box One pad */ XPAD_XBOXONE_VENDOR(0x2e24), /* Hyperkin Duke X-Box One pad */
XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */ XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */
XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */
XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */ XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */
{ } { }
}; };
@ -473,13 +517,52 @@ struct xboxone_init_packet {
.len = ARRAY_SIZE(_data), \ .len = ARRAY_SIZE(_data), \
} }
/*
* starting with xbox one, the game input protocol is used
* magic numbers are taken from
* - https://github.com/xpadneo/gip-dissector/blob/main/src/gip-dissector.lua
* - https://github.com/medusalix/xone/blob/master/bus/protocol.c
*/
#define GIP_CMD_ACK 0x01
#define GIP_CMD_IDENTIFY 0x04
#define GIP_CMD_POWER 0x05
#define GIP_CMD_AUTHENTICATE 0x06
#define GIP_CMD_VIRTUAL_KEY 0x07
#define GIP_CMD_RUMBLE 0x09
#define GIP_CMD_LED 0x0a
#define GIP_CMD_FIRMWARE 0x0c
#define GIP_CMD_INPUT 0x20
#define GIP_SEQ0 0x00
#define GIP_OPT_ACK 0x10
#define GIP_OPT_INTERNAL 0x20
/*
* length of the command payload encoded with
* https://en.wikipedia.org/wiki/LEB128
* which is a no-op for N < 128
*/
#define GIP_PL_LEN(N) (N)
/*
* payload specific defines
*/
#define GIP_PWR_ON 0x00
#define GIP_LED_ON 0x01
#define GIP_MOTOR_R BIT(0)
#define GIP_MOTOR_L BIT(1)
#define GIP_MOTOR_RT BIT(2)
#define GIP_MOTOR_LT BIT(3)
#define GIP_MOTOR_ALL (GIP_MOTOR_R | GIP_MOTOR_L | GIP_MOTOR_RT | GIP_MOTOR_LT)
/* /*
* This packet is required for all Xbox One pads with 2015 * This packet is required for all Xbox One pads with 2015
* or later firmware installed (or present from the factory). * or later firmware installed (or present from the factory).
*/ */
static const u8 xboxone_fw2015_init[] = { static const u8 xboxone_power_on[] = {
0x05, 0x20, 0x00, 0x01, 0x00 GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(1), GIP_PWR_ON
}; };
/* /*
@ -489,7 +572,16 @@ static const u8 xboxone_fw2015_init[] = {
* Bluetooth mode. * Bluetooth mode.
*/ */
static const u8 xboxone_s_init[] = { static const u8 xboxone_s_init[] = {
0x05, 0x20, 0x00, 0x0f, 0x06 GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, 0x0f, 0x06
};
/*
* This packet is required to get additional input data
* from Xbox One Elite Series 2 (0x045e:0x0b00) pads.
* We mostly do this right now to get paddle data
*/
static const u8 extra_input_packet_init[] = {
0x4d, 0x10, 0x01, 0x02, 0x07, 0x00
}; };
/* /*
@ -497,9 +589,9 @@ static const u8 xboxone_s_init[] = {
* (0x0e6f:0x0165) to finish initialization and for Hori pads * (0x0e6f:0x0165) to finish initialization and for Hori pads
* (0x0f0d:0x0067) to make the analog sticks work. * (0x0f0d:0x0067) to make the analog sticks work.
*/ */
static const u8 xboxone_hori_init[] = { static const u8 xboxone_hori_ack_id[] = {
0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a, GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
0x00, 0x00, 0x00, 0x80, 0x00 0x00, GIP_CMD_IDENTIFY, GIP_OPT_INTERNAL, 0x3a, 0x00, 0x00, 0x00, 0x80, 0x00
}; };
/* /*
@ -507,8 +599,8 @@ static const u8 xboxone_hori_init[] = {
* sending input reports. These pads include: (0x0e6f:0x02ab), * sending input reports. These pads include: (0x0e6f:0x02ab),
* (0x0e6f:0x02a4), (0x0e6f:0x02a6). * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
*/ */
static const u8 xboxone_pdp_init1[] = { static const u8 xboxone_pdp_led_on[] = {
0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14 GIP_CMD_LED, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(3), 0x00, GIP_LED_ON, 0x14
}; };
/* /*
@ -516,8 +608,8 @@ static const u8 xboxone_pdp_init1[] = {
* sending input reports. These pads include: (0x0e6f:0x02ab), * sending input reports. These pads include: (0x0e6f:0x02ab),
* (0x0e6f:0x02a4), (0x0e6f:0x02a6). * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
*/ */
static const u8 xboxone_pdp_init2[] = { static const u8 xboxone_pdp_auth[] = {
0x06, 0x20, 0x00, 0x02, 0x01, 0x00 GIP_CMD_AUTHENTICATE, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(2), 0x01, 0x00
}; };
/* /*
@ -525,8 +617,8 @@ static const u8 xboxone_pdp_init2[] = {
* sending input reports. One of those pads is (0x24c6:0x543a). * sending input reports. One of those pads is (0x24c6:0x543a).
*/ */
static const u8 xboxone_rumblebegin_init[] = { static const u8 xboxone_rumblebegin_init[] = {
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
0x1D, 0x1D, 0xFF, 0x00, 0x00 0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x1D, 0x1D, 0xFF, 0x00, 0x00
}; };
/* /*
@ -536,8 +628,8 @@ static const u8 xboxone_rumblebegin_init[] = {
* spin up to enough speed to actually vibrate the gamepad. * spin up to enough speed to actually vibrate the gamepad.
*/ */
static const u8 xboxone_rumbleend_init[] = { static const u8 xboxone_rumbleend_init[] = {
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
0x00, 0x00, 0x00, 0x00, 0x00 0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
/* /*
@ -547,13 +639,14 @@ static const u8 xboxone_rumbleend_init[] = {
* packet is going to be sent. * packet is going to be sent.
*/ */
static const struct xboxone_init_packet xboxone_init_packets[] = { static const struct xboxone_init_packet xboxone_init_packets[] = {
XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init), XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_ack_id),
XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init), XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_ack_id),
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init), XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_power_on),
XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init), XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init), XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1), XBOXONE_INIT_PKT(0x045e, 0x0b00, extra_input_packet_init),
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2), XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_led_on),
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_auth),
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
@ -608,14 +701,17 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */ int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */ int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */ int pad_nr; /* the order x360 pads were attached */
const char *name; /* name of the device */ const char *name; /* name of the device */
struct work_struct work; /* init/remove device from callback */ struct work_struct work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
}; };
static int xpad_init_input(struct usb_xpad *xpad); static int xpad_init_input(struct usb_xpad *xpad);
static void xpad_deinit_input(struct usb_xpad *xpad); static void xpad_deinit_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num); static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
/* /*
* xpad_process_packet * xpad_process_packet
@ -656,10 +752,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
/* digital pad */ /* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */ /* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
} else { } else {
input_report_abs(dev, ABS_HAT0X, input_report_abs(dev, ABS_HAT0X,
!!(data[2] & 0x08) - !!(data[2] & 0x04)); !!(data[2] & 0x08) - !!(data[2] & 0x04));
@ -668,10 +764,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
} }
/* start/back buttons and stick press left/right */ /* start/back buttons and stick press left/right */
input_report_key(dev, BTN_START, data[2] & 0x10); input_report_key(dev, BTN_START, data[2] & BIT(4));
input_report_key(dev, BTN_SELECT, data[2] & 0x20); input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
input_report_key(dev, BTN_THUMBL, data[2] & 0x40); input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
input_report_key(dev, BTN_THUMBR, data[2] & 0x80); input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
/* "analog" buttons A, B, X, Y */ /* "analog" buttons A, B, X, Y */
input_report_key(dev, BTN_A, data[4]); input_report_key(dev, BTN_A, data[4]);
@ -683,6 +779,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
input_report_key(dev, BTN_C, data[8]); input_report_key(dev, BTN_C, data[8]);
input_report_key(dev, BTN_Z, data[9]); input_report_key(dev, BTN_Z, data[9]);
/* Profile button has a value of 0-3, so it is reported as an axis */
if (xpad->mapping & MAP_PROFILE_BUTTON)
input_report_abs(dev, ABS_PROFILE, data[34]);
input_sync(dev); input_sync(dev);
} }
@ -706,10 +806,10 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
/* digital pad */ /* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */ /* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
} }
/* /*
@ -727,21 +827,21 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
} }
/* start/back buttons */ /* start/back buttons */
input_report_key(dev, BTN_START, data[2] & 0x10); input_report_key(dev, BTN_START, data[2] & BIT(4));
input_report_key(dev, BTN_SELECT, data[2] & 0x20); input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
/* stick press left/right */ /* stick press left/right */
input_report_key(dev, BTN_THUMBL, data[2] & 0x40); input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
input_report_key(dev, BTN_THUMBR, data[2] & 0x80); input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
/* buttons A,B,X,Y,TL,TR and MODE */ /* buttons A,B,X,Y,TL,TR and MODE */
input_report_key(dev, BTN_A, data[3] & 0x10); input_report_key(dev, BTN_A, data[3] & BIT(4));
input_report_key(dev, BTN_B, data[3] & 0x20); input_report_key(dev, BTN_B, data[3] & BIT(5));
input_report_key(dev, BTN_X, data[3] & 0x40); input_report_key(dev, BTN_X, data[3] & BIT(6));
input_report_key(dev, BTN_Y, data[3] & 0x80); input_report_key(dev, BTN_Y, data[3] & BIT(7));
input_report_key(dev, BTN_TL, data[3] & 0x01); input_report_key(dev, BTN_TL, data[3] & BIT(0));
input_report_key(dev, BTN_TR, data[3] & 0x02); input_report_key(dev, BTN_TR, data[3] & BIT(1));
input_report_key(dev, BTN_MODE, data[3] & 0x04); input_report_key(dev, BTN_MODE, data[3] & BIT(2));
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
/* left stick */ /* left stick */
@ -767,6 +867,23 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
} }
input_sync(dev); input_sync(dev);
/* XBOX360W controllers can't be turned off without driver assistance */
if (xpad->xtype == XTYPE_XBOX360W) {
if (xpad->mode_btn_down_ts > 0 && xpad->pad_present &&
((ktime_get_seconds() - xpad->mode_btn_down_ts) >=
XPAD360W_POWEROFF_TIMEOUT)) {
xpad360w_poweroff_controller(xpad);
xpad->mode_btn_down_ts = 0;
return;
}
/* mode button down/up */
if (data[3] & BIT(2))
xpad->mode_btn_down_ts = ktime_get_seconds();
else
xpad->mode_btn_down_ts = 0;
}
} }
static void xpad_presence_work(struct work_struct *work) static void xpad_presence_work(struct work_struct *work)
@ -846,87 +963,154 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
{ {
struct input_dev *dev = xpad->dev; struct input_dev *dev = xpad->dev;
bool do_sync = false;
/* the xbox button has its own special report */ /* the xbox button has its own special report */
if (data[0] == 0X07) { if (data[0] == GIP_CMD_VIRTUAL_KEY) {
/* /*
* The Xbox One S controller requires these reports to be * The Xbox One S controller requires these reports to be
* acked otherwise it continues sending them forever and * acked otherwise it continues sending them forever and
* won't report further mode button events. * won't report further mode button events.
*/ */
if (data[1] == 0x30) if (data[1] == (GIP_OPT_ACK | GIP_OPT_INTERNAL))
xpadone_ack_mode_report(xpad, data[2]); xpadone_ack_mode_report(xpad, data[2]);
input_report_key(dev, BTN_MODE, data[4] & 0x01); input_report_key(dev, BTN_MODE, data[4] & GENMASK(1, 0));
input_sync(dev); input_sync(dev);
return;
}
/* check invalid packet */
else if (data[0] != 0X20)
return;
/* menu/view buttons */ do_sync = true;
input_report_key(dev, BTN_START, data[4] & 0x04); } else if (data[0] == GIP_CMD_FIRMWARE) {
input_report_key(dev, BTN_SELECT, data[4] & 0x08); /* Some packet formats force us to use this separate to poll paddle inputs */
if (xpad->mapping & MAP_SELECT_BUTTON) if (xpad->packet_type == PKT_XBE2_FW_5_11) {
input_report_key(dev, KEY_RECORD, data[22] & 0x01); /* Mute paddles if controller is in a custom profile slot
* Checked by looking at the active profile slot to
* verify it's the default slot
*/
if (data[19] != 0)
data[18] = 0;
/* buttons A,B,X,Y */ /* Elite Series 2 split packet paddle bits */
input_report_key(dev, BTN_A, data[4] & 0x10); input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
input_report_key(dev, BTN_B, data[4] & 0x20); input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
input_report_key(dev, BTN_X, data[4] & 0x40); input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
input_report_key(dev, BTN_Y, data[4] & 0x80); input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
/* digital pad */ do_sync = true;
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { }
/* dpad as buttons (left, right, up, down) */ } else if (data[0] == GIP_CMD_INPUT) { /* The main valid packet type for inputs */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04); /* menu/view buttons */
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08); input_report_key(dev, BTN_START, data[4] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01); input_report_key(dev, BTN_SELECT, data[4] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02); if (xpad->mapping & MAP_SELECT_BUTTON)
} else { input_report_key(dev, KEY_RECORD, data[22] & BIT(0));
input_report_abs(dev, ABS_HAT0X,
!!(data[5] & 0x08) - !!(data[5] & 0x04)); /* buttons A,B,X,Y */
input_report_abs(dev, ABS_HAT0Y, input_report_key(dev, BTN_A, data[4] & BIT(4));
!!(data[5] & 0x02) - !!(data[5] & 0x01)); input_report_key(dev, BTN_B, data[4] & BIT(5));
input_report_key(dev, BTN_X, data[4] & BIT(6));
input_report_key(dev, BTN_Y, data[4] & BIT(7));
/* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & BIT(1));
} else {
input_report_abs(dev, ABS_HAT0X,
!!(data[5] & 0x08) - !!(data[5] & 0x04));
input_report_abs(dev, ABS_HAT0Y,
!!(data[5] & 0x02) - !!(data[5] & 0x01));
}
/* TL/TR */
input_report_key(dev, BTN_TL, data[5] & BIT(4));
input_report_key(dev, BTN_TR, data[5] & BIT(5));
/* stick press left/right */
input_report_key(dev, BTN_THUMBL, data[5] & BIT(6));
input_report_key(dev, BTN_THUMBR, data[5] & BIT(7));
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
/* left stick */
input_report_abs(dev, ABS_X,
(__s16) le16_to_cpup((__le16 *)(data + 10)));
input_report_abs(dev, ABS_Y,
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
/* right stick */
input_report_abs(dev, ABS_RX,
(__s16) le16_to_cpup((__le16 *)(data + 14)));
input_report_abs(dev, ABS_RY,
~(__s16) le16_to_cpup((__le16 *)(data + 16)));
}
/* triggers left/right */
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
input_report_key(dev, BTN_TL2,
(__u16) le16_to_cpup((__le16 *)(data + 6)));
input_report_key(dev, BTN_TR2,
(__u16) le16_to_cpup((__le16 *)(data + 8)));
} else {
input_report_abs(dev, ABS_Z,
(__u16) le16_to_cpup((__le16 *)(data + 6)));
input_report_abs(dev, ABS_RZ,
(__u16) le16_to_cpup((__le16 *)(data + 8)));
}
/* paddle handling */
/* based on SDL's SDL_hidapi_xboxone.c */
if (xpad->mapping & MAP_PADDLES) {
if (xpad->packet_type == PKT_XBE1) {
/* Mute paddles if controller has a custom mapping applied.
* Checked by comparing the current mapping
* config against the factory mapping config
*/
if (memcmp(&data[4], &data[18], 2) != 0)
data[32] = 0;
/* OG Elite Series Controller paddle bits */
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[32] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[32] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[32] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[32] & BIT(2));
} else if (xpad->packet_type == PKT_XBE2_FW_OLD) {
/* Mute paddles if controller has a custom mapping applied.
* Checked by comparing the current mapping
* config against the factory mapping config
*/
if (data[19] != 0)
data[18] = 0;
/* Elite Series 2 4.x firmware paddle bits */
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
} else if (xpad->packet_type == PKT_XBE2_FW_5_EARLY) {
/* Mute paddles if controller has a custom mapping applied.
* Checked by comparing the current mapping
* config against the factory mapping config
*/
if (data[23] != 0)
data[22] = 0;
/* Elite Series 2 5.x firmware paddle bits
* (before the packet was split)
*/
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[22] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[22] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[22] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[22] & BIT(3));
}
}
do_sync = true;
} }
/* TL/TR */ if (do_sync)
input_report_key(dev, BTN_TL, data[5] & 0x10); input_sync(dev);
input_report_key(dev, BTN_TR, data[5] & 0x20);
/* stick press left/right */
input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
/* left stick */
input_report_abs(dev, ABS_X,
(__s16) le16_to_cpup((__le16 *)(data + 10)));
input_report_abs(dev, ABS_Y,
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
/* right stick */
input_report_abs(dev, ABS_RX,
(__s16) le16_to_cpup((__le16 *)(data + 14)));
input_report_abs(dev, ABS_RY,
~(__s16) le16_to_cpup((__le16 *)(data + 16)));
}
/* triggers left/right */
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
input_report_key(dev, BTN_TL2,
(__u16) le16_to_cpup((__le16 *)(data + 6)));
input_report_key(dev, BTN_TR2,
(__u16) le16_to_cpup((__le16 *)(data + 8)));
} else {
input_report_abs(dev, ABS_Z,
(__u16) le16_to_cpup((__le16 *)(data + 6)));
input_report_abs(dev, ABS_RZ,
(__u16) le16_to_cpup((__le16 *)(data + 8)));
}
input_sync(dev);
} }
static void xpad_irq_in(struct urb *urb) static void xpad_irq_in(struct urb *urb)
@ -1226,8 +1410,8 @@ static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num)
struct xpad_output_packet *packet = struct xpad_output_packet *packet =
&xpad->out_packets[XPAD_OUT_CMD_IDX]; &xpad->out_packets[XPAD_OUT_CMD_IDX];
static const u8 mode_report_ack[] = { static const u8 mode_report_ack[] = {
0x01, 0x20, 0x00, 0x09, 0x00, 0x07, 0x20, 0x02, GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
0x00, 0x00, 0x00, 0x00, 0x00 0x00, GIP_CMD_VIRTUAL_KEY, GIP_OPT_INTERNAL, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
spin_lock_irqsave(&xpad->odata_lock, flags); spin_lock_irqsave(&xpad->odata_lock, flags);
@ -1305,14 +1489,14 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
break; break;
case XTYPE_XBOXONE: case XTYPE_XBOXONE:
packet->data[0] = 0x09; /* activate rumble */ packet->data[0] = GIP_CMD_RUMBLE; /* activate rumble */
packet->data[1] = 0x00; packet->data[1] = 0x00;
packet->data[2] = xpad->odata_serial++; packet->data[2] = xpad->odata_serial++;
packet->data[3] = 0x09; packet->data[3] = GIP_PL_LEN(9);
packet->data[4] = 0x00; packet->data[4] = 0x00;
packet->data[5] = 0x0F; packet->data[5] = GIP_MOTOR_ALL;
packet->data[6] = 0x00; packet->data[6] = 0x00; /* left trigger */
packet->data[7] = 0x00; packet->data[7] = 0x00; /* right trigger */
packet->data[8] = strong / 512; /* left actuator */ packet->data[8] = strong / 512; /* left actuator */
packet->data[9] = weak / 512; /* right actuator */ packet->data[9] = weak / 512; /* right actuator */
packet->data[10] = 0xFF; /* on period */ packet->data[10] = 0xFF; /* on period */
@ -1622,6 +1806,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
input_set_abs_params(input_dev, abs, -1, 1, 0, 0); input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
break; break;
case ABS_PROFILE: /* 4 value profile button (such as on XAC) */
input_set_abs_params(input_dev, abs, 0, 4, 0, 0);
break;
default: default:
input_set_abs_params(input_dev, abs, 0, 0, 0, 0); input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
break; break;
@ -1693,6 +1880,12 @@ static int xpad_init_input(struct usb_xpad *xpad)
xpad_btn_pad[i]); xpad_btn_pad[i]);
} }
/* set up paddles if the controller has them */
if (xpad->mapping & MAP_PADDLES) {
for (i = 0; xpad_btn_paddles[i] >= 0; i++)
input_set_capability(input_dev, EV_KEY, xpad_btn_paddles[i]);
}
/* /*
* This should be a simple else block. However historically * This should be a simple else block. However historically
* xbox360w has mapped DPAD to buttons while xbox360 did not. This * xbox360w has mapped DPAD to buttons while xbox360 did not. This
@ -1714,6 +1907,10 @@ static int xpad_init_input(struct usb_xpad *xpad)
xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
} }
/* setup profile button as an axis with 4 possible values */
if (xpad->mapping & MAP_PROFILE_BUTTON)
xpad_set_up_abs(input_dev, ABS_PROFILE);
error = xpad_init_ff(xpad); error = xpad_init_ff(xpad);
if (error) if (error)
goto err_free_input; goto err_free_input;
@ -1779,6 +1976,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->mapping = xpad_device[i].mapping; xpad->mapping = xpad_device[i].mapping;
xpad->xtype = xpad_device[i].xtype; xpad->xtype = xpad_device[i].xtype;
xpad->name = xpad_device[i].name; xpad->name = xpad_device[i].name;
xpad->packet_type = PKT_XB;
INIT_WORK(&xpad->work, xpad_presence_work); INIT_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) { if (xpad->xtype == XTYPE_UNKNOWN) {
@ -1844,6 +2042,38 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
usb_set_intfdata(intf, xpad); usb_set_intfdata(intf, xpad);
/* Packet type detection */
if (le16_to_cpu(udev->descriptor.idVendor) == 0x045e) { /* Microsoft controllers */
if (le16_to_cpu(udev->descriptor.idProduct) == 0x02e3) {
/* The original elite controller always uses the oldest
* type of extended packet
*/
xpad->packet_type = PKT_XBE1;
} else if (le16_to_cpu(udev->descriptor.idProduct) == 0x0b00) {
/* The elite 2 controller has seen multiple packet
* revisions. These are tied to specific firmware
* versions
*/
if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x0500) {
/* This is the format that the Elite 2 used
* prior to the BLE update
*/
xpad->packet_type = PKT_XBE2_FW_OLD;
} else if (le16_to_cpu(udev->descriptor.bcdDevice) <
0x050b) {
/* This is the format that the Elite 2 used
* prior to the update that split the packet
*/
xpad->packet_type = PKT_XBE2_FW_5_EARLY;
} else {
/* The split packet format that was introduced
* in firmware v5.11
*/
xpad->packet_type = PKT_XBE2_FW_5_11;
}
}
}
if (xpad->xtype == XTYPE_XBOX360W) { if (xpad->xtype == XTYPE_XBOX360W) {
/* /*
* Submit the int URB immediately rather than waiting for open * Submit the int URB immediately rather than waiting for open
@ -1972,7 +2202,6 @@ static struct usb_driver xpad_driver = {
.disconnect = xpad_disconnect, .disconnect = xpad_disconnect,
.suspend = xpad_suspend, .suspend = xpad_suspend,
.resume = xpad_resume, .resume = xpad_resume,
.reset_resume = xpad_resume,
.id_table = xpad_table, .id_table = xpad_table,
}; };

View File

@ -28,9 +28,6 @@
* coder :-( * coder :-(
*/ */
/*
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -40,6 +40,9 @@ config KEYBOARD_ADP5520
config KEYBOARD_ADP5588 config KEYBOARD_ADP5588
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander" tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
depends on I2C depends on I2C
select GPIOLIB
select GPIOLIB_IRQCHIP
select INPUT_MATRIXKMAP
help help
Say Y here if you want to use a ADP5588/87 attached to your Say Y here if you want to use a ADP5588/87 attached to your
system I2C bus. system I2C bus.
@ -186,7 +189,7 @@ config KEYBOARD_QT2160
config KEYBOARD_CLPS711X config KEYBOARD_CLPS711X
tristate "CLPS711X Keypad support" tristate "CLPS711X Keypad support"
depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST) depends on ARCH_CLPS711X || COMPILE_TEST
select INPUT_MATRIXKMAP select INPUT_MATRIXKMAP
help help
Say Y here to enable the matrix keypad on the Cirrus Logic Say Y here to enable the matrix keypad on the Cirrus Logic
@ -524,6 +527,19 @@ config KEYBOARD_OPENCORES
To compile this driver as a module, choose M here; the To compile this driver as a module, choose M here; the
module will be called opencores-kbd. module will be called opencores-kbd.
config KEYBOARD_PINEPHONE
tristate "Pine64 PinePhone Keyboard"
depends on I2C && REGULATOR
select CRC8
select INPUT_MATRIXKMAP
help
Say Y here to enable support for the keyboard in the Pine64 PinePhone
keyboard case. This driver supports the FLOSS firmware available at
https://megous.com/git/pinephone-keyboard/
To compile this driver as a module, choose M here; the
module will be called pinephone-keyboard.
config KEYBOARD_PXA27x config KEYBOARD_PXA27x
tristate "PXA27x/PXA3xx keypad support" tristate "PXA27x/PXA3xx keypad support"
depends on PXA27x || PXA3xx || ARCH_MMP depends on PXA27x || PXA3xx || ARCH_MMP

View File

@ -52,6 +52,7 @@ obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PINEPHONE) += pinephone-keyboard.o
obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o

View File

@ -8,27 +8,163 @@
* Copyright (C) 2008-2010 Analog Devices Inc. * Copyright (C) 2008-2010 Analog Devices Inc.
*/ */
#include <linux/bits.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/timekeeping.h> #include <linux/timekeeping.h>
#include <linux/platform_data/adp5588.h> #define DEV_ID 0x00 /* Device ID */
#define CFG 0x01 /* Configuration Register1 */
#define INT_STAT 0x02 /* Interrupt Status Register */
#define KEY_LCK_EC_STAT 0x03 /* Key Lock and Event Counter Register */
#define KEY_EVENTA 0x04 /* Key Event Register A */
#define KEY_EVENTB 0x05 /* Key Event Register B */
#define KEY_EVENTC 0x06 /* Key Event Register C */
#define KEY_EVENTD 0x07 /* Key Event Register D */
#define KEY_EVENTE 0x08 /* Key Event Register E */
#define KEY_EVENTF 0x09 /* Key Event Register F */
#define KEY_EVENTG 0x0A /* Key Event Register G */
#define KEY_EVENTH 0x0B /* Key Event Register H */
#define KEY_EVENTI 0x0C /* Key Event Register I */
#define KEY_EVENTJ 0x0D /* Key Event Register J */
#define KP_LCK_TMR 0x0E /* Keypad Lock1 to Lock2 Timer */
#define UNLOCK1 0x0F /* Unlock Key1 */
#define UNLOCK2 0x10 /* Unlock Key2 */
#define GPIO_INT_STAT1 0x11 /* GPIO Interrupt Status */
#define GPIO_INT_STAT2 0x12 /* GPIO Interrupt Status */
#define GPIO_INT_STAT3 0x13 /* GPIO Interrupt Status */
#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */
#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */
#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */
#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */
#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */
#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */
#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */
#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */
#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */
#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */
#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */
#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */
#define GPI_EM1 0x20 /* GPI Event Mode 1 */
#define GPI_EM2 0x21 /* GPI Event Mode 2 */
#define GPI_EM3 0x22 /* GPI Event Mode 3 */
#define GPIO_DIR1 0x23 /* GPIO Data Direction */
#define GPIO_DIR2 0x24 /* GPIO Data Direction */
#define GPIO_DIR3 0x25 /* GPIO Data Direction */
#define GPIO_INT_LVL1 0x26 /* GPIO Edge/Level Detect */
#define GPIO_INT_LVL2 0x27 /* GPIO Edge/Level Detect */
#define GPIO_INT_LVL3 0x28 /* GPIO Edge/Level Detect */
#define DEBOUNCE_DIS1 0x29 /* Debounce Disable */
#define DEBOUNCE_DIS2 0x2A /* Debounce Disable */
#define DEBOUNCE_DIS3 0x2B /* Debounce Disable */
#define GPIO_PULL1 0x2C /* GPIO Pull Disable */
#define GPIO_PULL2 0x2D /* GPIO Pull Disable */
#define GPIO_PULL3 0x2E /* GPIO Pull Disable */
#define CMP_CFG_STAT 0x30 /* Comparator Configuration and Status Register */
#define CMP_CONFG_SENS1 0x31 /* Sensor1 Comparator Configuration Register */
#define CMP_CONFG_SENS2 0x32 /* L2 Light Sensor Reference Level, Output Falling for Sensor 1 */
#define CMP1_LVL2_TRIP 0x33 /* L2 Light Sensor Hysteresis (Active when Output Rising) for Sensor 1 */
#define CMP1_LVL2_HYS 0x34 /* L3 Light Sensor Reference Level, Output Falling For Sensor 1 */
#define CMP1_LVL3_TRIP 0x35 /* L3 Light Sensor Hysteresis (Active when Output Rising) For Sensor 1 */
#define CMP1_LVL3_HYS 0x36 /* Sensor 2 Comparator Configuration Register */
#define CMP2_LVL2_TRIP 0x37 /* L2 Light Sensor Reference Level, Output Falling for Sensor 2 */
#define CMP2_LVL2_HYS 0x38 /* L2 Light Sensor Hysteresis (Active when Output Rising) for Sensor 2 */
#define CMP2_LVL3_TRIP 0x39 /* L3 Light Sensor Reference Level, Output Falling For Sensor 2 */
#define CMP2_LVL3_HYS 0x3A /* L3 Light Sensor Hysteresis (Active when Output Rising) For Sensor 2 */
#define CMP1_ADC_DAT_R1 0x3B /* Comparator 1 ADC data Register1 */
#define CMP1_ADC_DAT_R2 0x3C /* Comparator 1 ADC data Register2 */
#define CMP2_ADC_DAT_R1 0x3D /* Comparator 2 ADC data Register1 */
#define CMP2_ADC_DAT_R2 0x3E /* Comparator 2 ADC data Register2 */
#define ADP5588_DEVICE_ID_MASK 0xF
/* Configuration Register1 */
#define ADP5588_AUTO_INC BIT(7)
#define ADP5588_GPIEM_CFG BIT(6)
#define ADP5588_OVR_FLOW_M BIT(5)
#define ADP5588_INT_CFG BIT(4)
#define ADP5588_OVR_FLOW_IEN BIT(3)
#define ADP5588_K_LCK_IM BIT(2)
#define ADP5588_GPI_IEN BIT(1)
#define ADP5588_KE_IEN BIT(0)
/* Interrupt Status Register */
#define ADP5588_CMP2_INT BIT(5)
#define ADP5588_CMP1_INT BIT(4)
#define ADP5588_OVR_FLOW_INT BIT(3)
#define ADP5588_K_LCK_INT BIT(2)
#define ADP5588_GPI_INT BIT(1)
#define ADP5588_KE_INT BIT(0)
/* Key Lock and Event Counter Register */
#define ADP5588_K_LCK_EN BIT(6)
#define ADP5588_LCK21 0x30
#define ADP5588_KEC GENMASK(3, 0)
#define ADP5588_MAXGPIO 18
#define ADP5588_BANK(offs) ((offs) >> 3)
#define ADP5588_BIT(offs) (1u << ((offs) & 0x7))
/* Put one of these structures in i2c_board_info platform_data */
/*
* 128 so it fits matrix-keymap maximum number of keys when the full
* 10cols * 8rows are used.
*/
#define ADP5588_KEYMAPSIZE 128
#define GPI_PIN_ROW0 97
#define GPI_PIN_ROW1 98
#define GPI_PIN_ROW2 99
#define GPI_PIN_ROW3 100
#define GPI_PIN_ROW4 101
#define GPI_PIN_ROW5 102
#define GPI_PIN_ROW6 103
#define GPI_PIN_ROW7 104
#define GPI_PIN_COL0 105
#define GPI_PIN_COL1 106
#define GPI_PIN_COL2 107
#define GPI_PIN_COL3 108
#define GPI_PIN_COL4 109
#define GPI_PIN_COL5 110
#define GPI_PIN_COL6 111
#define GPI_PIN_COL7 112
#define GPI_PIN_COL8 113
#define GPI_PIN_COL9 114
#define GPI_PIN_ROW_BASE GPI_PIN_ROW0
#define GPI_PIN_ROW_END GPI_PIN_ROW7
#define GPI_PIN_COL_BASE GPI_PIN_COL0
#define GPI_PIN_COL_END GPI_PIN_COL9
#define GPI_PIN_BASE GPI_PIN_ROW_BASE
#define GPI_PIN_END GPI_PIN_COL_END
#define ADP5588_ROWS_MAX (GPI_PIN_ROW7 - GPI_PIN_ROW0 + 1)
#define ADP5588_COLS_MAX (GPI_PIN_COL9 - GPI_PIN_COL0 + 1)
#define ADP5588_GPIMAPSIZE_MAX (GPI_PIN_END - GPI_PIN_BASE + 1)
/* Key Event Register xy */ /* Key Event Register xy */
#define KEY_EV_PRESSED (1 << 7) #define KEY_EV_PRESSED BIT(7)
#define KEY_EV_MASK (0x7F) #define KEY_EV_MASK GENMASK(6, 0)
#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */ #define KP_SEL(x) (BIT(x) - 1) /* 2^x-1 */
#define KEYP_MAX_EVENT 10 #define KEYP_MAX_EVENT 10
@ -40,21 +176,27 @@
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) #define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
#define WA_DELAYED_READOUT_TIME 25 #define WA_DELAYED_READOUT_TIME 25
#define ADP5588_INVALID_HWIRQ (~0UL)
struct adp5588_kpad { struct adp5588_kpad {
struct i2c_client *client; struct i2c_client *client;
struct input_dev *input; struct input_dev *input;
ktime_t irq_time; ktime_t irq_time;
unsigned long delay; unsigned long delay;
u32 row_shift;
u32 rows;
u32 cols;
u32 unlock_keys[2];
int nkeys_unlock;
unsigned short keycode[ADP5588_KEYMAPSIZE]; unsigned short keycode[ADP5588_KEYMAPSIZE];
const struct adp5588_gpi_map *gpimap;
unsigned short gpimapsize;
#ifdef CONFIG_GPIOLIB
unsigned char gpiomap[ADP5588_MAXGPIO]; unsigned char gpiomap[ADP5588_MAXGPIO];
struct gpio_chip gc; struct gpio_chip gc;
struct mutex gpio_lock; /* Protect cached dir, dat_out */ struct mutex gpio_lock; /* Protect cached dir, dat_out */
u8 dat_out[3]; u8 dat_out[3];
u8 dir[3]; u8 dir[3];
#endif u8 int_en[3];
u8 irq_mask[3];
u8 pull_dis[3];
}; };
static int adp5588_read(struct i2c_client *client, u8 reg) static int adp5588_read(struct i2c_client *client, u8 reg)
@ -72,8 +214,7 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
return i2c_smbus_write_byte_data(client, reg, val); return i2c_smbus_write_byte_data(client, reg, val);
} }
#ifdef CONFIG_GPIOLIB static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned int off)
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
{ {
struct adp5588_kpad *kpad = gpiochip_get_data(chip); struct adp5588_kpad *kpad = gpiochip_get_data(chip);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
@ -93,7 +234,7 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
} }
static void adp5588_gpio_set_value(struct gpio_chip *chip, static void adp5588_gpio_set_value(struct gpio_chip *chip,
unsigned off, int val) unsigned int off, int val)
{ {
struct adp5588_kpad *kpad = gpiochip_get_data(chip); struct adp5588_kpad *kpad = gpiochip_get_data(chip);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
@ -106,13 +247,47 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip,
else else
kpad->dat_out[bank] &= ~bit; kpad->dat_out[bank] &= ~bit;
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, kpad->dat_out[bank]);
kpad->dat_out[bank]);
mutex_unlock(&kpad->gpio_lock); mutex_unlock(&kpad->gpio_lock);
} }
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off,
unsigned long config)
{
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
bool pull_disable;
int ret;
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_BIAS_PULL_UP:
pull_disable = false;
break;
case PIN_CONFIG_BIAS_DISABLE:
pull_disable = true;
break;
default:
return -ENOTSUPP;
}
mutex_lock(&kpad->gpio_lock);
if (pull_disable)
kpad->pull_dis[bank] |= bit;
else
kpad->pull_dis[bank] &= bit;
ret = adp5588_write(kpad->client, GPIO_PULL1 + bank,
kpad->pull_dis[bank]);
mutex_unlock(&kpad->gpio_lock);
return ret;
}
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
{ {
struct adp5588_kpad *kpad = gpiochip_get_data(chip); struct adp5588_kpad *kpad = gpiochip_get_data(chip);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
@ -130,7 +305,7 @@ static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
} }
static int adp5588_gpio_direction_output(struct gpio_chip *chip, static int adp5588_gpio_direction_output(struct gpio_chip *chip,
unsigned off, int val) unsigned int off, int val)
{ {
struct adp5588_kpad *kpad = gpiochip_get_data(chip); struct adp5588_kpad *kpad = gpiochip_get_data(chip);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
@ -147,17 +322,19 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip,
kpad->dat_out[bank] &= ~bit; kpad->dat_out[bank] &= ~bit;
ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
kpad->dat_out[bank]); kpad->dat_out[bank]);
ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank, if (ret)
kpad->dir[bank]); goto out_unlock;
ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
out_unlock:
mutex_unlock(&kpad->gpio_lock); mutex_unlock(&kpad->gpio_lock);
return ret; return ret;
} }
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad, static int adp5588_build_gpiomap(struct adp5588_kpad *kpad)
const struct adp5588_kpad_platform_data *pdata)
{ {
bool pin_used[ADP5588_MAXGPIO]; bool pin_used[ADP5588_MAXGPIO];
int n_unused = 0; int n_unused = 0;
@ -165,15 +342,12 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
memset(pin_used, 0, sizeof(pin_used)); memset(pin_used, 0, sizeof(pin_used));
for (i = 0; i < pdata->rows; i++) for (i = 0; i < kpad->rows; i++)
pin_used[i] = true; pin_used[i] = true;
for (i = 0; i < pdata->cols; i++) for (i = 0; i < kpad->cols; i++)
pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true; pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true;
for (i = 0; i < kpad->gpimapsize; i++)
pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true;
for (i = 0; i < ADP5588_MAXGPIO; i++) for (i = 0; i < ADP5588_MAXGPIO; i++)
if (!pin_used[i]) if (!pin_used[i])
kpad->gpiomap[n_unused++] = i; kpad->gpiomap[n_unused++] = i;
@ -181,47 +355,101 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
return n_unused; return n_unused;
} }
static void adp5588_gpio_do_teardown(void *_kpad) static void adp5588_irq_bus_lock(struct irq_data *d)
{ {
struct adp5588_kpad *kpad = _kpad; struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct device *dev = &kpad->client->dev; struct adp5588_kpad *kpad = gpiochip_get_data(gc);
const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
int error;
error = gpio_data->teardown(kpad->client, mutex_lock(&kpad->gpio_lock);
kpad->gc.base, kpad->gc.ngpio,
gpio_data->context);
if (error)
dev_warn(&kpad->client->dev, "teardown failed %d\n", error);
} }
static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
int i;
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
if (kpad->int_en[i] ^ kpad->irq_mask[i]) {
kpad->int_en[i] = kpad->irq_mask[i];
adp5588_write(kpad->client, GPI_EM1 + i, kpad->int_en[i]);
}
}
mutex_unlock(&kpad->gpio_lock);
}
static void adp5588_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long real_irq = kpad->gpiomap[hwirq];
kpad->irq_mask[ADP5588_BANK(real_irq)] &= ~ADP5588_BIT(real_irq);
gpiochip_disable_irq(gc, hwirq);
}
static void adp5588_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
unsigned long real_irq = kpad->gpiomap[hwirq];
gpiochip_enable_irq(gc, hwirq);
kpad->irq_mask[ADP5588_BANK(real_irq)] |= ADP5588_BIT(real_irq);
}
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
{
if (!(type & IRQ_TYPE_EDGE_BOTH))
return -EINVAL;
irq_set_handler_locked(d, handle_edge_irq);
return 0;
}
static const struct irq_chip adp5588_irq_chip = {
.name = "adp5588",
.irq_mask = adp5588_irq_mask,
.irq_unmask = adp5588_irq_unmask,
.irq_bus_lock = adp5588_irq_bus_lock,
.irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
.irq_set_type = adp5588_irq_set_type,
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static int adp5588_gpio_add(struct adp5588_kpad *kpad) static int adp5588_gpio_add(struct adp5588_kpad *kpad)
{ {
struct device *dev = &kpad->client->dev; struct device *dev = &kpad->client->dev;
const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); struct gpio_irq_chip *girq;
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
int i, error; int i, error;
if (!gpio_data) kpad->gc.ngpio = adp5588_build_gpiomap(kpad);
return 0;
kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata);
if (kpad->gc.ngpio == 0) { if (kpad->gc.ngpio == 0) {
dev_info(dev, "No unused gpios left to export\n"); dev_info(dev, "No unused gpios left to export\n");
return 0; return 0;
} }
kpad->gc.parent = &kpad->client->dev;
kpad->gc.direction_input = adp5588_gpio_direction_input; kpad->gc.direction_input = adp5588_gpio_direction_input;
kpad->gc.direction_output = adp5588_gpio_direction_output; kpad->gc.direction_output = adp5588_gpio_direction_output;
kpad->gc.get = adp5588_gpio_get_value; kpad->gc.get = adp5588_gpio_get_value;
kpad->gc.set = adp5588_gpio_set_value; kpad->gc.set = adp5588_gpio_set_value;
kpad->gc.set_config = adp5588_gpio_set_config;
kpad->gc.can_sleep = 1; kpad->gc.can_sleep = 1;
kpad->gc.base = gpio_data->gpio_start; kpad->gc.base = -1;
kpad->gc.label = kpad->client->name; kpad->gc.label = kpad->client->name;
kpad->gc.owner = THIS_MODULE; kpad->gc.owner = THIS_MODULE;
kpad->gc.names = gpio_data->names;
girq = &kpad->gc.irq;
gpio_irq_chip_set_chip(girq, &adp5588_irq_chip);
girq->handler = handle_bad_irq;
girq->threaded = true;
mutex_init(&kpad->gpio_lock); mutex_init(&kpad->gpio_lock);
@ -235,54 +463,87 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
kpad->dat_out[i] = adp5588_read(kpad->client, kpad->dat_out[i] = adp5588_read(kpad->client,
GPIO_DAT_OUT1 + i); GPIO_DAT_OUT1 + i);
kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i);
} kpad->pull_dis[i] = adp5588_read(kpad->client, GPIO_PULL1 + i);
if (gpio_data->setup) {
error = gpio_data->setup(kpad->client,
kpad->gc.base, kpad->gc.ngpio,
gpio_data->context);
if (error)
dev_warn(dev, "setup failed: %d\n", error);
}
if (gpio_data->teardown) {
error = devm_add_action(dev, adp5588_gpio_do_teardown, kpad);
if (error)
dev_warn(dev, "failed to schedule teardown: %d\n",
error);
} }
return 0; return 0;
} }
#else static unsigned long adp5588_gpiomap_get_hwirq(struct device *dev,
static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) const u8 *map, unsigned int gpio,
unsigned int ngpios)
{ {
return 0; unsigned int hwirq;
for (hwirq = 0; hwirq < ngpios; hwirq++)
if (map[hwirq] == gpio)
return hwirq;
/* should never happen */
dev_warn_ratelimited(dev, "could not find the hwirq for gpio(%u)\n", gpio);
return ADP5588_INVALID_HWIRQ;
}
static void adp5588_gpio_irq_handle(struct adp5588_kpad *kpad, int key_val,
int key_press)
{
unsigned int irq, gpio = key_val - GPI_PIN_BASE, irq_type;
struct i2c_client *client = kpad->client;
struct irq_data *irqd;
unsigned long hwirq;
hwirq = adp5588_gpiomap_get_hwirq(&client->dev, kpad->gpiomap,
gpio, kpad->gc.ngpio);
if (hwirq == ADP5588_INVALID_HWIRQ) {
dev_err(&client->dev, "Could not get hwirq for key(%u)\n", key_val);
return;
}
irq = irq_find_mapping(kpad->gc.irq.domain, hwirq);
if (!irq)
return;
irqd = irq_get_irq_data(irq);
if (!irqd) {
dev_err(&client->dev, "Could not get irq(%u) data\n", irq);
return;
}
irq_type = irqd_get_trigger_type(irqd);
/*
* Default is active low which means key_press is asserted on
* the falling edge.
*/
if ((irq_type & IRQ_TYPE_EDGE_RISING && !key_press) ||
(irq_type & IRQ_TYPE_EDGE_FALLING && key_press))
handle_nested_irq(irq);
} }
#endif
static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt)
{ {
int i, j; int i;
for (i = 0; i < ev_cnt; i++) { for (i = 0; i < ev_cnt; i++) {
int key = adp5588_read(kpad->client, Key_EVENTA + i); int key = adp5588_read(kpad->client, KEY_EVENTA + i);
int key_val = key & KEY_EV_MASK; int key_val = key & KEY_EV_MASK;
int key_press = key & KEY_EV_PRESSED;
if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) { if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
for (j = 0; j < kpad->gpimapsize; j++) { /* gpio line used as IRQ source */
if (key_val == kpad->gpimap[j].pin) { adp5588_gpio_irq_handle(kpad, key_val, key_press);
input_report_switch(kpad->input,
kpad->gpimap[j].sw_evt,
key & KEY_EV_PRESSED);
break;
}
}
} else { } else {
int row = (key_val - 1) / ADP5588_COLS_MAX;
int col = (key_val - 1) % ADP5588_COLS_MAX;
int code = MATRIX_SCAN_CODE(row, col, kpad->row_shift);
dev_dbg_ratelimited(&kpad->client->dev,
"report key(%d) r(%d) c(%d) code(%d)\n",
key_val, row, col, kpad->keycode[code]);
input_report_key(kpad->input, input_report_key(kpad->input,
kpad->keycode[key_val - 1], kpad->keycode[code], key_press);
key & KEY_EV_PRESSED);
} }
} }
} }
@ -335,176 +596,145 @@ static irqreturn_t adp5588_thread_irq(int irq, void *handle)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int adp5588_setup(struct i2c_client *client) static int adp5588_setup(struct adp5588_kpad *kpad)
{ {
const struct adp5588_kpad_platform_data *pdata = struct i2c_client *client = kpad->client;
dev_get_platdata(&client->dev);
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
int i, ret; int i, ret;
unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows)); ret = adp5588_write(client, KP_GPIO1, KP_SEL(kpad->rows));
ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF); if (ret)
ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
if (pdata->en_keylock) {
ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1);
ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2);
ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
}
for (i = 0; i < KEYP_MAX_EVENT; i++)
ret |= adp5588_read(client, Key_EVENTA);
for (i = 0; i < pdata->gpimapsize; i++) {
unsigned short pin = pdata->gpimap[i].pin;
if (pin <= GPI_PIN_ROW_END) {
evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE));
} else {
evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF);
evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8);
}
}
if (pdata->gpimapsize) {
ret |= adp5588_write(client, GPI_EM1, evt_mode1);
ret |= adp5588_write(client, GPI_EM2, evt_mode2);
ret |= adp5588_write(client, GPI_EM3, evt_mode3);
}
if (gpio_data) {
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
int pull_mask = gpio_data->pullup_dis_mask;
ret |= adp5588_write(client, GPIO_PULL1 + i,
(pull_mask >> (8 * i)) & 0xFF);
}
}
ret |= adp5588_write(client, INT_STAT,
ADP5588_CMP2_INT | ADP5588_CMP1_INT |
ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT |
ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */
ret |= adp5588_write(client, CFG, ADP5588_INT_CFG |
ADP5588_OVR_FLOW_IEN |
ADP5588_KE_IEN);
if (ret < 0) {
dev_err(&client->dev, "Write Error\n");
return ret; return ret;
ret = adp5588_write(client, KP_GPIO2, KP_SEL(kpad->cols) & 0xFF);
if (ret)
return ret;
ret = adp5588_write(client, KP_GPIO3, KP_SEL(kpad->cols) >> 8);
if (ret)
return ret;
for (i = 0; i < kpad->nkeys_unlock; i++) {
ret = adp5588_write(client, UNLOCK1 + i, kpad->unlock_keys[i]);
if (ret)
return ret;
}
if (kpad->nkeys_unlock) {
ret = adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
if (ret)
return ret;
}
for (i = 0; i < KEYP_MAX_EVENT; i++) {
ret = adp5588_read(client, KEY_EVENTA);
if (ret)
return ret;
}
ret = adp5588_write(client, INT_STAT,
ADP5588_CMP2_INT | ADP5588_CMP1_INT |
ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT |
ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */
if (ret)
return ret;
return adp5588_write(client, CFG, ADP5588_INT_CFG |
ADP5588_OVR_FLOW_IEN | ADP5588_KE_IEN);
}
static int adp5588_fw_parse(struct adp5588_kpad *kpad)
{
struct i2c_client *client = kpad->client;
int ret, i;
ret = matrix_keypad_parse_properties(&client->dev, &kpad->rows,
&kpad->cols);
if (ret)
return ret;
if (kpad->rows > ADP5588_ROWS_MAX || kpad->cols > ADP5588_COLS_MAX) {
dev_err(&client->dev, "Invalid nr of rows(%u) or cols(%u)\n",
kpad->rows, kpad->cols);
return -EINVAL;
}
ret = matrix_keypad_build_keymap(NULL, NULL, kpad->rows, kpad->cols,
kpad->keycode, kpad->input);
if (ret)
return ret;
kpad->row_shift = get_count_order(kpad->cols);
if (device_property_read_bool(&client->dev, "autorepeat"))
__set_bit(EV_REP, kpad->input->evbit);
kpad->nkeys_unlock = device_property_count_u32(&client->dev,
"adi,unlock-keys");
if (kpad->nkeys_unlock <= 0) {
/* so that we don't end up enabling key lock */
kpad->nkeys_unlock = 0;
return 0;
}
if (kpad->nkeys_unlock > ARRAY_SIZE(kpad->unlock_keys)) {
dev_err(&client->dev, "number of unlock keys(%d) > (%zu)\n",
kpad->nkeys_unlock, ARRAY_SIZE(kpad->unlock_keys));
return -EINVAL;
}
ret = device_property_read_u32_array(&client->dev, "adi,unlock-keys",
kpad->unlock_keys,
kpad->nkeys_unlock);
if (ret)
return ret;
for (i = 0; i < kpad->nkeys_unlock; i++) {
/*
* Even though it should be possible (as stated in the datasheet)
* to use GPIs (which are part of the keys event) as unlock keys,
* it was not working at all and was leading to overflow events
* at some point. Hence, for now, let's just allow keys which are
* part of keypad matrix to be used and if a reliable way of
* using GPIs is found, this condition can be removed/lightened.
*/
if (kpad->unlock_keys[i] >= kpad->cols * kpad->rows) {
dev_err(&client->dev, "Invalid unlock key(%d)\n",
kpad->unlock_keys[i]);
return -EINVAL;
}
/*
* Firmware properties keys start from 0 but on the device they
* start from 1.
*/
kpad->unlock_keys[i] += 1;
} }
return 0; return 0;
} }
static void adp5588_report_switch_state(struct adp5588_kpad *kpad) static void adp5588_disable_regulator(void *reg)
{ {
int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1); regulator_disable(reg);
int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2);
int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3);
int gpi_stat_tmp, pin_loc;
int i;
for (i = 0; i < kpad->gpimapsize; i++) {
unsigned short pin = kpad->gpimap[i].pin;
if (pin <= GPI_PIN_ROW_END) {
gpi_stat_tmp = gpi_stat1;
pin_loc = pin - GPI_PIN_ROW_BASE;
} else if ((pin - GPI_PIN_COL_BASE) < 8) {
gpi_stat_tmp = gpi_stat2;
pin_loc = pin - GPI_PIN_COL_BASE;
} else {
gpi_stat_tmp = gpi_stat3;
pin_loc = pin - GPI_PIN_COL_BASE - 8;
}
if (gpi_stat_tmp < 0) {
dev_err(&kpad->client->dev,
"Can't read GPIO_DAT_STAT switch %d default to OFF\n",
pin);
gpi_stat_tmp = 0;
}
input_report_switch(kpad->input,
kpad->gpimap[i].sw_evt,
!(gpi_stat_tmp & (1 << pin_loc)));
}
input_sync(kpad->input);
} }
static int adp5588_probe(struct i2c_client *client, static int adp5588_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct adp5588_kpad *kpad; struct adp5588_kpad *kpad;
const struct adp5588_kpad_platform_data *pdata =
dev_get_platdata(&client->dev);
struct input_dev *input; struct input_dev *input;
struct gpio_desc *gpio;
struct regulator *vcc;
unsigned int revid; unsigned int revid;
int ret, i; int ret;
int error; int error;
if (!i2c_check_functionality(client->adapter, if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) { I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
return -EIO; return -EIO;
} }
if (!pdata) {
dev_err(&client->dev, "no platform data?\n");
return -EINVAL;
}
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
return -EINVAL;
}
if (pdata->keymapsize != ADP5588_KEYMAPSIZE) {
dev_err(&client->dev, "invalid keymapsize\n");
return -EINVAL;
}
if (!pdata->gpimap && pdata->gpimapsize) {
dev_err(&client->dev, "invalid gpimap from pdata\n");
return -EINVAL;
}
if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) {
dev_err(&client->dev, "invalid gpimapsize\n");
return -EINVAL;
}
for (i = 0; i < pdata->gpimapsize; i++) {
unsigned short pin = pdata->gpimap[i].pin;
if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) {
dev_err(&client->dev, "invalid gpi pin data\n");
return -EINVAL;
}
if (pin <= GPI_PIN_ROW_END) {
if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) {
dev_err(&client->dev, "invalid gpi row data\n");
return -EINVAL;
}
} else {
if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) {
dev_err(&client->dev, "invalid gpi col data\n");
return -EINVAL;
}
}
}
if (!client->irq) {
dev_err(&client->dev, "no IRQ?\n");
return -EINVAL;
}
kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL); kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL);
if (!kpad) if (!kpad)
return -ENOMEM; return -ENOMEM;
@ -516,11 +746,38 @@ static int adp5588_probe(struct i2c_client *client,
kpad->client = client; kpad->client = client;
kpad->input = input; kpad->input = input;
error = adp5588_fw_parse(kpad);
if (error)
return error;
vcc = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(vcc))
return PTR_ERR(vcc);
error = regulator_enable(vcc);
if (error)
return error;
error = devm_add_action_or_reset(&client->dev,
adp5588_disable_regulator, vcc);
if (error)
return error;
gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
fsleep(30);
gpiod_set_value_cansleep(gpio, 0);
fsleep(60);
}
ret = adp5588_read(client, DEV_ID); ret = adp5588_read(client, DEV_ID);
if (ret < 0) if (ret < 0)
return ret; return ret;
revid = (u8) ret & ADP5588_DEVICE_ID_MASK; revid = ret & ADP5588_DEVICE_ID_MASK;
if (WA_DELAYED_READOUT_REVID(revid)) if (WA_DELAYED_READOUT_REVID(revid))
kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME); kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME);
@ -534,32 +791,6 @@ static int adp5588_probe(struct i2c_client *client,
input->id.product = 0x0001; input->id.product = 0x0001;
input->id.version = revid; input->id.version = revid;
input->keycodesize = sizeof(kpad->keycode[0]);
input->keycodemax = pdata->keymapsize;
input->keycode = kpad->keycode;
memcpy(kpad->keycode, pdata->keymap,
pdata->keymapsize * input->keycodesize);
kpad->gpimap = pdata->gpimap;
kpad->gpimapsize = pdata->gpimapsize;
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
if (kpad->keycode[i] <= KEY_MAX)
__set_bit(kpad->keycode[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
if (kpad->gpimapsize)
__set_bit(EV_SW, input->evbit);
for (i = 0; i < kpad->gpimapsize; i++)
__set_bit(kpad->gpimap[i].sw_evt, input->swbit);
error = input_register_device(input); error = input_register_device(input);
if (error) { if (error) {
dev_err(&client->dev, "unable to register input device: %d\n", dev_err(&client->dev, "unable to register input device: %d\n",
@ -567,6 +798,14 @@ static int adp5588_probe(struct i2c_client *client,
return error; return error;
} }
error = adp5588_setup(kpad);
if (error)
return error;
error = adp5588_gpio_add(kpad);
if (error)
return error;
error = devm_request_threaded_irq(&client->dev, client->irq, error = devm_request_threaded_irq(&client->dev, client->irq,
adp5588_hard_irq, adp5588_thread_irq, adp5588_hard_irq, adp5588_thread_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@ -577,17 +816,6 @@ static int adp5588_probe(struct i2c_client *client,
return error; return error;
} }
error = adp5588_setup(client);
if (error)
return error;
if (kpad->gpimapsize)
adp5588_report_switch_state(kpad);
error = adp5588_gpio_add(kpad);
if (error)
return error;
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
return 0; return 0;
} }
@ -599,7 +827,7 @@ static void adp5588_remove(struct i2c_client *client)
/* all resources will be freed by devm */ /* all resources will be freed by devm */
} }
static int __maybe_unused adp5588_suspend(struct device *dev) static int adp5588_suspend(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
@ -608,7 +836,7 @@ static int __maybe_unused adp5588_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused adp5588_resume(struct device *dev) static int adp5588_resume(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
@ -617,7 +845,7 @@ static int __maybe_unused adp5588_resume(struct device *dev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume); static DEFINE_SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume);
static const struct i2c_device_id adp5588_id[] = { static const struct i2c_device_id adp5588_id[] = {
{ "adp5588-keys", 0 }, { "adp5588-keys", 0 },
@ -626,10 +854,18 @@ static const struct i2c_device_id adp5588_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, adp5588_id); MODULE_DEVICE_TABLE(i2c, adp5588_id);
static const struct of_device_id adp5588_of_match[] = {
{ .compatible = "adi,adp5588" },
{ .compatible = "adi,adp5587" },
{}
};
MODULE_DEVICE_TABLE(of, adp5588_of_match);
static struct i2c_driver adp5588_driver = { static struct i2c_driver adp5588_driver = {
.driver = { .driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.pm = &adp5588_dev_pm_ops, .of_match_table = adp5588_of_match,
.pm = pm_sleep_ptr(&adp5588_dev_pm_ops),
}, },
.probe = adp5588_probe, .probe = adp5588_probe,
.remove = adp5588_remove, .remove = adp5588_remove,

View File

@ -10,9 +10,6 @@
* Amiga keyboard driver for Linux/m68k * Amiga keyboard driver for Linux/m68k
*/ */
/*
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/input.h> #include <linux/input.h>

View File

@ -202,7 +202,7 @@ struct command_protocol_tp_info {
}; };
/** /**
* struct touchpad_info - touchpad info response. * struct touchpad_info_protocol - touchpad info response.
* message.type = 0x1020, message.length = 0x006e * message.type = 0x1020, message.length = 0x006e
* *
* @unknown1: unknown * @unknown1: unknown
@ -311,7 +311,7 @@ struct message {
struct command_protocol_mt_init init_mt_command; struct command_protocol_mt_init init_mt_command;
struct command_protocol_capsl capsl_command; struct command_protocol_capsl capsl_command;
struct command_protocol_bl bl_command; struct command_protocol_bl bl_command;
u8 data[0]; DECLARE_FLEX_ARRAY(u8, data);
}; };
}; };

View File

@ -21,9 +21,6 @@
* This driver only deals with handing key events off to the input layer. * This driver only deals with handing key events off to the input layer.
*/ */
/*
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/input.h> #include <linux/input.h>

View File

@ -323,11 +323,13 @@ static umode_t atkbd_attr_is_visible(struct kobject *kobj,
return attr->mode; return attr->mode;
} }
static struct attribute_group atkbd_attribute_group = { static const struct attribute_group atkbd_attribute_group = {
.attrs = atkbd_attributes, .attrs = atkbd_attributes,
.is_visible = atkbd_attr_is_visible, .is_visible = atkbd_attr_is_visible,
}; };
__ATTRIBUTE_GROUPS(atkbd_attribute);
static const unsigned int xl_table[] = { static const unsigned int xl_table[] = {
ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK, ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL, ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
@ -922,8 +924,6 @@ static void atkbd_disconnect(struct serio *serio)
{ {
struct atkbd *atkbd = serio_get_drvdata(serio); struct atkbd *atkbd = serio_get_drvdata(serio);
sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
atkbd_disable(atkbd); atkbd_disable(atkbd);
input_unregister_device(atkbd->dev); input_unregister_device(atkbd->dev);
@ -1271,21 +1271,16 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
atkbd_set_keycode_table(atkbd); atkbd_set_keycode_table(atkbd);
atkbd_set_device_attrs(atkbd); atkbd_set_device_attrs(atkbd);
err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
if (err)
goto fail3;
atkbd_enable(atkbd); atkbd_enable(atkbd);
if (serio->write) if (serio->write)
atkbd_activate(atkbd); atkbd_activate(atkbd);
err = input_register_device(atkbd->dev); err = input_register_device(atkbd->dev);
if (err) if (err)
goto fail4; goto fail3;
return 0; return 0;
fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
fail3: serio_close(serio); fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL); fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(dev); fail1: input_free_device(dev);
@ -1378,7 +1373,8 @@ MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
static struct serio_driver atkbd_drv = { static struct serio_driver atkbd_drv = {
.driver = { .driver = {
.name = "atkbd", .name = "atkbd",
.dev_groups = atkbd_attribute_groups,
}, },
.description = DRIVER_DESC, .description = DRIVER_DESC,
.id_table = atkbd_serio_ids, .id_table = atkbd_serio_ids,

View File

@ -6,9 +6,11 @@
*/ */
#include <linux/input.h> #include <linux/input.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_gpio.h> #include <linux/gpio/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
@ -86,7 +88,6 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
{ {
struct clps711x_keypad_data *priv; struct clps711x_keypad_data *priv;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct input_dev *input; struct input_dev *input;
u32 poll_interval; u32 poll_interval;
int i, err; int i, err;
@ -95,11 +96,11 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); priv->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
if (IS_ERR(priv->syscon)) if (IS_ERR(priv->syscon))
return PTR_ERR(priv->syscon); return PTR_ERR(priv->syscon);
priv->row_count = of_gpio_named_count(np, "row-gpios"); priv->row_count = gpiod_count(dev, "row");
if (priv->row_count < 1) if (priv->row_count < 1)
return -EINVAL; return -EINVAL;
@ -119,7 +120,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
return PTR_ERR(data->desc); return PTR_ERR(data->desc);
} }
err = of_property_read_u32(np, "poll-interval", &poll_interval); err = device_property_read_u32(dev, "poll-interval", &poll_interval);
if (err) if (err)
return err; return err;
@ -143,7 +144,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
return err; return err;
input_set_capability(input, EV_MSC, MSC_SCAN); input_set_capability(input, EV_MSC, MSC_SCAN);
if (of_property_read_bool(np, "autorepeat")) if (device_property_read_bool(dev, "autorepeat"))
__set_bit(EV_REP, input->evbit); __set_bit(EV_REP, input->evbit);
/* Set all columns to low */ /* Set all columns to low */

View File

@ -23,6 +23,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/soc/cirrus/ep93xx.h> #include <linux/soc/cirrus/ep93xx.h>

View File

@ -7,6 +7,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>

View File

@ -46,9 +46,6 @@
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1 * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
@ -359,18 +356,18 @@ static void lkkbd_detection_done(struct lkkbd *lk)
*/ */
switch (lk->id[4]) { switch (lk->id[4]) {
case 1: case 1:
strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name)); strscpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
if (lk201_compose_is_alt) if (lk201_compose_is_alt)
lk->keycode[0xb1] = KEY_LEFTALT; lk->keycode[0xb1] = KEY_LEFTALT;
break; break;
case 2: case 2:
strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name)); strscpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
break; break;
default: default:
strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name)); strscpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
printk(KERN_ERR printk(KERN_ERR
"lkkbd: keyboard on %s is unknown, please report to " "lkkbd: keyboard on %s is unknown, please report to "
"Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys); "Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
@ -626,7 +623,7 @@ static int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
lk->ctrlclick_volume = ctrlclick_volume; lk->ctrlclick_volume = ctrlclick_volume;
memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode)); memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name)); strscpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys); snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
input_dev->name = lk->name; input_dev->name = lk->name;

View File

@ -4,13 +4,13 @@
* Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de> * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
*/ */
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/input.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
#include <linux/input/lm8333.h> #include <linux/input/lm8333.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#define LM8333_FIFO_READ 0x20 #define LM8333_FIFO_READ 0x20
#define LM8333_DEBOUNCE 0x22 #define LM8333_DEBOUNCE 0x22

View File

@ -9,6 +9,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/irq.h> #include <linux/irq.h>
@ -416,9 +417,9 @@ matrix_keypad_parse_dt(struct device *dev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios"); pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios"); pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
if (nrow <= 0 || ncol <= 0) { if (nrow < 0 || ncol < 0) {
dev_err(dev, "number of keypad rows/columns not specified\n"); dev_err(dev, "number of keypad rows/columns not specified\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }

View File

@ -5,6 +5,7 @@
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
@ -18,6 +19,7 @@
#define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0) #define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0)
#define MTK_KPD_DEBOUNCE_MAX_MS 256 #define MTK_KPD_DEBOUNCE_MAX_MS 256
#define MTK_KPD_SEL 0x0020 #define MTK_KPD_SEL 0x0020
#define MTK_KPD_SEL_DOUBLE_KP_MODE BIT(0)
#define MTK_KPD_SEL_COL GENMASK(15, 10) #define MTK_KPD_SEL_COL GENMASK(15, 10)
#define MTK_KPD_SEL_ROW GENMASK(9, 4) #define MTK_KPD_SEL_ROW GENMASK(9, 4)
#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10) #define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10)
@ -31,6 +33,8 @@ struct mt6779_keypad {
struct clk *clk; struct clk *clk;
u32 n_rows; u32 n_rows;
u32 n_cols; u32 n_cols;
void (*calc_row_col)(unsigned int key,
unsigned int *row, unsigned int *col);
DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS); DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS);
}; };
@ -67,8 +71,7 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id)
continue; continue;
key = bit_nr / 32 * 16 + bit_nr % 32; key = bit_nr / 32 * 16 + bit_nr % 32;
row = key / 9; keypad->calc_row_col(key, &row, &col);
col = key % 9;
scancode = MATRIX_SCAN_CODE(row, col, row_shift); scancode = MATRIX_SCAN_CODE(row, col, row_shift);
/* 1: not pressed, 0: pressed */ /* 1: not pressed, 0: pressed */
@ -94,12 +97,29 @@ static void mt6779_keypad_clk_disable(void *data)
clk_disable_unprepare(data); clk_disable_unprepare(data);
} }
static void mt6779_keypad_calc_row_col_single(unsigned int key,
unsigned int *row,
unsigned int *col)
{
*row = key / 9;
*col = key % 9;
}
static void mt6779_keypad_calc_row_col_double(unsigned int key,
unsigned int *row,
unsigned int *col)
{
*row = key / 13;
*col = (key % 13) / 2;
}
static int mt6779_keypad_pdrv_probe(struct platform_device *pdev) static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
{ {
struct mt6779_keypad *keypad; struct mt6779_keypad *keypad;
void __iomem *base; void __iomem *base;
int irq; int irq;
u32 debounce; u32 debounce;
u32 keys_per_group;
bool wakeup; bool wakeup;
int error; int error;
@ -148,6 +168,23 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
if (device_property_read_u32(&pdev->dev, "mediatek,keys-per-group",
&keys_per_group))
keys_per_group = 1;
switch (keys_per_group) {
case 1:
keypad->calc_row_col = mt6779_keypad_calc_row_col_single;
break;
case 2:
keypad->calc_row_col = mt6779_keypad_calc_row_col_double;
break;
default:
dev_err(&pdev->dev,
"Invalid keys-per-group: %d\n", keys_per_group);
return -EINVAL;
}
wakeup = device_property_read_bool(&pdev->dev, "wakeup-source"); wakeup = device_property_read_bool(&pdev->dev, "wakeup-source");
dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n", dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n",
@ -166,6 +203,11 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE, regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE,
(debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK); (debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK);
if (keys_per_group == 2)
regmap_update_bits(keypad->regmap, MTK_KPD_SEL,
MTK_KPD_SEL_DOUBLE_KP_MODE,
MTK_KPD_SEL_DOUBLE_KP_MODE);
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW, regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW,
MTK_KPD_SEL_ROWMASK(keypad->n_rows)); MTK_KPD_SEL_ROWMASK(keypad->n_rows));
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL, regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL,

View File

@ -9,6 +9,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mfd/mt6323/registers.h> #include <linux/mfd/mt6323/registers.h>
#include <linux/mfd/mt6331/registers.h>
#include <linux/mfd/mt6358/registers.h> #include <linux/mfd/mt6358/registers.h>
#include <linux/mfd/mt6397/core.h> #include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6397/registers.h> #include <linux/mfd/mt6397/registers.h>
@ -22,6 +23,10 @@
#define MTK_PMIC_PWRKEY_RST BIT(6) #define MTK_PMIC_PWRKEY_RST BIT(6)
#define MTK_PMIC_HOMEKEY_RST BIT(5) #define MTK_PMIC_HOMEKEY_RST BIT(5)
#define MTK_PMIC_MT6331_RST_DU_MASK GENMASK(13, 12)
#define MTK_PMIC_MT6331_PWRKEY_RST BIT(9)
#define MTK_PMIC_MT6331_HOMEKEY_RST BIT(8)
#define MTK_PMIC_PWRKEY_INDEX 0 #define MTK_PMIC_PWRKEY_INDEX 0
#define MTK_PMIC_HOMEKEY_INDEX 1 #define MTK_PMIC_HOMEKEY_INDEX 1
#define MTK_PMIC_MAX_KEY_COUNT 2 #define MTK_PMIC_MAX_KEY_COUNT 2
@ -72,6 +77,19 @@ static const struct mtk_pmic_regs mt6323_regs = {
.rst_lprst_mask = MTK_PMIC_RST_DU_MASK, .rst_lprst_mask = MTK_PMIC_RST_DU_MASK,
}; };
static const struct mtk_pmic_regs mt6331_regs = {
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6331_TOPSTATUS, 0x2,
MT6331_INT_MISC_CON, 0x4,
MTK_PMIC_MT6331_PWRKEY_RST),
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6331_TOPSTATUS, 0x4,
MT6331_INT_MISC_CON, 0x2,
MTK_PMIC_MT6331_HOMEKEY_RST),
.pmic_rst_reg = MT6331_TOP_RST_MISC,
.rst_lprst_mask = MTK_PMIC_MT6331_RST_DU_MASK,
};
static const struct mtk_pmic_regs mt6358_regs = { static const struct mtk_pmic_regs mt6358_regs = {
.keys_regs[MTK_PMIC_PWRKEY_INDEX] = .keys_regs[MTK_PMIC_PWRKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
@ -255,6 +273,9 @@ static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
}, { }, {
.compatible = "mediatek,mt6323-keys", .compatible = "mediatek,mt6323-keys",
.data = &mt6323_regs, .data = &mt6323_regs,
}, {
.compatible = "mediatek,mt6331-keys",
.data = &mt6331_regs,
}, { }, {
.compatible = "mediatek,mt6358-keys", .compatible = "mediatek,mt6358-keys",
.data = &mt6358_regs, .data = &mt6358_regs,

View File

@ -7,9 +7,6 @@
* Newton keyboard driver for Linux * Newton keyboard driver for Linux
*/ */
/*
*/
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/input.h> #include <linux/input.h>

View File

@ -0,0 +1,468 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#define DRV_NAME "pinephone-keyboard"
#define PPKB_CRC8_POLYNOMIAL 0x07
#define PPKB_DEVICE_ID_HI 0x00
#define PPKB_DEVICE_ID_HI_VALUE 'K'
#define PPKB_DEVICE_ID_LO 0x01
#define PPKB_DEVICE_ID_LO_VALUE 'B'
#define PPKB_FW_REVISION 0x02
#define PPKB_FW_FEATURES 0x03
#define PPKB_MATRIX_SIZE 0x06
#define PPKB_SCAN_CRC 0x07
#define PPKB_SCAN_DATA 0x08
#define PPKB_SYS_CONFIG 0x20
#define PPKB_SYS_CONFIG_DISABLE_SCAN BIT(0)
#define PPKB_SYS_SMBUS_COMMAND 0x21
#define PPKB_SYS_SMBUS_DATA 0x22
#define PPKB_SYS_COMMAND 0x23
#define PPKB_SYS_COMMAND_SMBUS_READ 0x91
#define PPKB_SYS_COMMAND_SMBUS_WRITE 0xa1
#define PPKB_ROWS 6
#define PPKB_COLS 12
/* Size of the scan buffer, including the CRC byte at the beginning. */
#define PPKB_BUF_LEN (1 + PPKB_COLS)
static const uint32_t ppkb_keymap[] = {
KEY(0, 0, KEY_ESC),
KEY(0, 1, KEY_1),
KEY(0, 2, KEY_2),
KEY(0, 3, KEY_3),
KEY(0, 4, KEY_4),
KEY(0, 5, KEY_5),
KEY(0, 6, KEY_6),
KEY(0, 7, KEY_7),
KEY(0, 8, KEY_8),
KEY(0, 9, KEY_9),
KEY(0, 10, KEY_0),
KEY(0, 11, KEY_BACKSPACE),
KEY(1, 0, KEY_TAB),
KEY(1, 1, KEY_Q),
KEY(1, 2, KEY_W),
KEY(1, 3, KEY_E),
KEY(1, 4, KEY_R),
KEY(1, 5, KEY_T),
KEY(1, 6, KEY_Y),
KEY(1, 7, KEY_U),
KEY(1, 8, KEY_I),
KEY(1, 9, KEY_O),
KEY(1, 10, KEY_P),
KEY(1, 11, KEY_ENTER),
KEY(2, 0, KEY_LEFTMETA),
KEY(2, 1, KEY_A),
KEY(2, 2, KEY_S),
KEY(2, 3, KEY_D),
KEY(2, 4, KEY_F),
KEY(2, 5, KEY_G),
KEY(2, 6, KEY_H),
KEY(2, 7, KEY_J),
KEY(2, 8, KEY_K),
KEY(2, 9, KEY_L),
KEY(2, 10, KEY_SEMICOLON),
KEY(3, 0, KEY_LEFTSHIFT),
KEY(3, 1, KEY_Z),
KEY(3, 2, KEY_X),
KEY(3, 3, KEY_C),
KEY(3, 4, KEY_V),
KEY(3, 5, KEY_B),
KEY(3, 6, KEY_N),
KEY(3, 7, KEY_M),
KEY(3, 8, KEY_COMMA),
KEY(3, 9, KEY_DOT),
KEY(3, 10, KEY_SLASH),
KEY(4, 1, KEY_LEFTCTRL),
KEY(4, 4, KEY_SPACE),
KEY(4, 6, KEY_APOSTROPHE),
KEY(4, 8, KEY_RIGHTBRACE),
KEY(4, 9, KEY_LEFTBRACE),
KEY(5, 2, KEY_FN),
KEY(5, 3, KEY_LEFTALT),
KEY(5, 5, KEY_RIGHTALT),
/* FN layer */
KEY(PPKB_ROWS + 0, 0, KEY_FN_ESC),
KEY(PPKB_ROWS + 0, 1, KEY_F1),
KEY(PPKB_ROWS + 0, 2, KEY_F2),
KEY(PPKB_ROWS + 0, 3, KEY_F3),
KEY(PPKB_ROWS + 0, 4, KEY_F4),
KEY(PPKB_ROWS + 0, 5, KEY_F5),
KEY(PPKB_ROWS + 0, 6, KEY_F6),
KEY(PPKB_ROWS + 0, 7, KEY_F7),
KEY(PPKB_ROWS + 0, 8, KEY_F8),
KEY(PPKB_ROWS + 0, 9, KEY_F9),
KEY(PPKB_ROWS + 0, 10, KEY_F10),
KEY(PPKB_ROWS + 0, 11, KEY_DELETE),
KEY(PPKB_ROWS + 1, 10, KEY_PAGEUP),
KEY(PPKB_ROWS + 2, 0, KEY_SYSRQ),
KEY(PPKB_ROWS + 2, 9, KEY_PAGEDOWN),
KEY(PPKB_ROWS + 2, 10, KEY_INSERT),
KEY(PPKB_ROWS + 3, 0, KEY_LEFTSHIFT),
KEY(PPKB_ROWS + 3, 8, KEY_HOME),
KEY(PPKB_ROWS + 3, 9, KEY_UP),
KEY(PPKB_ROWS + 3, 10, KEY_END),
KEY(PPKB_ROWS + 4, 1, KEY_LEFTCTRL),
KEY(PPKB_ROWS + 4, 6, KEY_LEFT),
KEY(PPKB_ROWS + 4, 8, KEY_RIGHT),
KEY(PPKB_ROWS + 4, 9, KEY_DOWN),
KEY(PPKB_ROWS + 5, 3, KEY_LEFTALT),
KEY(PPKB_ROWS + 5, 5, KEY_RIGHTALT),
};
static const struct matrix_keymap_data ppkb_keymap_data = {
.keymap = ppkb_keymap,
.keymap_size = ARRAY_SIZE(ppkb_keymap),
};
struct pinephone_keyboard {
struct i2c_adapter adapter;
struct input_dev *input;
u8 buf[2][PPKB_BUF_LEN];
u8 crc_table[CRC8_TABLE_SIZE];
u8 fn_state[PPKB_COLS];
bool buf_swap;
bool fn_pressed;
};
static int ppkb_adap_smbus_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data *data)
{
struct i2c_client *client = adap->algo_data;
u8 buf[3];
int ret;
buf[0] = command;
buf[1] = data->byte;
buf[2] = read_write == I2C_SMBUS_READ ? PPKB_SYS_COMMAND_SMBUS_READ
: PPKB_SYS_COMMAND_SMBUS_WRITE;
ret = i2c_smbus_write_i2c_block_data(client, PPKB_SYS_SMBUS_COMMAND,
sizeof(buf), buf);
if (ret)
return ret;
/* Read back the command status until it passes or fails. */
do {
usleep_range(300, 500);
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_COMMAND);
} while (ret == buf[2]);
if (ret < 0)
return ret;
/* Commands return 0x00 on success and 0xff on failure. */
if (ret)
return -EIO;
if (read_write == I2C_SMBUS_READ) {
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_SMBUS_DATA);
if (ret < 0)
return ret;
data->byte = ret;
}
return 0;
}
static u32 ppkg_adap_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_BYTE_DATA;
}
static const struct i2c_algorithm ppkb_adap_algo = {
.smbus_xfer = ppkb_adap_smbus_xfer,
.functionality = ppkg_adap_functionality,
};
static void ppkb_update(struct i2c_client *client)
{
struct pinephone_keyboard *ppkb = i2c_get_clientdata(client);
unsigned short *keymap = ppkb->input->keycode;
int row_shift = get_count_order(PPKB_COLS);
u8 *old_buf = ppkb->buf[!ppkb->buf_swap];
u8 *new_buf = ppkb->buf[ppkb->buf_swap];
int col, crc, ret, row;
struct device *dev = &client->dev;
ret = i2c_smbus_read_i2c_block_data(client, PPKB_SCAN_CRC,
PPKB_BUF_LEN, new_buf);
if (ret != PPKB_BUF_LEN) {
dev_err(dev, "Failed to read scan data: %d\n", ret);
return;
}
crc = crc8(ppkb->crc_table, &new_buf[1], PPKB_COLS, CRC8_INIT_VALUE);
if (crc != new_buf[0]) {
dev_err(dev, "Bad scan data (%02x != %02x)\n", crc, new_buf[0]);
return;
}
ppkb->buf_swap = !ppkb->buf_swap;
for (col = 0; col < PPKB_COLS; ++col) {
u8 old = old_buf[1 + col];
u8 new = new_buf[1 + col];
u8 changed = old ^ new;
if (!changed)
continue;
for (row = 0; row < PPKB_ROWS; ++row) {
u8 mask = BIT(row);
u8 value = new & mask;
unsigned short code;
bool fn_state;
if (!(changed & mask))
continue;
/*
* Save off the FN key state when the key was pressed,
* and use that to determine the code during a release.
*/
fn_state = value ? ppkb->fn_pressed : ppkb->fn_state[col] & mask;
if (fn_state)
ppkb->fn_state[col] ^= mask;
/* The FN layer is a second set of rows. */
code = MATRIX_SCAN_CODE(fn_state ? PPKB_ROWS + row : row,
col, row_shift);
input_event(ppkb->input, EV_MSC, MSC_SCAN, code);
input_report_key(ppkb->input, keymap[code], value);
if (keymap[code] == KEY_FN)
ppkb->fn_pressed = value;
}
}
input_sync(ppkb->input);
}
static irqreturn_t ppkb_irq_thread(int irq, void *data)
{
struct i2c_client *client = data;
ppkb_update(client);
return IRQ_HANDLED;
}
static int ppkb_set_scan(struct i2c_client *client, bool enable)
{
struct device *dev = &client->dev;
int ret, val;
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_CONFIG);
if (ret < 0) {
dev_err(dev, "Failed to read config: %d\n", ret);
return ret;
}
if (enable)
val = ret & ~PPKB_SYS_CONFIG_DISABLE_SCAN;
else
val = ret | PPKB_SYS_CONFIG_DISABLE_SCAN;
ret = i2c_smbus_write_byte_data(client, PPKB_SYS_CONFIG, val);
if (ret) {
dev_err(dev, "Failed to write config: %d\n", ret);
return ret;
}
return 0;
}
static int ppkb_open(struct input_dev *input)
{
struct i2c_client *client = input_get_drvdata(input);
int error;
error = ppkb_set_scan(client, true);
if (error)
return error;
return 0;
}
static void ppkb_close(struct input_dev *input)
{
struct i2c_client *client = input_get_drvdata(input);
ppkb_set_scan(client, false);
}
static void ppkb_regulator_disable(void *regulator)
{
regulator_disable(regulator);
}
static int ppkb_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
unsigned int phys_rows, phys_cols;
struct pinephone_keyboard *ppkb;
struct regulator *vbat_supply;
u8 info[PPKB_MATRIX_SIZE + 1];
struct device_node *i2c_bus;
int ret;
int error;
vbat_supply = devm_regulator_get(dev, "vbat");
error = PTR_ERR_OR_ZERO(vbat_supply);
if (error) {
dev_err(dev, "Failed to get VBAT supply: %d\n", error);
return error;
}
error = regulator_enable(vbat_supply);
if (error) {
dev_err(dev, "Failed to enable VBAT: %d\n", error);
return error;
}
error = devm_add_action_or_reset(dev, ppkb_regulator_disable,
vbat_supply);
if (error)
return error;
ret = i2c_smbus_read_i2c_block_data(client, 0, sizeof(info), info);
if (ret != sizeof(info)) {
error = ret < 0 ? ret : -EIO;
dev_err(dev, "Failed to read device ID: %d\n", error);
return error;
}
if (info[PPKB_DEVICE_ID_HI] != PPKB_DEVICE_ID_HI_VALUE ||
info[PPKB_DEVICE_ID_LO] != PPKB_DEVICE_ID_LO_VALUE) {
dev_warn(dev, "Unexpected device ID: %#02x %#02x\n",
info[PPKB_DEVICE_ID_HI], info[PPKB_DEVICE_ID_LO]);
return -ENODEV;
}
dev_info(dev, "Found firmware version %d.%d features %#x\n",
info[PPKB_FW_REVISION] >> 4,
info[PPKB_FW_REVISION] & 0xf,
info[PPKB_FW_FEATURES]);
phys_rows = info[PPKB_MATRIX_SIZE] & 0xf;
phys_cols = info[PPKB_MATRIX_SIZE] >> 4;
if (phys_rows != PPKB_ROWS || phys_cols != PPKB_COLS) {
dev_err(dev, "Unexpected keyboard size %ux%u\n",
phys_rows, phys_cols);
return -EINVAL;
}
/* Disable scan by default to save power. */
error = ppkb_set_scan(client, false);
if (error)
return error;
ppkb = devm_kzalloc(dev, sizeof(*ppkb), GFP_KERNEL);
if (!ppkb)
return -ENOMEM;
i2c_set_clientdata(client, ppkb);
i2c_bus = of_get_child_by_name(dev->of_node, "i2c");
if (i2c_bus) {
ppkb->adapter.owner = THIS_MODULE;
ppkb->adapter.algo = &ppkb_adap_algo;
ppkb->adapter.algo_data = client;
ppkb->adapter.dev.parent = dev;
ppkb->adapter.dev.of_node = i2c_bus;
strscpy(ppkb->adapter.name, DRV_NAME, sizeof(ppkb->adapter.name));
error = devm_i2c_add_adapter(dev, &ppkb->adapter);
if (error) {
dev_err(dev, "Failed to add I2C adapter: %d\n", error);
return error;
}
}
crc8_populate_msb(ppkb->crc_table, PPKB_CRC8_POLYNOMIAL);
ppkb->input = devm_input_allocate_device(dev);
if (!ppkb->input)
return -ENOMEM;
input_set_drvdata(ppkb->input, client);
ppkb->input->name = "PinePhone Keyboard";
ppkb->input->phys = DRV_NAME "/input0";
ppkb->input->id.bustype = BUS_I2C;
ppkb->input->open = ppkb_open;
ppkb->input->close = ppkb_close;
input_set_capability(ppkb->input, EV_MSC, MSC_SCAN);
__set_bit(EV_REP, ppkb->input->evbit);
error = matrix_keypad_build_keymap(&ppkb_keymap_data, NULL,
2 * PPKB_ROWS, PPKB_COLS, NULL,
ppkb->input);
if (error) {
dev_err(dev, "Failed to build keymap: %d\n", error);
return error;
}
error = input_register_device(ppkb->input);
if (error) {
dev_err(dev, "Failed to register input: %d\n", error);
return error;
}
error = devm_request_threaded_irq(dev, client->irq,
NULL, ppkb_irq_thread,
IRQF_ONESHOT, client->name, client);
if (error) {
dev_err(dev, "Failed to request IRQ: %d\n", error);
return error;
}
return 0;
}
static const struct of_device_id ppkb_of_match[] = {
{ .compatible = "pine64,pinephone-keyboard" },
{ }
};
MODULE_DEVICE_TABLE(of, ppkb_of_match);
static struct i2c_driver ppkb_driver = {
.probe_new = ppkb_probe,
.driver = {
.name = DRV_NAME,
.of_match_table = ppkb_of_match,
},
};
module_i2c_driver(ppkb_driver);
MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
MODULE_DESCRIPTION("Pine64 PinePhone keyboard driver");
MODULE_LICENSE("GPL");

View File

@ -8,12 +8,14 @@
* Based on sh_keysc.c, copyright 2008 Magnus Damm * Based on sh_keysc.c, copyright 2008 Magnus Damm
*/ */
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/input.h>
#include <linux/input/matrix_keypad.h> #include <linux/input/matrix_keypad.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#define ST_KEYSCAN_MAXKEYS 16 #define ST_KEYSCAN_MAXKEYS 16

View File

@ -10,9 +10,6 @@
* by Justin Cormack * by Justin Cormack
*/ */
/*
*/
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/input.h> #include <linux/input.h>

View File

@ -7,9 +7,6 @@
* Sun keyboard driver for Linux * Sun keyboard driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -70,7 +70,7 @@
#define TC3589x_KBD_INT_CLR 0x1 #define TC3589x_KBD_INT_CLR 0x1
/** /**
* struct tc35893_keypad_platform_data - platform specific keypad data * struct tc3589x_keypad_platform_data - platform specific keypad data
* @keymap_data: matrix scan code table for keycodes * @keymap_data: matrix scan code table for keycodes
* @krow: mask for available rows, value is 0xFF * @krow: mask for available rows, value is 0xFF
* @kcol: mask for available columns, value is 0xFF * @kcol: mask for available columns, value is 0xFF

View File

@ -7,9 +7,6 @@
* XT keyboard driver for Linux * XT keyboard driver for Linux
*/ */
/*
*/
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/input.h> #include <linux/input.h>

View File

@ -730,6 +730,24 @@ config INPUT_ADXL34X_SPI
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called adxl34x-spi. module will be called adxl34x-spi.
config INPUT_IBM_PANEL
tristate "IBM Operation Panel driver"
depends on I2C && I2C_SLAVE
help
Say Y here if you have an IBM Operation Panel connected to your system
over I2C. The panel is typically connected only to a system's service
processor (BMC).
If unsure, say N.
The Operation Panel is a controller with some buttons and an LCD
display that allows someone with physical access to the system to
perform various administrative tasks. This driver only supports the part
of the controller that sends commands to the system.
To compile this driver as a module, choose M here: the module will be
called ibm-panel.
config INPUT_IMS_PCU config INPUT_IMS_PCU
tristate "IMS Passenger Control Unit driver" tristate "IMS Passenger Control Unit driver"
depends on USB depends on USB
@ -891,6 +909,15 @@ config INPUT_SC27XX_VIBRA
To compile this driver as a module, choose M here. The module will To compile this driver as a module, choose M here. The module will
be called sc27xx_vibra. be called sc27xx_vibra.
config INPUT_RT5120_PWRKEY
tristate "RT5120 PMIC power key support"
depends on MFD_RT5120 || COMPILE_TEST
help
This enables support for RT5120 PMIC power key driver.
To compile this driver as a module, choose M here. the module will
be called rt5120-pwrkey.
config INPUT_STPMIC1_ONKEY config INPUT_STPMIC1_ONKEY
tristate "STPMIC1 PMIC Onkey support" tristate "STPMIC1 PMIC Onkey support"
depends on MFD_STPMIC1 depends on MFD_STPMIC1

View File

@ -41,6 +41,7 @@ obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o
obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IBM_PANEL) += ibm-panel.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o
@ -69,6 +70,7 @@ obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
obj-$(CONFIG_INPUT_RT5120_PWRKEY) += rt5120-pwrkey.o
obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.o obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.o

View File

@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) IBM Corporation 2020
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spinlock.h>
#define DEVICE_NAME "ibm-panel"
#define PANEL_KEYCODES_COUNT 3
struct ibm_panel {
u8 idx;
u8 command[11];
u32 keycodes[PANEL_KEYCODES_COUNT];
spinlock_t lock; /* protects writes to idx and command */
struct input_dev *input;
};
static u8 ibm_panel_calculate_checksum(struct ibm_panel *panel)
{
u8 chksum;
u16 sum = 0;
unsigned int i;
for (i = 0; i < sizeof(panel->command) - 1; ++i) {
sum += panel->command[i];
if (sum & 0xff00) {
sum &= 0xff;
sum++;
}
}
chksum = sum & 0xff;
chksum = ~chksum;
chksum++;
return chksum;
}
static void ibm_panel_process_command(struct ibm_panel *panel)
{
u8 button;
u8 chksum;
if (panel->command[0] != 0xff && panel->command[1] != 0xf0) {
dev_dbg(&panel->input->dev, "command invalid: %02x %02x\n",
panel->command[0], panel->command[1]);
return;
}
chksum = ibm_panel_calculate_checksum(panel);
if (chksum != panel->command[sizeof(panel->command) - 1]) {
dev_dbg(&panel->input->dev,
"command failed checksum: %u != %u\n", chksum,
panel->command[sizeof(panel->command) - 1]);
return;
}
button = panel->command[2] & 0xf;
if (button < PANEL_KEYCODES_COUNT) {
input_report_key(panel->input, panel->keycodes[button],
!(panel->command[2] & 0x80));
input_sync(panel->input);
} else {
dev_dbg(&panel->input->dev, "unknown button %u\n",
button);
}
}
static int ibm_panel_i2c_slave_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
unsigned long flags;
struct ibm_panel *panel = i2c_get_clientdata(client);
dev_dbg(&panel->input->dev, "event: %u data: %02x\n", event, *val);
spin_lock_irqsave(&panel->lock, flags);
switch (event) {
case I2C_SLAVE_STOP:
if (panel->idx == sizeof(panel->command))
ibm_panel_process_command(panel);
else
dev_dbg(&panel->input->dev,
"command incorrect size %u\n", panel->idx);
fallthrough;
case I2C_SLAVE_WRITE_REQUESTED:
panel->idx = 0;
break;
case I2C_SLAVE_WRITE_RECEIVED:
if (panel->idx < sizeof(panel->command))
panel->command[panel->idx++] = *val;
else
/*
* The command is too long and therefore invalid, so set the index
* to it's largest possible value. When a STOP is finally received,
* the command will be rejected upon processing.
*/
panel->idx = U8_MAX;
break;
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
*val = 0xff;
break;
default:
break;
}
spin_unlock_irqrestore(&panel->lock, flags);
return 0;
}
static int ibm_panel_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ibm_panel *panel;
int i;
int error;
panel = devm_kzalloc(&client->dev, sizeof(*panel), GFP_KERNEL);
if (!panel)
return -ENOMEM;
spin_lock_init(&panel->lock);
panel->input = devm_input_allocate_device(&client->dev);
if (!panel->input)
return -ENOMEM;
panel->input->name = client->name;
panel->input->id.bustype = BUS_I2C;
error = device_property_read_u32_array(&client->dev,
"linux,keycodes",
panel->keycodes,
PANEL_KEYCODES_COUNT);
if (error) {
/*
* Use gamepad buttons as defaults for compatibility with
* existing applications.
*/
panel->keycodes[0] = BTN_NORTH;
panel->keycodes[1] = BTN_SOUTH;
panel->keycodes[2] = BTN_SELECT;
}
for (i = 0; i < PANEL_KEYCODES_COUNT; ++i)
input_set_capability(panel->input, EV_KEY, panel->keycodes[i]);
error = input_register_device(panel->input);
if (error) {
dev_err(&client->dev,
"Failed to register input device: %d\n", error);
return error;
}
i2c_set_clientdata(client, panel);
error = i2c_slave_register(client, ibm_panel_i2c_slave_cb);
if (error) {
dev_err(&client->dev,
"Failed to register as i2c slave: %d\n", error);
return error;
}
return 0;
}
static void ibm_panel_remove(struct i2c_client *client)
{
i2c_slave_unregister(client);
}
static const struct of_device_id ibm_panel_match[] = {
{ .compatible = "ibm,op-panel" },
{ }
};
MODULE_DEVICE_TABLE(of, ibm_panel_match);
static struct i2c_driver ibm_panel_driver = {
.driver = {
.name = DEVICE_NAME,
.of_match_table = ibm_panel_match,
},
.probe = ibm_panel_probe,
.remove = ibm_panel_remove,
};
module_i2c_driver(ibm_panel_driver);
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
MODULE_DESCRIPTION("IBM Operation Panel Driver");
MODULE_LICENSE("GPL");

View File

@ -744,7 +744,7 @@ static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0); error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
if (error) { if (error) {
dev_err(pcu->dev, dev_err(pcu->dev,
"Failure when sending JUMP TO BOOLTLOADER command, error: %d\n", "Failure when sending JUMP TO BOOTLOADER command, error: %d\n",
error); error);
return error; return error;
} }

View File

@ -1077,7 +1077,7 @@ static int iqs7222_hard_reset(struct iqs7222_private *iqs7222)
static int iqs7222_force_comms(struct iqs7222_private *iqs7222) static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
{ {
u8 msg_buf[] = { 0xFF, 0x00, }; u8 msg_buf[] = { 0xFF, };
int ret; int ret;
/* /*
@ -1771,11 +1771,9 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, int chan_index)
if (!chan_node) if (!chan_node)
return 0; return 0;
if (dev_desc->allow_offset) { if (dev_desc->allow_offset &&
sys_setup[dev_desc->allow_offset] |= BIT(chan_index); fwnode_property_present(chan_node, "azoteq,ulp-allow"))
if (fwnode_property_present(chan_node, "azoteq,ulp-allow")) sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
}
chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN; chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
@ -2206,6 +2204,9 @@ static int iqs7222_parse_all(struct iqs7222_private *iqs7222)
u16 *sys_setup = iqs7222->sys_setup; u16 *sys_setup = iqs7222->sys_setup;
int error, i; int error, i;
if (dev_desc->allow_offset)
sys_setup[dev_desc->allow_offset] = U16_MAX;
if (dev_desc->event_offset) if (dev_desc->event_offset)
sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI; sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
@ -2326,6 +2327,9 @@ static int iqs7222_report(struct iqs7222_private *iqs7222)
int k = 2 + j * (num_chan > 16 ? 2 : 1); int k = 2 + j * (num_chan > 16 ? 2 : 1);
u16 state = le16_to_cpu(status[k + i / 16]); u16 state = le16_to_cpu(status[k + i / 16]);
if (!iqs7222->kp_type[i][j])
continue;
input_event(iqs7222->keypad, input_event(iqs7222->keypad,
iqs7222->kp_type[i][j], iqs7222->kp_type[i][j],
iqs7222->kp_code[i][j], iqs7222->kp_code[i][j],

View File

@ -485,7 +485,7 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic
} }
if (udev->manufacturer) if (udev->manufacturer)
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name)); strscpy(remote->name, udev->manufacturer, sizeof(remote->name));
if (udev->product) { if (udev->product) {
if (udev->manufacturer) if (udev->manufacturer)

View File

@ -0,0 +1,120 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 Richtek Technology Corp.
* Author: ChiYuan Huang <cy_huang@richtek.com>
*/
#include <linux/bits.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define RT5120_REG_INTSTAT 0x1E
#define RT5120_PWRKEYSTAT_MASK BIT(7)
struct rt5120_priv {
struct regmap *regmap;
struct input_dev *input;
};
static irqreturn_t rt5120_pwrkey_handler(int irq, void *devid)
{
struct rt5120_priv *priv = devid;
unsigned int stat;
int error;
error = regmap_read(priv->regmap, RT5120_REG_INTSTAT, &stat);
if (error)
return IRQ_NONE;
input_report_key(priv->input, KEY_POWER,
!(stat & RT5120_PWRKEYSTAT_MASK));
input_sync(priv->input);
return IRQ_HANDLED;
}
static int rt5120_pwrkey_probe(struct platform_device *pdev)
{
struct rt5120_priv *priv;
struct device *dev = &pdev->dev;
int press_irq, release_irq;
int error;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regmap = dev_get_regmap(dev->parent, NULL);
if (!priv->regmap) {
dev_err(dev, "Failed to init regmap\n");
return -ENODEV;
}
press_irq = platform_get_irq_byname(pdev, "pwrkey-press");
if (press_irq < 0)
return press_irq;
release_irq = platform_get_irq_byname(pdev, "pwrkey-release");
if (release_irq < 0)
return release_irq;
/* Make input device be device resource managed */
priv->input = devm_input_allocate_device(dev);
if (!priv->input)
return -ENOMEM;
priv->input->name = "rt5120_pwrkey";
priv->input->phys = "rt5120_pwrkey/input0";
priv->input->id.bustype = BUS_I2C;
input_set_capability(priv->input, EV_KEY, KEY_POWER);
error = input_register_device(priv->input);
if (error) {
dev_err(dev, "Failed to register input device: %d\n", error);
return error;
}
error = devm_request_threaded_irq(dev, press_irq,
NULL, rt5120_pwrkey_handler,
0, "pwrkey-press", priv);
if (error) {
dev_err(dev,
"Failed to register pwrkey press irq: %d\n", error);
return error;
}
error = devm_request_threaded_irq(dev, release_irq,
NULL, rt5120_pwrkey_handler,
0, "pwrkey-release", priv);
if (error) {
dev_err(dev,
"Failed to register pwrkey release irq: %d\n", error);
return error;
}
return 0;
}
static const struct of_device_id r5120_pwrkey_match_table[] = {
{ .compatible = "richtek,rt5120-pwrkey" },
{}
};
MODULE_DEVICE_TABLE(of, r5120_pwrkey_match_table);
static struct platform_driver rt5120_pwrkey_driver = {
.driver = {
.name = "rt5120-pwrkey",
.of_match_table = r5120_pwrkey_match_table,
},
.probe = rt5120_pwrkey_probe,
};
module_platform_driver(rt5120_pwrkey_driver);
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_DESCRIPTION("Richtek RT5120 power key driver");
MODULE_LICENSE("GPL");

View File

@ -26,6 +26,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/twl.h> #include <linux/mfd/twl.h>

View File

@ -163,14 +163,10 @@ static int __maybe_unused twl4030_vibra_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
twl4030_vibra_suspend, twl4030_vibra_resume); twl4030_vibra_suspend, twl4030_vibra_resume);
static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, static bool twl4030_vibra_check_coexist(struct device_node *parent)
struct device_node *parent)
{ {
struct device_node *node; struct device_node *node;
if (pdata && pdata->coexist)
return true;
node = of_get_child_by_name(parent, "codec"); node = of_get_child_by_name(parent, "codec");
if (node) { if (node) {
of_node_put(node); of_node_put(node);
@ -182,13 +178,12 @@ static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
static int twl4030_vibra_probe(struct platform_device *pdev) static int twl4030_vibra_probe(struct platform_device *pdev)
{ {
struct twl4030_vibra_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *twl4030_core_node = pdev->dev.parent->of_node; struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
struct vibra_info *info; struct vibra_info *info;
int ret; int ret;
if (!pdata && !twl4030_core_node) { if (!twl4030_core_node) {
dev_dbg(&pdev->dev, "platform_data not available\n"); dev_dbg(&pdev->dev, "twl4030 OF node is missing\n");
return -EINVAL; return -EINVAL;
} }
@ -197,7 +192,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
info->dev = &pdev->dev; info->dev = &pdev->dev;
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); info->coexist = twl4030_vibra_check_coexist(twl4030_core_node);
INIT_WORK(&info->play_work, vibra_play_work); INIT_WORK(&info->play_work, vibra_play_work);
info->input_dev = devm_input_allocate_device(&pdev->dev); info->input_dev = devm_input_allocate_device(&pdev->dev);

View File

@ -1311,12 +1311,6 @@ static int elan_probe(struct i2c_client *client,
return error; return error;
} }
error = devm_device_add_groups(dev, elan_sysfs_groups);
if (error) {
dev_err(dev, "failed to create sysfs attributes: %d\n", error);
return error;
}
error = input_register_device(data->input); error = input_register_device(data->input);
if (error) { if (error) {
dev_err(dev, "failed to register input device: %d\n", error); dev_err(dev, "failed to register input device: %d\n", error);
@ -1442,6 +1436,7 @@ static struct i2c_driver elan_driver = {
.acpi_match_table = ACPI_PTR(elan_acpi_id), .acpi_match_table = ACPI_PTR(elan_acpi_id),
.of_match_table = of_match_ptr(elan_of_match), .of_match_table = of_match_ptr(elan_of_match),
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.dev_groups = elan_sysfs_groups,
}, },
.probe = elan_probe, .probe = elan_probe,
.id_table = elan_id, .id_table = elan_id,

View File

@ -884,7 +884,7 @@ static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
/* /*
* We queue work instead of doing recalibration right here * We queue work instead of doing recalibration right here
* to avoid adding locking to to hgpk_force_recalibrate() * to avoid adding locking to hgpk_force_recalibrate()
* since workqueue provides serialization. * since workqueue provides serialization.
*/ */
psmouse_queue_work(psmouse, &priv->recalib_wq, 0); psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
@ -1057,7 +1057,7 @@ void hgpk_module_init(void)
strlen(hgpk_mode_name)); strlen(hgpk_mode_name));
if (hgpk_default_mode == HGPK_MODE_INVALID) { if (hgpk_default_mode == HGPK_MODE_INVALID) {
hgpk_default_mode = HGPK_MODE_MOUSE; hgpk_default_mode = HGPK_MODE_MOUSE;
strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], strscpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE],
sizeof(hgpk_mode_name)); sizeof(hgpk_mode_name));
} }
} }

View File

@ -13,9 +13,6 @@
* Inport (ATI XL and Microsoft) busmouse driver for Linux * Inport (ATI XL and Microsoft) busmouse driver for Linux
*/ */
/*
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/init.h> #include <linux/init.h>

View File

@ -14,9 +14,6 @@
* Logitech Bus Mouse Driver for Linux * Logitech Bus Mouse Driver for Linux
*/ */
/*
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/ioport.h> #include <linux/ioport.h>

View File

@ -10,9 +10,6 @@
* IBM PC110 touchpad driver for Linux * IBM PC110 touchpad driver for Linux
*/ */
/*
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>

View File

@ -94,7 +94,7 @@ PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
(void *) offsetof(struct psmouse, resync_time), (void *) offsetof(struct psmouse, resync_time),
psmouse_show_int_attr, psmouse_set_int_attr); psmouse_show_int_attr, psmouse_set_int_attr);
static struct attribute *psmouse_attributes[] = { static struct attribute *psmouse_dev_attrs[] = {
&psmouse_attr_protocol.dattr.attr, &psmouse_attr_protocol.dattr.attr,
&psmouse_attr_rate.dattr.attr, &psmouse_attr_rate.dattr.attr,
&psmouse_attr_resolution.dattr.attr, &psmouse_attr_resolution.dattr.attr,
@ -103,9 +103,7 @@ static struct attribute *psmouse_attributes[] = {
NULL NULL
}; };
static const struct attribute_group psmouse_attribute_group = { ATTRIBUTE_GROUPS(psmouse_dev);
.attrs = psmouse_attributes,
};
/* /*
* psmouse_mutex protects all operations changing state of mouse * psmouse_mutex protects all operations changing state of mouse
@ -1481,8 +1479,6 @@ static void psmouse_disconnect(struct serio *serio)
struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *psmouse = serio_get_drvdata(serio);
struct psmouse *parent = NULL; struct psmouse *parent = NULL;
sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
mutex_lock(&psmouse_mutex); mutex_lock(&psmouse_mutex);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
@ -1647,10 +1643,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
if (parent && parent->pt_activate) if (parent && parent->pt_activate)
parent->pt_activate(parent); parent->pt_activate(parent);
error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group);
if (error)
goto err_pt_deactivate;
/* /*
* PS/2 devices having SMBus companions should stay disabled * PS/2 devices having SMBus companions should stay disabled
* on PS/2 side, in order to have SMBus part operable. * on PS/2 side, in order to have SMBus part operable.
@ -1666,13 +1658,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
mutex_unlock(&psmouse_mutex); mutex_unlock(&psmouse_mutex);
return retval; return retval;
err_pt_deactivate:
if (parent && parent->pt_deactivate)
parent->pt_deactivate(parent);
if (input_dev) {
input_unregister_device(input_dev);
input_dev = NULL; /* so we don't try to free it below */
}
err_protocol_disconnect: err_protocol_disconnect:
if (psmouse->disconnect) if (psmouse->disconnect)
psmouse->disconnect(psmouse); psmouse->disconnect(psmouse);
@ -1791,7 +1776,8 @@ MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
static struct serio_driver psmouse_drv = { static struct serio_driver psmouse_drv = {
.driver = { .driver = {
.name = "psmouse", .name = "psmouse",
.dev_groups = psmouse_dev_groups,
}, },
.description = DRIVER_DESC, .description = DRIVER_DESC,
.id_table = psmouse_serio_ids, .id_table = psmouse_serio_ids,

View File

@ -7,9 +7,6 @@
* Serial mouse driver for Linux * Serial mouse driver for Linux
*/ */
/*
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -182,6 +182,7 @@ static const char * const smbus_pnp_ids[] = {
"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */ "LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
"LEN009b", /* T580 */ "LEN009b", /* T580 */
"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */ "LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
"LEN040f", /* P1 Gen 3 */
"LEN200f", /* T450s */ "LEN200f", /* T450s */
"LEN2044", /* L470 */ "LEN2044", /* L470 */
"LEN2054", /* E480 */ "LEN2054", /* E480 */
@ -714,8 +715,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
} }
serio->id.type = SERIO_PS_PSTHRU; serio->id.type = SERIO_PS_PSTHRU;
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); strscpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys)); strscpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys));
serio->write = synaptics_pt_write; serio->write = synaptics_pt_write;
serio->start = synaptics_pt_start; serio->start = synaptics_pt_start;
serio->stop = synaptics_pt_stop; serio->stop = synaptics_pt_stop;

View File

@ -354,7 +354,7 @@ static int synusb_probe(struct usb_interface *intf,
synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (udev->manufacturer) if (udev->manufacturer)
strlcpy(synusb->name, udev->manufacturer, strscpy(synusb->name, udev->manufacturer,
sizeof(synusb->name)); sizeof(synusb->name));
if (udev->product) { if (udev->product) {

View File

@ -12,9 +12,6 @@
* Later on, I had access to the device's documentation (referenced below). * Later on, I had access to the device's documentation (referenced below).
*/ */
/*
*/
/* /*
* Building an adaptor to DE9 / DB25 RS232 * Building an adaptor to DE9 / DB25 RS232
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -138,12 +135,12 @@ static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
{ {
switch (mouse->type) { switch (mouse->type) {
case 0x02: case 0x02:
strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse", strscpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
sizeof(mouse->name)); sizeof(mouse->name));
break; break;
case 0x04: case 0x04:
strlcpy(mouse->name, "DEC VSXXX-AB digitizer", strscpy(mouse->name, "DEC VSXXX-AB digitizer",
sizeof(mouse->name)); sizeof(mouse->name));
break; break;

View File

@ -181,7 +181,7 @@ static int rmi_f03_register_pt(struct f03_data *f03)
serio->close = rmi_f03_pt_close; serio->close = rmi_f03_pt_close;
serio->port_data = f03; serio->port_data = f03;
strlcpy(serio->name, "RMI4 PS/2 pass-through", sizeof(serio->name)); strscpy(serio->name, "RMI4 PS/2 pass-through", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", snprintf(serio->phys, sizeof(serio->phys), "%s/serio0",
dev_name(&f03->fn->dev)); dev_name(&f03->fn->dev));
serio->dev.parent = &f03->fn->dev; serio->dev.parent = &f03->fn->dev;

View File

@ -114,13 +114,13 @@ static irqreturn_t rmi_f34_attention(int irq, void *ctx)
complete(&f34->v5.cmd_done); complete(&f34->v5.cmd_done);
} else { } else {
ret = rmi_read_block(f34->fn->rmi_dev, ret = rmi_read_block(f34->fn->rmi_dev,
f34->fn->fd.data_base_addr + f34->fn->fd.data_base_addr +
f34->v7.off.flash_status, V7_COMMAND_OFFSET,
&status, sizeof(status)); &status, sizeof(status));
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n", rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: cmd: %#02x, ret: %d\n",
__func__, status, ret); __func__, status, ret);
if (!ret && !(status & 0x1f)) if (!ret && status == CMD_V7_IDLE)
complete(&f34->v7.cmd_done); complete(&f34->v7.cmd_done);
} }
@ -321,13 +321,13 @@ static ssize_t rmi_driver_bootloader_id_show(struct device *dev,
f34 = dev_get_drvdata(&fn->dev); f34 = dev_get_drvdata(&fn->dev);
if (f34->bl_version == 5) if (f34->bl_version == 5)
return scnprintf(buf, PAGE_SIZE, "%c%c\n", return sysfs_emit(buf, "%c%c\n",
f34->bootloader_id[0], f34->bootloader_id[0],
f34->bootloader_id[1]); f34->bootloader_id[1]);
else else
return scnprintf(buf, PAGE_SIZE, "V%d.%d\n", return sysfs_emit(buf, "V%d.%d\n",
f34->bootloader_id[1], f34->bootloader_id[1],
f34->bootloader_id[0]); f34->bootloader_id[0]);
} }
return 0; return 0;
@ -346,7 +346,7 @@ static ssize_t rmi_driver_configuration_id_show(struct device *dev,
if (fn) { if (fn) {
f34 = dev_get_drvdata(&fn->dev); f34 = dev_get_drvdata(&fn->dev);
return scnprintf(buf, PAGE_SIZE, "%s\n", f34->configuration_id); return sysfs_emit(buf, "%s\n", f34->configuration_id);
} }
return 0; return 0;
@ -370,7 +370,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
f34 = dev_get_drvdata(&data->f34_container->dev); f34 = dev_get_drvdata(&data->f34_container->dev);
if (f34->bl_version == 7) { if (f34->bl_version >= 7) {
if (data->pdt_props & HAS_BSR) { if (data->pdt_props & HAS_BSR) {
dev_err(dev, "%s: LTS not supported\n", __func__); dev_err(dev, "%s: LTS not supported\n", __func__);
return -ENODEV; return -ENODEV;
@ -382,7 +382,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
} }
/* Enter flash mode */ /* Enter flash mode */
if (f34->bl_version == 7) if (f34->bl_version >= 7)
ret = rmi_f34v7_start_reflash(f34, fw); ret = rmi_f34v7_start_reflash(f34, fw);
else else
ret = rmi_f34_enable_flash(f34); ret = rmi_f34_enable_flash(f34);
@ -413,7 +413,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
f34 = dev_get_drvdata(&data->f34_container->dev); f34 = dev_get_drvdata(&data->f34_container->dev);
/* Perform firmware update */ /* Perform firmware update */
if (f34->bl_version == 7) if (f34->bl_version >= 7)
ret = rmi_f34v7_do_reflash(f34, fw); ret = rmi_f34v7_do_reflash(f34, fw);
else else
ret = rmi_f34_update_firmware(f34, fw); ret = rmi_f34_update_firmware(f34, fw);
@ -499,7 +499,7 @@ static ssize_t rmi_driver_update_fw_status_show(struct device *dev,
if (data->f34_container) if (data->f34_container)
update_status = rmi_f34_status(data->f34_container); update_status = rmi_f34_status(data->f34_container);
return scnprintf(buf, PAGE_SIZE, "%d\n", update_status); return sysfs_emit(buf, "%d\n", update_status);
} }
static DEVICE_ATTR(update_fw_status, 0444, static DEVICE_ATTR(update_fw_status, 0444,

View File

@ -222,20 +222,6 @@ struct image_metadata {
struct physical_address phyaddr; struct physical_address phyaddr;
}; };
struct register_offset {
u8 properties;
u8 properties_2;
u8 block_size;
u8 block_count;
u8 gc_block_count;
u8 flash_status;
u8 partition_id;
u8 block_number;
u8 transfer_length;
u8 flash_cmd;
u8 payload;
};
struct rmi_f34_firmware { struct rmi_f34_firmware {
__le32 checksum; __le32 checksum;
u8 pad1[3]; u8 pad1[3];
@ -262,7 +248,6 @@ struct f34v5_data {
struct f34v7_data { struct f34v7_data {
bool has_display_cfg; bool has_display_cfg;
bool has_guest_code; bool has_guest_code;
bool force_update;
bool in_bl_mode; bool in_bl_mode;
u8 *read_config_buf; u8 *read_config_buf;
size_t read_config_buf_size; size_t read_config_buf_size;
@ -276,9 +261,7 @@ struct f34v7_data {
u16 payload_length; u16 payload_length;
u8 partitions; u8 partitions;
u16 partition_table_bytes; u16 partition_table_bytes;
bool new_partition_table;
struct register_offset off;
struct block_count blkcount; struct block_count blkcount;
struct physical_address phyaddr; struct physical_address phyaddr;
struct image_metadata img; struct image_metadata img;

View File

@ -25,7 +25,7 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
int ret; int ret;
ret = rmi_read_block(f34->fn->rmi_dev, ret = rmi_read_block(f34->fn->rmi_dev,
f34->fn->fd.data_base_addr + f34->v7.off.flash_status, f34->fn->fd.data_base_addr + V7_FLASH_STATUS_OFFSET,
&status, &status,
sizeof(status)); sizeof(status));
if (ret < 0) { if (ret < 0) {
@ -43,7 +43,7 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
} }
ret = rmi_read_block(f34->fn->rmi_dev, ret = rmi_read_block(f34->fn->rmi_dev,
f34->fn->fd.data_base_addr + f34->v7.off.flash_cmd, f34->fn->fd.data_base_addr + V7_COMMAND_OFFSET,
&command, &command,
sizeof(command)); sizeof(command));
if (ret < 0) { if (ret < 0) {
@ -72,6 +72,24 @@ static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms)
return 0; return 0;
} }
static int rmi_f34v7_check_command_status(struct f34_data *f34, int timeout_ms)
{
int ret;
ret = rmi_f34v7_wait_for_idle(f34, timeout_ms);
if (ret < 0)
return ret;
ret = rmi_f34v7_read_flash_status(f34);
if (ret < 0)
return ret;
if (f34->v7.flash_status != 0x00)
return -EIO;
return 0;
}
static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34, static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
u8 cmd) u8 cmd)
{ {
@ -122,7 +140,7 @@ static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
data_1_5.payload[1] = f34->bootloader_id[1]; data_1_5.payload[1] = f34->bootloader_id[1];
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.partition_id, base + V7_PARTITION_ID_OFFSET,
&data_1_5, sizeof(data_1_5)); &data_1_5, sizeof(data_1_5));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, dev_err(&f34->fn->dev,
@ -195,7 +213,7 @@ static int rmi_f34v7_write_command(struct f34_data *f34, u8 cmd)
__func__, command); __func__, command);
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.flash_cmd, base + V7_COMMAND_OFFSET,
&command, sizeof(command)); &command, sizeof(command));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write flash command\n", dev_err(&f34->fn->dev, "%s: Failed to write flash command\n",
@ -262,7 +280,7 @@ static int rmi_f34v7_write_partition_id(struct f34_data *f34, u8 cmd)
} }
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.partition_id, base + V7_PARTITION_ID_OFFSET,
&partition, sizeof(partition)); &partition, sizeof(partition));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n", dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n",
@ -290,7 +308,7 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
return ret; return ret;
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.block_number, base + V7_BLOCK_NUMBER_OFFSET,
&block_number, sizeof(block_number)); &block_number, sizeof(block_number));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write block number\n", dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
@ -301,7 +319,7 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
put_unaligned_le16(f34->v7.flash_config_length, &length); put_unaligned_le16(f34->v7.flash_config_length, &length);
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.transfer_length, base + V7_TRANSFER_LENGTH_OFFSET,
&length, sizeof(length)); &length, sizeof(length));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n", dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n",
@ -318,6 +336,10 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
return ret; return ret;
} }
/*
* rmi_f34v7_check_command_status() can't be used here, as this
* function is called before IRQs are available
*/
timeout = msecs_to_jiffies(F34_WRITE_WAIT_MS); timeout = msecs_to_jiffies(F34_WRITE_WAIT_MS);
while (time_before(jiffies, timeout)) { while (time_before(jiffies, timeout)) {
usleep_range(5000, 6000); usleep_range(5000, 6000);
@ -330,7 +352,7 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
} }
ret = rmi_read_block(f34->fn->rmi_dev, ret = rmi_read_block(f34->fn->rmi_dev,
base + f34->v7.off.payload, base + V7_PAYLOAD_OFFSET,
f34->v7.read_config_buf, f34->v7.read_config_buf,
f34->v7.partition_table_bytes); f34->v7.partition_table_bytes);
if (ret < 0) { if (ret < 0) {
@ -504,13 +526,6 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n", rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n",
__func__, f34->v7.block_size); __func__, f34->v7.block_size);
f34->v7.off.flash_status = V7_FLASH_STATUS_OFFSET;
f34->v7.off.partition_id = V7_PARTITION_ID_OFFSET;
f34->v7.off.block_number = V7_BLOCK_NUMBER_OFFSET;
f34->v7.off.transfer_length = V7_TRANSFER_LENGTH_OFFSET;
f34->v7.off.flash_cmd = V7_COMMAND_OFFSET;
f34->v7.off.payload = V7_PAYLOAD_OFFSET;
f34->v7.has_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG; f34->v7.has_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG;
f34->v7.has_guest_code = f34->v7.has_guest_code =
query_1_7.partition_support[1] & HAS_GUEST_CODE; query_1_7.partition_support[1] & HAS_GUEST_CODE;
@ -571,68 +586,6 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
return 0; return 0;
} }
static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34)
{
u16 block_count;
block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size;
f34->update_size += block_count;
if (block_count != f34->v7.blkcount.ui_firmware) {
dev_err(&f34->fn->dev,
"UI firmware size mismatch: %d != %d\n",
block_count, f34->v7.blkcount.ui_firmware);
return -EINVAL;
}
return 0;
}
static int rmi_f34v7_check_ui_config_size(struct f34_data *f34)
{
u16 block_count;
block_count = f34->v7.img.ui_config.size / f34->v7.block_size;
f34->update_size += block_count;
if (block_count != f34->v7.blkcount.ui_config) {
dev_err(&f34->fn->dev, "UI config size mismatch\n");
return -EINVAL;
}
return 0;
}
static int rmi_f34v7_check_dp_config_size(struct f34_data *f34)
{
u16 block_count;
block_count = f34->v7.img.dp_config.size / f34->v7.block_size;
f34->update_size += block_count;
if (block_count != f34->v7.blkcount.dp_config) {
dev_err(&f34->fn->dev, "Display config size mismatch\n");
return -EINVAL;
}
return 0;
}
static int rmi_f34v7_check_guest_code_size(struct f34_data *f34)
{
u16 block_count;
block_count = f34->v7.img.guest_code.size / f34->v7.block_size;
f34->update_size += block_count;
if (block_count != f34->v7.blkcount.guest_code) {
dev_err(&f34->fn->dev, "Guest code size mismatch\n");
return -EINVAL;
}
return 0;
}
static int rmi_f34v7_check_bl_config_size(struct f34_data *f34) static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
{ {
u16 block_count; u16 block_count;
@ -648,58 +601,6 @@ static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
return 0; return 0;
} }
static int rmi_f34v7_erase_config(struct f34_data *f34)
{
int ret;
dev_info(&f34->fn->dev, "Erasing config...\n");
init_completion(&f34->v7.cmd_done);
switch (f34->v7.config_area) {
case v7_UI_CONFIG_AREA:
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG);
if (ret < 0)
return ret;
break;
case v7_DP_CONFIG_AREA:
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_DISP_CONFIG);
if (ret < 0)
return ret;
break;
case v7_BL_CONFIG_AREA:
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_BL_CONFIG);
if (ret < 0)
return ret;
break;
}
ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
if (ret < 0)
return ret;
return 0;
}
static int rmi_f34v7_erase_guest_code(struct f34_data *f34)
{
int ret;
dev_info(&f34->fn->dev, "Erasing guest code...\n");
init_completion(&f34->v7.cmd_done);
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE);
if (ret < 0)
return ret;
ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
if (ret < 0)
return ret;
return 0;
}
static int rmi_f34v7_erase_all(struct f34_data *f34) static int rmi_f34v7_erase_all(struct f34_data *f34)
{ {
int ret; int ret;
@ -708,32 +609,14 @@ static int rmi_f34v7_erase_all(struct f34_data *f34)
init_completion(&f34->v7.cmd_done); init_completion(&f34->v7.cmd_done);
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE); ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_ALL);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS); ret = rmi_f34v7_check_command_status(f34, F34_ERASE_WAIT_MS);
if (ret < 0) if (ret < 0)
return ret; return ret;
f34->v7.config_area = v7_UI_CONFIG_AREA;
ret = rmi_f34v7_erase_config(f34);
if (ret < 0)
return ret;
if (f34->v7.has_display_cfg) {
f34->v7.config_area = v7_DP_CONFIG_AREA;
ret = rmi_f34v7_erase_config(f34);
if (ret < 0)
return ret;
}
if (f34->v7.new_partition_table && f34->v7.has_guest_code) {
ret = rmi_f34v7_erase_guest_code(f34);
if (ret < 0)
return ret;
}
return 0; return 0;
} }
@ -756,7 +639,7 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
return ret; return ret;
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.block_number, base + V7_BLOCK_NUMBER_OFFSET,
&block_number, sizeof(block_number)); &block_number, sizeof(block_number));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write block number\n", dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
@ -772,7 +655,7 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
put_unaligned_le16(transfer, &length); put_unaligned_le16(transfer, &length);
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.transfer_length, base + V7_TRANSFER_LENGTH_OFFSET,
&length, sizeof(length)); &length, sizeof(length));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, dev_err(&f34->fn->dev,
@ -787,12 +670,12 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS); ret = rmi_f34v7_check_command_status(f34, F34_ENABLE_WAIT_MS);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = rmi_read_block(f34->fn->rmi_dev, ret = rmi_read_block(f34->fn->rmi_dev,
base + f34->v7.off.payload, base + V7_PAYLOAD_OFFSET,
&f34->v7.read_config_buf[index], &f34->v7.read_config_buf[index],
transfer * f34->v7.block_size); transfer * f34->v7.block_size);
if (ret < 0) { if (ret < 0) {
@ -828,7 +711,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
return ret; return ret;
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.block_number, base + V7_BLOCK_NUMBER_OFFSET,
&block_number, sizeof(block_number)); &block_number, sizeof(block_number));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write block number\n", dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
@ -848,7 +731,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
init_completion(&f34->v7.cmd_done); init_completion(&f34->v7.cmd_done);
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.transfer_length, base + V7_TRANSFER_LENGTH_OFFSET,
&length, sizeof(length)); &length, sizeof(length));
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, dev_err(&f34->fn->dev,
@ -862,7 +745,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
return ret; return ret;
ret = rmi_write_block(f34->fn->rmi_dev, ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.payload, base + V7_PAYLOAD_OFFSET,
block_ptr, transfer * f34->v7.block_size); block_ptr, transfer * f34->v7.block_size);
if (ret < 0) { if (ret < 0) {
dev_err(&f34->fn->dev, dev_err(&f34->fn->dev,
@ -871,7 +754,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
return ret; return ret;
} }
ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS); ret = rmi_f34v7_check_command_status(f34, F34_ENABLE_WAIT_MS);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -937,17 +820,6 @@ static int rmi_f34v7_write_flash_config(struct f34_data *f34)
init_completion(&f34->v7.cmd_done); init_completion(&f34->v7.cmd_done);
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG);
if (ret < 0)
return ret;
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
"%s: Erase flash config command written\n", __func__);
ret = rmi_f34v7_wait_for_idle(f34, F34_WRITE_WAIT_MS);
if (ret < 0)
return ret;
ret = rmi_f34v7_write_config(f34); ret = rmi_f34v7_write_config(f34);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -977,10 +849,6 @@ static int rmi_f34v7_write_partition_table(struct f34_data *f34)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = rmi_f34v7_erase_config(f34);
if (ret < 0)
return ret;
ret = rmi_f34v7_write_flash_config(f34); ret = rmi_f34v7_write_flash_config(f34);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1007,33 +875,6 @@ static int rmi_f34v7_write_firmware(struct f34_data *f34)
blk_count, v7_CMD_WRITE_FW); blk_count, v7_CMD_WRITE_FW);
} }
static void rmi_f34v7_compare_partition_tables(struct f34_data *f34)
{
if (f34->v7.phyaddr.ui_firmware != f34->v7.img.phyaddr.ui_firmware) {
f34->v7.new_partition_table = true;
return;
}
if (f34->v7.phyaddr.ui_config != f34->v7.img.phyaddr.ui_config) {
f34->v7.new_partition_table = true;
return;
}
if (f34->v7.has_display_cfg &&
f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) {
f34->v7.new_partition_table = true;
return;
}
if (f34->v7.has_guest_code &&
f34->v7.phyaddr.guest_code != f34->v7.img.phyaddr.guest_code) {
f34->v7.new_partition_table = true;
return;
}
f34->v7.new_partition_table = false;
}
static void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34, static void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34,
const void *image) const void *image)
{ {
@ -1180,8 +1021,6 @@ static int rmi_f34v7_parse_image_info(struct f34_data *f34)
rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data, rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data,
&f34->v7.img.blkcount, &f34->v7.img.phyaddr); &f34->v7.img.blkcount, &f34->v7.img.phyaddr);
rmi_f34v7_compare_partition_tables(f34);
return 0; return 0;
} }
@ -1200,53 +1039,35 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
ret = rmi_f34v7_parse_image_info(f34); ret = rmi_f34v7_parse_image_info(f34);
if (ret < 0) if (ret < 0)
goto fail; return ret;
if (!f34->v7.new_partition_table) { ret = rmi_f34v7_check_bl_config_size(f34);
ret = rmi_f34v7_check_ui_firmware_size(f34); if (ret < 0)
if (ret < 0) return ret;
goto fail;
ret = rmi_f34v7_check_ui_config_size(f34);
if (ret < 0)
goto fail;
if (f34->v7.has_display_cfg &&
f34->v7.img.contains_display_cfg) {
ret = rmi_f34v7_check_dp_config_size(f34);
if (ret < 0)
goto fail;
}
if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
ret = rmi_f34v7_check_guest_code_size(f34);
if (ret < 0)
goto fail;
}
} else {
ret = rmi_f34v7_check_bl_config_size(f34);
if (ret < 0)
goto fail;
}
ret = rmi_f34v7_erase_all(f34); ret = rmi_f34v7_erase_all(f34);
if (ret < 0) if (ret < 0)
goto fail; return ret;
if (f34->v7.new_partition_table) { ret = rmi_f34v7_write_partition_table(f34);
ret = rmi_f34v7_write_partition_table(f34); if (ret < 0)
if (ret < 0) return ret;
goto fail; dev_info(&f34->fn->dev, "%s: Partition table programmed\n", __func__);
dev_info(&f34->fn->dev, "%s: Partition table programmed\n",
__func__); /*
} * Reset to reload partition table - as the previous firmware has been
* erased, we remain in bootloader mode.
*/
ret = rmi_scan_pdt(f34->fn->rmi_dev, NULL, rmi_initial_reset);
if (ret < 0)
dev_warn(&f34->fn->dev, "RMI reset failed!\n");
dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n", dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n",
f34->v7.img.ui_firmware.size); f34->v7.img.ui_firmware.size);
ret = rmi_f34v7_write_firmware(f34); ret = rmi_f34v7_write_firmware(f34);
if (ret < 0) if (ret < 0)
goto fail; return ret;
dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n", dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n",
f34->v7.img.ui_config.size); f34->v7.img.ui_config.size);
@ -1254,28 +1075,25 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
f34->v7.config_area = v7_UI_CONFIG_AREA; f34->v7.config_area = v7_UI_CONFIG_AREA;
ret = rmi_f34v7_write_ui_config(f34); ret = rmi_f34v7_write_ui_config(f34);
if (ret < 0) if (ret < 0)
goto fail; return ret;
if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) { if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) {
dev_info(&f34->fn->dev, "Writing display config...\n"); dev_info(&f34->fn->dev, "Writing display config...\n");
ret = rmi_f34v7_write_dp_config(f34); ret = rmi_f34v7_write_dp_config(f34);
if (ret < 0) if (ret < 0)
goto fail; return ret;
} }
if (f34->v7.new_partition_table) { if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) { dev_info(&f34->fn->dev, "Writing guest code...\n");
dev_info(&f34->fn->dev, "Writing guest code...\n");
ret = rmi_f34v7_write_guest_code(f34); ret = rmi_f34v7_write_guest_code(f34);
if (ret < 0) if (ret < 0)
goto fail; return ret;
}
} }
fail: return 0;
return ret;
} }
static int rmi_f34v7_enter_flash_prog(struct f34_data *f34) static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
@ -1288,8 +1106,11 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (f34->v7.in_bl_mode) if (f34->v7.in_bl_mode) {
dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n",
__func__);
return 0; return 0;
}
init_completion(&f34->v7.cmd_done); init_completion(&f34->v7.cmd_done);
@ -1297,7 +1118,7 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS); ret = rmi_f34v7_check_command_status(f34, F34_ENABLE_WAIT_MS);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1308,39 +1129,16 @@ int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw)
{ {
int ret = 0; int ret = 0;
f34->fn->rmi_dev->driver->set_irq_bits(f34->fn->rmi_dev, f34->fn->irq_mask);
f34->v7.config_area = v7_UI_CONFIG_AREA; f34->v7.config_area = v7_UI_CONFIG_AREA;
f34->v7.image = fw->data; f34->v7.image = fw->data;
ret = rmi_f34v7_parse_image_info(f34); ret = rmi_f34v7_parse_image_info(f34);
if (ret < 0) if (ret < 0)
goto exit; return ret;
if (!f34->v7.force_update && f34->v7.new_partition_table) {
dev_err(&f34->fn->dev, "%s: Partition table mismatch\n",
__func__);
ret = -EINVAL;
goto exit;
}
dev_info(&f34->fn->dev, "Firmware image OK\n"); dev_info(&f34->fn->dev, "Firmware image OK\n");
ret = rmi_f34v7_read_flash_status(f34); return rmi_f34v7_enter_flash_prog(f34);
if (ret < 0)
goto exit;
if (f34->v7.in_bl_mode) {
dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n",
__func__);
}
rmi_f34v7_enter_flash_prog(f34);
return 0;
exit:
return ret;
} }
int rmi_f34v7_probe(struct f34_data *f34) int rmi_f34v7_probe(struct f34_data *f34)
@ -1384,6 +1182,5 @@ int rmi_f34v7_probe(struct f34_data *f34)
if (ret < 0) if (ret < 0)
return ret; return ret;
f34->v7.force_update = true;
return 0; return 0;
} }

View File

@ -390,8 +390,8 @@ static int rmi_f54_vidioc_querycap(struct file *file, void *priv,
{ {
struct f54_data *f54 = video_drvdata(file); struct f54_data *f54 = video_drvdata(file);
strlcpy(cap->driver, F54_NAME, sizeof(cap->driver)); strscpy(cap->driver, F54_NAME, sizeof(cap->driver));
strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card)); strscpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info), snprintf(cap->bus_info, sizeof(cap->bus_info),
"rmi4:%s", dev_name(&f54->fn->dev)); "rmi4:%s", dev_name(&f54->fn->dev));
@ -410,7 +410,7 @@ static int rmi_f54_vidioc_enum_input(struct file *file, void *priv,
i->type = V4L2_INPUT_TYPE_TOUCH; i->type = V4L2_INPUT_TYPE_TOUCH;
strlcpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name)); strscpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name));
return 0; return 0;
} }
@ -696,7 +696,7 @@ static int rmi_f54_probe(struct rmi_function *fn)
rmi_f54_set_input(f54, 0); rmi_f54_set_input(f54, 0);
/* register video device */ /* register video device */
strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name)); strscpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name));
ret = v4l2_device_register(&fn->dev, &f54->v4l2); ret = v4l2_device_register(&fn->dev, &f54->v4l2);
if (ret) { if (ret) {
dev_err(&fn->dev, "Unable to register video dev.\n"); dev_err(&fn->dev, "Unable to register video dev.\n");

View File

@ -110,8 +110,8 @@ static int altera_ps2_probe(struct platform_device *pdev)
serio->write = altera_ps2_write; serio->write = altera_ps2_write;
serio->open = altera_ps2_open; serio->open = altera_ps2_open;
serio->close = altera_ps2_close; serio->close = altera_ps2_close;
strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name)); strscpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); strscpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
serio->port_data = ps2if; serio->port_data = ps2if;
serio->dev.parent = &pdev->dev; serio->dev.parent = &pdev->dev;
ps2if->io = serio; ps2if->io = serio;

View File

@ -126,8 +126,8 @@ static int amba_kmi_probe(struct amba_device *dev,
io->write = amba_kmi_write; io->write = amba_kmi_write;
io->open = amba_kmi_open; io->open = amba_kmi_open;
io->close = amba_kmi_close; io->close = amba_kmi_close;
strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name)); strscpy(io->name, dev_name(&dev->dev), sizeof(io->name));
strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys)); strscpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
io->port_data = kmi; io->port_data = kmi;
io->dev.parent = &dev->dev; io->dev.parent = &dev->dev;

Some files were not shown because too many files have changed in this diff Show More