mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 14:04:54 +02:00
Merge 5d26c176d5 ("Merge tag 'thermal-v5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux") into android-mainline
Steps on the way to 5.12-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I54c08034e04eac4b8d245afab8d3d13ff7d38844
This commit is contained in:
commit
4cf0c39dc0
|
|
@ -13,3 +13,24 @@ Contact: Xu Yilun <yilun.xu@intel.com>
|
|||
Description: Read only. Returns the firmware version of Intel MAX10
|
||||
BMC chip.
|
||||
Format: "0x%x".
|
||||
|
||||
What: /sys/bus/spi/devices/.../mac_address
|
||||
Date: January 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: Russ Weight <russell.h.weight@intel.com>
|
||||
Description: Read only. Returns the first MAC address in a block
|
||||
of sequential MAC addresses assigned to the board
|
||||
that is managed by the Intel MAX10 BMC. It is stored in
|
||||
FLASH storage and is mirrored in the MAX10 BMC register
|
||||
space.
|
||||
Format: "%02x:%02x:%02x:%02x:%02x:%02x".
|
||||
|
||||
What: /sys/bus/spi/devices/.../mac_count
|
||||
Date: January 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: Russ Weight <russell.h.weight@intel.com>
|
||||
Description: Read only. Returns the number of sequential MAC
|
||||
addresses assigned to the board managed by the Intel
|
||||
MAX10 BMC. This value is stored in FLASH and is mirrored
|
||||
in the MAX10 BMC register space.
|
||||
Format: "%u".
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
What: /sys/devices/platform/ideapad/camera_power
|
||||
What: /sys/bus/platform/devices/VPC2004:*/camera_power
|
||||
Date: Dec 2010
|
||||
KernelVersion: 2.6.37
|
||||
Contact: "Ike Panhc <ike.pan@canonical.com>"
|
||||
Description:
|
||||
Control the power of camera module. 1 means on, 0 means off.
|
||||
|
||||
What: /sys/devices/platform/ideapad/fan_mode
|
||||
What: /sys/bus/platform/devices/VPC2004:*/fan_mode
|
||||
Date: June 2012
|
||||
KernelVersion: 3.6
|
||||
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
|
||||
|
|
@ -18,7 +18,7 @@ Description:
|
|||
* 2 -> Dust Cleaning
|
||||
* 4 -> Efficient Thermal Dissipation Mode
|
||||
|
||||
What: /sys/devices/platform/ideapad/touchpad
|
||||
What: /sys/bus/platform/devices/VPC2004:*/touchpad
|
||||
Date: May 2017
|
||||
KernelVersion: 4.13
|
||||
Contact: "Ritesh Raj Sarraf <rrs@debian.org>"
|
||||
|
|
@ -27,7 +27,16 @@ Description:
|
|||
* 1 -> Switched On
|
||||
* 0 -> Switched Off
|
||||
|
||||
What: /sys/bus/pci/devices/<bdf>/<device>/VPC2004:00/fn_lock
|
||||
What: /sys/bus/platform/devices/VPC2004:*/conservation_mode
|
||||
Date: Aug 2017
|
||||
KernelVersion: 4.14
|
||||
Contact: platform-driver-x86@vger.kernel.org
|
||||
Description:
|
||||
Controls whether the conservation mode is enabled or not.
|
||||
This feature limits the maximum battery charge percentage to
|
||||
around 50-60% in order to prolong the lifetime of the battery.
|
||||
|
||||
What: /sys/bus/platform/devices/VPC2004:*/fn_lock
|
||||
Date: May 2018
|
||||
KernelVersion: 4.18
|
||||
Contact: "Oleg Keri <ezhi99@gmail.com>"
|
||||
|
|
@ -41,3 +50,12 @@ Description:
|
|||
|
||||
# echo "0" > \
|
||||
/sys/bus/pci/devices/0000:00:1f.0/PNP0C09:00/VPC2004:00/fn_lock
|
||||
|
||||
What: /sys/bus/platform/devices/VPC2004:*/usb_charging
|
||||
Date: Feb 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: platform-driver-x86@vger.kernel.org
|
||||
Description:
|
||||
Controls whether the "always on USB charging" feature is
|
||||
enabled or not. This feature enables charging USB devices
|
||||
even if the computer is not turned on.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ detailed description):
|
|||
- UWB enable and disable
|
||||
- LCD Shadow (PrivacyGuard) enable and disable
|
||||
- Lap mode sensor
|
||||
- Setting keyboard language
|
||||
|
||||
A compatibility table by model and feature is maintained on the web
|
||||
site, http://ibm-acpi.sf.net/. I appreciate any success or failure
|
||||
|
|
@ -1466,6 +1467,30 @@ Sysfs notes
|
|||
rfkill controller switch "tpacpi_uwb_sw": refer to
|
||||
Documentation/driver-api/rfkill.rst for details.
|
||||
|
||||
|
||||
Setting keyboard language
|
||||
-------------------------
|
||||
|
||||
sysfs: keyboard_lang
|
||||
|
||||
This feature is used to set keyboard language to ECFW using ASL interface.
|
||||
Fewer thinkpads models like T580 , T590 , T15 Gen 1 etc.. has "=", "(',
|
||||
")" numeric keys, which are not displaying correctly, when keyboard language
|
||||
is other than "english". This is because the default keyboard language in ECFW
|
||||
is set as "english". Hence using this sysfs, user can set the correct keyboard
|
||||
language to ECFW and then these key's will work correctly.
|
||||
|
||||
Example of command to set keyboard language is mentioned below::
|
||||
|
||||
echo jp > /sys/devices/platform/thinkpad_acpi/keyboard_lang
|
||||
|
||||
Text corresponding to keyboard layout to be set in sysfs are: be(Belgian),
|
||||
cz(Czech), da(Danish), de(German), en(English), es(Spain), et(Estonian),
|
||||
fr(French), fr-ch(French(Switzerland)), hu(Hungarian), it(Italy), jp (Japan),
|
||||
nl(Dutch), nn(Norway), pl(Polish), pt(portugese), sl(Slovenian), sv(Sweden),
|
||||
tr(Turkey)
|
||||
|
||||
|
||||
Adaptive keyboard
|
||||
-----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -182,8 +182,9 @@ API presented to device drivers
|
|||
|
||||
A :c:type:``struct blk_keyslot_manager`` should be set up by device drivers in
|
||||
the ``request_queue`` of the device. The device driver needs to call
|
||||
``blk_ksm_init`` on the ``blk_keyslot_manager``, which specifying the number of
|
||||
keyslots supported by the hardware.
|
||||
``blk_ksm_init`` (or its resource-managed variant ``devm_blk_ksm_init``) on the
|
||||
``blk_keyslot_manager``, while specifying the number of keyslots supported by
|
||||
the hardware.
|
||||
|
||||
The device driver also needs to tell the KSM how to actually manipulate the
|
||||
IE hardware in the device to do things like programming the crypto key into
|
||||
|
|
@ -202,10 +203,9 @@ needs each and every of its keyslots to be reprogrammed with the key it
|
|||
"should have" at the point in time when the function is called. This is useful
|
||||
e.g. if a device loses all its keys on runtime power down/up.
|
||||
|
||||
``blk_ksm_destroy`` should be called to free up all resources used by a keyslot
|
||||
manager upon ``blk_ksm_init``, once the ``blk_keyslot_manager`` is no longer
|
||||
needed.
|
||||
|
||||
If the driver used ``blk_ksm_init`` instead of ``devm_blk_ksm_init``, then
|
||||
``blk_ksm_destroy`` should be called to free up all resources used by a
|
||||
``blk_keyslot_manager`` once it is no longer needed.
|
||||
|
||||
Layered Devices
|
||||
===============
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/auxdisplay/holtek,ht16k33.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Holtek HT16K33 RAM mapping 16*8 LED controller with keyscan
|
||||
|
||||
maintainers:
|
||||
- Robin van der Gracht <robin@protonic.nl>
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/input/matrix-keymap.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: holtek,ht16k33
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
refresh-rate-hz:
|
||||
maxItems: 1
|
||||
description: Display update interval in Hertz
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
debounce-delay-ms:
|
||||
maxItems: 1
|
||||
description: Debouncing interval time in milliseconds
|
||||
|
||||
linux,keymap: true
|
||||
|
||||
linux,no-autorepeat:
|
||||
description: Disable keyrepeat
|
||||
|
||||
default-brightness-level:
|
||||
minimum: 1
|
||||
maximum: 16
|
||||
default: 16
|
||||
description: Initial brightness level
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- refresh-rate-hz
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ht16k33: ht16k33@70 {
|
||||
compatible = "holtek,ht16k33";
|
||||
reg = <0x70>;
|
||||
refresh-rate-hz = <20>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>;
|
||||
debounce-delay-ms = <50>;
|
||||
linux,keymap = <MATRIX_KEY(2, 0, KEY_F6)>,
|
||||
<MATRIX_KEY(3, 0, KEY_F8)>,
|
||||
<MATRIX_KEY(4, 0, KEY_F10)>,
|
||||
<MATRIX_KEY(5, 0, KEY_F4)>,
|
||||
<MATRIX_KEY(6, 0, KEY_F2)>,
|
||||
<MATRIX_KEY(2, 1, KEY_F5)>,
|
||||
<MATRIX_KEY(3, 1, KEY_F7)>,
|
||||
<MATRIX_KEY(4, 1, KEY_F9)>,
|
||||
<MATRIX_KEY(5, 1, KEY_F3)>,
|
||||
<MATRIX_KEY(6, 1, KEY_F1)>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
Holtek ht16k33 RAM mapping 16*8 LED controller driver with keyscan
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: "holtek,ht16k33"
|
||||
- reg: I2C slave address of the chip.
|
||||
- interrupts: Interrupt specification for the key pressed interrupt.
|
||||
- refresh-rate-hz: Display update interval in HZ.
|
||||
- debounce-delay-ms: Debouncing interval time in milliseconds.
|
||||
- linux,keymap: The keymap for keys as described in the binding
|
||||
document (devicetree/bindings/input/matrix-keymap.txt).
|
||||
|
||||
Optional properties:
|
||||
- linux,no-autorepeat: Disable keyrepeat.
|
||||
- default-brightness-level: Initial brightness level [0-15] (default: 15).
|
||||
|
||||
Example:
|
||||
|
||||
&i2c1 {
|
||||
ht16k33: ht16k33@70 {
|
||||
compatible = "holtek,ht16k33";
|
||||
reg = <0x70>;
|
||||
refresh-rate-hz = <20>;
|
||||
debounce-delay-ms = <50>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>;
|
||||
linux,keymap = <
|
||||
MATRIX_KEY(2, 0, KEY_F6)
|
||||
MATRIX_KEY(3, 0, KEY_F8)
|
||||
MATRIX_KEY(4, 0, KEY_F10)
|
||||
MATRIX_KEY(5, 0, KEY_F4)
|
||||
MATRIX_KEY(6, 0, KEY_F2)
|
||||
MATRIX_KEY(2, 1, KEY_F5)
|
||||
MATRIX_KEY(3, 1, KEY_F7)
|
||||
MATRIX_KEY(4, 1, KEY_F9)
|
||||
MATRIX_KEY(5, 1, KEY_F3)
|
||||
MATRIX_KEY(6, 1, KEY_F1)
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
|
@ -95,9 +95,6 @@ properties:
|
|||
pattern: spd$
|
||||
# These are special cases that don't conform to the above pattern.
|
||||
# Each requires a standard at24 model as fallback.
|
||||
- items:
|
||||
- const: rohm,br24t01
|
||||
- const: atmel,24c01
|
||||
- items:
|
||||
- const: nxp,se97b
|
||||
- const: atmel,24c02
|
||||
|
|
@ -113,6 +110,12 @@ properties:
|
|||
- items:
|
||||
- const: renesas,r1ex24128
|
||||
- const: atmel,24c128
|
||||
- items:
|
||||
- const: rohm,br24g01
|
||||
- const: atmel,24c01
|
||||
- items:
|
||||
- const: rohm,br24t01
|
||||
- const: atmel,24c01
|
||||
|
||||
label:
|
||||
description: Descriptive name of the EEPROM.
|
||||
|
|
|
|||
51
Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
Normal file
51
Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
|
||||
$id: http://devicetree.org/schemas/hwmon/ti,tps23861.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI TPS23861 PoE PSE
|
||||
|
||||
maintainers:
|
||||
- Robert Marko <robert.marko@sartura.hr>
|
||||
|
||||
description: |
|
||||
The TPS23861 is a IEEE 802.3at Quad Port Power-over-Ethernet PSE Controller.
|
||||
|
||||
Datasheets:
|
||||
https://www.ti.com/lit/gpn/tps23861
|
||||
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,tps23861
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
shunt-resistor-micro-ohms:
|
||||
description: The value of curent sense resistor in microohms.
|
||||
default: 255000
|
||||
minimum: 250000
|
||||
maximum: 255000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
tps23861@30 {
|
||||
compatible = "ti,tps23861";
|
||||
reg = <0x30>;
|
||||
shunt-resistor-micro-ohms = <255000>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
I2C for SiRFprimaII platforms
|
||||
|
||||
Required properties :
|
||||
- compatible : Must be "sirf,prima2-i2c"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
|
||||
The absence of the property indicates the default frequency 100 kHz.
|
||||
|
||||
Examples :
|
||||
|
||||
i2c0: i2c@b00e0000 {
|
||||
compatible = "sirf,prima2-i2c";
|
||||
reg = <0xb00e0000 0x10000>;
|
||||
interrupts = <24>;
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
ST Microelectronics DDC I2C
|
||||
|
||||
Required properties :
|
||||
- compatible : Must be "st,ddci2c"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Optional properties:
|
||||
- Child nodes conforming to i2c bus binding
|
||||
|
||||
Examples :
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
ZTE zx2967 I2C controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "zte,zx296718-i2c"
|
||||
- reg: physical address and length of the device registers
|
||||
- interrupts: a single interrupt specifier
|
||||
- clocks: clock for the device
|
||||
- #address-cells: should be <1>
|
||||
- #size-cells: should be <0>
|
||||
- clock-frequency: the desired I2C bus clock frequency.
|
||||
|
||||
Examples:
|
||||
|
||||
i2c@112000 {
|
||||
compatible = "zte,zx296718-i2c";
|
||||
reg = <0x00112000 0x1000>;
|
||||
interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&osc24m>;
|
||||
#address-cells = <1>
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <1600000>;
|
||||
};
|
||||
|
|
@ -18,21 +18,14 @@ properties:
|
|||
- const: allwinner,sun4i-a10-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun8i-a23-i2c
|
||||
- enum:
|
||||
- allwinner,sun8i-a23-i2c
|
||||
- allwinner,sun8i-a83t-i2c
|
||||
- allwinner,sun50i-a64-i2c
|
||||
- allwinner,sun50i-a100-i2c
|
||||
- allwinner,sun50i-h6-i2c
|
||||
- allwinner,sun50i-h616-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun50i-a100-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun50i-h6-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
|
||||
- const: marvell,mv64xxx-i2c
|
||||
- const: marvell,mv78230-i2c
|
||||
- const: marvell,mv78230-a0-i2c
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ Required properties:
|
|||
"renesas,i2c-r8a77980" if the device is a part of a R8A77980 SoC.
|
||||
"renesas,i2c-r8a77990" if the device is a part of a R8A77990 SoC.
|
||||
"renesas,i2c-r8a77995" if the device is a part of a R8A77995 SoC.
|
||||
"renesas,i2c-r8a779a0" if the device is a part of a R8A779A0 SoC.
|
||||
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
|
||||
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 or RZ/G1 compatible
|
||||
device.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
* ROHM BD9571MWV Power Management Integrated Circuit (PMIC) bindings
|
||||
* ROHM BD9571MWV/BD9574MWF Power Management Integrated Circuit (PMIC) bindings
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "rohm,bd9571mwv".
|
||||
- compatible : Should be "rohm,bd9571mwv" or "rohm,bd9574mwf".
|
||||
- reg : I2C slave address.
|
||||
- interrupts : The interrupt line the device is connected to.
|
||||
- interrupt-controller : Marks the device node as an interrupt controller.
|
||||
|
|
|
|||
65
Documentation/devicetree/bindings/mfd/ene-kb930.yaml
Normal file
65
Documentation/devicetree/bindings/mfd/ene-kb930.yaml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/ene-kb930.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ENE KB930 Embedded Controller bindings
|
||||
|
||||
description: |
|
||||
This binding describes the ENE KB930 Embedded Controller attached to an
|
||||
I2C bus.
|
||||
|
||||
maintainers:
|
||||
- Dmitry Osipenko <digetx@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- acer,a500-iconia-ec # Acer A500 Iconia tablet device
|
||||
- const: ene,kb930
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
monitored-battery: true
|
||||
power-supplies: true
|
||||
system-power-controller: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
battery: battery-cell {
|
||||
compatible = "simple-battery";
|
||||
charge-full-design-microamp-hours = <3260000>;
|
||||
energy-full-design-microwatt-hours = <24000000>;
|
||||
operating-range-celsius = <0 40>;
|
||||
};
|
||||
|
||||
mains: ac-adapter {
|
||||
compatible = "gpio-charger";
|
||||
charger-type = "mains";
|
||||
gpios = <&gpio 125 0>;
|
||||
};
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
embedded-controller@58 {
|
||||
compatible = "acer,a500-iconia-ec", "ene,kb930";
|
||||
reg = <0x58>;
|
||||
|
||||
system-power-controller;
|
||||
|
||||
monitored-battery = <&battery>;
|
||||
power-supplies = <&mains>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -83,8 +83,9 @@ properties:
|
|||
2 - scaled voltage based on an optional resistor divider
|
||||
and optional offset
|
||||
3 - pre-scaled 16-bit voltage value
|
||||
4 - fan tach input to report RPM's
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1, 2, 3]
|
||||
enum: [0, 1, 2, 3, 4]
|
||||
|
||||
gw,voltage-divider-ohms:
|
||||
description: Values of resistors for divider on raw ADC input
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ examples:
|
|||
pwmleds {
|
||||
compatible = "pwm-leds";
|
||||
|
||||
panel {
|
||||
led-1 {
|
||||
pwms = <&iqs620a_pwm 0 1000000>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ properties:
|
|||
- const: allwinner,sun9i-a80-mmc
|
||||
- const: allwinner,sun50i-a64-emmc
|
||||
- const: allwinner,sun50i-a64-mmc
|
||||
- const: allwinner,sun50i-a100-emmc
|
||||
- const: allwinner,sun50i-a100-mmc
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-mmc
|
||||
- const: allwinner,sun7i-a20-mmc
|
||||
|
|
@ -47,6 +49,12 @@ properties:
|
|||
- items:
|
||||
- const: allwinner,sun50i-h6-mmc
|
||||
- const: allwinner,sun50i-a64-mmc
|
||||
- items:
|
||||
- const: allwinner,sun50i-h616-emmc
|
||||
- const: allwinner,sun50i-a100-emmc
|
||||
- items:
|
||||
- const: allwinner,sun50i-h616-mmc
|
||||
- const: allwinner,sun50i-a100-mmc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
223
Documentation/devicetree/bindings/mmc/arm,pl18x.yaml
Normal file
223
Documentation/devicetree/bindings/mmc/arm,pl18x.yaml
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/arm,pl18x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM PrimeCell MultiMedia Card Interface (MMCI) PL180 and PL181
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
description:
|
||||
The ARM PrimeCells MMCI PL180 and PL181 provides an interface for
|
||||
reading and writing to MultiMedia and SD cards alike. Over the years
|
||||
vendors have use the VHDL code from ARM to create derivative MMC/SD/SDIO
|
||||
host controllers with very similar characteristics.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/arm/primecell.yaml#
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
# We need a select here so we don't match all nodes with 'arm,primecell'
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- arm,pl180
|
||||
- arm,pl181
|
||||
- arm,pl18x
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- description: The first version of the block, simply called
|
||||
PL180 and found in the ARM Integrator IM/PD1 logic module.
|
||||
items:
|
||||
- const: arm,pl180
|
||||
- const: arm,primecell
|
||||
- description: The improved version of the block, found in the
|
||||
ARM Versatile and later reference designs. Further revisions
|
||||
exist but get detected at runtime by reading some magic numbers
|
||||
in the PrimeCell ID registers.
|
||||
items:
|
||||
- const: arm,pl181
|
||||
- const: arm,primecell
|
||||
- description: Wildcard entry that will let the operating system
|
||||
inspect the PrimeCell ID registers to determine which hardware
|
||||
variant of PL180 or PL181 this is.
|
||||
items:
|
||||
- const: arm,pl18x
|
||||
- const: arm,primecell
|
||||
|
||||
clocks:
|
||||
description: One or two clocks, the "apb_pclk" and the "MCLK"
|
||||
which is the core block clock. The names are not compulsory.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
power-domains: true
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
description: the MMIO memory window must be exactly 4KB (0x1000) and the
|
||||
layout should provide the PrimeCell ID registers so that the device can
|
||||
be discovered. On ST Micro variants, a second register window may be
|
||||
defined if a delay block is present and used for tuning.
|
||||
|
||||
interrupts:
|
||||
description: The first interrupt is the command interrupt and corresponds
|
||||
to the event at the end of a command. The second interrupt is the
|
||||
PIO (polled I/O) interrupt and occurs when the FIFO needs to be
|
||||
emptied as part of a bulk read from the card. Some variants have these
|
||||
two interrupts wired into the same line (logic OR) and in that case
|
||||
only one interrupt may be provided.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
st,sig-dir-dat0:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[0].
|
||||
|
||||
st,sig-dir-dat2:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[2].
|
||||
|
||||
st,sig-dir-dat31:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[3] and DAT[1].
|
||||
|
||||
st,sig-dir-dat74:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, bus signal direction pins used for
|
||||
DAT[7] and DAT[4].
|
||||
|
||||
st,sig-dir-cmd:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, CMD signal direction used for
|
||||
pin CMD.
|
||||
|
||||
st,sig-pin-fbclk:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, feedback clock FBCLK signal pin
|
||||
in use.
|
||||
|
||||
st,sig-dir:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, signal direction polarity used for
|
||||
pins CMD, DAT[0], DAT[1], DAT[2] and DAT[3].
|
||||
|
||||
st,neg-edge:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, data and command phase relation,
|
||||
generated on the sd clock falling edge.
|
||||
|
||||
st,use-ckin:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: ST Micro-specific property, use CKIN pin from an external
|
||||
driver to sample the receive data (for example with a voltage switch
|
||||
transceiver).
|
||||
|
||||
st,cmd-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
The GPIO matching the CMD pin.
|
||||
|
||||
st,ck-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
The GPIO matching the CK pin.
|
||||
|
||||
st,ckin-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
The GPIO matching the CKIN pin.
|
||||
|
||||
dependencies:
|
||||
st,cmd-gpios: [ "st,use-ckin" ]
|
||||
st,ck-gpios: [ "st,use-ckin" ]
|
||||
st,ckin-gpios: [ "st,use-ckin" ]
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
mmc@5000 {
|
||||
compatible = "arm,pl180", "arm,primecell";
|
||||
reg = <0x5000 0x1000>;
|
||||
interrupts-extended = <&vic 22 &sic 1>;
|
||||
clocks = <&xtal24mhz>, <&pclk>;
|
||||
clock-names = "mclk", "apb_pclk";
|
||||
};
|
||||
|
||||
mmc@80126000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
reg = <0x80126000 0x1000>;
|
||||
interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&dma 29 0 0x2>, <&dma 29 0 0x0>;
|
||||
dma-names = "rx", "tx";
|
||||
clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>;
|
||||
clock-names = "sdi", "apb_pclk";
|
||||
max-frequency = <100000000>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
cap-mmc-highspeed;
|
||||
cd-gpios = <&gpio2 31 0x4>;
|
||||
st,sig-dir-dat0;
|
||||
st,sig-dir-dat2;
|
||||
st,sig-dir-cmd;
|
||||
st,sig-pin-fbclk;
|
||||
vmmc-supply = <&ab8500_ldo_aux3_reg>;
|
||||
vqmmc-supply = <&vmmci>;
|
||||
};
|
||||
|
||||
mmc@101f6000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
reg = <0x101f6000 0x1000>;
|
||||
clocks = <&sdiclk>, <&pclksdi>;
|
||||
clock-names = "mclk", "apb_pclk";
|
||||
interrupt-parent = <&vica>;
|
||||
interrupts = <22>;
|
||||
max-frequency = <400000>;
|
||||
bus-width = <4>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
full-pwr-cycle;
|
||||
st,sig-dir-dat0;
|
||||
st,sig-dir-dat2;
|
||||
st,sig-dir-dat31;
|
||||
st,sig-dir-cmd;
|
||||
st,sig-pin-fbclk;
|
||||
vmmc-supply = <&vmmc_regulator>;
|
||||
};
|
||||
|
||||
mmc@52007000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
arm,primecell-periphid = <0x10153180>;
|
||||
reg = <0x52007000 0x1000>;
|
||||
interrupts = <49>;
|
||||
interrupt-names = "cmd_irq";
|
||||
clocks = <&rcc 0>;
|
||||
clock-names = "apb_pclk";
|
||||
resets = <&rcc 1>;
|
||||
cap-sd-highspeed;
|
||||
cap-mmc-highspeed;
|
||||
max-frequency = <120000000>;
|
||||
};
|
||||
|
|
@ -12,6 +12,7 @@ Required Properties:
|
|||
- "marvell,armada-3700-sdhci": For controllers on Armada-3700 SoC.
|
||||
Must provide a second register area and marvell,pad-type.
|
||||
- "marvell,armada-ap806-sdhci": For controllers on Armada AP806.
|
||||
- "marvell,armada-ap807-sdhci": For controllers on Armada AP807.
|
||||
- "marvell,armada-cp110-sdhci": For controllers on Armada CP110.
|
||||
|
||||
- clocks:
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1
|
||||
|
||||
The ARM PrimeCell MMCI PL180 and PL181 provides an interface for
|
||||
reading and writing to MultiMedia and SD cards alike.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the mmci driver. Using "st" as
|
||||
the prefix for a property, indicates support by the ST Micro variant.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains "arm,pl18x", "arm,primecell".
|
||||
- vmmc-supply : phandle to the regulator device tree node, mentioned
|
||||
as the VCC/VDD supply in the eMMC/SD specs.
|
||||
|
||||
Optional properties:
|
||||
- arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides
|
||||
the ID provided by the HW
|
||||
- resets : phandle to internal reset line.
|
||||
Should be defined for sdmmc variant.
|
||||
- vqmmc-supply : phandle to the regulator device tree node, mentioned
|
||||
as the VCCQ/VDD_IO supply in the eMMC/SD specs.
|
||||
specific for ux500 variant:
|
||||
- st,sig-dir-dat0 : bus signal direction pin used for DAT[0].
|
||||
- st,sig-dir-dat2 : bus signal direction pin used for DAT[2].
|
||||
- st,sig-dir-dat31 : bus signal direction pin used for DAT[3] and DAT[1].
|
||||
- st,sig-dir-dat74 : bus signal direction pin used for DAT[4] to DAT[7].
|
||||
- st,sig-dir-cmd : cmd signal direction pin used for CMD.
|
||||
- st,sig-pin-fbclk : feedback clock signal pin used.
|
||||
|
||||
specific for sdmmc variant:
|
||||
- reg : a second base register may be defined if a delay
|
||||
block is present and used for tuning.
|
||||
- st,sig-dir : signal direction polarity used for cmd, dat0 dat123.
|
||||
- st,neg-edge : data & command phase relation, generated on
|
||||
sd clock falling edge.
|
||||
- st,use-ckin : use ckin pin from an external driver to sample
|
||||
the receive data (example: with voltage
|
||||
switch transceiver).
|
||||
|
||||
Deprecated properties:
|
||||
- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable.
|
||||
- mmc-cap-sd-highspeed : indicates whether SD is high speed capable.
|
||||
|
||||
Example:
|
||||
|
||||
sdi0_per1@80126000 {
|
||||
compatible = "arm,pl18x", "arm,primecell";
|
||||
reg = <0x80126000 0x1000>;
|
||||
interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
dmas = <&dma 29 0 0x2>, /* Logical - DevToMem */
|
||||
<&dma 29 0 0x0>; /* Logical - MemToDev */
|
||||
dma-names = "rx", "tx";
|
||||
|
||||
clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>;
|
||||
clock-names = "sdi", "apb_pclk";
|
||||
|
||||
max-frequency = <100000000>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
cap-mmc-highspeed;
|
||||
cd-gpios = <&gpio2 31 0x4>; // 95
|
||||
st,sig-dir-dat0;
|
||||
st,sig-dir-dat2;
|
||||
st,sig-dir-cmd;
|
||||
st,sig-pin-fbclk;
|
||||
|
||||
vmmc-supply = <&ab8500_ldo_aux3_reg>;
|
||||
vqmmc-supply = <&vmmci>;
|
||||
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&sdi0_default_mode>;
|
||||
pinctrl-1 = <&sdi0_sleep_mode>;
|
||||
};
|
||||
|
|
@ -59,6 +59,7 @@ properties:
|
|||
- renesas,sdhi-r8a77980 # R-Car V3H
|
||||
- renesas,sdhi-r8a77990 # R-Car E3
|
||||
- renesas,sdhi-r8a77995 # R-Car D3
|
||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -15,12 +15,19 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,am654-sdhci-5.1
|
||||
- ti,j721e-sdhci-8bit
|
||||
- ti,j721e-sdhci-4bit
|
||||
- ti,j7200-sdhci-8bit
|
||||
- ti,j721e-sdhci-4bit
|
||||
oneOf:
|
||||
- const: ti,am654-sdhci-5.1
|
||||
- const: ti,j721e-sdhci-8bit
|
||||
- const: ti,j721e-sdhci-4bit
|
||||
- const: ti,j721e-sdhci-4bit
|
||||
- const: ti,am64-sdhci-8bit
|
||||
- const: ti,am64-sdhci-4bit
|
||||
- items:
|
||||
- const: ti,j7200-sdhci-8bit
|
||||
- const: ti,j721e-sdhci-8bit
|
||||
- items:
|
||||
- const: ti,j7200-sdhci-4bit
|
||||
- const: ti,j721e-sdhci-4bit
|
||||
|
||||
reg:
|
||||
maxItems: 2
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@ Required properties:
|
|||
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
|
||||
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sdx55-sdhci", "qcom,sdhci-msm-v5";
|
||||
"qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
|
||||
NOTE that some old device tree files may be floating around that only
|
||||
have the string "qcom,sdhci-msm-v4" without the SoC compatible string
|
||||
but doing that should be considered a deprecated practice.
|
||||
|
|
@ -30,10 +31,12 @@ Required properties:
|
|||
- SD Core register map (required for controllers earlier than msm-v5)
|
||||
- CQE register map (Optional, CQE support is present on SDHC instance meant
|
||||
for eMMC and version v4.2 and above)
|
||||
- Inline Crypto Engine register map (optional)
|
||||
- reg-names: When CQE register map is supplied, below reg-names are required
|
||||
- "hc" for Host controller register map
|
||||
- "core" for SD core register map
|
||||
- "cqhci" for CQE register map
|
||||
- "ice" for Inline Crypto Engine register map (optional)
|
||||
- interrupts: Should contain an interrupt-specifiers for the interrupts:
|
||||
- Host controller interrupt (required)
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
|
@ -46,6 +49,7 @@ Required properties:
|
|||
"xo" - TCXO clock (optional)
|
||||
"cal" - reference clock for RCLK delay calibration (optional)
|
||||
"sleep" - sleep clock for RCLK delay calibration (optional)
|
||||
"ice" - clock for Inline Crypto Engine (optional)
|
||||
|
||||
- qcom,ddr-config: Certain chipsets and platforms require particular settings
|
||||
for the DDR_CONFIG register. Use this field to specify the register
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
* SiRFprimII/marco/atlas6 SDHCI Controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-sirf driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: sirf,prima2-sdhc
|
||||
|
||||
Optional properties:
|
||||
- cd-gpios: card detect gpio, with zero flags.
|
||||
|
||||
Example:
|
||||
|
||||
sd0: sdhci@56000000 {
|
||||
compatible = "sirf,prima2-sdhc";
|
||||
reg = <0xcd000000 0x100000>;
|
||||
cd-gpios = <&gpio 6 0>;
|
||||
};
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
* ZTE specific extensions to the Synopsys Designware Mobile Storage
|
||||
Host Controller
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsys dw mshc controller properties described
|
||||
by synopsys-dw-mshc.txt and the properties used by the ZTE specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "zte,zx296718-dw-mshc": for ZX SoCs
|
||||
|
||||
Example:
|
||||
|
||||
mmc1: mmc@1110000 {
|
||||
compatible = "zte,zx296718-dw-mshc";
|
||||
reg = <0x01110000 0x1000>;
|
||||
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
|
||||
fifo-depth = <32>;
|
||||
data-addr = <0x200>;
|
||||
fifo-watermark-aligned;
|
||||
bus-width = <4>;
|
||||
clock-frequency = <50000000>;
|
||||
clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>;
|
||||
clock-names = "biu", "ciu";
|
||||
max-frequency = <50000000>;
|
||||
cap-sdio-irq;
|
||||
cap-sd-highspeed;
|
||||
};
|
||||
|
|
@ -35,6 +35,7 @@ Optional properties:
|
|||
- interrupts: Interrupt specifiers for two interrupt sources.
|
||||
- First interrupt specifier is for 'irq1' interrupt.
|
||||
- Second interrupt specifier is for 'alert' interrupt.
|
||||
- charger-supply: regulator node for charging current.
|
||||
- max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs.
|
||||
- max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs.
|
||||
- max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ Required properties:
|
|||
- compatible: "microchip,mcp16502"
|
||||
- reg: I2C slave address
|
||||
- lpm-gpios: GPIO for LPM pin. Note that this GPIO *must* remain high during
|
||||
suspend-to-ram, keeping the PMIC into HIBERNATE mode.
|
||||
suspend-to-ram, keeping the PMIC into HIBERNATE mode; this
|
||||
property is optional;
|
||||
- regulators: A node that houses a sub-node for each regulator within
|
||||
the device. Each sub-node is identified using the node's
|
||||
name. The content of each sub-node is defined by the
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/mt6315-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Mediatek MT6315 Regulator
|
||||
|
||||
maintainers:
|
||||
- Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>
|
||||
|
||||
description: |
|
||||
The MT6315 is a power management IC (PMIC) configurable with SPMI.
|
||||
that contains 4 BUCKs output which can combine with each other
|
||||
by different efuse settings.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mediatek,mt6315-regulator
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
description: List of regulators and its properties
|
||||
|
||||
patternProperties:
|
||||
"^vbuck[1-4]$":
|
||||
type: object
|
||||
$ref: "regulator.yaml#"
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^vbuck[1-4]$"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
pmic@6 {
|
||||
compatible = "mediatek,mt6315-regulator";
|
||||
reg = <0x6 0>;
|
||||
|
||||
regulators {
|
||||
vbuck1 {
|
||||
regulator-compatible = "vbuck1";
|
||||
regulator-min-microvolt = <300000>;
|
||||
regulator-max-microvolt = <1193750>;
|
||||
regulator-enable-ramp-delay = <256>;
|
||||
regulator-allowed-modes = <0 1 2 4>;
|
||||
};
|
||||
|
||||
vbuck3 {
|
||||
regulator-compatible = "vbuck3";
|
||||
regulator-min-microvolt = <300000>;
|
||||
regulator-max-microvolt = <1193750>;
|
||||
regulator-enable-ramp-delay = <256>;
|
||||
regulator-allowed-modes = <0 1 2 4>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -87,6 +87,11 @@ properties:
|
|||
|
||||
additionalProperties: false
|
||||
|
||||
sd-vsel-gpios:
|
||||
description: GPIO that is used to switch LDO5 between being configured by
|
||||
LDO5CTRL_L or LDO5CTRL_H register. Use this if the SD_VSEL signal is
|
||||
connected to a host GPIO.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -62,8 +62,11 @@ properties:
|
|||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
minimum: 2100
|
||||
maximum: 4500
|
||||
deprecated: true
|
||||
description:
|
||||
BUCK regulators current limit in mA.
|
||||
This property is deprecated, please use
|
||||
"regulator-max-microamp" instead.
|
||||
|
||||
Listed current limits in mA are,
|
||||
2100 (default)
|
||||
|
|
@ -73,21 +76,11 @@ properties:
|
|||
|
||||
nxp,phase-shift:
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
minimum: 45
|
||||
maximum: 0
|
||||
default: 0
|
||||
enum: [ 0, 45, 90, 135, 180, 225, 270, 315 ]
|
||||
description:
|
||||
BUCK regulators phase shift control in degrees.
|
||||
|
||||
Listed phase shift control values in degrees are,
|
||||
45
|
||||
90
|
||||
135
|
||||
180
|
||||
225
|
||||
270
|
||||
315
|
||||
0 (default)
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^vsnvs$":
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ First Level Nodes - PMIC
|
|||
"qcom,pm8350-rpmh-regulators"
|
||||
"qcom,pm8350c-rpmh-regulators"
|
||||
"qcom,pm8998-rpmh-regulators"
|
||||
"qcom,pmc8180-rpmh-regulators"
|
||||
"qcom,pmc8180c-rpmh-regulators"
|
||||
"qcom,pmi8998-rpmh-regulators"
|
||||
"qcom,pm6150-rpmh-regulators"
|
||||
"qcom,pm6150l-rpmh-regulators"
|
||||
|
|
|
|||
|
|
@ -22,11 +22,17 @@ properties:
|
|||
type: object
|
||||
|
||||
properties:
|
||||
qcom,soft-start-us:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Regulator soft start time in microseconds.
|
||||
enum: [200, 400, 600, 800]
|
||||
default: 200
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
Short-circuit interrupt for lab.
|
||||
Short-circuit and over-current interrupts for lab.
|
||||
|
||||
required:
|
||||
- interrupts
|
||||
|
|
@ -35,11 +41,17 @@ properties:
|
|||
type: object
|
||||
|
||||
properties:
|
||||
qcom,discharge-resistor-kohms:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Discharge resistor value in KiloOhms.
|
||||
enum: [300, 64, 32, 16]
|
||||
default: 300
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
Short-circuit interrupt for lab.
|
||||
Short-circuit and over-current interrupts for ibb.
|
||||
|
||||
required:
|
||||
- interrupts
|
||||
|
|
@ -57,13 +69,15 @@ examples:
|
|||
compatible = "qcom,pmi8998-lab-ibb";
|
||||
|
||||
lab {
|
||||
interrupts = <0x3 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "sc-err";
|
||||
interrupts = <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>,
|
||||
<0x3 0xde 0x0 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "sc-err", "ocp";
|
||||
};
|
||||
|
||||
ibb {
|
||||
interrupts = <0x3 0x2 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "sc-err";
|
||||
interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>,
|
||||
<0x3 0xdc 0x0 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "sc-err", "ocp";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/richtek,rt4831-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Richtek RT4831 Display Bias Voltage Regulator
|
||||
|
||||
maintainers:
|
||||
- ChiYuan Huang <cy_huang@richtek.com>
|
||||
|
||||
description: |
|
||||
RT4831 is a multifunctional device that can provide power to the LCD display
|
||||
and LCD backlight.
|
||||
|
||||
For Display Bias Voltage DSVP and DSVN, the output range is about 4V to 6.5V.
|
||||
It is sufficient to meet the current LCD power requirement.
|
||||
|
||||
DSVLCM is a boost regulator in IC internal as DSVP and DSVN input power.
|
||||
Its voltage should be configured above 0.15V to 0.2V gap larger than the
|
||||
voltage needed for DSVP and DSVN. Too much voltage gap could improve the
|
||||
voltage drop from the heavy loading scenario. But it also make the power
|
||||
efficiency worse. It's a trade-off.
|
||||
|
||||
Datasheet is available at
|
||||
https://www.richtek.com/assets/product_file/RT4831A/DS4831A-05.pdf
|
||||
|
||||
patternProperties:
|
||||
"^DSV(LCM|P|N)$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single Display Bias Voltage regulator.
|
||||
|
||||
additionalProperties: false
|
||||
|
|
@ -25,6 +25,7 @@ properties:
|
|||
- enum:
|
||||
- allwinner,sun8i-r40-spi
|
||||
- allwinner,sun50i-h6-spi
|
||||
- allwinner,sun50i-h616-spi
|
||||
- const: allwinner,sun8i-h3-spi
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Required properties:
|
|||
Generic default - "cdns,qspi-nor".
|
||||
For TI 66AK2G SoC - "ti,k2g-qspi", "cdns,qspi-nor".
|
||||
For TI AM654 SoC - "ti,am654-ospi", "cdns,qspi-nor".
|
||||
For Intel LGM SoC - "intel,lgm-qspi", "cdns,qspi-nor".
|
||||
- reg : Contains two entries, each of which is a tuple consisting of a
|
||||
physical address and length. The first entry is the address and
|
||||
length of the controller register set. The second entry is the
|
||||
117
Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml
Normal file
117
Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/nvidia,tegra210-quad.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Tegra Quad SPI Controller
|
||||
|
||||
maintainers:
|
||||
- Thierry Reding <thierry.reding@gmail.com>
|
||||
- Jonathan Hunter <jonathanh@nvidia.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nvidia,tegra210-qspi
|
||||
- nvidia,tegra186-qspi
|
||||
- nvidia,tegra194-qspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: qspi
|
||||
- const: qspi_out
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
patternProperties:
|
||||
"@[0-9a-f]+":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
spi-rx-bus-width:
|
||||
enum: [1, 2, 4]
|
||||
|
||||
spi-tx-bus-width:
|
||||
enum: [1, 2, 4]
|
||||
|
||||
nvidia,tx-clk-tap-delay:
|
||||
description:
|
||||
Delays the clock going out to device with this tap value.
|
||||
Tap value varies based on platform design trace lengths from Tegra
|
||||
QSPI to corresponding slave device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 31
|
||||
|
||||
nvidia,rx-clk-tap-delay:
|
||||
description:
|
||||
Delays the clock coming in from the device with this tap value.
|
||||
Tap value varies based on platform design trace lengths from Tegra
|
||||
QSPI to corresponding slave device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clock-names
|
||||
- clocks
|
||||
- resets
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/tegra210-car.h>
|
||||
#include <dt-bindings/reset/tegra210-car.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
spi@70410000 {
|
||||
compatible = "nvidia,tegra210-qspi";
|
||||
reg = <0x70410000 0x1000>;
|
||||
interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&tegra_car TEGRA210_CLK_QSPI>,
|
||||
<&tegra_car TEGRA210_CLK_QSPI_PM>;
|
||||
clock-names = "qspi", "qspi_out";
|
||||
resets = <&tegra_car 211>;
|
||||
dmas = <&apbdma 5>, <&apbdma 5>;
|
||||
dma-names = "rx", "tx";
|
||||
|
||||
flash@0 {
|
||||
compatible = "spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <104000000>;
|
||||
spi-tx-bus-width = <2>;
|
||||
spi-rx-bus-width = <2>;
|
||||
nvidia,tx-clk-tap-delay = <0>;
|
||||
nvidia,rx-clk-tap-delay = <0>;
|
||||
};
|
||||
};
|
||||
41
Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml
Normal file
41
Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/realtek,rtl-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek RTL838x/RTL839x SPI controller
|
||||
|
||||
maintainers:
|
||||
- Bert Vermeulen <bert@biot.com>
|
||||
- Birger Koblitz <mail@birger-koblitz.de>
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: realtek,rtl8380-spi
|
||||
- const: realtek,rtl8382-spi
|
||||
- const: realtek,rtl8391-spi
|
||||
- const: realtek,rtl8392-spi
|
||||
- const: realtek,rtl8393-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi: spi@1200 {
|
||||
compatible = "realtek,rtl8382-spi";
|
||||
reg = <0x1200 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
|
@ -47,6 +47,7 @@ properties:
|
|||
- renesas,msiof-r8a77980 # R-Car V3H
|
||||
- renesas,msiof-r8a77990 # R-Car E3
|
||||
- renesas,msiof-r8a77995 # R-Car D3
|
||||
- renesas,msiof-r8a779a0 # R-Car V3U
|
||||
- const: renesas,rcar-gen3-msiof # generic R-Car Gen3 and RZ/G2
|
||||
# compatible device
|
||||
- items:
|
||||
|
|
|
|||
|
|
@ -152,8 +152,9 @@ patternProperties:
|
|||
spi-rx-bus-width:
|
||||
description:
|
||||
Bus width to the SPI bus used for read transfers.
|
||||
If 0 is provided, then no RX will be possible on this device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [1, 2, 4, 8]
|
||||
enum: [0, 1, 2, 4, 8]
|
||||
default: 1
|
||||
|
||||
spi-rx-delay-us:
|
||||
|
|
@ -163,8 +164,9 @@ patternProperties:
|
|||
spi-tx-bus-width:
|
||||
description:
|
||||
Bus width to the SPI bus used for write transfers.
|
||||
If 0 is provided, then no TX will be possible on this device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [1, 2, 4, 8]
|
||||
enum: [0, 1, 2, 4, 8]
|
||||
default: 1
|
||||
|
||||
spi-tx-delay-us:
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
* CSR SiRFprimaII Serial Peripheral Interface
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "sirf,prima2-spi", "sirf,prima2-usp"
|
||||
or "sirf,atlas7-usp"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain SPI interrupt
|
||||
- resets: phandle to the reset controller asserting this device in
|
||||
reset
|
||||
See ../reset/reset.txt for details.
|
||||
- dmas : Must contain an entry for each entry in clock-names.
|
||||
See ../dma/dma.txt for details.
|
||||
- dma-names : Must include the following entries:
|
||||
- rx
|
||||
- tx
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
|
||||
- #address-cells: Number of cells required to define a chip select
|
||||
address on the SPI bus. Should be set to 1.
|
||||
- #size-cells: Should be zero.
|
||||
|
||||
Optional properties:
|
||||
- spi-max-frequency: Specifies maximum SPI clock frequency,
|
||||
Units - Hz. Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- cs-gpios: should specify GPIOs used for chipselects.
|
||||
|
||||
Example:
|
||||
|
||||
spi0: spi@b00d0000 {
|
||||
compatible = "sirf,prima2-spi";
|
||||
reg = <0xb00d0000 0x10000>;
|
||||
interrupts = <15>;
|
||||
dmas = <&dmac1 9>,
|
||||
<&dmac1 4>;
|
||||
dma-names = "rx", "tx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&clks 19>;
|
||||
resets = <&rstc 26>;
|
||||
};
|
||||
153
Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml
Normal file
153
Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/qcom-spmi-adc-tm5.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm's SPMI PMIC ADC Thermal Monitoring
|
||||
maintainers:
|
||||
- Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,spmi-adc-tm5
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
description:
|
||||
Number of cells required to uniquely identify the thermal sensors. Since
|
||||
we have multiple sensors this is set to 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
qcom,avg-samples:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Number of samples to be used for measurement.
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 8
|
||||
- 16
|
||||
default: 1
|
||||
|
||||
qcom,decimation:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: This parameter is used to decrease ADC sampling rate.
|
||||
Quicker measurements can be made by reducing decimation ratio.
|
||||
enum:
|
||||
- 250
|
||||
- 420
|
||||
- 840
|
||||
default: 840
|
||||
|
||||
patternProperties:
|
||||
"^([-a-z0-9]*)@[0-7]$":
|
||||
type: object
|
||||
description:
|
||||
Represent one thermal sensor.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Specify the sensor channel. There are 8 channels in PMIC5's ADC TM
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
|
||||
io-channels:
|
||||
description:
|
||||
From common IIO binding. Used to pipe PMIC ADC channel to thermal monitor
|
||||
|
||||
qcom,ratiometric:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Channel calibration type.
|
||||
If this property is specified VADC will use the VDD reference
|
||||
(1.875V) and GND for channel calibration. If property is not found,
|
||||
channel will be calibrated with 0V and 1.25V reference channels,
|
||||
also known as absolute calibration.
|
||||
|
||||
qcom,hw-settle-time-us:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Time between AMUX getting configured and the ADC starting conversion.
|
||||
enum: [15, 100, 200, 300, 400, 500, 600, 700, 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000]
|
||||
|
||||
qcom,pre-scaling:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: Used for scaling the channel input signal before the
|
||||
signal is fed to VADC. The configuration for this node is to know the
|
||||
pre-determined ratio and use it for post scaling. It is a pair of
|
||||
integers, denoting the numerator and denominator of the fraction by
|
||||
which input signal is multiplied. For example, <1 3> indicates the
|
||||
signal is scaled down to 1/3 of its value before ADC measurement. If
|
||||
property is not found default value depending on chip will be used.
|
||||
items:
|
||||
- const: 1
|
||||
- enum: [ 1, 3, 4, 6, 20, 8, 10 ]
|
||||
|
||||
required:
|
||||
- reg
|
||||
- io-channels
|
||||
|
||||
additionalProperties:
|
||||
false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- "#thermal-sensor-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spmi_bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pm8150b_adc: adc@3100 {
|
||||
reg = <0x3100>;
|
||||
compatible = "qcom,spmi-adc5";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
/* Other propreties are omitted */
|
||||
conn-therm@4f {
|
||||
reg = <ADC5_AMUX_THM3_100K_PU>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
pm8150b_adc_tm: adc-tm@3500 {
|
||||
compatible = "qcom,spmi-adc-tm5";
|
||||
reg = <0x3500>;
|
||||
interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
conn-therm@0 {
|
||||
reg = <0>;
|
||||
io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time-us = <200>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
* Tango Thermal
|
||||
|
||||
The SMP8758 SoC includes 3 instances of this temperature sensor
|
||||
(in the CPU, video decoder, and PCIe controller).
|
||||
|
||||
Required properties:
|
||||
- #thermal-sensor-cells: Should be 0 (see Documentation/devicetree/bindings/thermal/thermal-sensor.yaml)
|
||||
- compatible: "sigma,smp8758-thermal"
|
||||
- reg: Address range of the thermal registers
|
||||
|
||||
Example:
|
||||
|
||||
cpu_temp: thermal@920100 {
|
||||
#thermal-sensor-cells = <0>;
|
||||
compatible = "sigma,smp8758-thermal";
|
||||
reg = <0x920100 12>;
|
||||
};
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
* ZTE zx2967 family Thermal
|
||||
|
||||
Required Properties:
|
||||
- compatible: should be one of the following.
|
||||
* zte,zx296718-thermal
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- clocks : Pairs of phandle and specifier referencing the controller's clocks.
|
||||
- clock-names: "topcrm" for the topcrm clock.
|
||||
"apb" for the apb clock.
|
||||
- #thermal-sensor-cells: must be 0.
|
||||
|
||||
Please note: slope coefficient defined in thermal-zones section need to be
|
||||
multiplied by 1000.
|
||||
|
||||
Example for tempsensor:
|
||||
|
||||
tempsensor: tempsensor@148a000 {
|
||||
compatible = "zte,zx296718-thermal";
|
||||
reg = <0x0148a000 0x20>;
|
||||
clocks = <&topcrm TEMPSENSOR_GATE>, <&audiocrm AUDIO_TS_PCLK>;
|
||||
clock-names = "topcrm", "apb";
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
|
||||
Example for cooling device:
|
||||
|
||||
cooling_dev: cooling_dev {
|
||||
cluster0_cooling_dev: cluster0-cooling-dev {
|
||||
#cooling-cells = <2>;
|
||||
cpumask = <0xf>;
|
||||
capacitance = <1500>;
|
||||
};
|
||||
|
||||
cluster1_cooling_dev: cluster1-cooling-dev {
|
||||
#cooling-cells = <2>;
|
||||
cpumask = <0x30>;
|
||||
capacitance = <2000>;
|
||||
};
|
||||
};
|
||||
|
||||
Example for thermal zones:
|
||||
|
||||
thermal-zones {
|
||||
zx296718_thermal: zx296718_thermal {
|
||||
polling-delay-passive = <500>;
|
||||
polling-delay = <1000>;
|
||||
sustainable-power = <6500>;
|
||||
|
||||
thermal-sensors = <&tempsensor 0>;
|
||||
/*
|
||||
* slope need to be multiplied by 1000.
|
||||
*/
|
||||
coefficients = <1951 (-922)>;
|
||||
|
||||
trips {
|
||||
trip0: switch_on_temperature {
|
||||
temperature = <90000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
trip1: desired_temperature {
|
||||
temperature = <100000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
crit: critical_temperature {
|
||||
temperature = <110000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&trip0>;
|
||||
cooling-device = <&gpu 2 5>;
|
||||
};
|
||||
|
||||
map1 {
|
||||
trip = <&trip0>;
|
||||
cooling-device = <&cluster0_cooling_dev 1 2>;
|
||||
};
|
||||
|
||||
map2 {
|
||||
trip = <&trip1>;
|
||||
cooling-device = <&cluster0_cooling_dev 1 2>;
|
||||
};
|
||||
|
||||
map3 {
|
||||
trip = <&crit>;
|
||||
cooling-device = <&cluster0_cooling_dev 1 2>;
|
||||
};
|
||||
|
||||
map4 {
|
||||
trip = <&trip0>;
|
||||
cooling-device = <&cluster1_cooling_dev 1 2>;
|
||||
contribution = <9000>;
|
||||
};
|
||||
|
||||
map5 {
|
||||
trip = <&trip1>;
|
||||
cooling-device = <&cluster1_cooling_dev 1 2>;
|
||||
contribution = <4096>;
|
||||
};
|
||||
|
||||
map6 {
|
||||
trip = <&crit>;
|
||||
cooling-device = <&cluster1_cooling_dev 1 2>;
|
||||
contribution = <4096>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -19,13 +19,11 @@ properties:
|
|||
- const: allwinner,sun4i-a10-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,sun50i-a100-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,sun50i-h6-wdt
|
||||
- enum:
|
||||
- allwinner,sun50i-a64-wdt
|
||||
- allwinner,sun50i-a100-wdt
|
||||
- allwinner,sun50i-h6-wdt
|
||||
- allwinner,sun50i-h616-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,suniv-f1c100s-wdt
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/intel,keembay-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel Keem Bay SoC non-secure Watchdog Timer
|
||||
|
||||
maintainers:
|
||||
- Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- intel,keembay-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt specifier for threshold interrupt line
|
||||
- description: interrupt specifier for timeout interrupt line
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: threshold
|
||||
- const: timeout
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#define KEEM_BAY_A53_TIM
|
||||
|
||||
watchdog: watchdog@2033009c {
|
||||
compatible = "intel,keembay-wdt";
|
||||
reg = <0x2033009c 0x10>;
|
||||
interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "threshold", "timeout";
|
||||
clocks = <&scmi_clk KEEM_BAY_A53_TIM>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -4,14 +4,15 @@ Required properties:
|
|||
|
||||
- compatible should contain:
|
||||
"mediatek,mt2701-wdt", "mediatek,mt6589-wdt": for MT2701
|
||||
"mediatek,mt2712-wdt", "mediatek,mt6589-wdt": for MT2712
|
||||
"mediatek,mt2712-wdt": for MT2712
|
||||
"mediatek,mt6589-wdt": for MT6589
|
||||
"mediatek,mt6797-wdt", "mediatek,mt6589-wdt": for MT6797
|
||||
"mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
|
||||
"mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623
|
||||
"mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
|
||||
"mediatek,mt8183-wdt", "mediatek,mt6589-wdt": for MT8183
|
||||
"mediatek,mt8183-wdt": for MT8183
|
||||
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
|
||||
"mediatek,mt8192-wdt": for MT8192
|
||||
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ properties:
|
|||
- renesas,r8a77980-wdt # R-Car V3H
|
||||
- renesas,r8a77990-wdt # R-Car E3
|
||||
- renesas,r8a77995-wdt # R-Car D3
|
||||
- renesas,r8a779a0-wdt # R-Car V3U
|
||||
- const: renesas,rcar-gen3-wdt # R-Car Gen3 and RZ/G2
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
Sigma Designs SMP86xx/SMP87xx watchdog
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sigma,smp8642-wdt"
|
||||
- reg: Specifies the physical address region
|
||||
- clocks: Should be a phandle to the clock
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec: watchdog timeout in seconds
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@1fd00 {
|
||||
compatible = "sigma,smp8642-wdt";
|
||||
reg = <0x1fd00 8>;
|
||||
clocks = <&xtal_in_clk>;
|
||||
timeout-sec = <30>;
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
SiRFSoC Timer and Watchdog Timer(WDT) Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "sirf,prima2-tick"
|
||||
- reg: Address range of tick timer/WDT register set
|
||||
- interrupts: interrupt number to the cpu
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec : Contains the watchdog timeout in seconds
|
||||
|
||||
Example:
|
||||
|
||||
timer@b0020000 {
|
||||
compatible = "sirf,prima2-tick";
|
||||
reg = <0xb0020000 0x1000>;
|
||||
interrupts = <0>;
|
||||
timeout-sec = <30>;
|
||||
};
|
||||
|
|
@ -18,10 +18,16 @@ properties:
|
|||
- const: snps,dw-wdt
|
||||
- items:
|
||||
- enum:
|
||||
- rockchip,px30-wdt
|
||||
- rockchip,rk3066-wdt
|
||||
- rockchip,rk3188-wdt
|
||||
- rockchip,rk3228-wdt
|
||||
- rockchip,rk3288-wdt
|
||||
- rockchip,rk3308-wdt
|
||||
- rockchip,rk3328-wdt
|
||||
- rockchip,rk3368-wdt
|
||||
- rockchip,rk3399-wdt
|
||||
- rockchip,rv1108-wdt
|
||||
- const: snps,dw-wdt
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
ST-Ericsson COH 901 327 Watchdog timer
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "stericsson,coh901327".
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: the interrupt used for the watchdog timeout warning.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec: contains the watchdog timeout in seconds.
|
||||
|
||||
Example:
|
||||
|
||||
watchdog: watchdog@c0012000 {
|
||||
compatible = "stericsson,coh901327";
|
||||
reg = <0xc0012000 0x1000>;
|
||||
interrupts = <3>;
|
||||
timeout-sec = <60>;
|
||||
};
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
ZTE zx2967 Watchdog timer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be one of the following.
|
||||
* zte,zx296718-wdt
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- clocks : Pairs of phandle and specifier referencing the controller's clocks.
|
||||
- resets : Reference to the reset controller controlling the watchdog
|
||||
controller.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- timeout-sec : Contains the watchdog timeout in seconds.
|
||||
- zte,wdt-reset-sysctrl : Directs how to reset system by the watchdog.
|
||||
if we don't want to restart system when watchdog been triggered,
|
||||
it's not required, vice versa.
|
||||
It should include following fields.
|
||||
* phandle of aon-sysctrl.
|
||||
* offset of register that be written, should be 0xb0.
|
||||
* configure value that be written to aon-sysctrl.
|
||||
* bit mask, corresponding bits will be affected.
|
||||
|
||||
Example:
|
||||
|
||||
wdt: watchdog@1465000 {
|
||||
compatible = "zte,zx296718-wdt";
|
||||
reg = <0x1465000 0x1000>;
|
||||
clocks = <&topcrm WDT_WCLK>;
|
||||
resets = <&toprst 35>;
|
||||
zte,wdt-reset-sysctrl = <&aon_sysctrl 0xb0 1 0x115>;
|
||||
};
|
||||
|
|
@ -99,6 +99,7 @@ available subsections can be seen below.
|
|||
rfkill
|
||||
serial/index
|
||||
sm501
|
||||
surface_aggregator/index
|
||||
switchtec
|
||||
sync_file
|
||||
vfio-mediated-device
|
||||
|
|
|
|||
38
Documentation/driver-api/surface_aggregator/client-api.rst
Normal file
38
Documentation/driver-api/surface_aggregator/client-api.rst
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
===============================
|
||||
Client Driver API Documentation
|
||||
===============================
|
||||
|
||||
.. contents::
|
||||
:depth: 2
|
||||
|
||||
|
||||
Serial Hub Communication
|
||||
========================
|
||||
|
||||
.. kernel-doc:: include/linux/surface_aggregator/serial_hub.h
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.c
|
||||
:export:
|
||||
|
||||
|
||||
Controller and Core Interface
|
||||
=============================
|
||||
|
||||
.. kernel-doc:: include/linux/surface_aggregator/controller.h
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/controller.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/core.c
|
||||
:export:
|
||||
|
||||
|
||||
Client Bus and Client Device API
|
||||
================================
|
||||
|
||||
.. kernel-doc:: include/linux/surface_aggregator/device.h
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/bus.c
|
||||
:export:
|
||||
393
Documentation/driver-api/surface_aggregator/client.rst
Normal file
393
Documentation/driver-api/surface_aggregator/client.rst
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
|
||||
.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
|
||||
.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
|
||||
.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
|
||||
.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
|
||||
.. |ssam_get_controller| replace:: :c:func:`ssam_get_controller`
|
||||
.. |ssam_controller_get| replace:: :c:func:`ssam_controller_get`
|
||||
.. |ssam_controller_put| replace:: :c:func:`ssam_controller_put`
|
||||
.. |ssam_device_alloc| replace:: :c:func:`ssam_device_alloc`
|
||||
.. |ssam_device_add| replace:: :c:func:`ssam_device_add`
|
||||
.. |ssam_device_remove| replace:: :c:func:`ssam_device_remove`
|
||||
.. |ssam_device_driver_register| replace:: :c:func:`ssam_device_driver_register`
|
||||
.. |ssam_device_driver_unregister| replace:: :c:func:`ssam_device_driver_unregister`
|
||||
.. |module_ssam_device_driver| replace:: :c:func:`module_ssam_device_driver`
|
||||
.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`
|
||||
.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
|
||||
.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
|
||||
.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
|
||||
.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
|
||||
|
||||
|
||||
======================
|
||||
Writing Client Drivers
|
||||
======================
|
||||
|
||||
For the API documentation, refer to:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
client-api
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Client drivers can be set up in two main ways, depending on how the
|
||||
corresponding device is made available to the system. We specifically
|
||||
differentiate between devices that are presented to the system via one of
|
||||
the conventional ways, e.g. as platform devices via ACPI, and devices that
|
||||
are non-discoverable and instead need to be explicitly provided by some
|
||||
other mechanism, as discussed further below.
|
||||
|
||||
|
||||
Non-SSAM Client Drivers
|
||||
=======================
|
||||
|
||||
All communication with the SAM EC is handled via the |ssam_controller|
|
||||
representing that EC to the kernel. Drivers targeting a non-SSAM device (and
|
||||
thus not being a |ssam_device_driver|) need to explicitly establish a
|
||||
connection/relation to that controller. This can be done via the
|
||||
|ssam_client_bind| function. Said function returns a reference to the SSAM
|
||||
controller, but, more importantly, also establishes a device link between
|
||||
client device and controller (this can also be done separate via
|
||||
|ssam_client_link|). It is important to do this, as it, first, guarantees
|
||||
that the returned controller is valid for use in the client driver for as
|
||||
long as this driver is bound to its device, i.e. that the driver gets
|
||||
unbound before the controller ever becomes invalid, and, second, as it
|
||||
ensures correct suspend/resume ordering. This setup should be done in the
|
||||
driver's probe function, and may be used to defer probing in case the SSAM
|
||||
subsystem is not ready yet, for example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static int client_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ssam_controller *ctrl;
|
||||
|
||||
ctrl = ssam_client_bind(&pdev->dev);
|
||||
if (IS_ERR(ctrl))
|
||||
return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
|
||||
|
||||
// ...
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
The controller may be separately obtained via |ssam_get_controller| and its
|
||||
lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|.
|
||||
Note that none of these functions, however, guarantee that the controller
|
||||
will not be shut down or suspended. These functions essentially only operate
|
||||
on the reference, i.e. only guarantee a bare minimum of accessibility
|
||||
without any guarantees at all on practical operability.
|
||||
|
||||
|
||||
Adding SSAM Devices
|
||||
===================
|
||||
|
||||
If a device does not already exist/is not already provided via conventional
|
||||
means, it should be provided as |ssam_device| via the SSAM client device
|
||||
hub. New devices can be added to this hub by entering their UID into the
|
||||
corresponding registry. SSAM devices can also be manually allocated via
|
||||
|ssam_device_alloc|, subsequently to which they have to be added via
|
||||
|ssam_device_add| and eventually removed via |ssam_device_remove|. By
|
||||
default, the parent of the device is set to the controller device provided
|
||||
for allocation, however this may be changed before the device is added. Note
|
||||
that, when changing the parent device, care must be taken to ensure that the
|
||||
controller lifetime and suspend/resume ordering guarantees, in the default
|
||||
setup provided through the parent-child relation, are preserved. If
|
||||
necessary, by use of |ssam_client_link| as is done for non-SSAM client
|
||||
drivers and described in more detail above.
|
||||
|
||||
A client device must always be removed by the party which added the
|
||||
respective device before the controller shuts down. Such removal can be
|
||||
guaranteed by linking the driver providing the SSAM device to the controller
|
||||
via |ssam_client_link|, causing it to unbind before the controller driver
|
||||
unbinds. Client devices registered with the controller as parent are
|
||||
automatically removed when the controller shuts down, but this should not be
|
||||
relied upon, especially as this does not extend to client devices with a
|
||||
different parent.
|
||||
|
||||
|
||||
SSAM Client Drivers
|
||||
===================
|
||||
|
||||
SSAM client device drivers are, in essence, no different than other device
|
||||
driver types. They are represented via |ssam_device_driver| and bind to a
|
||||
|ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`)
|
||||
member and the match table
|
||||
(:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`),
|
||||
which should be set when declaring the driver struct instance. Refer to the
|
||||
|SSAM_DEVICE| macro documentation for more details on how to define members
|
||||
of the driver's match table.
|
||||
|
||||
The UID for SSAM client devices consists of a ``domain``, a ``category``,
|
||||
a ``target``, an ``instance``, and a ``function``. The ``domain`` is used
|
||||
differentiate between physical SAM devices
|
||||
(:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can
|
||||
be accessed via the Surface Serial Hub, and virtual ones
|
||||
(:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device
|
||||
hubs, that have no real representation on the SAM EC and are solely used on
|
||||
the kernel/driver-side. For physical devices, ``category`` represents the
|
||||
target category, ``target`` the target ID, and ``instance`` the instance ID
|
||||
used to access the physical SAM device. In addition, ``function`` references
|
||||
a specific device functionality, but has no meaning to the SAM EC. The
|
||||
(default) name of a client device is generated based on its UID.
|
||||
|
||||
A driver instance can be registered via |ssam_device_driver_register| and
|
||||
unregistered via |ssam_device_driver_unregister|. For convenience, the
|
||||
|module_ssam_device_driver| macro may be used to define module init- and
|
||||
exit-functions registering the driver.
|
||||
|
||||
The controller associated with a SSAM client device can be found in its
|
||||
:c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is
|
||||
guaranteed to be valid for at least as long as the client driver is bound,
|
||||
but should also be valid for as long as the client device exists. Note,
|
||||
however, that access outside of the bound client driver must ensure that the
|
||||
controller device is not suspended while making any requests or
|
||||
(un-)registering event notifiers (and thus should generally be avoided). This
|
||||
is guaranteed when the controller is accessed from inside the bound client
|
||||
driver.
|
||||
|
||||
|
||||
Making Synchronous Requests
|
||||
===========================
|
||||
|
||||
Synchronous requests are (currently) the main form of host-initiated
|
||||
communication with the EC. There are a couple of ways to define and execute
|
||||
such requests, however, most of them boil down to something similar as shown
|
||||
in the example below. This example defines a write-read request, meaning
|
||||
that the caller provides an argument to the SAM EC and receives a response.
|
||||
The caller needs to know the (maximum) length of the response payload and
|
||||
provide a buffer for it.
|
||||
|
||||
Care must be taken to ensure that any command payload data passed to the SAM
|
||||
EC is provided in little-endian format and, similarly, any response payload
|
||||
data received from it is converted from little-endian to host endianness.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret)
|
||||
{
|
||||
struct ssam_request rqst;
|
||||
struct ssam_response resp;
|
||||
int status;
|
||||
|
||||
/* Convert request argument to little-endian. */
|
||||
__le32 arg_le = cpu_to_le32(arg);
|
||||
__le32 ret_le = cpu_to_le32(0);
|
||||
|
||||
/*
|
||||
* Initialize request specification. Replace this with your values.
|
||||
* The rqst.payload field may be NULL if rqst.length is zero,
|
||||
* indicating that the request does not have any argument.
|
||||
*
|
||||
* Note: The request parameters used here are not valid, i.e.
|
||||
* they do not correspond to an actual SAM/EC request.
|
||||
*/
|
||||
rqst.target_category = SSAM_SSH_TC_SAM;
|
||||
rqst.target_id = 0x01;
|
||||
rqst.command_id = 0x02;
|
||||
rqst.instance_id = 0x03;
|
||||
rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
|
||||
rqst.length = sizeof(arg_le);
|
||||
rqst.payload = (u8 *)&arg_le;
|
||||
|
||||
/* Initialize request response. */
|
||||
resp.capacity = sizeof(ret_le);
|
||||
resp.length = 0;
|
||||
resp.pointer = (u8 *)&ret_le;
|
||||
|
||||
/*
|
||||
* Perform actual request. The response pointer may be null in case
|
||||
* the request does not have any response. This must be consistent
|
||||
* with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification
|
||||
* above.
|
||||
*/
|
||||
status = ssam_request_sync(ctrl, &rqst, &resp);
|
||||
|
||||
/*
|
||||
* Alternatively use
|
||||
*
|
||||
* ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
|
||||
*
|
||||
* to perform the request, allocating the message buffer directly
|
||||
* on the stack as opposed to allocation via kzalloc().
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convert request response back to native format. Note that in the
|
||||
* error case, this value is not touched by the SSAM core, i.e.
|
||||
* 'ret_le' will be zero as specified in its initialization.
|
||||
*/
|
||||
*ret = le32_to_cpu(ret_le);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Note that |ssam_request_sync| in its essence is a wrapper over lower-level
|
||||
request primitives, which may also be used to perform requests. Refer to its
|
||||
implementation and documentation for more details.
|
||||
|
||||
An arguably more user-friendly way of defining such functions is by using
|
||||
one of the generator macros, for example via:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, {
|
||||
.target_category = SSAM_SSH_TC_TMP,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x03,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
This example defines a function
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
|
||||
|
||||
executing the specified request, with the controller passed in when calling
|
||||
said function. In this example, the argument is provided via the ``arg``
|
||||
pointer. Note that the generated function allocates the message buffer on
|
||||
the stack. Thus, if the argument provided via the request is large, these
|
||||
kinds of macros should be avoided. Also note that, in contrast to the
|
||||
previous non-macro example, this function does not do any endianness
|
||||
conversion, which has to be handled by the caller. Apart from those
|
||||
differences the function generated by the macro is similar to the one
|
||||
provided in the non-macro example above.
|
||||
|
||||
The full list of such function-generating macros is
|
||||
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and
|
||||
without argument.
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no
|
||||
argument.
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but
|
||||
with argument.
|
||||
|
||||
Refer to their respective documentation for more details. For each one of
|
||||
these macros, a special variant is provided, which targets request types
|
||||
applicable to multiple instances of the same device type:
|
||||
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N`
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R`
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W`
|
||||
|
||||
The difference of those macros to the previously mentioned versions is, that
|
||||
the device target and instance IDs are not fixed for the generated function,
|
||||
but instead have to be provided by the caller of said function.
|
||||
|
||||
Additionally, variants for direct use with client devices, i.e.
|
||||
|ssam_device|, are also provided. These can, for example, be used as
|
||||
follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
|
||||
.target_category = SSAM_SSH_TC_BAT,
|
||||
.command_id = 0x01,
|
||||
});
|
||||
|
||||
This invocation of the macro defines a function
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
|
||||
|
||||
executing the specified request, using the device IDs and controller given
|
||||
in the client device. The full list of such macros for client devices is:
|
||||
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N`
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R`
|
||||
- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W`
|
||||
|
||||
|
||||
Handling Events
|
||||
===============
|
||||
|
||||
To receive events from the SAM EC, an event notifier must be registered for
|
||||
the desired event via |ssam_notifier_register|. The notifier must be
|
||||
unregistered via |ssam_notifier_unregister| once it is not required any
|
||||
more.
|
||||
|
||||
Event notifiers are registered by providing (at minimum) a callback to call
|
||||
in case an event has been received, the registry specifying how the event
|
||||
should be enabled, an event ID specifying for which target category and,
|
||||
optionally and depending on the registry used, for which instance ID events
|
||||
should be enabled, and finally, flags describing how the EC will send these
|
||||
events. If the specific registry does not enable events by instance ID, the
|
||||
instance ID must be set to zero. Additionally, a priority for the respective
|
||||
notifier may be specified, which determines its order in relation to any
|
||||
other notifier registered for the same target category.
|
||||
|
||||
By default, event notifiers will receive all events for the specific target
|
||||
category, regardless of the instance ID specified when registering the
|
||||
notifier. The core may be instructed to only call a notifier if the target
|
||||
ID or instance ID (or both) of the event match the ones implied by the
|
||||
notifier IDs (in case of target ID, the target ID of the registry), by
|
||||
providing an event mask (see |ssam_event_mask|).
|
||||
|
||||
In general, the target ID of the registry is also the target ID of the
|
||||
enabled event (with the notable exception being keyboard input events on the
|
||||
Surface Laptop 1 and 2, which are enabled via a registry with target ID 1,
|
||||
but provide events with target ID 2).
|
||||
|
||||
A full example for registering an event notifier and handling received
|
||||
events is provided below:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
u32 notifier_callback(struct ssam_event_notifier *nf,
|
||||
const struct ssam_event *event)
|
||||
{
|
||||
int status = ...
|
||||
|
||||
/* Handle the event here ... */
|
||||
|
||||
/* Convert return value and indicate that we handled the event. */
|
||||
return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
|
||||
}
|
||||
|
||||
int setup_notifier(struct ssam_device *sdev,
|
||||
struct ssam_event_notifier *nf)
|
||||
{
|
||||
/* Set priority wrt. other handlers of same target category. */
|
||||
nf->base.priority = 1;
|
||||
|
||||
/* Set event/notifier callback. */
|
||||
nf->base.fn = notifier_callback;
|
||||
|
||||
/* Specify event registry, i.e. how events get enabled/disabled. */
|
||||
nf->event.reg = SSAM_EVENT_REGISTRY_KIP;
|
||||
|
||||
/* Specify which event to enable/disable */
|
||||
nf->event.id.target_category = sdev->uid.category;
|
||||
nf->event.id.instance = sdev->uid.instance;
|
||||
|
||||
/*
|
||||
* Specify for which events the notifier callback gets executed.
|
||||
* This essentially tells the core if it can skip notifiers that
|
||||
* don't have target or instance IDs matching those of the event.
|
||||
*/
|
||||
nf->event.mask = SSAM_EVENT_MASK_STRICT;
|
||||
|
||||
/* Specify event flags. */
|
||||
nf->event.flags = SSAM_EVENT_SEQUENCED;
|
||||
|
||||
return ssam_notifier_register(sdev->ctrl, nf);
|
||||
}
|
||||
|
||||
Multiple event notifiers can be registered for the same event. The event
|
||||
handler core takes care of enabling and disabling events when notifiers are
|
||||
registered and unregistered, by keeping track of how many notifiers for a
|
||||
specific event (combination of registry, event target category, and event
|
||||
instance ID) are currently registered. This means that a specific event will
|
||||
be enabled when the first notifier for it is being registered and disabled
|
||||
when the last notifier for it is being unregistered. Note that the event
|
||||
flags are therefore only used on the first registered notifier, however, one
|
||||
should take care that notifiers for a specific event are always registered
|
||||
with the same flag and it is considered a bug to do otherwise.
|
||||
87
Documentation/driver-api/surface_aggregator/clients/cdev.rst
Normal file
87
Documentation/driver-api/surface_aggregator/clients/cdev.rst
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
.. |u8| replace:: :c:type:`u8 <u8>`
|
||||
.. |u16| replace:: :c:type:`u16 <u16>`
|
||||
.. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>`
|
||||
.. |ssam_cdev_request_flags| replace:: :c:type:`enum ssam_cdev_request_flags <ssam_cdev_request_flags>`
|
||||
|
||||
==============================
|
||||
User-Space EC Interface (cdev)
|
||||
==============================
|
||||
|
||||
The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM
|
||||
controller to allow for a (more or less) direct connection from user-space to
|
||||
the SAM EC. It is intended to be used for development and debugging, and
|
||||
therefore should not be used or relied upon in any other way. Note that this
|
||||
module is not loaded automatically, but instead must be loaded manually.
|
||||
|
||||
The provided interface is accessible through the ``/dev/surface/aggregator``
|
||||
device-file. All functionality of this interface is provided via IOCTLs.
|
||||
These IOCTLs and their respective input/output parameter structs are defined in
|
||||
``include/uapi/linux/surface_aggregator/cdev.h``.
|
||||
|
||||
A small python library and scripts for accessing this interface can be found
|
||||
at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam.
|
||||
|
||||
|
||||
Controller IOCTLs
|
||||
=================
|
||||
|
||||
The following IOCTLs are provided:
|
||||
|
||||
.. flat-table:: Controller IOCTLs
|
||||
:widths: 1 1 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Type
|
||||
- Number
|
||||
- Direction
|
||||
- Name
|
||||
- Description
|
||||
|
||||
* - ``0xA5``
|
||||
- ``1``
|
||||
- ``WR``
|
||||
- ``REQUEST``
|
||||
- Perform synchronous SAM request.
|
||||
|
||||
|
||||
``REQUEST``
|
||||
-----------
|
||||
|
||||
Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.
|
||||
|
||||
Executes a synchronous SAM request. The request specification is passed in
|
||||
as argument of type |ssam_cdev_request|, which is then written to/modified
|
||||
by the IOCTL to return status and result of the request.
|
||||
|
||||
Request payload data must be allocated separately and is passed in via the
|
||||
``payload.data`` and ``payload.length`` members. If a response is required,
|
||||
the response buffer must be allocated by the caller and passed in via the
|
||||
``response.data`` member. The ``response.length`` member must be set to the
|
||||
capacity of this buffer, or if no response is required, zero. Upon
|
||||
completion of the request, the call will write the response to the response
|
||||
buffer (if its capacity allows it) and overwrite the length field with the
|
||||
actual size of the response, in bytes.
|
||||
|
||||
Additionally, if the request has a response, this must be indicated via the
|
||||
request flags, as is done with in-kernel requests. Request flags can be set
|
||||
via the ``flags`` member and the values correspond to the values found in
|
||||
|ssam_cdev_request_flags|.
|
||||
|
||||
Finally, the status of the request itself is returned in the ``status``
|
||||
member (a negative errno value indicating failure). Note that failure
|
||||
indication of the IOCTL is separated from failure indication of the request:
|
||||
The IOCTL returns a negative status code if anything failed during setup of
|
||||
the request (``-EFAULT``) or if the provided argument or any of its fields
|
||||
are invalid (``-EINVAL``). In this case, the status value of the request
|
||||
argument may be set, providing more detail on what went wrong (e.g.
|
||||
``-ENOMEM`` for out-of-memory), but this value may also be zero. The IOCTL
|
||||
will return with a zero status code in case the request has been set up,
|
||||
submitted, and completed (i.e. handed back to user-space) successfully from
|
||||
inside the IOCTL, but the request ``status`` member may still be negative in
|
||||
case the actual execution of the request failed after it has been submitted.
|
||||
|
||||
A full definition of the argument struct is provided below:
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
===========================
|
||||
Client Driver Documentation
|
||||
===========================
|
||||
|
||||
This is the documentation for client drivers themselves. Refer to
|
||||
:doc:`../client` for documentation on how to write client drivers.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
cdev
|
||||
san
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indices
|
||||
=======
|
||||
|
||||
* :ref:`genindex`
|
||||
44
Documentation/driver-api/surface_aggregator/clients/san.rst
Normal file
44
Documentation/driver-api/surface_aggregator/clients/san.rst
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
.. |san_client_link| replace:: :c:func:`san_client_link`
|
||||
.. |san_dgpu_notifier_register| replace:: :c:func:`san_dgpu_notifier_register`
|
||||
.. |san_dgpu_notifier_unregister| replace:: :c:func:`san_dgpu_notifier_unregister`
|
||||
|
||||
===================
|
||||
Surface ACPI Notify
|
||||
===================
|
||||
|
||||
The Surface ACPI Notify (SAN) device provides the bridge between ACPI and
|
||||
SAM controller. Specifically, ACPI code can execute requests and handle
|
||||
battery and thermal events via this interface. In addition to this, events
|
||||
relating to the discrete GPU (dGPU) of the Surface Book 2 can be sent from
|
||||
ACPI code (note: the Surface Book 3 uses a different method for this). The
|
||||
only currently known event sent via this interface is a dGPU power-on
|
||||
notification. While this driver handles the former part internally, it only
|
||||
relays the dGPU events to any other driver interested via its public API and
|
||||
does not handle them.
|
||||
|
||||
The public interface of this driver is split into two parts: Client
|
||||
registration and notifier-block registration.
|
||||
|
||||
A client to the SAN interface can be linked as consumer to the SAN device
|
||||
via |san_client_link|. This can be used to ensure that the a client
|
||||
receiving dGPU events does not miss any events due to the SAN interface not
|
||||
being set up as this forces the client driver to unbind once the SAN driver
|
||||
is unbound.
|
||||
|
||||
Notifier-blocks can be registered by any device for as long as the module is
|
||||
loaded, regardless of being linked as client or not. Registration is done
|
||||
with |san_dgpu_notifier_register|. If the notifier is not needed any more, it
|
||||
should be unregistered via |san_dgpu_notifier_unregister|.
|
||||
|
||||
Consult the API documentation below for more details.
|
||||
|
||||
|
||||
API Documentation
|
||||
=================
|
||||
|
||||
.. kernel-doc:: include/linux/surface_acpi_notify.h
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/surface_acpi_notify.c
|
||||
:export:
|
||||
21
Documentation/driver-api/surface_aggregator/index.rst
Normal file
21
Documentation/driver-api/surface_aggregator/index.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
=======================================
|
||||
Surface System Aggregator Module (SSAM)
|
||||
=======================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
overview
|
||||
client
|
||||
clients/index
|
||||
ssh
|
||||
internal
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indices
|
||||
=======
|
||||
|
||||
* :ref:`genindex`
|
||||
67
Documentation/driver-api/surface_aggregator/internal-api.rst
Normal file
67
Documentation/driver-api/surface_aggregator/internal-api.rst
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
==========================
|
||||
Internal API Documentation
|
||||
==========================
|
||||
|
||||
.. contents::
|
||||
:depth: 2
|
||||
|
||||
|
||||
Packet Transport Layer
|
||||
======================
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_parser.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_parser.c
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_msgb.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.c
|
||||
:internal:
|
||||
|
||||
|
||||
Request Transport Layer
|
||||
=======================
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_request_layer.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/ssh_request_layer.c
|
||||
:internal:
|
||||
|
||||
|
||||
Controller
|
||||
==========
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/controller.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/controller.c
|
||||
:internal:
|
||||
|
||||
|
||||
Client Device Bus
|
||||
=================
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/bus.c
|
||||
:internal:
|
||||
|
||||
|
||||
Core
|
||||
====
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/core.c
|
||||
:internal:
|
||||
|
||||
|
||||
Trace Helpers
|
||||
=============
|
||||
|
||||
.. kernel-doc:: drivers/platform/surface/aggregator/trace.h
|
||||
577
Documentation/driver-api/surface_aggregator/internal.rst
Normal file
577
Documentation/driver-api/surface_aggregator/internal.rst
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
.. |ssh_ptl| replace:: :c:type:`struct ssh_ptl <ssh_ptl>`
|
||||
.. |ssh_ptl_submit| replace:: :c:func:`ssh_ptl_submit`
|
||||
.. |ssh_ptl_cancel| replace:: :c:func:`ssh_ptl_cancel`
|
||||
.. |ssh_ptl_shutdown| replace:: :c:func:`ssh_ptl_shutdown`
|
||||
.. |ssh_ptl_rx_rcvbuf| replace:: :c:func:`ssh_ptl_rx_rcvbuf`
|
||||
.. |ssh_rtl| replace:: :c:type:`struct ssh_rtl <ssh_rtl>`
|
||||
.. |ssh_rtl_submit| replace:: :c:func:`ssh_rtl_submit`
|
||||
.. |ssh_rtl_cancel| replace:: :c:func:`ssh_rtl_cancel`
|
||||
.. |ssh_rtl_shutdown| replace:: :c:func:`ssh_rtl_shutdown`
|
||||
.. |ssh_packet| replace:: :c:type:`struct ssh_packet <ssh_packet>`
|
||||
.. |ssh_packet_get| replace:: :c:func:`ssh_packet_get`
|
||||
.. |ssh_packet_put| replace:: :c:func:`ssh_packet_put`
|
||||
.. |ssh_packet_ops| replace:: :c:type:`struct ssh_packet_ops <ssh_packet_ops>`
|
||||
.. |ssh_packet_base_priority| replace:: :c:type:`enum ssh_packet_base_priority <ssh_packet_base_priority>`
|
||||
.. |ssh_packet_flags| replace:: :c:type:`enum ssh_packet_flags <ssh_packet_flags>`
|
||||
.. |SSH_PACKET_PRIORITY| replace:: :c:func:`SSH_PACKET_PRIORITY`
|
||||
.. |ssh_frame| replace:: :c:type:`struct ssh_frame <ssh_frame>`
|
||||
.. |ssh_command| replace:: :c:type:`struct ssh_command <ssh_command>`
|
||||
.. |ssh_request| replace:: :c:type:`struct ssh_request <ssh_request>`
|
||||
.. |ssh_request_get| replace:: :c:func:`ssh_request_get`
|
||||
.. |ssh_request_put| replace:: :c:func:`ssh_request_put`
|
||||
.. |ssh_request_ops| replace:: :c:type:`struct ssh_request_ops <ssh_request_ops>`
|
||||
.. |ssh_request_init| replace:: :c:func:`ssh_request_init`
|
||||
.. |ssh_request_flags| replace:: :c:type:`enum ssh_request_flags <ssh_request_flags>`
|
||||
.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
|
||||
.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
|
||||
.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
|
||||
.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
|
||||
.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
|
||||
.. |ssam_request_sync| replace:: :c:type:`struct ssam_request_sync <ssam_request_sync>`
|
||||
.. |ssam_event_registry| replace:: :c:type:`struct ssam_event_registry <ssam_event_registry>`
|
||||
.. |ssam_event_id| replace:: :c:type:`struct ssam_event_id <ssam_event_id>`
|
||||
.. |ssam_nf| replace:: :c:type:`struct ssam_nf <ssam_nf>`
|
||||
.. |ssam_nf_refcount_inc| replace:: :c:func:`ssam_nf_refcount_inc`
|
||||
.. |ssam_nf_refcount_dec| replace:: :c:func:`ssam_nf_refcount_dec`
|
||||
.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
|
||||
.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
|
||||
.. |ssam_cplt| replace:: :c:type:`struct ssam_cplt <ssam_cplt>`
|
||||
.. |ssam_event_queue| replace:: :c:type:`struct ssam_event_queue <ssam_event_queue>`
|
||||
.. |ssam_request_sync_submit| replace:: :c:func:`ssam_request_sync_submit`
|
||||
|
||||
=====================
|
||||
Core Driver Internals
|
||||
=====================
|
||||
|
||||
Architectural overview of the Surface System Aggregator Module (SSAM) core
|
||||
and Surface Serial Hub (SSH) driver. For the API documentation, refer to:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
internal-api
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The SSAM core implementation is structured in layers, somewhat following the
|
||||
SSH protocol structure:
|
||||
|
||||
Lower-level packet transport is implemented in the *packet transport layer
|
||||
(PTL)*, directly building on top of the serial device (serdev)
|
||||
infrastructure of the kernel. As the name indicates, this layer deals with
|
||||
the packet transport logic and handles things like packet validation, packet
|
||||
acknowledgment (ACKing), packet (retransmission) timeouts, and relaying
|
||||
packet payloads to higher-level layers.
|
||||
|
||||
Above this sits the *request transport layer (RTL)*. This layer is centered
|
||||
around command-type packet payloads, i.e. requests (sent from host to EC),
|
||||
responses of the EC to those requests, and events (sent from EC to host).
|
||||
It, specifically, distinguishes events from request responses, matches
|
||||
responses to their corresponding requests, and implements request timeouts.
|
||||
|
||||
The *controller* layer is building on top of this and essentially decides
|
||||
how request responses and, especially, events are dealt with. It provides an
|
||||
event notifier system, handles event activation/deactivation, provides a
|
||||
workqueue for event and asynchronous request completion, and also manages
|
||||
the message counters required for building command messages (``SEQ``,
|
||||
``RQID``). This layer basically provides a fundamental interface to the SAM
|
||||
EC for use in other kernel drivers.
|
||||
|
||||
While the controller layer already provides an interface for other kernel
|
||||
drivers, the client *bus* extends this interface to provide support for
|
||||
native SSAM devices, i.e. devices that are not defined in ACPI and not
|
||||
implemented as platform devices, via |ssam_device| and |ssam_device_driver|
|
||||
simplify management of client devices and client drivers.
|
||||
|
||||
Refer to :doc:`client` for documentation regarding the client device/driver
|
||||
API and interface options for other kernel drivers. It is recommended to
|
||||
familiarize oneself with that chapter and the :doc:`ssh` before continuing
|
||||
with the architectural overview below.
|
||||
|
||||
|
||||
Packet Transport Layer
|
||||
======================
|
||||
|
||||
The packet transport layer is represented via |ssh_ptl| and is structured
|
||||
around the following key concepts:
|
||||
|
||||
Packets
|
||||
-------
|
||||
|
||||
Packets are the fundamental transmission unit of the SSH protocol. They are
|
||||
managed by the packet transport layer, which is essentially the lowest layer
|
||||
of the driver and is built upon by other components of the SSAM core.
|
||||
Packets to be transmitted by the SSAM core are represented via |ssh_packet|
|
||||
(in contrast, packets received by the core do not have any specific
|
||||
structure and are managed entirely via the raw |ssh_frame|).
|
||||
|
||||
This structure contains the required fields to manage the packet inside the
|
||||
transport layer, as well as a reference to the buffer containing the data to
|
||||
be transmitted (i.e. the message wrapped in |ssh_frame|). Most notably, it
|
||||
contains an internal reference count, which is used for managing its
|
||||
lifetime (accessible via |ssh_packet_get| and |ssh_packet_put|). When this
|
||||
counter reaches zero, the ``release()`` callback provided to the packet via
|
||||
its |ssh_packet_ops| reference is executed, which may then deallocate the
|
||||
packet or its enclosing structure (e.g. |ssh_request|).
|
||||
|
||||
In addition to the ``release`` callback, the |ssh_packet_ops| reference also
|
||||
provides a ``complete()`` callback, which is run once the packet has been
|
||||
completed and provides the status of this completion, i.e. zero on success
|
||||
or a negative errno value in case of an error. Once the packet has been
|
||||
submitted to the packet transport layer, the ``complete()`` callback is
|
||||
always guaranteed to be executed before the ``release()`` callback, i.e. the
|
||||
packet will always be completed, either successfully, with an error, or due
|
||||
to cancellation, before it will be released.
|
||||
|
||||
The state of a packet is managed via its ``state`` flags
|
||||
(|ssh_packet_flags|), which also contains the packet type. In particular,
|
||||
the following bits are noteworthy:
|
||||
|
||||
* ``SSH_PACKET_SF_LOCKED_BIT``: This bit is set when completion, either
|
||||
through error or success, is imminent. It indicates that no further
|
||||
references of the packet should be taken and any existing references
|
||||
should be dropped as soon as possible. The process setting this bit is
|
||||
responsible for removing any references to this packet from the packet
|
||||
queue and pending set.
|
||||
|
||||
* ``SSH_PACKET_SF_COMPLETED_BIT``: This bit is set by the process running the
|
||||
``complete()`` callback and is used to ensure that this callback only runs
|
||||
once.
|
||||
|
||||
* ``SSH_PACKET_SF_QUEUED_BIT``: This bit is set when the packet is queued on
|
||||
the packet queue and cleared when it is dequeued.
|
||||
|
||||
* ``SSH_PACKET_SF_PENDING_BIT``: This bit is set when the packet is added to
|
||||
the pending set and cleared when it is removed from it.
|
||||
|
||||
Packet Queue
|
||||
------------
|
||||
|
||||
The packet queue is the first of the two fundamental collections in the
|
||||
packet transport layer. It is a priority queue, with priority of the
|
||||
respective packets based on the packet type (major) and number of tries
|
||||
(minor). See |SSH_PACKET_PRIORITY| for more details on the priority value.
|
||||
|
||||
All packets to be transmitted by the transport layer must be submitted to
|
||||
this queue via |ssh_ptl_submit|. Note that this includes control packets
|
||||
sent by the transport layer itself. Internally, data packets can be
|
||||
re-submitted to this queue due to timeouts or NAK packets sent by the EC.
|
||||
|
||||
Pending Set
|
||||
-----------
|
||||
|
||||
The pending set is the second of the two fundamental collections in the
|
||||
packet transport layer. It stores references to packets that have already
|
||||
been transmitted, but wait for acknowledgment (e.g. the corresponding ACK
|
||||
packet) by the EC.
|
||||
|
||||
Note that a packet may both be pending and queued if it has been
|
||||
re-submitted due to a packet acknowledgment timeout or NAK. On such a
|
||||
re-submission, packets are not removed from the pending set.
|
||||
|
||||
Transmitter Thread
|
||||
------------------
|
||||
|
||||
The transmitter thread is responsible for most of the actual work regarding
|
||||
packet transmission. In each iteration, it (waits for and) checks if the
|
||||
next packet on the queue (if any) can be transmitted and, if so, removes it
|
||||
from the queue and increments its counter for the number of transmission
|
||||
attempts, i.e. tries. If the packet is sequenced, i.e. requires an ACK by
|
||||
the EC, the packet is added to the pending set. Next, the packet's data is
|
||||
submitted to the serdev subsystem. In case of an error or timeout during
|
||||
this submission, the packet is completed by the transmitter thread with the
|
||||
status value of the callback set accordingly. In case the packet is
|
||||
unsequenced, i.e. does not require an ACK by the EC, the packet is completed
|
||||
with success on the transmitter thread.
|
||||
|
||||
Transmission of sequenced packets is limited by the number of concurrently
|
||||
pending packets, i.e. a limit on how many packets may be waiting for an ACK
|
||||
from the EC in parallel. This limit is currently set to one (see :doc:`ssh`
|
||||
for the reasoning behind this). Control packets (i.e. ACK and NAK) can
|
||||
always be transmitted.
|
||||
|
||||
Receiver Thread
|
||||
---------------
|
||||
|
||||
Any data received from the EC is put into a FIFO buffer for further
|
||||
processing. This processing happens on the receiver thread. The receiver
|
||||
thread parses and validates the received message into its |ssh_frame| and
|
||||
corresponding payload. It prepares and submits the necessary ACK (and on
|
||||
validation error or invalid data NAK) packets for the received messages.
|
||||
|
||||
This thread also handles further processing, such as matching ACK messages
|
||||
to the corresponding pending packet (via sequence ID) and completing it, as
|
||||
well as initiating re-submission of all currently pending packets on
|
||||
receival of a NAK message (re-submission in case of a NAK is similar to
|
||||
re-submission due to timeout, see below for more details on that). Note that
|
||||
the successful completion of a sequenced packet will always run on the
|
||||
receiver thread (whereas any failure-indicating completion will run on the
|
||||
process where the failure occurred).
|
||||
|
||||
Any payload data is forwarded via a callback to the next upper layer, i.e.
|
||||
the request transport layer.
|
||||
|
||||
Timeout Reaper
|
||||
--------------
|
||||
|
||||
The packet acknowledgment timeout is a per-packet timeout for sequenced
|
||||
packets, started when the respective packet begins (re-)transmission (i.e.
|
||||
this timeout is armed once per transmission attempt on the transmitter
|
||||
thread). It is used to trigger re-submission or, when the number of tries
|
||||
has been exceeded, cancellation of the packet in question.
|
||||
|
||||
This timeout is handled via a dedicated reaper task, which is essentially a
|
||||
work item (re-)scheduled to run when the next packet is set to time out. The
|
||||
work item then checks the set of pending packets for any packets that have
|
||||
exceeded the timeout and, if there are any remaining packets, re-schedules
|
||||
itself to the next appropriate point in time.
|
||||
|
||||
If a timeout has been detected by the reaper, the packet will either be
|
||||
re-submitted if it still has some remaining tries left, or completed with
|
||||
``-ETIMEDOUT`` as status if not. Note that re-submission, in this case and
|
||||
triggered by receival of a NAK, means that the packet is added to the queue
|
||||
with a now incremented number of tries, yielding a higher priority. The
|
||||
timeout for the packet will be disabled until the next transmission attempt
|
||||
and the packet remains on the pending set.
|
||||
|
||||
Note that due to transmission and packet acknowledgment timeouts, the packet
|
||||
transport layer is always guaranteed to make progress, if only through
|
||||
timing out packets, and will never fully block.
|
||||
|
||||
Concurrency and Locking
|
||||
-----------------------
|
||||
|
||||
There are two main locks in the packet transport layer: One guarding access
|
||||
to the packet queue and one guarding access to the pending set. These
|
||||
collections may only be accessed and modified under the respective lock. If
|
||||
access to both collections is needed, the pending lock must be acquired
|
||||
before the queue lock to avoid deadlocks.
|
||||
|
||||
In addition to guarding the collections, after initial packet submission
|
||||
certain packet fields may only be accessed under one of the locks.
|
||||
Specifically, the packet priority must only be accessed while holding the
|
||||
queue lock and the packet timestamp must only be accessed while holding the
|
||||
pending lock.
|
||||
|
||||
Other parts of the packet transport layer are guarded independently. State
|
||||
flags are managed by atomic bit operations and, if necessary, memory
|
||||
barriers. Modifications to the timeout reaper work item and expiration date
|
||||
are guarded by their own lock.
|
||||
|
||||
The reference of the packet to the packet transport layer (``ptl``) is
|
||||
somewhat special. It is either set when the upper layer request is submitted
|
||||
or, if there is none, when the packet is first submitted. After it is set,
|
||||
it will not change its value. Functions that may run concurrently with
|
||||
submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
|
||||
set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
|
||||
setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
|
||||
|
||||
Some packet fields may be read outside of the respective locks guarding
|
||||
them, specifically priority and state for tracing. In those cases, proper
|
||||
access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
|
||||
read-only access is only allowed when stale values are not critical.
|
||||
|
||||
With respect to the interface for higher layers, packet submission
|
||||
(|ssh_ptl_submit|), packet cancellation (|ssh_ptl_cancel|), data receival
|
||||
(|ssh_ptl_rx_rcvbuf|), and layer shutdown (|ssh_ptl_shutdown|) may always be
|
||||
executed concurrently with respect to each other. Note that packet
|
||||
submission may not run concurrently with itself for the same packet.
|
||||
Equally, shutdown and data receival may also not run concurrently with
|
||||
themselves (but may run concurrently with each other).
|
||||
|
||||
|
||||
Request Transport Layer
|
||||
=======================
|
||||
|
||||
The request transport layer is represented via |ssh_rtl| and builds on top
|
||||
of the packet transport layer. It deals with requests, i.e. SSH packets sent
|
||||
by the host containing a |ssh_command| as frame payload. This layer
|
||||
separates responses to requests from events, which are also sent by the EC
|
||||
via a |ssh_command| payload. While responses are handled in this layer,
|
||||
events are relayed to the next upper layer, i.e. the controller layer, via
|
||||
the corresponding callback. The request transport layer is structured around
|
||||
the following key concepts:
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
Requests are packets with a command-type payload, sent from host to EC to
|
||||
query data from or trigger an action on it (or both simultaneously). They
|
||||
are represented by |ssh_request|, wrapping the underlying |ssh_packet|
|
||||
storing its message data (i.e. SSH frame with command payload). Note that
|
||||
all top-level representations, e.g. |ssam_request_sync| are built upon this
|
||||
struct.
|
||||
|
||||
As |ssh_request| extends |ssh_packet|, its lifetime is also managed by the
|
||||
reference counter inside the packet struct (which can be accessed via
|
||||
|ssh_request_get| and |ssh_request_put|). Once the counter reaches zero, the
|
||||
``release()`` callback of the |ssh_request_ops| reference of the request is
|
||||
called.
|
||||
|
||||
Requests can have an optional response that is equally sent via a SSH
|
||||
message with command-type payload (from EC to host). The party constructing
|
||||
the request must know if a response is expected and mark this in the request
|
||||
flags provided to |ssh_request_init|, so that the request transport layer
|
||||
can wait for this response.
|
||||
|
||||
Similar to |ssh_packet|, |ssh_request| also has a ``complete()`` callback
|
||||
provided via its request ops reference and is guaranteed to be completed
|
||||
before it is released once it has been submitted to the request transport
|
||||
layer via |ssh_rtl_submit|. For a request without a response, successful
|
||||
completion will occur once the underlying packet has been successfully
|
||||
transmitted by the packet transport layer (i.e. from within the packet
|
||||
completion callback). For a request with response, successful completion
|
||||
will occur once the response has been received and matched to the request
|
||||
via its request ID (which happens on the packet layer's data-received
|
||||
callback running on the receiver thread). If the request is completed with
|
||||
an error, the status value will be set to the corresponding (negative) errno
|
||||
value.
|
||||
|
||||
The state of a request is again managed via its ``state`` flags
|
||||
(|ssh_request_flags|), which also encode the request type. In particular,
|
||||
the following bits are noteworthy:
|
||||
|
||||
* ``SSH_REQUEST_SF_LOCKED_BIT``: This bit is set when completion, either
|
||||
through error or success, is imminent. It indicates that no further
|
||||
references of the request should be taken and any existing references
|
||||
should be dropped as soon as possible. The process setting this bit is
|
||||
responsible for removing any references to this request from the request
|
||||
queue and pending set.
|
||||
|
||||
* ``SSH_REQUEST_SF_COMPLETED_BIT``: This bit is set by the process running the
|
||||
``complete()`` callback and is used to ensure that this callback only runs
|
||||
once.
|
||||
|
||||
* ``SSH_REQUEST_SF_QUEUED_BIT``: This bit is set when the request is queued on
|
||||
the request queue and cleared when it is dequeued.
|
||||
|
||||
* ``SSH_REQUEST_SF_PENDING_BIT``: This bit is set when the request is added to
|
||||
the pending set and cleared when it is removed from it.
|
||||
|
||||
Request Queue
|
||||
-------------
|
||||
|
||||
The request queue is the first of the two fundamental collections in the
|
||||
request transport layer. In contrast to the packet queue of the packet
|
||||
transport layer, it is not a priority queue and the simple first come first
|
||||
serve principle applies.
|
||||
|
||||
All requests to be transmitted by the request transport layer must be
|
||||
submitted to this queue via |ssh_rtl_submit|. Once submitted, requests may
|
||||
not be re-submitted, and will not be re-submitted automatically on timeout.
|
||||
Instead, the request is completed with a timeout error. If desired, the
|
||||
caller can create and submit a new request for another try, but it must not
|
||||
submit the same request again.
|
||||
|
||||
Pending Set
|
||||
-----------
|
||||
|
||||
The pending set is the second of the two fundamental collections in the
|
||||
request transport layer. This collection stores references to all pending
|
||||
requests, i.e. requests awaiting a response from the EC (similar to what the
|
||||
pending set of the packet transport layer does for packets).
|
||||
|
||||
Transmitter Task
|
||||
----------------
|
||||
|
||||
The transmitter task is scheduled when a new request is available for
|
||||
transmission. It checks if the next request on the request queue can be
|
||||
transmitted and, if so, submits its underlying packet to the packet
|
||||
transport layer. This check ensures that only a limited number of
|
||||
requests can be pending, i.e. waiting for a response, at the same time. If
|
||||
the request requires a response, the request is added to the pending set
|
||||
before its packet is submitted.
|
||||
|
||||
Packet Completion Callback
|
||||
--------------------------
|
||||
|
||||
The packet completion callback is executed once the underlying packet of a
|
||||
request has been completed. In case of an error completion, the
|
||||
corresponding request is completed with the error value provided in this
|
||||
callback.
|
||||
|
||||
On successful packet completion, further processing depends on the request.
|
||||
If the request expects a response, it is marked as transmitted and the
|
||||
request timeout is started. If the request does not expect a response, it is
|
||||
completed with success.
|
||||
|
||||
Data-Received Callback
|
||||
----------------------
|
||||
|
||||
The data received callback notifies the request transport layer of data
|
||||
being received by the underlying packet transport layer via a data-type
|
||||
frame. In general, this is expected to be a command-type payload.
|
||||
|
||||
If the request ID of the command is one of the request IDs reserved for
|
||||
events (one to ``SSH_NUM_EVENTS``, inclusively), it is forwarded to the
|
||||
event callback registered in the request transport layer. If the request ID
|
||||
indicates a response to a request, the respective request is looked up in
|
||||
the pending set and, if found and marked as transmitted, completed with
|
||||
success.
|
||||
|
||||
Timeout Reaper
|
||||
--------------
|
||||
|
||||
The request-response-timeout is a per-request timeout for requests expecting
|
||||
a response. It is used to ensure that a request does not wait indefinitely
|
||||
on a response from the EC and is started after the underlying packet has
|
||||
been successfully completed.
|
||||
|
||||
This timeout is, similar to the packet acknowledgment timeout on the packet
|
||||
transport layer, handled via a dedicated reaper task. This task is
|
||||
essentially a work-item (re-)scheduled to run when the next request is set
|
||||
to time out. The work item then scans the set of pending requests for any
|
||||
requests that have timed out and completes them with ``-ETIMEDOUT`` as
|
||||
status. Requests will not be re-submitted automatically. Instead, the issuer
|
||||
of the request must construct and submit a new request, if so desired.
|
||||
|
||||
Note that this timeout, in combination with packet transmission and
|
||||
acknowledgment timeouts, guarantees that the request layer will always make
|
||||
progress, even if only through timing out packets, and never fully block.
|
||||
|
||||
Concurrency and Locking
|
||||
-----------------------
|
||||
|
||||
Similar to the packet transport layer, there are two main locks in the
|
||||
request transport layer: One guarding access to the request queue and one
|
||||
guarding access to the pending set. These collections may only be accessed
|
||||
and modified under the respective lock.
|
||||
|
||||
Other parts of the request transport layer are guarded independently. State
|
||||
flags are (again) managed by atomic bit operations and, if necessary, memory
|
||||
barriers. Modifications to the timeout reaper work item and expiration date
|
||||
are guarded by their own lock.
|
||||
|
||||
Some request fields may be read outside of the respective locks guarding
|
||||
them, specifically the state for tracing. In those cases, proper access is
|
||||
ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
|
||||
access is only allowed when stale values are not critical.
|
||||
|
||||
With respect to the interface for higher layers, request submission
|
||||
(|ssh_rtl_submit|), request cancellation (|ssh_rtl_cancel|), and layer
|
||||
shutdown (|ssh_rtl_shutdown|) may always be executed concurrently with
|
||||
respect to each other. Note that request submission may not run concurrently
|
||||
with itself for the same request (and also may only be called once per
|
||||
request). Equally, shutdown may also not run concurrently with itself.
|
||||
|
||||
|
||||
Controller Layer
|
||||
================
|
||||
|
||||
The controller layer extends on the request transport layer to provide an
|
||||
easy-to-use interface for client drivers. It is represented by
|
||||
|ssam_controller| and the SSH driver. While the lower level transport layers
|
||||
take care of transmitting and handling packets and requests, the controller
|
||||
layer takes on more of a management role. Specifically, it handles device
|
||||
initialization, power management, and event handling, including event
|
||||
delivery and registration via the (event) completion system (|ssam_cplt|).
|
||||
|
||||
Event Registration
|
||||
------------------
|
||||
|
||||
In general, an event (or rather a class of events) has to be explicitly
|
||||
requested by the host before the EC will send it (HID input events seem to
|
||||
be the exception). This is done via an event-enable request (similarly,
|
||||
events should be disabled via an event-disable request once no longer
|
||||
desired).
|
||||
|
||||
The specific request used to enable (or disable) an event is given via an
|
||||
event registry, i.e. the governing authority of this event (so to speak),
|
||||
represented by |ssam_event_registry|. As parameters to this request, the
|
||||
target category and, depending on the event registry, instance ID of the
|
||||
event to be enabled must be provided. This (optional) instance ID must be
|
||||
zero if the registry does not use it. Together, target category and instance
|
||||
ID form the event ID, represented by |ssam_event_id|. In short, both, event
|
||||
registry and event ID, are required to uniquely identify a respective class
|
||||
of events.
|
||||
|
||||
Note that a further *request ID* parameter must be provided for the
|
||||
enable-event request. This parameter does not influence the class of events
|
||||
being enabled, but instead is set as the request ID (RQID) on each event of
|
||||
this class sent by the EC. It is used to identify events (as a limited
|
||||
number of request IDs is reserved for use in events only, specifically one
|
||||
to ``SSH_NUM_EVENTS`` inclusively) and also map events to their specific
|
||||
class. Currently, the controller always sets this parameter to the target
|
||||
category specified in |ssam_event_id|.
|
||||
|
||||
As multiple client drivers may rely on the same (or overlapping) classes of
|
||||
events and enable/disable calls are strictly binary (i.e. on/off), the
|
||||
controller has to manage access to these events. It does so via reference
|
||||
counting, storing the counter inside an RB-tree based mapping with event
|
||||
registry and ID as key (there is no known list of valid event registry and
|
||||
event ID combinations). See |ssam_nf|, |ssam_nf_refcount_inc|, and
|
||||
|ssam_nf_refcount_dec| for details.
|
||||
|
||||
This management is done together with notifier registration (described in
|
||||
the next section) via the top-level |ssam_notifier_register| and
|
||||
|ssam_notifier_unregister| functions.
|
||||
|
||||
Event Delivery
|
||||
--------------
|
||||
|
||||
To receive events, a client driver has to register an event notifier via
|
||||
|ssam_notifier_register|. This increments the reference counter for that
|
||||
specific class of events (as detailed in the previous section), enables the
|
||||
class on the EC (if it has not been enabled already), and installs the
|
||||
provided notifier callback.
|
||||
|
||||
Notifier callbacks are stored in lists, with one (RCU) list per target
|
||||
category (provided via the event ID; NB: there is a fixed known number of
|
||||
target categories). There is no known association from the combination of
|
||||
event registry and event ID to the command data (target ID, target category,
|
||||
command ID, and instance ID) that can be provided by an event class, apart
|
||||
from target category and instance ID given via the event ID.
|
||||
|
||||
Note that due to the way notifiers are (or rather have to be) stored, client
|
||||
drivers may receive events that they have not requested and need to account
|
||||
for them. Specifically, they will, by default, receive all events from the
|
||||
same target category. To simplify dealing with this, filtering of events by
|
||||
target ID (provided via the event registry) and instance ID (provided via
|
||||
the event ID) can be requested when registering a notifier. This filtering
|
||||
is applied when iterating over the notifiers at the time they are executed.
|
||||
|
||||
All notifier callbacks are executed on a dedicated workqueue, the so-called
|
||||
completion workqueue. After an event has been received via the callback
|
||||
installed in the request layer (running on the receiver thread of the packet
|
||||
transport layer), it will be put on its respective event queue
|
||||
(|ssam_event_queue|). From this event queue the completion work item of that
|
||||
queue (running on the completion workqueue) will pick up the event and
|
||||
execute the notifier callback. This is done to avoid blocking on the
|
||||
receiver thread.
|
||||
|
||||
There is one event queue per combination of target ID and target category.
|
||||
This is done to ensure that notifier callbacks are executed in sequence for
|
||||
events of the same target ID and target category. Callbacks can be executed
|
||||
in parallel for events with a different combination of target ID and target
|
||||
category.
|
||||
|
||||
Concurrency and Locking
|
||||
-----------------------
|
||||
|
||||
Most of the concurrency related safety guarantees of the controller are
|
||||
provided by the lower-level request transport layer. In addition to this,
|
||||
event (un-)registration is guarded by its own lock.
|
||||
|
||||
Access to the controller state is guarded by the state lock. This lock is a
|
||||
read/write semaphore. The reader part can be used to ensure that the state
|
||||
does not change while functions depending on the state to stay the same
|
||||
(e.g. |ssam_notifier_register|, |ssam_notifier_unregister|,
|
||||
|ssam_request_sync_submit|, and derivatives) are executed and this guarantee
|
||||
is not already provided otherwise (e.g. through |ssam_client_bind| or
|
||||
|ssam_client_link|). The writer part guards any transitions that will change
|
||||
the state, i.e. initialization, destruction, suspension, and resumption.
|
||||
|
||||
The controller state may be accessed (read-only) outside the state lock for
|
||||
smoke-testing against invalid API usage (e.g. in |ssam_request_sync_submit|).
|
||||
Note that such checks are not supposed to (and will not) protect against all
|
||||
invalid usages, but rather aim to help catch them. In those cases, proper
|
||||
variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
|
||||
|
||||
Assuming any preconditions on the state not changing have been satisfied,
|
||||
all non-initialization and non-shutdown functions may run concurrently with
|
||||
each other. This includes |ssam_notifier_register|, |ssam_notifier_unregister|,
|
||||
|ssam_request_sync_submit|, as well as all functions building on top of those.
|
||||
77
Documentation/driver-api/surface_aggregator/overview.rst
Normal file
77
Documentation/driver-api/surface_aggregator/overview.rst
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
========
|
||||
Overview
|
||||
========
|
||||
|
||||
The Surface/System Aggregator Module (SAM, SSAM) is an (arguably *the*)
|
||||
embedded controller (EC) on Microsoft Surface devices. It has been originally
|
||||
introduced on 4th generation devices (Surface Pro 4, Surface Book 1), but
|
||||
its responsibilities and feature-set have since been expanded significantly
|
||||
with the following generations.
|
||||
|
||||
|
||||
Features and Integration
|
||||
========================
|
||||
|
||||
Not much is currently known about SAM on 4th generation devices (Surface Pro
|
||||
4, Surface Book 1), due to the use of a different communication interface
|
||||
between host and EC (as detailed below). On 5th (Surface Pro 2017, Surface
|
||||
Book 2, Surface Laptop 1) and later generation devices, SAM is responsible
|
||||
for providing battery information (both current status and static values,
|
||||
such as maximum capacity etc.), as well as an assortment of temperature
|
||||
sensors (e.g. skin temperature) and cooling/performance-mode setting to the
|
||||
host. On the Surface Book 2, specifically, it additionally provides an
|
||||
interface for properly handling clipboard detachment (i.e. separating the
|
||||
display part from the keyboard part of the device), on the Surface Laptop 1
|
||||
and 2 it is required for keyboard HID input. This HID subsystem has been
|
||||
restructured for 7th generation devices and on those, specifically Surface
|
||||
Laptop 3 and Surface Book 3, is responsible for all major HID input (i.e.
|
||||
keyboard and touchpad).
|
||||
|
||||
While features have not changed much on a coarse level since the 5th
|
||||
generation, internal interfaces have undergone some rather large changes. On
|
||||
5th and 6th generation devices, both battery and temperature information is
|
||||
exposed to ACPI via a shim driver (referred to as Surface ACPI Notify, or
|
||||
SAN), translating ACPI generic serial bus write-/read-accesses to SAM
|
||||
requests. On 7th generation devices, this additional layer is gone and these
|
||||
devices require a driver hooking directly into the SAM interface. Equally,
|
||||
on newer generations, less devices are declared in ACPI, making them a bit
|
||||
harder to discover and requiring us to hard-code a sort of device registry.
|
||||
Due to this, a SSAM bus and subsystem with client devices
|
||||
(:c:type:`struct ssam_device <ssam_device>`) has been implemented.
|
||||
|
||||
|
||||
Communication
|
||||
=============
|
||||
|
||||
The type of communication interface between host and EC depends on the
|
||||
generation of the Surface device. On 4th generation devices, host and EC
|
||||
communicate via HID, specifically using a HID-over-I2C device, whereas on
|
||||
5th and later generations, communication takes place via a USART serial
|
||||
device. In accordance to the drivers found on other operating systems, we
|
||||
refer to the serial device and its driver as Surface Serial Hub (SSH). When
|
||||
needed, we differentiate between both types of SAM by referring to them as
|
||||
SAM-over-SSH and SAM-over-HID.
|
||||
|
||||
Currently, this subsystem only supports SAM-over-SSH. The SSH communication
|
||||
interface is described in more detail below. The HID interface has not been
|
||||
reverse engineered yet and it is, at the moment, unclear how many (and
|
||||
which) concepts of the SSH interface detailed below can be transferred to
|
||||
it.
|
||||
|
||||
Surface Serial Hub
|
||||
------------------
|
||||
|
||||
As already elaborated above, the Surface Serial Hub (SSH) is the
|
||||
communication interface for SAM on 5th- and all later-generation Surface
|
||||
devices. On the highest level, communication can be separated into two main
|
||||
types: Requests, messages sent from host to EC that may trigger a direct
|
||||
response from the EC (explicitly associated with the request), and events
|
||||
(sometimes also referred to as notifications), sent from EC to host without
|
||||
being a direct response to a previous request. We may also refer to requests
|
||||
without response as commands. In general, events need to be enabled via one
|
||||
of multiple dedicated requests before they are sent by the EC.
|
||||
|
||||
See :doc:`ssh` for a more technical protocol documentation and
|
||||
:doc:`internal` for an overview of the internal driver architecture.
|
||||
344
Documentation/driver-api/surface_aggregator/ssh.rst
Normal file
344
Documentation/driver-api/surface_aggregator/ssh.rst
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
.. |u8| replace:: :c:type:`u8 <u8>`
|
||||
.. |u16| replace:: :c:type:`u16 <u16>`
|
||||
.. |TYPE| replace:: ``TYPE``
|
||||
.. |LEN| replace:: ``LEN``
|
||||
.. |SEQ| replace:: ``SEQ``
|
||||
.. |SYN| replace:: ``SYN``
|
||||
.. |NAK| replace:: ``NAK``
|
||||
.. |ACK| replace:: ``ACK``
|
||||
.. |DATA| replace:: ``DATA``
|
||||
.. |DATA_SEQ| replace:: ``DATA_SEQ``
|
||||
.. |DATA_NSQ| replace:: ``DATA_NSQ``
|
||||
.. |TC| replace:: ``TC``
|
||||
.. |TID| replace:: ``TID``
|
||||
.. |IID| replace:: ``IID``
|
||||
.. |RQID| replace:: ``RQID``
|
||||
.. |CID| replace:: ``CID``
|
||||
|
||||
===========================
|
||||
Surface Serial Hub Protocol
|
||||
===========================
|
||||
|
||||
The Surface Serial Hub (SSH) is the central communication interface for the
|
||||
embedded Surface Aggregator Module controller (SAM or EC), found on newer
|
||||
Surface generations. We will refer to this protocol and interface as
|
||||
SAM-over-SSH, as opposed to SAM-over-HID for the older generations.
|
||||
|
||||
On Surface devices with SAM-over-SSH, SAM is connected to the host via UART
|
||||
and defined in ACPI as device with ID ``MSHW0084``. On these devices,
|
||||
significant functionality is provided via SAM, including access to battery
|
||||
and power information and events, thermal read-outs and events, and many
|
||||
more. For Surface Laptops, keyboard input is handled via HID directed
|
||||
through SAM, on the Surface Laptop 3 and Surface Book 3 this also includes
|
||||
touchpad input.
|
||||
|
||||
Note that the standard disclaimer for this subsystem also applies to this
|
||||
document: All of this has been reverse-engineered and may thus be erroneous
|
||||
and/or incomplete.
|
||||
|
||||
All CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``.
|
||||
All multi-byte values are little-endian, there is no implicit padding between
|
||||
values.
|
||||
|
||||
|
||||
SSH Packet Protocol: Definitions
|
||||
================================
|
||||
|
||||
The fundamental communication unit of the SSH protocol is a frame
|
||||
(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following
|
||||
fields, packed together and in order:
|
||||
|
||||
.. flat-table:: SSH Frame
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - |TYPE|
|
||||
- |u8|
|
||||
- Type identifier of the frame.
|
||||
|
||||
* - |LEN|
|
||||
- |u16|
|
||||
- Length of the payload associated with the frame.
|
||||
|
||||
* - |SEQ|
|
||||
- |u8|
|
||||
- Sequence ID (see explanation below).
|
||||
|
||||
Each frame structure is followed by a CRC over this structure. The CRC over
|
||||
the frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly
|
||||
after the frame structure and before the payload. The payload is followed by
|
||||
its own CRC (over all payload bytes). If the payload is not present (i.e.
|
||||
the frame has ``LEN=0``), the CRC of the payload is still present and will
|
||||
evaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it
|
||||
equals the number of bytes inbetween the CRC of the frame and the CRC of the
|
||||
payload.
|
||||
|
||||
Additionally, the following fixed two-byte sequences are used:
|
||||
|
||||
.. flat-table:: SSH Byte Sequences
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Value
|
||||
- Description
|
||||
|
||||
* - |SYN|
|
||||
- ``[0xAA, 0x55]``
|
||||
- Synchronization bytes.
|
||||
|
||||
A message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and
|
||||
CRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes,
|
||||
followed finally, regardless if the payload is present, the payload CRC. The
|
||||
messages corresponding to an exchange are, in part, identified by having the
|
||||
same sequence ID (|SEQ|), stored inside the frame (more on this in the next
|
||||
section). The sequence ID is a wrapping counter.
|
||||
|
||||
A frame can have the following types
|
||||
(:c:type:`enum ssh_frame_type <ssh_frame_type>`):
|
||||
|
||||
.. flat-table:: SSH Frame Types
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Value
|
||||
- Short Description
|
||||
|
||||
* - |NAK|
|
||||
- ``0x04``
|
||||
- Sent on error in previously received message.
|
||||
|
||||
* - |ACK|
|
||||
- ``0x40``
|
||||
- Sent to acknowledge receival of |DATA| frame.
|
||||
|
||||
* - |DATA_SEQ|
|
||||
- ``0x80``
|
||||
- Sent to transfer data. Sequenced.
|
||||
|
||||
* - |DATA_NSQ|
|
||||
- ``0x00``
|
||||
- Same as |DATA_SEQ|, but does not need to be ACKed.
|
||||
|
||||
Both |NAK|- and |ACK|-type frames are used to control flow of messages and
|
||||
thus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the
|
||||
other hand must carry a payload. The flow sequence and interaction of
|
||||
different frame types will be described in more depth in the next section.
|
||||
|
||||
|
||||
SSH Packet Protocol: Flow Sequence
|
||||
==================================
|
||||
|
||||
Each exchange begins with |SYN|, followed by a |DATA_SEQ|- or
|
||||
|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In
|
||||
case of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a
|
||||
|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of
|
||||
the frame by responding with a message containing an |ACK|-type frame with
|
||||
the same sequence ID of the |DATA| frame. In other words, the sequence ID of
|
||||
the |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an
|
||||
error, e.g. an invalid CRC, the receiving party responds with a message
|
||||
containing an |NAK|-type frame. As the sequence ID of the previous data
|
||||
frame, for which an error is indicated via the |NAK| frame, cannot be relied
|
||||
upon, the sequence ID of the |NAK| frame should not be used and is set to
|
||||
zero. After receival of an |NAK| frame, the sending party should re-send all
|
||||
outstanding (non-ACKed) messages.
|
||||
|
||||
Sequence IDs are not synchronized between the two parties, meaning that they
|
||||
are managed independently for each party. Identifying the messages
|
||||
corresponding to a single exchange thus relies on the sequence ID as well as
|
||||
the type of the message, and the context. Specifically, the sequence ID is
|
||||
used to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not
|
||||
``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames.
|
||||
|
||||
An example exchange might look like this:
|
||||
|
||||
::
|
||||
|
||||
tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
|
||||
rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) --
|
||||
|
||||
where both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)``
|
||||
indicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame,
|
||||
``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the
|
||||
previous payload. In case of an error, the exchange would look like this:
|
||||
|
||||
::
|
||||
|
||||
tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
|
||||
rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) --
|
||||
|
||||
upon which the sender should re-send the message. ``FRAME(N)`` indicates an
|
||||
|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed
|
||||
to zero. For |DATA_NSQ|-type frames, both exchanges are the same:
|
||||
|
||||
::
|
||||
|
||||
tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ----------------------
|
||||
rx: -------------------------------------------------------------------
|
||||
|
||||
Here, an error can be detected, but not corrected or indicated to the
|
||||
sending party. These exchanges are symmetric, i.e. switching ``rx`` and
|
||||
``tx`` results again in a valid exchange. Currently, no longer exchanges are
|
||||
known.
|
||||
|
||||
|
||||
Commands: Requests, Responses, and Events
|
||||
=========================================
|
||||
|
||||
Commands are sent as payload inside a data frame. Currently, this is the
|
||||
only known payload type of |DATA| frames, with a payload-type value of
|
||||
``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`).
|
||||
|
||||
The command-type payload (:c:type:`struct ssh_command <ssh_command>`)
|
||||
consists of an eight-byte command structure, followed by optional and
|
||||
variable length command data. The length of this optional data is derived
|
||||
from the frame payload length given in the corresponding frame, i.e. it is
|
||||
``frame.len - sizeof(struct ssh_command)``. The command struct contains the
|
||||
following fields, packed together and in order:
|
||||
|
||||
.. flat-table:: SSH Command
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - |TYPE|
|
||||
- |u8|
|
||||
- Type of the payload. For commands always ``0x80``.
|
||||
|
||||
* - |TC|
|
||||
- |u8|
|
||||
- Target category.
|
||||
|
||||
* - |TID| (out)
|
||||
- |u8|
|
||||
- Target ID for outgoing (host to EC) commands.
|
||||
|
||||
* - |TID| (in)
|
||||
- |u8|
|
||||
- Target ID for incoming (EC to host) commands.
|
||||
|
||||
* - |IID|
|
||||
- |u8|
|
||||
- Instance ID.
|
||||
|
||||
* - |RQID|
|
||||
- |u16|
|
||||
- Request ID.
|
||||
|
||||
* - |CID|
|
||||
- |u8|
|
||||
- Command ID.
|
||||
|
||||
The command struct and data, in general, does not contain any failure
|
||||
detection mechanism (e.g. CRCs), this is solely done on the frame level.
|
||||
|
||||
Command-type payloads are used by the host to send commands and requests to
|
||||
the EC as well as by the EC to send responses and events back to the host.
|
||||
We differentiate between requests (sent by the host), responses (sent by the
|
||||
EC in response to a request), and events (sent by the EC without a preceding
|
||||
request).
|
||||
|
||||
Commands and events are uniquely identified by their target category
|
||||
(``TC``) and command ID (``CID``). The target category specifies a general
|
||||
category for the command (e.g. system in general, vs. battery and AC, vs.
|
||||
temperature, and so on), while the command ID specifies the command inside
|
||||
that category. Only the combination of |TC| + |CID| is unique. Additionally,
|
||||
commands have an instance ID (``IID``), which is used to differentiate
|
||||
between different sub-devices. For example ``TC=3`` ``CID=1`` is a
|
||||
request to get the temperature on a thermal sensor, where |IID| specifies
|
||||
the respective sensor. If the instance ID is not used, it should be set to
|
||||
zero. If instance IDs are used, they, in general, start with a value of one,
|
||||
whereas zero may be used for instance independent queries, if applicable. A
|
||||
response to a request should have the same target category, command ID, and
|
||||
instance ID as the corresponding request.
|
||||
|
||||
Responses are matched to their corresponding request via the request ID
|
||||
(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence
|
||||
ID on the frames. Note that the sequence ID of the frames for a
|
||||
request-response pair does not match. Only the request ID has to match.
|
||||
Frame-protocol wise these are two separate exchanges, and may even be
|
||||
separated, e.g. by an event being sent after the request but before the
|
||||
response. Not all commands produce a response, and this is not detectable by
|
||||
|TC| + |CID|. It is the responsibility of the issuing party to wait for a
|
||||
response (or signal this to the communication framework, as is done in
|
||||
SAN/ACPI via the ``SNC`` flag).
|
||||
|
||||
Events are identified by unique and reserved request IDs. These IDs should
|
||||
not be used by the host when sending a new request. They are used on the
|
||||
host to, first, detect events and, second, match them with a registered
|
||||
event handler. Request IDs for events are chosen by the host and directed to
|
||||
the EC when setting up and enabling an event source (via the
|
||||
enable-event-source request). The EC then uses the specified request ID for
|
||||
events sent from the respective source. Note that an event should still be
|
||||
identified by its target category, command ID, and, if applicable, instance
|
||||
ID, as a single event source can send multiple different event types. In
|
||||
general, however, a single target category should map to a single reserved
|
||||
event request ID.
|
||||
|
||||
Furthermore, requests, responses, and events have an associated target ID
|
||||
(``TID``). This target ID is split into output (host to EC) and input (EC to
|
||||
host) fields, with the respecting other field (e.g. output field on incoming
|
||||
messages) set to zero. Two ``TID`` values are known: Primary (``0x01``) and
|
||||
secondary (``0x02``). In general, the response to a request should have the
|
||||
same ``TID`` value, however, the field (output vs. input) should be used in
|
||||
accordance to the direction in which the response is sent (i.e. on the input
|
||||
field, as responses are generally sent from the EC to the host).
|
||||
|
||||
Note that, even though requests and events should be uniquely identifiable
|
||||
by target category and command ID alone, the EC may require specific
|
||||
target ID and instance ID values to accept a command. A command that is
|
||||
accepted for ``TID=1``, for example, may not be accepted for ``TID=2``
|
||||
and vice versa.
|
||||
|
||||
|
||||
Limitations and Observations
|
||||
============================
|
||||
|
||||
The protocol can, in theory, handle up to ``U8_MAX`` frames in parallel,
|
||||
with up to ``U16_MAX`` pending requests (neglecting request IDs reserved for
|
||||
events). In practice, however, this is more limited. From our testing
|
||||
(although via a python and thus a user-space program), it seems that the EC
|
||||
can handle up to four requests (mostly) reliably in parallel at a certain
|
||||
time. With five or more requests in parallel, consistent discarding of
|
||||
commands (ACKed frame but no command response) has been observed. For five
|
||||
simultaneous commands, this reproducibly resulted in one command being
|
||||
dropped and four commands being handled.
|
||||
|
||||
However, it has also been noted that, even with three requests in parallel,
|
||||
occasional frame drops happen. Apart from this, with a limit of three
|
||||
pending requests, no dropped commands (i.e. command being dropped but frame
|
||||
carrying command being ACKed) have been observed. In any case, frames (and
|
||||
possibly also commands) should be re-sent by the host if a certain timeout
|
||||
is exceeded. This is done by the EC for frames with a timeout of one second,
|
||||
up to two re-tries (i.e. three transmissions in total). The limit of
|
||||
re-tries also applies to received NAKs, and, in a worst case scenario, can
|
||||
lead to entire messages being dropped.
|
||||
|
||||
While this also seems to work fine for pending data frames as long as no
|
||||
transmission failures occur, implementation and handling of these seems to
|
||||
depend on the assumption that there is only one non-acknowledged data frame.
|
||||
In particular, the detection of repeated frames relies on the last sequence
|
||||
number. This means that, if a frame that has been successfully received by
|
||||
the EC is sent again, e.g. due to the host not receiving an |ACK|, the EC
|
||||
will only detect this if it has the sequence ID of the last frame received
|
||||
by the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1``
|
||||
followed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0``
|
||||
frame as such, and thus execute the command in this frame each time it has
|
||||
been received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and
|
||||
then repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of
|
||||
the first one and ignore it, thus executing the contained command only once.
|
||||
|
||||
In conclusion, this suggests a limit of at most one pending un-ACKed frame
|
||||
(per party, effectively leading to synchronous communication regarding
|
||||
frames) and at most three pending commands. The limit to synchronous frame
|
||||
transfers seems to be consistent with behavior observed on Windows.
|
||||
|
|
@ -520,19 +520,6 @@ available_policies
|
|||
|
||||
RW, Optional
|
||||
|
||||
passive
|
||||
Attribute is only present for zones in which the passive cooling
|
||||
policy is not supported by native thermal driver. Default is zero
|
||||
and can be set to a temperature (in millidegrees) to enable a
|
||||
passive trip point for the zone. Activation is done by polling with
|
||||
an interval of 1 second.
|
||||
|
||||
Unit: millidegrees Celsius
|
||||
|
||||
Valid values: 0 (disabled) or greater than 1000
|
||||
|
||||
RW, Optional
|
||||
|
||||
emul_temp
|
||||
Interface to set the emulated temperature method in thermal zone
|
||||
(sensor). After setting this temperature, the thermal zone may pass
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
Kernel driver ab8500
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* ST-Ericsson AB8500
|
||||
|
||||
Prefix: 'ab8500'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: http://www.stericsson.com/developers/documentation.jsp
|
||||
|
||||
Authors:
|
||||
- Martin Persson <martin.persson@stericsson.com>
|
||||
- Hongbo Zhang <hongbo.zhang@linaro.org>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
See also Documentation/hwmon/abx500.rst. This is the ST-Ericsson AB8500 specific
|
||||
driver.
|
||||
|
||||
Currently only the AB8500 internal sensor and one external sensor for battery
|
||||
temperature are monitored. Other GPADC channels can also be monitored if needed
|
||||
in future.
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
Kernel driver abx500
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* ST-Ericsson ABx500 series
|
||||
|
||||
Prefix: 'abx500'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: http://www.stericsson.com/developers/documentation.jsp
|
||||
|
||||
Authors:
|
||||
Martin Persson <martin.persson@stericsson.com>
|
||||
Hongbo Zhang <hongbo.zhang@linaro.org>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
Every ST-Ericsson Ux500 SOC consists of both ABx500 and DBx500 physically,
|
||||
this is kernel hwmon driver for ABx500.
|
||||
|
||||
There are some GPADCs inside ABx500 which are designed for connecting to
|
||||
thermal sensors, and there is also a thermal sensor inside ABx500 too, which
|
||||
raises interrupt when critical temperature reached.
|
||||
|
||||
This abx500 is a common layer which can monitor all of the sensors, every
|
||||
specific abx500 chip has its special configurations in its own file, e.g. some
|
||||
sensors can be configured invisible if they are not available on that chip, and
|
||||
the corresponding gpadc_addr should be set to 0, thus this sensor won't be
|
||||
polled.
|
||||
46
Documentation/hwmon/aht10.rst
Normal file
46
Documentation/hwmon/aht10.rst
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver aht10
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Aosong AHT10
|
||||
|
||||
Prefix: 'aht10'
|
||||
|
||||
Addresses scanned: None
|
||||
|
||||
Datasheet:
|
||||
|
||||
Chinese: http://www.aosong.com/userfiles/files/media/AHT10%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C%20A3%2020201210.pdf
|
||||
English: https://server4.eca.ir/eshop/AHT10/Aosong_AHT10_en_draft_0c.pdf
|
||||
|
||||
Author: Johannes Cornelis Draaijer <jcdra1@gmail.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The AHT10 is a Temperature and Humidity sensor
|
||||
|
||||
The address of this i2c device may only be 0x38
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for AHT10 devices, as there is no reliable
|
||||
way to determine if an i2c chip is or isn't an AHT10. The device has
|
||||
to be instantiated explicitly with the address 0x38. See
|
||||
Documentation/i2c/instantiating-devices.rst for details.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
=============== ============================================
|
||||
temp1_input Measured temperature in millidegrees Celcius
|
||||
humidity1_input Measured humidity in %H
|
||||
update_interval The minimum interval for polling the sensor,
|
||||
in milliseconds. Writable. Must be at
|
||||
least 2000.
|
||||
=============== ============================================
|
||||
|
|
@ -74,7 +74,7 @@ bus supply voltage.
|
|||
|
||||
The shunt value in micro-ohms can be set via platform data or device tree at
|
||||
compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
|
||||
refer to the Documentation/devicetree/bindings/hwmon/ina2xx.txt for bindings
|
||||
refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings
|
||||
if the device tree is used.
|
||||
|
||||
Additionally ina226 supports update_interval attribute as described in
|
||||
|
|
|
|||
|
|
@ -18,10 +18,8 @@ Hardware Monitoring Kernel Drivers
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ab8500
|
||||
abituguru
|
||||
abituguru3
|
||||
abx500
|
||||
acpi_power_meter
|
||||
ad7314
|
||||
adc128d818
|
||||
|
|
@ -39,6 +37,7 @@ Hardware Monitoring Kernel Drivers
|
|||
adt7462
|
||||
adt7470
|
||||
adt7475
|
||||
aht10
|
||||
amc6821
|
||||
amd_energy
|
||||
asb100
|
||||
|
|
@ -178,6 +177,7 @@ Hardware Monitoring Kernel Drivers
|
|||
tmp401
|
||||
tmp421
|
||||
tmp513
|
||||
tps23861
|
||||
tps40422
|
||||
tps53679
|
||||
twl4030-madc-hwmon
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@ Kernel driver max16601
|
|||
|
||||
Supported chips:
|
||||
|
||||
* Maxim MAX16508
|
||||
|
||||
Prefix: 'max16508'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Not published
|
||||
|
||||
* Maxim MAX16601
|
||||
|
||||
Prefix: 'max16601'
|
||||
|
|
@ -19,8 +27,8 @@ Author: Guenter Roeck <linux@roeck-us.net>
|
|||
Description
|
||||
-----------
|
||||
|
||||
This driver supports the MAX16601 VR13.HC Dual-Output Voltage Regulator
|
||||
Chipset.
|
||||
This driver supports the MAX16508 VR13 Dual-Output Voltage Regulator
|
||||
as well as the MAX16601 VR13.HC Dual-Output Voltage Regulator chipsets.
|
||||
|
||||
The driver is a client driver to the core PMBus driver.
|
||||
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
|
|
@ -45,115 +53,76 @@ Sysfs entries
|
|||
|
||||
The following attributes are supported.
|
||||
|
||||
======================= =======================================================
|
||||
in1_label "vin1"
|
||||
in1_input VCORE input voltage.
|
||||
in1_alarm Input voltage alarm.
|
||||
=============================== ===============================================
|
||||
in1_label "vin1"
|
||||
in1_input VCORE input voltage.
|
||||
in1_alarm Input voltage alarm.
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input VCORE output voltage.
|
||||
in2_alarm Output voltage alarm.
|
||||
in2_label "vout1"
|
||||
in2_input VCORE output voltage.
|
||||
in2_alarm Output voltage alarm.
|
||||
|
||||
curr1_label "iin1"
|
||||
curr1_input VCORE input current, derived from duty cycle and output
|
||||
current.
|
||||
curr1_max Maximum input current.
|
||||
curr1_max_alarm Current high alarm.
|
||||
curr1_label "iin1"
|
||||
curr1_input VCORE input current, derived from duty cycle
|
||||
and output current.
|
||||
curr1_max Maximum input current.
|
||||
curr1_max_alarm Current high alarm.
|
||||
|
||||
curr2_label "iin1.0"
|
||||
curr2_input VCORE phase 0 input current.
|
||||
curr[P+2]_label "iin1.P"
|
||||
curr[P+2]_input VCORE phase P input current.
|
||||
|
||||
curr3_label "iin1.1"
|
||||
curr3_input VCORE phase 1 input current.
|
||||
curr[N+2]_label "iin2"
|
||||
curr[N+2]_input VCORE input current, derived from sensor
|
||||
element.
|
||||
'N' is the number of enabled/populated phases.
|
||||
|
||||
curr4_label "iin1.2"
|
||||
curr4_input VCORE phase 2 input current.
|
||||
curr[N+3]_label "iin3"
|
||||
curr[N+3]_input VSA input current.
|
||||
|
||||
curr5_label "iin1.3"
|
||||
curr5_input VCORE phase 3 input current.
|
||||
curr[N+4]_label "iout1"
|
||||
curr[N+4]_input VCORE output current.
|
||||
curr[N+4]_crit Critical output current.
|
||||
curr[N+4]_crit_alarm Output current critical alarm.
|
||||
curr[N+4]_max Maximum output current.
|
||||
curr[N+4]_max_alarm Output current high alarm.
|
||||
|
||||
curr6_label "iin1.4"
|
||||
curr6_input VCORE phase 4 input current.
|
||||
curr[N+P+5]_label "iout1.P"
|
||||
curr[N+P+5]_input VCORE phase P output current.
|
||||
|
||||
curr7_label "iin1.5"
|
||||
curr7_input VCORE phase 5 input current.
|
||||
curr[2*N+5]_label "iout3"
|
||||
curr[2*N+5]_input VSA output current.
|
||||
curr[2*N+5]_highest Historical maximum VSA output current.
|
||||
curr[2*N+5]_reset_history Write any value to reset curr21_highest.
|
||||
curr[2*N+5]_crit Critical output current.
|
||||
curr[2*N+5]_crit_alarm Output current critical alarm.
|
||||
curr[2*N+5]_max Maximum output current.
|
||||
curr[2*N+5]_max_alarm Output current high alarm.
|
||||
|
||||
curr8_label "iin1.6"
|
||||
curr8_input VCORE phase 6 input current.
|
||||
power1_label "pin1"
|
||||
power1_input Input power, derived from duty cycle and output
|
||||
current.
|
||||
power1_alarm Input power alarm.
|
||||
|
||||
curr9_label "iin1.7"
|
||||
curr9_input VCORE phase 7 input current.
|
||||
power2_label "pin2"
|
||||
power2_input Input power, derived from input current sensor.
|
||||
|
||||
curr10_label "iin2"
|
||||
curr10_input VCORE input current, derived from sensor element.
|
||||
power3_label "pout"
|
||||
power3_input Output power.
|
||||
|
||||
curr11_label "iin3"
|
||||
curr11_input VSA input current.
|
||||
temp1_input VCORE temperature.
|
||||
temp1_crit Critical high temperature.
|
||||
temp1_crit_alarm Chip temperature critical high alarm.
|
||||
temp1_max Maximum temperature.
|
||||
temp1_max_alarm Chip temperature high alarm.
|
||||
|
||||
curr12_label "iout1"
|
||||
curr12_input VCORE output current.
|
||||
curr12_crit Critical output current.
|
||||
curr12_crit_alarm Output current critical alarm.
|
||||
curr12_max Maximum output current.
|
||||
curr12_max_alarm Output current high alarm.
|
||||
temp2_input TSENSE_0 temperature
|
||||
temp3_input TSENSE_1 temperature
|
||||
temp4_input TSENSE_2 temperature
|
||||
temp5_input TSENSE_3 temperature
|
||||
|
||||
curr13_label "iout1.0"
|
||||
curr13_input VCORE phase 0 output current.
|
||||
|
||||
curr14_label "iout1.1"
|
||||
curr14_input VCORE phase 1 output current.
|
||||
|
||||
curr15_label "iout1.2"
|
||||
curr15_input VCORE phase 2 output current.
|
||||
|
||||
curr16_label "iout1.3"
|
||||
curr16_input VCORE phase 3 output current.
|
||||
|
||||
curr17_label "iout1.4"
|
||||
curr17_input VCORE phase 4 output current.
|
||||
|
||||
curr18_label "iout1.5"
|
||||
curr18_input VCORE phase 5 output current.
|
||||
|
||||
curr19_label "iout1.6"
|
||||
curr19_input VCORE phase 6 output current.
|
||||
|
||||
curr20_label "iout1.7"
|
||||
curr20_input VCORE phase 7 output current.
|
||||
|
||||
curr21_label "iout3"
|
||||
curr21_input VSA output current.
|
||||
curr21_highest Historical maximum VSA output current.
|
||||
curr21_reset_history Write any value to reset curr21_highest.
|
||||
curr21_crit Critical output current.
|
||||
curr21_crit_alarm Output current critical alarm.
|
||||
curr21_max Maximum output current.
|
||||
curr21_max_alarm Output current high alarm.
|
||||
|
||||
power1_label "pin1"
|
||||
power1_input Input power, derived from duty cycle and output current.
|
||||
power1_alarm Input power alarm.
|
||||
|
||||
power2_label "pin2"
|
||||
power2_input Input power, derived from input current sensor.
|
||||
|
||||
power3_label "pout"
|
||||
power3_input Output power.
|
||||
|
||||
temp1_input VCORE temperature.
|
||||
temp1_crit Critical high temperature.
|
||||
temp1_crit_alarm Chip temperature critical high alarm.
|
||||
temp1_max Maximum temperature.
|
||||
temp1_max_alarm Chip temperature high alarm.
|
||||
|
||||
temp2_input TSENSE_0 temperature
|
||||
temp3_input TSENSE_1 temperature
|
||||
temp4_input TSENSE_2 temperature
|
||||
temp5_input TSENSE_3 temperature
|
||||
|
||||
temp6_input VSA temperature.
|
||||
temp6_crit Critical high temperature.
|
||||
temp6_crit_alarm Chip temperature critical high alarm.
|
||||
temp6_max Maximum temperature.
|
||||
temp6_max_alarm Chip temperature high alarm.
|
||||
======================= =======================================================
|
||||
temp6_input VSA temperature.
|
||||
temp6_crit Critical high temperature.
|
||||
temp6_crit_alarm Chip temperature critical high alarm.
|
||||
temp6_max Maximum temperature.
|
||||
temp6_max_alarm Chip temperature high alarm.
|
||||
=============================== ===============================================
|
||||
|
|
|
|||
|
|
@ -61,5 +61,6 @@ Board Firmware version
|
|||
Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19
|
||||
MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20
|
||||
=============== ===============================================
|
||||
|
|
|
|||
41
Documentation/hwmon/tps23861.rst
Normal file
41
Documentation/hwmon/tps23861.rst
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
Kernel driver tps23861
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
* Texas Instruments TPS23861
|
||||
|
||||
Prefix: 'tps23861'
|
||||
|
||||
Datasheet: https://www.ti.com/lit/gpn/tps23861
|
||||
|
||||
Author: Robert Marko <robert.marko@sartura.hr>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for Texas Instruments TPS23861 PoE PSE.
|
||||
|
||||
TPS23861 is a quad port IEEE802.3at PSE controller with optional I2C control
|
||||
and monitoring capabilities.
|
||||
|
||||
TPS23861 offers three modes of operation: Auto, Semi-Auto and Manual.
|
||||
|
||||
This driver only supports the Auto mode of operation providing monitoring
|
||||
as well as enabling/disabling the four ports.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
======================= =====================================================================
|
||||
in[0-3]_input Voltage on ports [1-4]
|
||||
in[0-3]_label "Port[1-4]"
|
||||
in4_input IC input voltage
|
||||
in4_label "Input"
|
||||
temp1_input IC die temperature
|
||||
temp1_label "Die"
|
||||
curr[1-4]_input Current on ports [1-4]
|
||||
in[1-4]_label "Port[1-4]"
|
||||
in[0-3]_enable Enable/disable ports [1-4]
|
||||
======================= =====================================================================
|
||||
|
|
@ -22,8 +22,9 @@ Instantiating the device is regular. Example for bus 0, address 0x30:
|
|||
|
||||
After that, you will have a write-only device listening. Reads will just return
|
||||
an 8-bit version number of the testunit. When writing, the device consists of 4
|
||||
8-bit registers and all must be written to start a testcase, i.e. you must
|
||||
always write 4 bytes to the device. The registers are:
|
||||
8-bit registers and, except for some "partial" commands, all registers must be
|
||||
written to start a testcase, i.e. you usually write 4 bytes to the device. The
|
||||
registers are:
|
||||
|
||||
0x00 CMD - which test to trigger
|
||||
0x01 DATAL - configuration byte 1 for the test
|
||||
|
|
@ -67,3 +68,21 @@ status word is currently ignored in the Linux Kernel. Example to send a
|
|||
notification after 10ms:
|
||||
|
||||
# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i
|
||||
|
||||
0x03 SMBUS_BLOCK_PROC_CALL (partial command)
|
||||
DATAL - must be '1', i.e. one further byte will be written
|
||||
DATAH - number of bytes to be sent back
|
||||
DELAY - not applicable, partial command!
|
||||
|
||||
This test will respond to a block process call as defined by the SMBus
|
||||
specification. The one data byte written specifies how many bytes will be sent
|
||||
back in the following read transfer. Note that in this read transfer, the
|
||||
testunit will prefix the length of the bytes to follow. So, if your host bus
|
||||
driver emulates SMBus calls like the majority does, it needs to support the
|
||||
I2C_M_RECV_LEN flag of an i2c_msg. This is a good testcase for it. The returned
|
||||
data consists of the length first, and then of an array of bytes from length-1
|
||||
to 0. Here is an example which emulates i2c_smbus_block_process_call() using
|
||||
i2ctransfer (you need i2c-tools v4.2 or later):
|
||||
|
||||
# i2ctransfer -y 0 w3@0x30 0x03 0x01 0x10 r?
|
||||
0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00
|
||||
|
|
|
|||
|
|
@ -324,6 +324,8 @@ Code Seq# Include File Comments
|
|||
0xA3 90-9F linux/dtlk.h
|
||||
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
|
||||
0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
|
||||
0xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
|
||||
<mailto:luzmaximilian@gmail.com>
|
||||
0xAA 00-3F linux/uapi/linux/userfaultfd.h
|
||||
0xAB 00-1F linux/nbd.h
|
||||
0xAC 00-1F linux/raw.h
|
||||
|
|
|
|||
77
MAINTAINERS
77
MAINTAINERS
|
|
@ -2775,6 +2775,15 @@ F: Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2xxx-scu-ic.
|
|||
F: drivers/irqchip/irq-aspeed-scu-ic.c
|
||||
F: include/dt-bindings/interrupt-controller/aspeed-scu-ic.h
|
||||
|
||||
ASPEED SD/MMC DRIVER
|
||||
M: Andrew Jeffery <andrew@aj.id.au>
|
||||
L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
|
||||
L: openbmc@lists.ozlabs.org (moderated for non-subscribers)
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
|
||||
F: drivers/mmc/host/sdhci-of-aspeed*
|
||||
|
||||
ASPEED VIDEO ENGINE DRIVER
|
||||
M: Eddie James <eajames@linux.ibm.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
@ -4946,17 +4955,17 @@ M: Matthew Garrett <mjg59@srcf.ucam.org>
|
|||
M: Pali Rohár <pali@kernel.org>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-laptop.c
|
||||
F: drivers/platform/x86/dell/dell-laptop.c
|
||||
|
||||
DELL LAPTOP FREEFALL DRIVER
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smo8800.c
|
||||
F: drivers/platform/x86/dell/dell-smo8800.c
|
||||
|
||||
DELL LAPTOP RBTN DRIVER
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-rbtn.*
|
||||
F: drivers/platform/x86/dell/dell-rbtn.*
|
||||
|
||||
DELL LAPTOP SMM DRIVER
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
|
|
@ -4968,26 +4977,26 @@ DELL REMOTE BIOS UPDATE DRIVER
|
|||
M: Stuart Hayes <stuart.w.hayes@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell_rbu.c
|
||||
F: drivers/platform/x86/dell/dell_rbu.c
|
||||
|
||||
DELL SMBIOS DRIVER
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smbios.*
|
||||
F: drivers/platform/x86/dell/dell-smbios.*
|
||||
|
||||
DELL SMBIOS SMM DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smbios-smm.c
|
||||
F: drivers/platform/x86/dell/dell-smbios-smm.c
|
||||
|
||||
DELL SMBIOS WMI DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-smbios-wmi.c
|
||||
F: drivers/platform/x86/dell/dell-smbios-wmi.c
|
||||
F: tools/wmi/dell-smbios-example.c
|
||||
|
||||
DELL SYSTEMS MANAGEMENT BASE DRIVER (dcdbas)
|
||||
|
|
@ -4995,12 +5004,12 @@ M: Stuart Hayes <stuart.w.hayes@gmail.com>
|
|||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/driver-api/dcdbas.rst
|
||||
F: drivers/platform/x86/dcdbas.*
|
||||
F: drivers/platform/x86/dell/dcdbas.*
|
||||
|
||||
DELL WMI DESCRIPTOR DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-wmi-descriptor.c
|
||||
F: drivers/platform/x86/dell/dell-wmi-descriptor.c
|
||||
|
||||
DELL WMI SYSMAN DRIVER
|
||||
M: Divya Bharathi <divya.bharathi@dell.com>
|
||||
|
|
@ -5009,13 +5018,13 @@ M: Prasanth Ksr <prasanth.ksr@dell.com>
|
|||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-class-firmware-attributes
|
||||
F: drivers/platform/x86/dell-wmi-sysman/
|
||||
F: drivers/platform/x86/dell/dell-wmi-sysman/
|
||||
|
||||
DELL WMI NOTIFICATIONS DRIVER
|
||||
M: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-wmi.c
|
||||
F: drivers/platform/x86/dell/dell-wmi.c
|
||||
|
||||
DELTA ST MEDIA DRIVER
|
||||
M: Hugues Fruchet <hugues.fruchet@st.com>
|
||||
|
|
@ -8132,7 +8141,7 @@ F: net/hsr/
|
|||
HT16K33 LED CONTROLLER DRIVER
|
||||
M: Robin van der Gracht <robin@protonic.nl>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/ht16k33.txt
|
||||
F: Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml
|
||||
F: drivers/auxdisplay/ht16k33.c
|
||||
|
||||
HTCPEN TOUCHSCREEN DRIVER
|
||||
|
|
@ -8915,7 +8924,6 @@ L: linux-gpio@vger.kernel.org
|
|||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
|
||||
F: drivers/gpio/gpio-ich.c
|
||||
F: drivers/gpio/gpio-intel-mid.c
|
||||
F: drivers/gpio/gpio-merrifield.c
|
||||
F: drivers/gpio/gpio-ml-ioh.c
|
||||
F: drivers/gpio/gpio-pch.c
|
||||
|
|
@ -9087,7 +9095,6 @@ M: Andy Shevchenko <andy@kernel.org>
|
|||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
|
||||
F: drivers/gpio/gpio-*cove.c
|
||||
F: drivers/gpio/gpio-msic.c
|
||||
|
||||
INTEL PMIC MULTIFUNCTION DEVICE DRIVERS
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
|
|
@ -11688,9 +11695,9 @@ F: drivers/video/fbdev/atmel_lcdfb.c
|
|||
F: include/video/atmel_lcdc.h
|
||||
|
||||
MICROCHIP MCP16502 PMIC DRIVER
|
||||
M: Andrei Stefanescu <andrei.stefanescu@microchip.com>
|
||||
M: Claudiu Beznea <claudiu.beznea@microchip.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
|
||||
F: drivers/regulator/mcp16502.c
|
||||
|
||||
|
|
@ -11805,12 +11812,31 @@ S: Maintained
|
|||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
|
||||
F: drivers/platform/surface/
|
||||
|
||||
MICROSOFT SURFACE HOT-PLUG DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/surface/surface_hotplug.c
|
||||
|
||||
MICROSOFT SURFACE PRO 3 BUTTON DRIVER
|
||||
M: Chen Yu <yu.c.chen@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/platform/surface/surfacepro3_button.c
|
||||
|
||||
MICROSOFT SURFACE SYSTEM AGGREGATOR SUBSYSTEM
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
S: Maintained
|
||||
W: https://github.com/linux-surface/surface-aggregator-module
|
||||
C: irc://chat.freenode.net/##linux-surface
|
||||
F: Documentation/driver-api/surface_aggregator/
|
||||
F: drivers/platform/surface/aggregator/
|
||||
F: drivers/platform/surface/surface_acpi_notify.c
|
||||
F: drivers/platform/surface/surface_aggregator_cdev.c
|
||||
F: include/linux/surface_acpi_notify.h
|
||||
F: include/linux/surface_aggregator/
|
||||
F: include/uapi/linux/surface_aggregator/
|
||||
|
||||
MICROTEK X6 SCANNER
|
||||
M: Oliver Neukum <oliver@neukum.org>
|
||||
S: Maintained
|
||||
|
|
@ -17524,6 +17550,14 @@ M: Laxman Dewangan <ldewangan@nvidia.com>
|
|||
S: Supported
|
||||
F: drivers/spi/spi-tegra*
|
||||
|
||||
TEGRA QUAD SPI DRIVER
|
||||
M: Thierry Reding <thierry.reding@gmail.com>
|
||||
M: Jonathan Hunter <jonathanh@nvidia.com>
|
||||
M: Sowjanya Komatineni <skomatineni@nvidia.com>
|
||||
L: linux-tegra@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/spi/spi-tegra210-quad.c
|
||||
|
||||
TEGRA VIDEO DRIVER
|
||||
M: Thierry Reding <thierry.reding@gmail.com>
|
||||
M: Jonathan Hunter <jonathanh@nvidia.com>
|
||||
|
|
@ -17616,6 +17650,15 @@ F: include/dt-bindings/soc/ti,sci_pm_domain.h
|
|||
F: include/linux/soc/ti/ti_sci_inta_msi.h
|
||||
F: include/linux/soc/ti/ti_sci_protocol.h
|
||||
|
||||
TEXAS INSTRUMENTS TPS23861 PoE PSE DRIVER
|
||||
M: Robert Marko <robert.marko@sartura.hr>
|
||||
M: Luka Perkov <luka.perkov@sartura.hr>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
|
||||
F: Documentation/hwmon/tps23861.rst
|
||||
F: drivers/hwmon/tps23861.c
|
||||
|
||||
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
@ -17669,7 +17712,7 @@ F: drivers/thermal/gov_power_allocator.c
|
|||
F: include/trace/events/thermal_power_allocator.h
|
||||
|
||||
THINKPAD ACPI EXTRAS DRIVER
|
||||
M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
|
||||
M: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
|
||||
L: ibm-acpi-devel@lists.sourceforge.net
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/pxa2xx_spi.h>
|
||||
#include <linux/spi/libertas_spi.h>
|
||||
#include <linux/spi/lms283gf05.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/gpio.h>
|
||||
|
|
@ -578,8 +577,13 @@ static struct pxa2xx_spi_chip lms283_chip_info = {
|
|||
.gpio_cs = GPIO88_ZIPITZ2_LCD_CS,
|
||||
};
|
||||
|
||||
static const struct lms283gf05_pdata lms283_pdata = {
|
||||
.reset_gpio = GPIO19_ZIPITZ2_LCD_RESET,
|
||||
static struct gpiod_lookup_table lms283_gpio_table = {
|
||||
.dev_id = "spi2.0", /* SPI bus 2 chip select 0 */
|
||||
.table = {
|
||||
GPIO_LOOKUP("gpio-pxa", GPIO19_ZIPITZ2_LCD_RESET,
|
||||
"reset", GPIO_ACTIVE_LOW),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct spi_board_info spi_board_info[] __initdata = {
|
||||
|
|
@ -595,7 +599,6 @@ static struct spi_board_info spi_board_info[] __initdata = {
|
|||
{
|
||||
.modalias = "lms283gf05",
|
||||
.controller_data = &lms283_chip_info,
|
||||
.platform_data = &lms283_pdata,
|
||||
.max_speed_hz = 400000,
|
||||
.bus_num = 2,
|
||||
.chip_select = 0,
|
||||
|
|
@ -615,6 +618,7 @@ static void __init z2_spi_init(void)
|
|||
{
|
||||
pxa2xx_set_spi_info(1, &pxa_ssp1_master_info);
|
||||
pxa2xx_set_spi_info(2, &pxa_ssp2_master_info);
|
||||
gpiod_add_lookup_table(&lms283_gpio_table);
|
||||
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
|
||||
}
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ &adsp {
|
|||
|
||||
&apps_rsc {
|
||||
pm8009-rpmh-regulators {
|
||||
compatible = "qcom,pm8009-rpmh-regulators";
|
||||
compatible = "qcom,pm8009-1-rpmh-regulators";
|
||||
qcom,pmic-id = "f";
|
||||
|
||||
vdd-s1-supply = <&vph_pwr>;
|
||||
|
|
@ -241,6 +241,13 @@ pm8009-rpmh-regulators {
|
|||
vdd-l5-l6-supply = <&vreg_bob>;
|
||||
vdd-l7-supply = <&vreg_s4a_1p8>;
|
||||
|
||||
vreg_s2f_0p95: smps2 {
|
||||
regulator-name = "vreg_s2f_0p95";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <952000>;
|
||||
regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
|
||||
};
|
||||
|
||||
vreg_l1f_1p1: ldo1 {
|
||||
regulator-name = "vreg_l1f_1p1";
|
||||
regulator-min-microvolt = <1104000>;
|
||||
|
|
|
|||
|
|
@ -30,4 +30,3 @@ obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
|
|||
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
||||
obj-$(subst m,y,$(CONFIG_INTEL_MID_POWER_BUTTON)) += platform_mrfld_power_btn.o
|
||||
obj-$(subst m,y,$(CONFIG_RTC_DRV_CMOS)) += platform_mrfld_rtc.o
|
||||
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_mrfld_wdt.o
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#define pr_fmt(fmt) "blk-crypto: " fmt
|
||||
|
||||
#include <linux/keyslot-manager.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
@ -132,6 +133,34 @@ int blk_ksm_init(struct blk_keyslot_manager *ksm, unsigned int num_slots)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(blk_ksm_init);
|
||||
|
||||
static void blk_ksm_destroy_callback(void *ksm)
|
||||
{
|
||||
blk_ksm_destroy(ksm);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_blk_ksm_init() - Resource-managed blk_ksm_init()
|
||||
* @dev: The device which owns the blk_keyslot_manager.
|
||||
* @ksm: The blk_keyslot_manager to initialize.
|
||||
* @num_slots: The number of key slots to manage.
|
||||
*
|
||||
* Like blk_ksm_init(), but causes blk_ksm_destroy() to be called automatically
|
||||
* on driver detach.
|
||||
*
|
||||
* Return: 0 on success, or else a negative error code.
|
||||
*/
|
||||
int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm,
|
||||
unsigned int num_slots)
|
||||
{
|
||||
int err = blk_ksm_init(ksm, num_slots);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return devm_add_action_or_reset(dev, blk_ksm_destroy_callback, ksm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_blk_ksm_init);
|
||||
|
||||
static inline struct hlist_head *
|
||||
blk_ksm_hash_bucket_for_key(struct blk_keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key)
|
||||
|
|
|
|||
|
|
@ -670,27 +670,24 @@ static int thermal_get_trend(struct thermal_zone_device *thermal,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int thermal_notify(struct thermal_zone_device *thermal, int trip,
|
||||
enum thermal_trip_type trip_type)
|
||||
static void acpi_thermal_zone_device_hot(struct thermal_zone_device *thermal)
|
||||
{
|
||||
u8 type = 0;
|
||||
struct acpi_thermal *tz = thermal->devdata;
|
||||
|
||||
if (trip_type == THERMAL_TRIP_CRITICAL)
|
||||
type = ACPI_THERMAL_NOTIFY_CRITICAL;
|
||||
else if (trip_type == THERMAL_TRIP_HOT)
|
||||
type = ACPI_THERMAL_NOTIFY_HOT;
|
||||
else
|
||||
return 0;
|
||||
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
|
||||
dev_name(&tz->device->dev),
|
||||
ACPI_THERMAL_NOTIFY_HOT, 1);
|
||||
}
|
||||
|
||||
static void acpi_thermal_zone_device_critical(struct thermal_zone_device *thermal)
|
||||
{
|
||||
struct acpi_thermal *tz = thermal->devdata;
|
||||
|
||||
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
|
||||
dev_name(&tz->device->dev), type, 1);
|
||||
dev_name(&tz->device->dev),
|
||||
ACPI_THERMAL_NOTIFY_CRITICAL, 1);
|
||||
|
||||
if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
thermal_zone_device_critical(thermal);
|
||||
}
|
||||
|
||||
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
|
||||
|
|
@ -760,25 +757,6 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tz->devices.count; i++) {
|
||||
handle = tz->devices.handles[i];
|
||||
status = acpi_bus_get_device(handle, &dev);
|
||||
if (ACPI_SUCCESS(status) && (dev == device)) {
|
||||
if (bind)
|
||||
result = thermal_zone_bind_cooling_device
|
||||
(thermal, THERMAL_TRIPS_NONE,
|
||||
cdev, THERMAL_NO_LIMIT,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
else
|
||||
result = thermal_zone_unbind_cooling_device
|
||||
(thermal, THERMAL_TRIPS_NONE,
|
||||
cdev);
|
||||
if (result)
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
failed:
|
||||
return result;
|
||||
}
|
||||
|
|
@ -805,7 +783,8 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
|
|||
.get_trip_temp = thermal_get_trip_temp,
|
||||
.get_crit_temp = thermal_get_crit_temp,
|
||||
.get_trend = thermal_get_trend,
|
||||
.notify = thermal_notify,
|
||||
.hot = acpi_thermal_zone_device_hot,
|
||||
.critical = acpi_thermal_zone_device_critical,
|
||||
};
|
||||
|
||||
static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
|
||||
|
|
|
|||
|
|
@ -507,6 +507,3 @@ config PANEL
|
|||
depends on PARPORT
|
||||
select AUXDISPLAY
|
||||
select PARPORT_PANEL
|
||||
|
||||
config CHARLCD
|
||||
tristate "Character LCD core support" if COMPILE_TEST
|
||||
|
|
|
|||
|
|
@ -117,8 +117,7 @@ static void ht16k33_fb_queue(struct ht16k33_priv *priv)
|
|||
{
|
||||
struct ht16k33_fbdev *fbdev = &priv->fbdev;
|
||||
|
||||
schedule_delayed_work(&fbdev->work,
|
||||
msecs_to_jiffies(HZ / fbdev->refresh_rate));
|
||||
schedule_delayed_work(&fbdev->work, HZ / fbdev->refresh_rate);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -402,11 +401,6 @@ static int ht16k33_probe(struct i2c_client *client,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (client->irq <= 0) {
|
||||
dev_err(&client->dev, "No IRQ specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
|
@ -459,9 +453,12 @@ static int ht16k33_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
goto err_fbdev_info;
|
||||
|
||||
err = ht16k33_keypad_probe(client, &priv->keypad);
|
||||
if (err)
|
||||
goto err_fbdev_unregister;
|
||||
/* Keypad */
|
||||
if (client->irq > 0) {
|
||||
err = ht16k33_keypad_probe(client, &priv->keypad);
|
||||
if (err)
|
||||
goto err_fbdev_unregister;
|
||||
}
|
||||
|
||||
/* Backlight */
|
||||
memset(&bl_props, 0, sizeof(struct backlight_properties));
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ static int regcache_hw_init(struct regmap *map)
|
|||
map->cache_bypass = cache_bypass;
|
||||
if (ret == 0) {
|
||||
map->reg_defaults_raw = tmp_buf;
|
||||
map->cache_free = 1;
|
||||
map->cache_free = true;
|
||||
} else {
|
||||
kfree(tmp_buf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -965,8 +965,11 @@ EXPORT_SYMBOL(qcom_scm_ice_available);
|
|||
* qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
|
||||
* @index: the keyslot to invalidate
|
||||
*
|
||||
* The UFSHCI standard defines a standard way to do this, but it doesn't work on
|
||||
* these SoCs; only this SCM call does.
|
||||
* The UFSHCI and eMMC standards define a standard way to do this, but it
|
||||
* doesn't work on these SoCs; only this SCM call does.
|
||||
*
|
||||
* It is assumed that the SoC has only one ICE instance being used, as this SCM
|
||||
* call doesn't specify which ICE instance the keyslot belongs to.
|
||||
*
|
||||
* Return: 0 on success; -errno on failure.
|
||||
*/
|
||||
|
|
@ -995,10 +998,13 @@ EXPORT_SYMBOL(qcom_scm_ice_invalidate_key);
|
|||
* units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc.
|
||||
*
|
||||
* Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it
|
||||
* can then be used to encrypt/decrypt UFS I/O requests inline.
|
||||
* can then be used to encrypt/decrypt UFS or eMMC I/O requests inline.
|
||||
*
|
||||
* The UFSHCI standard defines a standard way to do this, but it doesn't work on
|
||||
* these SoCs; only this SCM call does.
|
||||
* The UFSHCI and eMMC standards define a standard way to do this, but it
|
||||
* doesn't work on these SoCs; only this SCM call does.
|
||||
*
|
||||
* It is assumed that the SoC has only one ICE instance being used, as this SCM
|
||||
* call doesn't specify which ICE instance the keyslot belongs to.
|
||||
*
|
||||
* Return: 0 on success; -errno on failure.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1253,13 +1253,6 @@ config GPIO_MAX77650
|
|||
GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor.
|
||||
These chips have a single pin that can be configured as GPIO.
|
||||
|
||||
config GPIO_MSIC
|
||||
bool "Intel MSIC mixed signal gpio support"
|
||||
depends on (X86 || COMPILE_TEST) && MFD_INTEL_MSIC
|
||||
help
|
||||
Enable support for GPIO on intel MSIC controllers found in
|
||||
intel MID devices
|
||||
|
||||
config GPIO_PALMAS
|
||||
bool "TI PALMAS series PMICs GPIO"
|
||||
depends on MFD_PALMAS
|
||||
|
|
@ -1455,13 +1448,6 @@ config GPIO_BT8XX
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config GPIO_INTEL_MID
|
||||
bool "Intel MID GPIO support"
|
||||
depends on X86_INTEL_MID
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to support Intel MID GPIO.
|
||||
|
||||
config GPIO_MERRIFIELD
|
||||
tristate "Intel Merrifield GPIO support"
|
||||
depends on X86_INTEL_MID
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o
|
|||
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
|
||||
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
|
||||
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
|
||||
obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
|
||||
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
|
||||
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
|
||||
obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ for a few GPIOs. Those should stay where they are.
|
|||
|
||||
At the same time it makes sense to get rid of code duplication in existing or
|
||||
new coming drivers. For example, gpio-ml-ioh should be incorporated into
|
||||
gpio-pch. In similar way gpio-intel-mid into gpio-pxa.
|
||||
gpio-pch.
|
||||
|
||||
|
||||
Generic MMIO GPIO
|
||||
|
|
|
|||
|
|
@ -1,31 +1,24 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* ROHM BD9571MWV-M GPIO driver
|
||||
* ROHM BD9571MWV-M and BD9574MWF-M GPIO driver
|
||||
*
|
||||
* Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether expressed or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License version 2 for more details.
|
||||
*
|
||||
* Based on the TPS65086 driver
|
||||
*
|
||||
* NOTE: Interrupts are not supported yet.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/rohm-generic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/mfd/bd9571mwv.h>
|
||||
|
||||
struct bd9571mwv_gpio {
|
||||
struct regmap *regmap;
|
||||
struct gpio_chip chip;
|
||||
struct bd9571mwv *bd;
|
||||
};
|
||||
|
||||
static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip,
|
||||
|
|
@ -34,7 +27,7 @@ static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip,
|
|||
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
|
||||
int ret, val;
|
||||
|
||||
ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_DIR, &val);
|
||||
ret = regmap_read(gpio->regmap, BD9571MWV_GPIO_DIR, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (val & BIT(offset))
|
||||
|
|
@ -48,8 +41,7 @@ static int bd9571mwv_gpio_direction_input(struct gpio_chip *chip,
|
|||
{
|
||||
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
|
||||
|
||||
regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_DIR,
|
||||
BIT(offset), 0);
|
||||
regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_DIR, BIT(offset), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -60,9 +52,9 @@ static int bd9571mwv_gpio_direction_output(struct gpio_chip *chip,
|
|||
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
|
||||
|
||||
/* Set the initial value */
|
||||
regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_OUT,
|
||||
regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT,
|
||||
BIT(offset), value ? BIT(offset) : 0);
|
||||
regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_DIR,
|
||||
regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_DIR,
|
||||
BIT(offset), BIT(offset));
|
||||
|
||||
return 0;
|
||||
|
|
@ -73,7 +65,7 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
|||
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
|
||||
int ret, val;
|
||||
|
||||
ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_IN, &val);
|
||||
ret = regmap_read(gpio->regmap, BD9571MWV_GPIO_IN, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -85,7 +77,7 @@ static void bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
|||
{
|
||||
struct bd9571mwv_gpio *gpio = gpiochip_get_data(chip);
|
||||
|
||||
regmap_update_bits(gpio->bd->regmap, BD9571MWV_GPIO_OUT,
|
||||
regmap_update_bits(gpio->regmap, BD9571MWV_GPIO_OUT,
|
||||
BIT(offset), value ? BIT(offset) : 0);
|
||||
}
|
||||
|
||||
|
|
@ -113,9 +105,9 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, gpio);
|
||||
|
||||
gpio->bd = dev_get_drvdata(pdev->dev.parent);
|
||||
gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
gpio->chip = template_chip;
|
||||
gpio->chip.parent = gpio->bd->dev;
|
||||
gpio->chip.parent = pdev->dev.parent;
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
|
||||
if (ret < 0) {
|
||||
|
|
@ -127,7 +119,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct platform_device_id bd9571mwv_gpio_id_table[] = {
|
||||
{ "bd9571mwv-gpio", },
|
||||
{ "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 },
|
||||
{ "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table);
|
||||
|
|
|
|||
|
|
@ -1,414 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel MID GPIO driver
|
||||
*
|
||||
* Copyright (c) 2008-2014,2016 Intel Corporation.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
* Moorestown platform Langwell chip.
|
||||
* Medfield platform Penwell chip.
|
||||
* Clovertrail platform Cloverview chip.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#define INTEL_MID_IRQ_TYPE_EDGE (1 << 0)
|
||||
#define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1)
|
||||
|
||||
/*
|
||||
* Langwell chip has 64 pins and thus there are 2 32bit registers to control
|
||||
* each feature, while Penwell chip has 96 pins for each block, and need 3 32bit
|
||||
* registers to control them, so we only define the order here instead of a
|
||||
* structure, to get a bit offset for a pin (use GPDR as an example):
|
||||
*
|
||||
* nreg = ngpio / 32;
|
||||
* reg = offset / 32;
|
||||
* bit = offset % 32;
|
||||
* reg_addr = reg_base + GPDR * nreg * 4 + reg * 4;
|
||||
*
|
||||
* so the bit of reg_addr is to control pin offset's GPDR feature
|
||||
*/
|
||||
|
||||
enum GPIO_REG {
|
||||
GPLR = 0, /* pin level read-only */
|
||||
GPDR, /* pin direction */
|
||||
GPSR, /* pin set */
|
||||
GPCR, /* pin clear */
|
||||
GRER, /* rising edge detect */
|
||||
GFER, /* falling edge detect */
|
||||
GEDR, /* edge detect result */
|
||||
GAFR, /* alt function */
|
||||
};
|
||||
|
||||
/* intel_mid gpio driver data */
|
||||
struct intel_mid_gpio_ddata {
|
||||
u16 ngpio; /* number of gpio pins */
|
||||
u32 chip_irq_type; /* chip interrupt type */
|
||||
};
|
||||
|
||||
struct intel_mid_gpio {
|
||||
struct gpio_chip chip;
|
||||
void __iomem *reg_base;
|
||||
spinlock_t lock;
|
||||
struct pci_dev *pdev;
|
||||
};
|
||||
|
||||
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
|
||||
enum GPIO_REG reg_type)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned nreg = chip->ngpio / 32;
|
||||
u8 reg = offset / 32;
|
||||
|
||||
return priv->reg_base + reg_type * nreg * 4 + reg * 4;
|
||||
}
|
||||
|
||||
static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset,
|
||||
enum GPIO_REG reg_type)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned nreg = chip->ngpio / 32;
|
||||
u8 reg = offset / 16;
|
||||
|
||||
return priv->reg_base + reg_type * nreg * 4 + reg * 4;
|
||||
}
|
||||
|
||||
static int intel_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR);
|
||||
u32 value = readl(gafr);
|
||||
int shift = (offset % 16) << 1, af = (value >> shift) & 3;
|
||||
|
||||
if (af) {
|
||||
value &= ~(3 << shift);
|
||||
writel(value, gafr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
void __iomem *gplr = gpio_reg(chip, offset, GPLR);
|
||||
|
||||
return !!(readl(gplr) & BIT(offset % 32));
|
||||
}
|
||||
|
||||
static void intel_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
void __iomem *gpsr, *gpcr;
|
||||
|
||||
if (value) {
|
||||
gpsr = gpio_reg(chip, offset, GPSR);
|
||||
writel(BIT(offset % 32), gpsr);
|
||||
} else {
|
||||
gpcr = gpio_reg(chip, offset, GPCR);
|
||||
writel(BIT(offset % 32), gpcr);
|
||||
}
|
||||
}
|
||||
|
||||
static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_get(&priv->pdev->dev);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
value = readl(gpdr);
|
||||
value &= ~BIT(offset % 32);
|
||||
writel(value, gpdr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_put(&priv->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
|
||||
unsigned long flags;
|
||||
|
||||
intel_gpio_set(chip, offset, value);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_get(&priv->pdev->dev);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
value = readl(gpdr);
|
||||
value |= BIT(offset % 32);
|
||||
writel(value, gpdr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_put(&priv->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_mid_irq_type(struct irq_data *d, unsigned type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(gc);
|
||||
u32 gpio = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
|
||||
void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
|
||||
|
||||
if (gpio >= priv->chip.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_get(&priv->pdev->dev);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
value = readl(grer) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(grer) & (~BIT(gpio % 32));
|
||||
writel(value, grer);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
value = readl(gfer) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(gfer) & (~BIT(gpio % 32));
|
||||
writel(value, gfer);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_put(&priv->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_mid_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void intel_mid_irq_mask(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static struct irq_chip intel_mid_irqchip = {
|
||||
.name = "INTEL_MID-GPIO",
|
||||
.irq_mask = intel_mid_irq_mask,
|
||||
.irq_unmask = intel_mid_irq_unmask,
|
||||
.irq_set_type = intel_mid_irq_type,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_lincroft = {
|
||||
.ngpio = 64,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_penwell_aon = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_penwell_core = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_cloverview_aon = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE | INTEL_MID_IRQ_TYPE_LEVEL,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_cloverview_core = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct pci_device_id intel_gpio_ids[] = {
|
||||
{
|
||||
/* Lincroft */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f),
|
||||
.driver_data = (kernel_ulong_t)&gpio_lincroft,
|
||||
},
|
||||
{
|
||||
/* Penwell AON */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f),
|
||||
.driver_data = (kernel_ulong_t)&gpio_penwell_aon,
|
||||
},
|
||||
{
|
||||
/* Penwell Core */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a),
|
||||
.driver_data = (kernel_ulong_t)&gpio_penwell_core,
|
||||
},
|
||||
{
|
||||
/* Cloverview Aon */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb),
|
||||
.driver_data = (kernel_ulong_t)&gpio_cloverview_aon,
|
||||
},
|
||||
{
|
||||
/* Cloverview Core */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
|
||||
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static void intel_mid_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(gc);
|
||||
struct irq_data *data = irq_desc_get_irq_data(desc);
|
||||
struct irq_chip *chip = irq_data_get_irq_chip(data);
|
||||
u32 base, gpio, mask;
|
||||
unsigned long pending;
|
||||
void __iomem *gedr;
|
||||
|
||||
/* check GPIO controller to check which pin triggered the interrupt */
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32) {
|
||||
gedr = gpio_reg(&priv->chip, base, GEDR);
|
||||
while ((pending = readl(gedr))) {
|
||||
gpio = __ffs(pending);
|
||||
mask = BIT(gpio);
|
||||
/* Clear before handling so we can't lose an edge */
|
||||
writel(mask, gedr);
|
||||
generic_handle_irq(irq_find_mapping(gc->irq.domain,
|
||||
base + gpio));
|
||||
}
|
||||
}
|
||||
|
||||
chip->irq_eoi(data);
|
||||
}
|
||||
|
||||
static int intel_mid_irq_init_hw(struct gpio_chip *chip)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *reg;
|
||||
unsigned base;
|
||||
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32) {
|
||||
/* Clear the rising-edge detect register */
|
||||
reg = gpio_reg(&priv->chip, base, GRER);
|
||||
writel(0, reg);
|
||||
/* Clear the falling-edge detect register */
|
||||
reg = gpio_reg(&priv->chip, base, GFER);
|
||||
writel(0, reg);
|
||||
/* Clear the edge detect status register */
|
||||
reg = gpio_reg(&priv->chip, base, GEDR);
|
||||
writel(~0, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused intel_gpio_runtime_idle(struct device *dev)
|
||||
{
|
||||
int err = pm_schedule_suspend(dev, 500);
|
||||
return err ?: -EBUSY;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops intel_gpio_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(NULL, NULL, intel_gpio_runtime_idle)
|
||||
};
|
||||
|
||||
static int intel_gpio_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct intel_mid_gpio *priv;
|
||||
u32 gpio_base;
|
||||
u32 irq_base;
|
||||
int retval;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct intel_mid_gpio_ddata *ddata =
|
||||
(struct intel_mid_gpio_ddata *)id->driver_data;
|
||||
|
||||
retval = pcim_enable_device(pdev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev));
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "I/O memory mapping error\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
base = pcim_iomap_table(pdev)[1];
|
||||
|
||||
irq_base = readl(base);
|
||||
gpio_base = readl(sizeof(u32) + base);
|
||||
|
||||
/* release the IO mapping, since we already get the info from bar1 */
|
||||
pcim_iounmap_regions(pdev, 1 << 1);
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->reg_base = pcim_iomap_table(pdev)[0];
|
||||
priv->chip.label = dev_name(&pdev->dev);
|
||||
priv->chip.parent = &pdev->dev;
|
||||
priv->chip.request = intel_gpio_request;
|
||||
priv->chip.direction_input = intel_gpio_direction_input;
|
||||
priv->chip.direction_output = intel_gpio_direction_output;
|
||||
priv->chip.get = intel_gpio_get;
|
||||
priv->chip.set = intel_gpio_set;
|
||||
priv->chip.base = gpio_base;
|
||||
priv->chip.ngpio = ddata->ngpio;
|
||||
priv->chip.can_sleep = false;
|
||||
priv->pdev = pdev;
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
girq = &priv->chip.irq;
|
||||
girq->chip = &intel_mid_irqchip;
|
||||
girq->init_hw = intel_mid_irq_init_hw;
|
||||
girq->parent_handler = intel_mid_irq_handler;
|
||||
girq->num_parents = 1;
|
||||
girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents,
|
||||
sizeof(*girq->parents),
|
||||
GFP_KERNEL);
|
||||
if (!girq->parents)
|
||||
return -ENOMEM;
|
||||
girq->parents[0] = pdev->irq;
|
||||
girq->first = irq_base;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_simple_irq;
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_driver intel_gpio_driver = {
|
||||
.name = "intel_mid_gpio",
|
||||
.id_table = intel_gpio_ids,
|
||||
.probe = intel_gpio_probe,
|
||||
.driver = {
|
||||
.pm = &intel_gpio_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_pci_driver(intel_gpio_driver);
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel Medfield MSIC GPIO driver>
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
*
|
||||
* Author: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
* Based on intel_pmic_gpio.c
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/intel_msic.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* the offset for the mapping of global gpio pin to irq */
|
||||
#define MSIC_GPIO_IRQ_OFFSET 0x100
|
||||
|
||||
#define MSIC_GPIO_DIR_IN 0
|
||||
#define MSIC_GPIO_DIR_OUT BIT(5)
|
||||
#define MSIC_GPIO_TRIG_FALL BIT(1)
|
||||
#define MSIC_GPIO_TRIG_RISE BIT(2)
|
||||
|
||||
/* masks for msic gpio output GPIOxxxxCTLO registers */
|
||||
#define MSIC_GPIO_DIR_MASK BIT(5)
|
||||
#define MSIC_GPIO_DRV_MASK BIT(4)
|
||||
#define MSIC_GPIO_REN_MASK BIT(3)
|
||||
#define MSIC_GPIO_RVAL_MASK (BIT(2) | BIT(1))
|
||||
#define MSIC_GPIO_DOUT_MASK BIT(0)
|
||||
|
||||
/* masks for msic gpio input GPIOxxxxCTLI registers */
|
||||
#define MSIC_GPIO_GLBYP_MASK BIT(5)
|
||||
#define MSIC_GPIO_DBNC_MASK (BIT(4) | BIT(3))
|
||||
#define MSIC_GPIO_INTCNT_MASK (BIT(2) | BIT(1))
|
||||
#define MSIC_GPIO_DIN_MASK BIT(0)
|
||||
|
||||
#define MSIC_NUM_GPIO 24
|
||||
|
||||
struct msic_gpio {
|
||||
struct platform_device *pdev;
|
||||
struct mutex buslock;
|
||||
struct gpio_chip chip;
|
||||
int irq;
|
||||
unsigned irq_base;
|
||||
unsigned long trig_change_mask;
|
||||
unsigned trig_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* MSIC has 24 gpios, 16 low voltage (1.2-1.8v) and 8 high voltage (3v).
|
||||
* Both the high and low voltage gpios are divided in two banks.
|
||||
* GPIOs are numbered with GPIO0LV0 as gpio_base in the following order:
|
||||
* GPIO0LV0..GPIO0LV7: low voltage, bank 0, gpio_base
|
||||
* GPIO1LV0..GPIO1LV7: low voltage, bank 1, gpio_base + 8
|
||||
* GPIO0HV0..GPIO0HV3: high voltage, bank 0, gpio_base + 16
|
||||
* GPIO1HV0..GPIO1HV3: high voltage, bank 1, gpio_base + 20
|
||||
*/
|
||||
|
||||
static int msic_gpio_to_ireg(unsigned offset)
|
||||
{
|
||||
if (offset >= MSIC_NUM_GPIO)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset < 8)
|
||||
return INTEL_MSIC_GPIO0LV0CTLI - offset;
|
||||
if (offset < 16)
|
||||
return INTEL_MSIC_GPIO1LV0CTLI - offset + 8;
|
||||
if (offset < 20)
|
||||
return INTEL_MSIC_GPIO0HV0CTLI - offset + 16;
|
||||
|
||||
return INTEL_MSIC_GPIO1HV0CTLI - offset + 20;
|
||||
}
|
||||
|
||||
static int msic_gpio_to_oreg(unsigned offset)
|
||||
{
|
||||
if (offset >= MSIC_NUM_GPIO)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset < 8)
|
||||
return INTEL_MSIC_GPIO0LV0CTLO - offset;
|
||||
if (offset < 16)
|
||||
return INTEL_MSIC_GPIO1LV0CTLO - offset + 8;
|
||||
if (offset < 20)
|
||||
return INTEL_MSIC_GPIO0HV0CTLO - offset + 16;
|
||||
|
||||
return INTEL_MSIC_GPIO1HV0CTLO - offset + 20;
|
||||
}
|
||||
|
||||
static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = msic_gpio_to_oreg(offset);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return intel_msic_reg_update(reg, MSIC_GPIO_DIR_IN, MSIC_GPIO_DIR_MASK);
|
||||
}
|
||||
|
||||
static int msic_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
int reg;
|
||||
unsigned mask;
|
||||
|
||||
value = (!!value) | MSIC_GPIO_DIR_OUT;
|
||||
mask = MSIC_GPIO_DIR_MASK | MSIC_GPIO_DOUT_MASK;
|
||||
|
||||
reg = msic_gpio_to_oreg(offset);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return intel_msic_reg_update(reg, value, mask);
|
||||
}
|
||||
|
||||
static int msic_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
u8 r;
|
||||
int ret;
|
||||
int reg;
|
||||
|
||||
reg = msic_gpio_to_ireg(offset);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
ret = intel_msic_reg_read(reg, &r);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(r & MSIC_GPIO_DIN_MASK);
|
||||
}
|
||||
|
||||
static void msic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = msic_gpio_to_oreg(offset);
|
||||
if (reg < 0)
|
||||
return;
|
||||
|
||||
intel_msic_reg_update(reg, !!value , MSIC_GPIO_DOUT_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called from genirq with mg->buslock locked and
|
||||
* irq_desc->lock held. We can not access the scu bus here, so we
|
||||
* store the change and update in the bus_sync_unlock() function below
|
||||
*/
|
||||
static int msic_irq_type(struct irq_data *data, unsigned type)
|
||||
{
|
||||
struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
|
||||
u32 gpio = data->irq - mg->irq_base;
|
||||
|
||||
if (gpio >= mg->chip.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
/* mark for which gpio the trigger changed, protected by buslock */
|
||||
mg->trig_change_mask |= (1 << gpio);
|
||||
mg->trig_type = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct msic_gpio *mg = gpiochip_get_data(chip);
|
||||
return mg->irq_base + offset;
|
||||
}
|
||||
|
||||
static void msic_bus_lock(struct irq_data *data)
|
||||
{
|
||||
struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
|
||||
mutex_lock(&mg->buslock);
|
||||
}
|
||||
|
||||
static void msic_bus_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
|
||||
int offset;
|
||||
int reg;
|
||||
u8 trig = 0;
|
||||
|
||||
/* We can only get one change at a time as the buslock covers the
|
||||
entire transaction. The irq_desc->lock is dropped before we are
|
||||
called but that is fine */
|
||||
if (mg->trig_change_mask) {
|
||||
offset = __ffs(mg->trig_change_mask);
|
||||
|
||||
reg = msic_gpio_to_ireg(offset);
|
||||
if (reg < 0)
|
||||
goto out;
|
||||
|
||||
if (mg->trig_type & IRQ_TYPE_EDGE_RISING)
|
||||
trig |= MSIC_GPIO_TRIG_RISE;
|
||||
if (mg->trig_type & IRQ_TYPE_EDGE_FALLING)
|
||||
trig |= MSIC_GPIO_TRIG_FALL;
|
||||
|
||||
intel_msic_reg_update(reg, trig, MSIC_GPIO_INTCNT_MASK);
|
||||
mg->trig_change_mask = 0;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&mg->buslock);
|
||||
}
|
||||
|
||||
/* Firmware does all the masking and unmasking for us, no masking here. */
|
||||
static void msic_irq_unmask(struct irq_data *data) { }
|
||||
|
||||
static void msic_irq_mask(struct irq_data *data) { }
|
||||
|
||||
static struct irq_chip msic_irqchip = {
|
||||
.name = "MSIC-GPIO",
|
||||
.irq_mask = msic_irq_mask,
|
||||
.irq_unmask = msic_irq_unmask,
|
||||
.irq_set_type = msic_irq_type,
|
||||
.irq_bus_lock = msic_bus_lock,
|
||||
.irq_bus_sync_unlock = msic_bus_sync_unlock,
|
||||
};
|
||||
|
||||
static void msic_gpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_data *data = irq_desc_get_irq_data(desc);
|
||||
struct msic_gpio *mg = irq_data_get_irq_handler_data(data);
|
||||
struct irq_chip *chip = irq_data_get_irq_chip(data);
|
||||
struct intel_msic *msic = pdev_to_intel_msic(mg->pdev);
|
||||
unsigned long pending;
|
||||
int i;
|
||||
int bitnr;
|
||||
u8 pin;
|
||||
|
||||
for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) {
|
||||
intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin);
|
||||
pending = pin;
|
||||
|
||||
for_each_set_bit(bitnr, &pending, BITS_PER_BYTE)
|
||||
generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr);
|
||||
}
|
||||
chip->irq_eoi(data);
|
||||
}
|
||||
|
||||
static int platform_msic_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct intel_msic_gpio_pdata *pdata = dev_get_platdata(dev);
|
||||
struct msic_gpio *mg;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no IRQ line: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
if (!pdata || !pdata->gpio_base) {
|
||||
dev_err(dev, "incorrect or missing platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mg = kzalloc(sizeof(*mg), GFP_KERNEL);
|
||||
if (!mg)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, mg);
|
||||
|
||||
mg->pdev = pdev;
|
||||
mg->irq = irq;
|
||||
mg->irq_base = pdata->gpio_base + MSIC_GPIO_IRQ_OFFSET;
|
||||
mg->chip.label = "msic_gpio";
|
||||
mg->chip.direction_input = msic_gpio_direction_input;
|
||||
mg->chip.direction_output = msic_gpio_direction_output;
|
||||
mg->chip.get = msic_gpio_get;
|
||||
mg->chip.set = msic_gpio_set;
|
||||
mg->chip.to_irq = msic_gpio_to_irq;
|
||||
mg->chip.base = pdata->gpio_base;
|
||||
mg->chip.ngpio = MSIC_NUM_GPIO;
|
||||
mg->chip.can_sleep = true;
|
||||
mg->chip.parent = dev;
|
||||
|
||||
mutex_init(&mg->buslock);
|
||||
|
||||
retval = gpiochip_add_data(&mg->chip, mg);
|
||||
if (retval) {
|
||||
dev_err(dev, "Adding MSIC gpio chip failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < mg->chip.ngpio; i++) {
|
||||
irq_set_chip_data(i + mg->irq_base, mg);
|
||||
irq_set_chip_and_handler(i + mg->irq_base,
|
||||
&msic_irqchip,
|
||||
handle_simple_irq);
|
||||
}
|
||||
irq_set_chained_handler_and_data(mg->irq, msic_gpio_irq_handler, mg);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree(mg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct platform_driver platform_msic_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "msic_gpio",
|
||||
},
|
||||
.probe = platform_msic_gpio_probe,
|
||||
};
|
||||
|
||||
static int __init platform_msic_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&platform_msic_gpio_driver);
|
||||
}
|
||||
subsys_initcall(platform_msic_gpio_init);
|
||||
|
|
@ -3469,6 +3469,10 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table);
|
|||
*/
|
||||
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
|
||||
{
|
||||
/* Nothing to remove */
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
mutex_lock(&gpio_lookup_lock);
|
||||
|
||||
list_del(&table->list);
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#include <drm/drm.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
|
|
|
|||
|
|
@ -386,6 +386,8 @@ struct psb_ops;
|
|||
|
||||
#define PSB_NUM_PIPE 3
|
||||
|
||||
struct intel_scu_ipc_dev;
|
||||
|
||||
struct drm_psb_private {
|
||||
struct drm_device *dev;
|
||||
struct pci_dev *aux_pdev; /* Currently only used by mrst */
|
||||
|
|
@ -525,6 +527,7 @@ struct drm_psb_private {
|
|||
* Used for modifying backlight from
|
||||
* xrandr -- consider removing and using HAL instead
|
||||
*/
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
struct backlight_device *backlight_device;
|
||||
struct drm_property *backlight_property;
|
||||
bool backlight_enabled;
|
||||
|
|
|
|||
|
|
@ -38,19 +38,6 @@ config HWMON_DEBUG_CHIP
|
|||
|
||||
comment "Native drivers"
|
||||
|
||||
config SENSORS_AB8500
|
||||
tristate "AB8500 thermal monitoring"
|
||||
depends on AB8500_GPADC && AB8500_BM && (IIO = y)
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for the thermal sensor part
|
||||
of the AB8500 chip. The driver includes thermal management for
|
||||
AB8500 die and two GPADC channels. The GPADC channel are preferably
|
||||
used to access sensors outside the AB8500 chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abx500-temp.
|
||||
|
||||
config SENSORS_ABITUGURU
|
||||
tristate "Abit uGuru (rev 1 & 2)"
|
||||
depends on X86 && DMI
|
||||
|
|
@ -257,6 +244,16 @@ config SENSORS_ADT7475
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called adt7475.
|
||||
|
||||
config SENSORS_AHT10
|
||||
tristate "Aosong AHT10"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here, you get support for the Aosong AHT10
|
||||
temperature and humidity sensors
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called aht10.
|
||||
|
||||
config SENSORS_AS370
|
||||
tristate "Synaptics AS370 SoC hardware monitoring driver"
|
||||
help
|
||||
|
|
@ -1136,6 +1133,17 @@ config SENSORS_TC654
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called tc654.
|
||||
|
||||
config SENSORS_TPS23861
|
||||
tristate "Texas Instruments TPS23861 PoE PSE"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments
|
||||
TPS23861 802.3at PoE PSE chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps23861.
|
||||
|
||||
config SENSORS_MENF21BMC_HWMON
|
||||
tristate "MEN 14F021P00 BMC Hardware Monitoring"
|
||||
depends on MFD_MENF21BMC
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
|
|||
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
|
||||
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
|
||||
|
|
@ -45,6 +44,7 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
|
|||
obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
|
||||
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
|
||||
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
|
||||
obj-$(CONFIG_SENSORS_AHT10) += aht10.o
|
||||
obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o
|
||||
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
||||
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
|
||||
|
|
@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
|
|||
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
|
||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||
obj-$(CONFIG_SENSORS_TC654) += tc654.o
|
||||
obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o
|
||||
obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
|
||||
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
||||
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
|
||||
|
|
|
|||
|
|
@ -1,224 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson 2010 - 2013
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
* Hongbo Zhang <hongbo.zhang@linaro.org>
|
||||
*
|
||||
* When the AB8500 thermal warning temperature is reached (threshold cannot
|
||||
* be changed by SW), an interrupt is set, and if no further action is taken
|
||||
* within a certain time frame, kernel_power_off will be called.
|
||||
*
|
||||
* When AB8500 thermal shutdown temperature is reached a hardware shutdown of
|
||||
* the AB8500 will occur.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500-bm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power/ab8500.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include "abx500.h"
|
||||
|
||||
#define DEFAULT_POWER_OFF_DELAY (HZ * 10)
|
||||
#define THERMAL_VCC 1800
|
||||
#define PULL_UP_RESISTOR 47000
|
||||
|
||||
#define AB8500_SENSOR_AUX1 0
|
||||
#define AB8500_SENSOR_AUX2 1
|
||||
#define AB8500_SENSOR_BTEMP_BALL 2
|
||||
#define AB8500_SENSOR_BAT_CTRL 3
|
||||
#define NUM_MONITORED_SENSORS 4
|
||||
|
||||
struct ab8500_gpadc_cfg {
|
||||
const struct abx500_res_to_temp *temp_tbl;
|
||||
int tbl_sz;
|
||||
int vcc;
|
||||
int r_up;
|
||||
};
|
||||
|
||||
struct ab8500_temp {
|
||||
struct iio_channel *aux1;
|
||||
struct iio_channel *aux2;
|
||||
struct ab8500_btemp *btemp;
|
||||
struct delayed_work power_off_work;
|
||||
struct ab8500_gpadc_cfg cfg;
|
||||
struct abx500_temp *abx500_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* The hardware connection is like this:
|
||||
* VCC----[ R_up ]-----[ NTC ]----GND
|
||||
* where R_up is pull-up resistance, and GPADC measures voltage on NTC.
|
||||
* and res_to_temp table is strictly sorted by falling resistance values.
|
||||
*/
|
||||
static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg,
|
||||
int v_ntc, int *temp)
|
||||
{
|
||||
int r_ntc, i = 0, tbl_sz = cfg->tbl_sz;
|
||||
const struct abx500_res_to_temp *tbl = cfg->temp_tbl;
|
||||
|
||||
if (cfg->vcc < 0 || v_ntc >= cfg->vcc)
|
||||
return -EINVAL;
|
||||
|
||||
r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc);
|
||||
if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist)
|
||||
return -EINVAL;
|
||||
|
||||
while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) &&
|
||||
i < tbl_sz - 2)
|
||||
i++;
|
||||
|
||||
/* return milli-Celsius */
|
||||
*temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 *
|
||||
(r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp)
|
||||
{
|
||||
int voltage, ret;
|
||||
struct ab8500_temp *ab8500_data = data->plat_data;
|
||||
|
||||
if (sensor == AB8500_SENSOR_BTEMP_BALL) {
|
||||
*temp = ab8500_btemp_get_temp(ab8500_data->btemp);
|
||||
} else if (sensor == AB8500_SENSOR_BAT_CTRL) {
|
||||
*temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp);
|
||||
} else if (sensor == AB8500_SENSOR_AUX1) {
|
||||
ret = iio_read_channel_processed(ab8500_data->aux1, &voltage);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (sensor == AB8500_SENSOR_AUX2) {
|
||||
ret = iio_read_channel_processed(ab8500_data->aux2, &voltage);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_thermal_power_off(struct work_struct *work)
|
||||
{
|
||||
struct ab8500_temp *ab8500_data = container_of(work,
|
||||
struct ab8500_temp, power_off_work.work);
|
||||
struct abx500_temp *abx500_data = ab8500_data->abx500_data;
|
||||
|
||||
dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n");
|
||||
|
||||
kernel_power_off();
|
||||
}
|
||||
|
||||
static ssize_t ab8500_show_name(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "ab8500\n");
|
||||
}
|
||||
|
||||
static ssize_t ab8500_show_label(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
char *label;
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
int index = attr->index;
|
||||
|
||||
switch (index) {
|
||||
case 1:
|
||||
label = "ext_adc1";
|
||||
break;
|
||||
case 2:
|
||||
label = "ext_adc2";
|
||||
break;
|
||||
case 3:
|
||||
label = "bat_temp";
|
||||
break;
|
||||
case 4:
|
||||
label = "bat_ctrl";
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", label);
|
||||
}
|
||||
|
||||
static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
|
||||
{
|
||||
struct ab8500_temp *ab8500_data = data->plat_data;
|
||||
|
||||
dev_warn(&data->pdev->dev, "Power off in %d s\n",
|
||||
DEFAULT_POWER_OFF_DELAY / HZ);
|
||||
|
||||
schedule_delayed_work(&ab8500_data->power_off_work,
|
||||
DEFAULT_POWER_OFF_DELAY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int abx500_hwmon_init(struct abx500_temp *data)
|
||||
{
|
||||
struct ab8500_temp *ab8500_data;
|
||||
|
||||
ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data),
|
||||
GFP_KERNEL);
|
||||
if (!ab8500_data)
|
||||
return -ENOMEM;
|
||||
|
||||
ab8500_data->btemp = ab8500_btemp_get();
|
||||
if (IS_ERR(ab8500_data->btemp))
|
||||
return PTR_ERR(ab8500_data->btemp);
|
||||
|
||||
INIT_DELAYED_WORK(&ab8500_data->power_off_work,
|
||||
ab8500_thermal_power_off);
|
||||
|
||||
ab8500_data->cfg.vcc = THERMAL_VCC;
|
||||
ab8500_data->cfg.r_up = PULL_UP_RESISTOR;
|
||||
ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor;
|
||||
ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size;
|
||||
|
||||
data->plat_data = ab8500_data;
|
||||
ab8500_data->aux1 = devm_iio_channel_get(&data->pdev->dev, "aux1");
|
||||
if (IS_ERR(ab8500_data->aux1)) {
|
||||
if (PTR_ERR(ab8500_data->aux1) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(&data->pdev->dev, "failed to get AUX1 ADC channel\n");
|
||||
return PTR_ERR(ab8500_data->aux1);
|
||||
}
|
||||
ab8500_data->aux2 = devm_iio_channel_get(&data->pdev->dev, "aux2");
|
||||
if (IS_ERR(ab8500_data->aux2)) {
|
||||
if (PTR_ERR(ab8500_data->aux2) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(&data->pdev->dev, "failed to get AUX2 ADC channel\n");
|
||||
return PTR_ERR(ab8500_data->aux2);
|
||||
}
|
||||
|
||||
data->gpadc_addr[0] = AB8500_SENSOR_AUX1;
|
||||
data->gpadc_addr[1] = AB8500_SENSOR_AUX2;
|
||||
data->gpadc_addr[2] = AB8500_SENSOR_BTEMP_BALL;
|
||||
data->gpadc_addr[3] = AB8500_SENSOR_BAT_CTRL;
|
||||
data->monitored_sensors = NUM_MONITORED_SENSORS;
|
||||
|
||||
data->ops.read_sensor = ab8500_read_sensor;
|
||||
data->ops.irq_handler = ab8500_temp_irq_handler;
|
||||
data->ops.show_name = ab8500_show_name;
|
||||
data->ops.show_label = ab8500_show_label;
|
||||
data->ops.is_visible = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_hwmon_init);
|
||||
|
||||
MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>");
|
||||
MODULE_DESCRIPTION("AB8500 temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,487 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson 2010 - 2013
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
* Hongbo Zhang <hongbo.zhang@linaro.org>
|
||||
*
|
||||
* ABX500 does not provide auto ADC, so to monitor the required temperatures,
|
||||
* a periodic work is used. It is more important to not wake up the CPU than
|
||||
* to perform this job, hence the use of a deferred delay.
|
||||
*
|
||||
* A deferred delay for thermal monitor is considered safe because:
|
||||
* If the chip gets too hot during a sleep state it's most likely due to
|
||||
* external factors, such as the surrounding temperature. I.e. no SW decisions
|
||||
* will make any difference.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "abx500.h"
|
||||
|
||||
#define DEFAULT_MONITOR_DELAY HZ
|
||||
#define DEFAULT_MAX_TEMP 130
|
||||
|
||||
static inline void schedule_monitor(struct abx500_temp *data)
|
||||
{
|
||||
data->work_active = true;
|
||||
schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY);
|
||||
}
|
||||
|
||||
static void threshold_updated(struct abx500_temp *data)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < data->monitored_sensors; i++)
|
||||
if (data->max[i] != 0 || data->min[i] != 0) {
|
||||
schedule_monitor(data);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(&data->pdev->dev, "No active thresholds.\n");
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
data->work_active = false;
|
||||
}
|
||||
|
||||
static void gpadc_monitor(struct work_struct *work)
|
||||
{
|
||||
int temp, i, ret;
|
||||
char alarm_node[30];
|
||||
bool updated_min_alarm, updated_max_alarm;
|
||||
struct abx500_temp *data;
|
||||
|
||||
data = container_of(work, struct abx500_temp, work.work);
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
for (i = 0; i < data->monitored_sensors; i++) {
|
||||
/* Thresholds are considered inactive if set to 0 */
|
||||
if (data->max[i] == 0 && data->min[i] == 0)
|
||||
continue;
|
||||
|
||||
if (data->max[i] < data->min[i])
|
||||
continue;
|
||||
|
||||
ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->pdev->dev, "GPADC read failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
updated_min_alarm = false;
|
||||
updated_max_alarm = false;
|
||||
|
||||
if (data->min[i] != 0) {
|
||||
if (temp < data->min[i]) {
|
||||
if (data->min_alarm[i] == false) {
|
||||
data->min_alarm[i] = true;
|
||||
updated_min_alarm = true;
|
||||
}
|
||||
} else {
|
||||
if (data->min_alarm[i] == true) {
|
||||
data->min_alarm[i] = false;
|
||||
updated_min_alarm = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data->max[i] != 0) {
|
||||
if (temp > data->max[i]) {
|
||||
if (data->max_alarm[i] == false) {
|
||||
data->max_alarm[i] = true;
|
||||
updated_max_alarm = true;
|
||||
}
|
||||
} else if (temp < data->max[i] - data->max_hyst[i]) {
|
||||
if (data->max_alarm[i] == true) {
|
||||
data->max_alarm[i] = false;
|
||||
updated_max_alarm = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updated_min_alarm) {
|
||||
ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1);
|
||||
sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
|
||||
}
|
||||
if (updated_max_alarm) {
|
||||
ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1);
|
||||
sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
|
||||
}
|
||||
}
|
||||
|
||||
schedule_monitor(data);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/* HWMON sysfs interfaces */
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
/* Show chip name */
|
||||
return data->ops.show_name(dev, devattr, buf);
|
||||
}
|
||||
|
||||
static ssize_t label_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
/* Show each sensor label */
|
||||
return data->ops.show_label(dev, devattr, buf);
|
||||
}
|
||||
|
||||
static ssize_t input_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int ret, temp;
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
u8 gpadc_addr = data->gpadc_addr[attr->index];
|
||||
|
||||
ret = data->ops.read_sensor(data, gpadc_addr, &temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
/* Set functions (RW nodes) */
|
||||
static ssize_t min_store(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
int res = kstrtol(buf, 10, &val);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
data->min[attr->index] = val;
|
||||
threshold_updated(data);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t max_store(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
int res = kstrtol(buf, 10, &val);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
data->max[attr->index] = val;
|
||||
threshold_updated(data);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t max_hyst_store(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
int res = kstrtoul(buf, 10, &val);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
data->max_hyst[attr->index] = val;
|
||||
threshold_updated(data);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Show functions (RO nodes) */
|
||||
static ssize_t min_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%lu\n", data->min[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t max_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%lu\n", data->max[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t max_hyst_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%lu\n", data->max_hyst[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t min_alarm_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%d\n", data->min_alarm[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t max_alarm_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%d\n", data->max_alarm[attr->index]);
|
||||
}
|
||||
|
||||
static umode_t abx500_attrs_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
|
||||
if (data->ops.is_visible)
|
||||
return data->ops.is_visible(attr, n);
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
/* Chip name, required by hwmon */
|
||||
static SENSOR_DEVICE_ATTR_RO(name, name, 0);
|
||||
|
||||
/* GPADC - SENSOR1 */
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_label, label, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_input, input, 0);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp1_min, min, 0);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max_hyst, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, min_alarm, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, max_alarm, 0);
|
||||
|
||||
/* GPADC - SENSOR2 */
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_label, label, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_input, input, 1);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_min, min, 1);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_max, max, 1);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, max_hyst, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, min_alarm, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, max_alarm, 1);
|
||||
|
||||
/* GPADC - SENSOR3 */
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_label, label, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_input, input, 2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp3_min, min, 2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp3_max, max, 2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, max_hyst, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, min_alarm, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, max_alarm, 2);
|
||||
|
||||
/* GPADC - SENSOR4 */
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_label, label, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_input, input, 3);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp4_min, min, 3);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp4_max, max, 3);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp4_max_hyst, max_hyst, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, min_alarm, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, max_alarm, 3);
|
||||
|
||||
static struct attribute *abx500_temp_attributes[] = {
|
||||
&sensor_dev_attr_name.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp2_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp3_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp4_label.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group abx500_temp_group = {
|
||||
.attrs = abx500_temp_attributes,
|
||||
.is_visible = abx500_attrs_visible,
|
||||
};
|
||||
|
||||
static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data)
|
||||
{
|
||||
struct platform_device *pdev = irq_data;
|
||||
struct abx500_temp *data = platform_get_drvdata(pdev);
|
||||
|
||||
data->ops.irq_handler(irq, data);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int setup_irqs(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM");
|
||||
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Get irq by name failed\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
abx500_temp_irq_handler, 0, "abx500-temp", pdev);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int abx500_temp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct abx500_temp *data;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->pdev = pdev;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
/* Chip specific initialization */
|
||||
err = abx500_hwmon_init(data);
|
||||
if (err < 0 || !data->ops.read_sensor || !data->ops.show_name ||
|
||||
!data->ops.show_label)
|
||||
return err;
|
||||
|
||||
INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor);
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
|
||||
goto exit_sysfs_group;
|
||||
}
|
||||
|
||||
if (data->ops.irq_handler) {
|
||||
err = setup_irqs(pdev);
|
||||
if (err < 0)
|
||||
goto exit_hwmon_reg;
|
||||
}
|
||||
return 0;
|
||||
|
||||
exit_hwmon_reg:
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
exit_sysfs_group:
|
||||
sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int abx500_temp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct abx500_temp *data = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int abx500_temp_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct abx500_temp *data = platform_get_drvdata(pdev);
|
||||
|
||||
if (data->work_active)
|
||||
cancel_delayed_work_sync(&data->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int abx500_temp_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct abx500_temp *data = platform_get_drvdata(pdev);
|
||||
|
||||
if (data->work_active)
|
||||
schedule_monitor(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id abx500_temp_match[] = {
|
||||
{ .compatible = "stericsson,abx500-temp" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, abx500_temp_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver abx500_temp_driver = {
|
||||
.driver = {
|
||||
.name = "abx500-temp",
|
||||
.of_match_table = of_match_ptr(abx500_temp_match),
|
||||
},
|
||||
.suspend = abx500_temp_suspend,
|
||||
.resume = abx500_temp_resume,
|
||||
.probe = abx500_temp_probe,
|
||||
.remove = abx500_temp_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(abx500_temp_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
|
||||
MODULE_DESCRIPTION("ABX500 temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson 2010 - 2013
|
||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
||||
* Hongbo Zhang <hongbo.zhang@linaro.com>
|
||||
*/
|
||||
|
||||
#ifndef _ABX500_H
|
||||
#define _ABX500_H
|
||||
|
||||
#define NUM_SENSORS 5
|
||||
|
||||
struct abx500_temp;
|
||||
|
||||
/*
|
||||
* struct abx500_temp_ops - abx500 chip specific ops
|
||||
* @read_sensor: reads gpadc output
|
||||
* @irq_handler: irq handler
|
||||
* @show_name: hwmon device name
|
||||
* @show_label: hwmon attribute label
|
||||
* @is_visible: is attribute visible
|
||||
*/
|
||||
struct abx500_temp_ops {
|
||||
int (*read_sensor)(struct abx500_temp *, u8, int *);
|
||||
int (*irq_handler)(int, struct abx500_temp *);
|
||||
ssize_t (*show_name)(struct device *,
|
||||
struct device_attribute *, char *);
|
||||
ssize_t (*show_label) (struct device *,
|
||||
struct device_attribute *, char *);
|
||||
int (*is_visible)(struct attribute *, int);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct abx500_temp - representation of temp mon device
|
||||
* @pdev: platform device
|
||||
* @hwmon_dev: hwmon device
|
||||
* @ops: abx500 chip specific ops
|
||||
* @gpadc_addr: gpadc channel address
|
||||
* @min: sensor temperature min value
|
||||
* @max: sensor temperature max value
|
||||
* @max_hyst: sensor temperature hysteresis value for max limit
|
||||
* @min_alarm: sensor temperature min alarm
|
||||
* @max_alarm: sensor temperature max alarm
|
||||
* @work: delayed work scheduled to monitor temperature periodically
|
||||
* @work_active: True if work is active
|
||||
* @lock: mutex
|
||||
* @monitored_sensors: number of monitored sensors
|
||||
* @plat_data: private usage, usually points to platform specific data
|
||||
*/
|
||||
struct abx500_temp {
|
||||
struct platform_device *pdev;
|
||||
struct device *hwmon_dev;
|
||||
struct abx500_temp_ops ops;
|
||||
u8 gpadc_addr[NUM_SENSORS];
|
||||
unsigned long min[NUM_SENSORS];
|
||||
unsigned long max[NUM_SENSORS];
|
||||
unsigned long max_hyst[NUM_SENSORS];
|
||||
bool min_alarm[NUM_SENSORS];
|
||||
bool max_alarm[NUM_SENSORS];
|
||||
struct delayed_work work;
|
||||
bool work_active;
|
||||
struct mutex lock;
|
||||
int monitored_sensors;
|
||||
void *plat_data;
|
||||
};
|
||||
|
||||
int abx500_hwmon_init(struct abx500_temp *data);
|
||||
|
||||
#endif /* _ABX500_H */
|
||||
348
drivers/hwmon/aht10.c
Normal file
348
drivers/hwmon/aht10.c
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* aht10.c - Linux hwmon driver for AHT10 Temperature and Humidity sensor
|
||||
* Copyright (C) 2020 Johannes Cornelis Draaijer
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define AHT10_MEAS_SIZE 6
|
||||
|
||||
/*
|
||||
* Poll intervals (in milliseconds)
|
||||
*/
|
||||
#define AHT10_DEFAULT_MIN_POLL_INTERVAL 2000
|
||||
#define AHT10_MIN_POLL_INTERVAL 2000
|
||||
|
||||
/*
|
||||
* I2C command delays (in microseconds)
|
||||
*/
|
||||
#define AHT10_MEAS_DELAY 80000
|
||||
#define AHT10_CMD_DELAY 350000
|
||||
#define AHT10_DELAY_EXTRA 100000
|
||||
|
||||
/*
|
||||
* Command bytes
|
||||
*/
|
||||
#define AHT10_CMD_INIT 0b11100001
|
||||
#define AHT10_CMD_MEAS 0b10101100
|
||||
#define AHT10_CMD_RST 0b10111010
|
||||
|
||||
/*
|
||||
* Flags in the answer byte/command
|
||||
*/
|
||||
#define AHT10_CAL_ENABLED BIT(3)
|
||||
#define AHT10_BUSY BIT(7)
|
||||
#define AHT10_MODE_NOR (BIT(5) | BIT(6))
|
||||
#define AHT10_MODE_CYC BIT(5)
|
||||
#define AHT10_MODE_CMD BIT(6)
|
||||
|
||||
#define AHT10_MAX_POLL_INTERVAL_LEN 30
|
||||
|
||||
/**
|
||||
* struct aht10_data - All the data required to operate an AHT10 chip
|
||||
* @client: the i2c client associated with the AHT10
|
||||
* @lock: a mutex that is used to prevent parallel access to the
|
||||
* i2c client
|
||||
* @min_poll_interval: the minimum poll interval
|
||||
* While the poll rate limit is not 100% necessary,
|
||||
* the datasheet recommends that a measurement
|
||||
* is not performed too often to prevent
|
||||
* the chip from warming up due to the heat it generates.
|
||||
* If it's unwanted, it can be ignored setting it to
|
||||
* it to 0. Default value is 2000 ms
|
||||
* @previous_poll_time: the previous time that the AHT10
|
||||
* was polled
|
||||
* @temperature: the latest temperature value received from
|
||||
* the AHT10
|
||||
* @humidity: the latest humidity value received from the
|
||||
* AHT10
|
||||
*/
|
||||
|
||||
struct aht10_data {
|
||||
struct i2c_client *client;
|
||||
/*
|
||||
* Prevent simultaneous access to the i2c
|
||||
* client and previous_poll_time
|
||||
*/
|
||||
struct mutex lock;
|
||||
ktime_t min_poll_interval;
|
||||
ktime_t previous_poll_time;
|
||||
int temperature;
|
||||
int humidity;
|
||||
};
|
||||
|
||||
/**
|
||||
* aht10_init() - Initialize an AHT10 chip
|
||||
* @client: the i2c client associated with the AHT10
|
||||
* @data: the data associated with this AHT10 chip
|
||||
* Return: 0 if succesfull, 1 if not
|
||||
*/
|
||||
static int aht10_init(struct aht10_data *data)
|
||||
{
|
||||
const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC,
|
||||
0x00};
|
||||
int res;
|
||||
u8 status;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
res = i2c_master_send(client, cmd_init, 3);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
usleep_range(AHT10_CMD_DELAY, AHT10_CMD_DELAY +
|
||||
AHT10_DELAY_EXTRA);
|
||||
|
||||
res = i2c_master_recv(client, &status, 1);
|
||||
if (res != 1)
|
||||
return -ENODATA;
|
||||
|
||||
if (status & AHT10_BUSY)
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aht10_polltime_expired() - check if the minimum poll interval has
|
||||
* expired
|
||||
* @data: the data containing the time to compare
|
||||
* Return: 1 if the minimum poll interval has expired, 0 if not
|
||||
*/
|
||||
static int aht10_polltime_expired(struct aht10_data *data)
|
||||
{
|
||||
ktime_t current_time = ktime_get_boottime();
|
||||
ktime_t difference = ktime_sub(current_time, data->previous_poll_time);
|
||||
|
||||
return ktime_after(difference, data->min_poll_interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* aht10_read_values() - read and parse the raw data from the AHT10
|
||||
* @aht10_data: the struct aht10_data to use for the lock
|
||||
* Return: 0 if succesfull, 1 if not
|
||||
*/
|
||||
static int aht10_read_values(struct aht10_data *data)
|
||||
{
|
||||
const u8 cmd_meas[] = {AHT10_CMD_MEAS, 0x33, 0x00};
|
||||
u32 temp, hum;
|
||||
int res;
|
||||
u8 raw_data[AHT10_MEAS_SIZE];
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
if (aht10_polltime_expired(data)) {
|
||||
res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas));
|
||||
if (res < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
usleep_range(AHT10_MEAS_DELAY,
|
||||
AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA);
|
||||
|
||||
res = i2c_master_recv(client, raw_data, AHT10_MEAS_SIZE);
|
||||
if (res != AHT10_MEAS_SIZE) {
|
||||
mutex_unlock(&data->lock);
|
||||
if (res >= 0)
|
||||
return -ENODATA;
|
||||
else
|
||||
return res;
|
||||
}
|
||||
|
||||
hum = ((u32)raw_data[1] << 12u) |
|
||||
((u32)raw_data[2] << 4u) |
|
||||
((raw_data[3] & 0xF0u) >> 4u);
|
||||
|
||||
temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) |
|
||||
((u32)raw_data[4] << 8u) |
|
||||
raw_data[5];
|
||||
|
||||
temp = ((temp * 625) >> 15u) * 10;
|
||||
hum = ((hum * 625) >> 16u) * 10;
|
||||
|
||||
data->temperature = (int)temp - 50000;
|
||||
data->humidity = hum;
|
||||
data->previous_poll_time = ktime_get_boottime();
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aht10_interval_write() - store the given minimum poll interval.
|
||||
* Return: 0 on success, -EINVAL if a value lower than the
|
||||
* AHT10_MIN_POLL_INTERVAL is given
|
||||
*/
|
||||
static ssize_t aht10_interval_write(struct aht10_data *data,
|
||||
long val)
|
||||
{
|
||||
data->min_poll_interval = ms_to_ktime(clamp_val(val, 2000, LONG_MAX));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aht10_interval_read() - read the minimum poll interval
|
||||
* in milliseconds
|
||||
*/
|
||||
static ssize_t aht10_interval_read(struct aht10_data *data,
|
||||
long *val)
|
||||
{
|
||||
*val = ktime_to_ms(data->min_poll_interval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aht10_temperature1_read() - read the temperature in millidegrees
|
||||
*/
|
||||
static int aht10_temperature1_read(struct aht10_data *data, long *val)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = aht10_read_values(data);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
*val = data->temperature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* aht10_humidity1_read() - read the relative humidity in millipercent
|
||||
*/
|
||||
static int aht10_humidity1_read(struct aht10_data *data, long *val)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = aht10_read_values(data);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
*val = data->humidity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static umode_t aht10_hwmon_visible(const void *data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
case hwmon_humidity:
|
||||
return 0444;
|
||||
case hwmon_chip:
|
||||
return 0644;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int aht10_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct aht10_data *data = dev_get_drvdata(dev);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
return aht10_temperature1_read(data, val);
|
||||
case hwmon_humidity:
|
||||
return aht10_humidity1_read(data, val);
|
||||
case hwmon_chip:
|
||||
return aht10_interval_read(data, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int aht10_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct aht10_data *data = dev_get_drvdata(dev);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return aht10_interval_write(data, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *aht10_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
|
||||
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
|
||||
HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct hwmon_ops aht10_hwmon_ops = {
|
||||
.is_visible = aht10_hwmon_visible,
|
||||
.read = aht10_hwmon_read,
|
||||
.write = aht10_hwmon_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info aht10_chip_info = {
|
||||
.ops = &aht10_hwmon_ops,
|
||||
.info = aht10_info,
|
||||
};
|
||||
|
||||
static int aht10_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *aht10_id)
|
||||
{
|
||||
struct device *device = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct aht10_data *data;
|
||||
int res;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENOENT;
|
||||
|
||||
data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL);
|
||||
data->client = client;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
res = aht10_init(data);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
res = aht10_read_values(data);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(device,
|
||||
client->name,
|
||||
data,
|
||||
&aht10_chip_info,
|
||||
NULL);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id aht10_id[] = {
|
||||
{ "aht10", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aht10_id);
|
||||
|
||||
static struct i2c_driver aht10_driver = {
|
||||
.driver = {
|
||||
.name = "aht10",
|
||||
},
|
||||
.probe = aht10_probe,
|
||||
.id_table = aht10_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(aht10_driver);
|
||||
|
||||
MODULE_AUTHOR("Johannes Cornelis Draaijer <jcdra1@gmail.com>");
|
||||
MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user