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:
Greg Kroah-Hartman 2021-03-03 18:12:05 +01:00
commit 4cf0c39dc0
463 changed files with 28481 additions and 16033 deletions

View File

@ -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".

View File

@ -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.

View File

@ -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
-----------------

View File

@ -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
===============

View File

@ -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)>;
};
};

View File

@ -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)
>;
};
};

View File

@ -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.

View 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>;
};
};

View File

@ -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>;
};

View File

@ -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 :

View File

@ -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>;
};

View File

@ -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

View File

@ -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.

View File

@ -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.

View 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>;
};
};
...

View File

@ -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

View File

@ -93,7 +93,7 @@ examples:
pwmleds {
compatible = "pwm-leds";
panel {
led-1 {
pwms = <&iqs620a_pwm 0 1000000>;
max-brightness = <255>;
};

View File

@ -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

View 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>;
};

View File

@ -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:

View File

@ -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>;
};

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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>;
};

View File

@ -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;
};

View File

@ -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.

View File

@ -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

View File

@ -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>;
};
};
};

View File

@ -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

View File

@ -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$":

View File

@ -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"

View File

@ -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";
};
};

View File

@ -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

View File

@ -25,6 +25,7 @@ properties:
- enum:
- allwinner,sun8i-r40-spi
- allwinner,sun50i-h6-spi
- allwinner,sun50i-h616-spi
- const: allwinner,sun8i-h3-spi
reg:

View File

@ -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

View 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>;
};
};

View 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>;
};

View File

@ -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:

View File

@ -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:

View File

@ -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>;
};

View 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>;
};
};
};
...

View File

@ -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>;
};

View File

@ -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>;
};
};
};
};

View File

@ -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

View File

@ -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>;
};
...

View File

@ -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.

View File

@ -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:

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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:

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -99,6 +99,7 @@ available subsections can be seen below.
rfkill
serial/index
sm501
surface_aggregator/index
switchtec
sync_file
vfio-mediated-device

View 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:

View 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.

View 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

View File

@ -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`

View 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:

View 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`

View 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

View 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.

View 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.

View 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.

View File

@ -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

View File

@ -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.

View File

@ -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.

View 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.
=============== ============================================

View File

@ -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

View File

@ -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

View File

@ -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.
=============================== ===============================================

View File

@ -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
=============== ===============================================

View 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]
======================= =====================================================================

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>;

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -507,6 +507,3 @@ config PANEL
depends on PARPORT
select AUXDISPLAY
select PARPORT_PANEL
config CHARLCD
tristate "Character LCD core support" if COMPILE_TEST

View File

@ -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));

View File

@ -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);
}

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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
View 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