Merge b3a6082223 ("Merge branch 'for-v5.6' of git://git.kernel.org:/pub/scm/linux/kernel/git/jmorris/linux-security") into android-mainline
Baby steps in the 5.6-rc1 merge cycle to make things easier to review and debug. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I084fc068d4c94625e63441029e08e143146d97b7
This commit is contained in:
commit
2b0f7f511d
705 changed files with 30405 additions and 8899 deletions
|
|
@ -25,11 +25,11 @@ Description:
|
|||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
[obj_user=] [obj_role=] [obj_type=]]
|
||||
option: [[appraise_type=]] [template=] [permit_directio]
|
||||
[appraise_flag=]
|
||||
[appraise_flag=] [keyrings=]
|
||||
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
||||
[FIRMWARE_CHECK]
|
||||
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
|
||||
[KEXEC_CMDLINE]
|
||||
[KEXEC_CMDLINE] [KEY_CHECK]
|
||||
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
|
||||
[[^]MAY_EXEC]
|
||||
fsmagic:= hex value
|
||||
|
|
@ -42,6 +42,9 @@ Description:
|
|||
appraise_flag:= [check_blacklist]
|
||||
Currently, blacklist check is only for files signed with appended
|
||||
signature.
|
||||
keyrings:= list of keyrings
|
||||
(eg, .builtin_trusted_keys|.ima). Only valid
|
||||
when action is "measure" and func is KEY_CHECK.
|
||||
template:= name of a defined IMA template type
|
||||
(eg, ima-ng). Only valid when action is "measure".
|
||||
pcr:= decimal value
|
||||
|
|
@ -113,3 +116,12 @@ Description:
|
|||
Example of appraise rule allowing modsig appended signatures:
|
||||
|
||||
appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig
|
||||
|
||||
Example of measure rule using KEY_CHECK to measure all keys:
|
||||
|
||||
measure func=KEY_CHECK
|
||||
|
||||
Example of measure rule using KEY_CHECK to only measure
|
||||
keys added to .builtin_trusted_keys or .ima keyring:
|
||||
|
||||
measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ Required properties:
|
|||
* "arb" : memory ARB line (required)
|
||||
* "rst" : dedicated device reset line (optional)
|
||||
- #sound-dai-cells: must be 0.
|
||||
- amlogic,fifo-depth: The size of the controller's fifo in bytes. This
|
||||
is useful for determining certain configuration such
|
||||
as the flush threshold of the fifo
|
||||
|
||||
Example of FRDDR A on the A113 SoC:
|
||||
|
||||
|
|
@ -27,4 +30,5 @@ frddr_a: audio-controller@1c0 {
|
|||
interrupts = <GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&clkc_audio AUD_CLKID_FRDDR_A>;
|
||||
resets = <&arb AXG_ARB_FRDDR_A>;
|
||||
fifo-depth = <512>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ three substreams within totally 10 channels.
|
|||
|
||||
Required properties:
|
||||
|
||||
- compatible : Contains "fsl,imx35-asrc" or "fsl,imx53-asrc".
|
||||
- compatible : Compatible list, should contain one of the following
|
||||
compatibles:
|
||||
"fsl,imx35-asrc",
|
||||
"fsl,imx53-asrc",
|
||||
"fsl,imx8qm-asrc",
|
||||
"fsl,imx8qxp-asrc",
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
@ -35,6 +40,11 @@ Required properties:
|
|||
|
||||
- fsl,asrc-width : Defines a mutual sample width used by DPCM Back Ends.
|
||||
|
||||
- fsl,asrc-clk-map : Defines clock map used in driver. which is required
|
||||
by imx8qm/imx8qxp platform
|
||||
<0> - select the map for asrc0 in imx8qm/imx8qxp
|
||||
<1> - select the map for asrc1 in imx8qm/imx8qxp
|
||||
|
||||
Optional properties:
|
||||
|
||||
- big-endian : If this property is absent, the little endian mode
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
GTM601 UMTS modem audio interface CODEC
|
||||
|
||||
This device has no configuration interface. Sample rate is fixed - 8kHz.
|
||||
This device has no configuration interface. The sample rate and channels are
|
||||
based on the compatible string
|
||||
"option,gtm601" = 8kHz mono
|
||||
"broadmobi,bm818" = 48KHz stereo
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "option,gtm601"
|
||||
- compatible : one of
|
||||
"option,gtm601"
|
||||
"broadmobi,bm818"
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
|||
55
Documentation/devicetree/bindings/sound/ingenic,codec.yaml
Normal file
55
Documentation/devicetree/bindings/sound/ingenic,codec.yaml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/ingenic,codec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ingenic JZ47xx internal codec DT bindings
|
||||
|
||||
maintainers:
|
||||
- Paul Cercueil <paul@crapouillou.net>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: '^audio-codec@.*'
|
||||
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: ingenic,jz4770-codec
|
||||
- const: ingenic,jz4725b-codec
|
||||
- const: ingenic,jz4740-codec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: aic
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 0
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#sound-dai-cells'
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/jz4740-cgu.h>
|
||||
codec: audio-codec@10020080 {
|
||||
compatible = "ingenic,jz4740-codec";
|
||||
reg = <0x10020080 0x8>;
|
||||
#sound-dai-cells = <0>;
|
||||
clocks = <&cgu JZ4740_CLK_AIC>;
|
||||
clock-names = "aic";
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
Ingenic JZ4725B codec controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "ingenic,jz4725b-codec"
|
||||
- reg : codec registers location and length
|
||||
- clocks : phandle to the AIC clock.
|
||||
- clock-names: must be set to "aic".
|
||||
- #sound-dai-cells: Must be set to 0.
|
||||
|
||||
Example:
|
||||
|
||||
codec: audio-codec@100200a4 {
|
||||
compatible = "ingenic,jz4725b-codec";
|
||||
reg = <0x100200a4 0x8>;
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
clocks = <&cgu JZ4725B_CLK_AIC>;
|
||||
clock-names = "aic";
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
Ingenic JZ4740 codec controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "ingenic,jz4740-codec"
|
||||
- reg : codec registers location and length
|
||||
- clocks : phandle to the AIC clock.
|
||||
- clock-names: must be set to "aic".
|
||||
- #sound-dai-cells: Must be set to 0.
|
||||
|
||||
Example:
|
||||
|
||||
codec: audio-codec@10020080 {
|
||||
compatible = "ingenic,jz4740-codec";
|
||||
reg = <0x10020080 0x8>;
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
clocks = <&cgu JZ4740_CLK_AIC>;
|
||||
clock-names = "aic";
|
||||
};
|
||||
|
|
@ -5,7 +5,10 @@ This binding describes the SDM845 sound card, which uses qdsp for audio.
|
|||
- compatible:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must be "qcom,sdm845-sndcard"
|
||||
Definition: must be one of this
|
||||
"qcom,sdm845-sndcard"
|
||||
"qcom,db845c-sndcard"
|
||||
"lenovo,yoga-c630-sndcard"
|
||||
|
||||
- audio-routing:
|
||||
Usage: Optional
|
||||
|
|
|
|||
175
Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
Normal file
175
Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/qcom,wcd934x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Bindings for Qualcomm WCD9340/WCD9341 Audio Codec
|
||||
|
||||
maintainers:
|
||||
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
|
||||
description: |
|
||||
Qualcomm WCD9340/WCD9341 Codec is a standalone Hi-Fi audio codec IC.
|
||||
It has in-built Soundwire controller, pin controller, interrupt mux and
|
||||
supports both I2S/I2C and SLIMbus audio interfaces.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: slim217,250
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO spec for reset line to use
|
||||
maxItems: 1
|
||||
|
||||
slim-ifc-dev: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: extclk
|
||||
|
||||
vdd-buck-supply:
|
||||
description: A reference to the 1.8V buck supply
|
||||
|
||||
vdd-buck-sido-supply:
|
||||
description: A reference to the 1.8V SIDO buck supply
|
||||
|
||||
vdd-rx-supply:
|
||||
description: A reference to the 1.8V rx supply
|
||||
|
||||
vdd-tx-supply:
|
||||
description: A reference to the 1.8V tx supply
|
||||
|
||||
vdd-vbat-supply:
|
||||
description: A reference to the vbat supply
|
||||
|
||||
vdd-io-supply:
|
||||
description: A reference to the 1.8V I/O supply
|
||||
|
||||
vdd-micbias-supply:
|
||||
description: A reference to the micbias supply
|
||||
|
||||
qcom,micbias1-microvolt:
|
||||
description: micbias1 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
qcom,micbias2-microvolt:
|
||||
description: micbias2 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
qcom,micbias3-microvolt:
|
||||
description: micbias3 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
qcom,micbias4-microvolt:
|
||||
description: micbias4 voltage
|
||||
minimum: 1800000
|
||||
maximum: 2850000
|
||||
|
||||
clock-output-names:
|
||||
const: mclk
|
||||
|
||||
clock-frequency:
|
||||
description: Clock frequency of output clk in Hz
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
gpio@42:
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: ../gpio/qcom,wcd934x-gpio.yaml#
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]+$":
|
||||
type: object
|
||||
description: |
|
||||
WCD934x subnode for each slave devices. Bindings of each subnodes
|
||||
depends on the specific driver providing the functionality and
|
||||
documented in their respective bindings.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reset-gpios
|
||||
- slim-ifc-dev
|
||||
- interrupts
|
||||
- interrupt-controller
|
||||
- clock-frequency
|
||||
- clock-output-names
|
||||
- qcom,micbias1-microvolt
|
||||
- qcom,micbias2-microvolt
|
||||
- qcom,micbias3-microvolt
|
||||
- qcom,micbias4-microvolt
|
||||
- "#interrupt-cells"
|
||||
- "#clock-cells"
|
||||
- "#sound-dai-cells"
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
examples:
|
||||
- |
|
||||
codec@1,0{
|
||||
compatible = "slim217,250";
|
||||
reg = <1 0>;
|
||||
reset-gpios = <&tlmm 64 0>;
|
||||
slim-ifc-dev = <&wcd9340_ifd>;
|
||||
#sound-dai-cells = <1>;
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <54 4>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <9600000>;
|
||||
clock-output-names = "mclk";
|
||||
qcom,micbias1-microvolt = <1800000>;
|
||||
qcom,micbias2-microvolt = <1800000>;
|
||||
qcom,micbias3-microvolt = <1800000>;
|
||||
qcom,micbias4-microvolt = <1800000>;
|
||||
clock-names = "extclk";
|
||||
clocks = <&rpmhcc 2>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
gpio@42 {
|
||||
compatible = "qcom,wcd9340-gpio";
|
||||
reg = <0x42 0x2>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
68
Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
Normal file
68
Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/qcom,wsa881x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Bindings for Qualcomm WSA8810/WSA8815 Class-D Smart Speaker Amplifier
|
||||
|
||||
maintainers:
|
||||
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
|
||||
description: |
|
||||
WSA8810 is a class-D smart speaker amplifier and WSA8815
|
||||
is a high-output power class-D smart speaker amplifier.
|
||||
Their primary operating mode uses a SoundWire digital audio
|
||||
interface. This binding is for SoundWire interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sdw10217201000
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
powerdown-gpios:
|
||||
description: GPIO spec for Powerdown/Shutdown line to use
|
||||
maxItems: 1
|
||||
|
||||
'#thermal-sensor-cells':
|
||||
const: 0
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- powerdown-gpios
|
||||
- "#thermal-sensor-cells"
|
||||
- "#sound-dai-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
soundwire@c2d0000 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0c2d0000 0x2000>;
|
||||
|
||||
speaker@0,1 {
|
||||
compatible = "sdw10217201000";
|
||||
reg = <0 1>;
|
||||
powerdown-gpios = <&wcdpinctrl 2 0>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
||||
|
||||
speaker@0,2 {
|
||||
compatible = "sdw10217201000";
|
||||
reg = <0 2>;
|
||||
powerdown-gpios = <&wcdpinctrl 2 0>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
#sound-dai-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
17
Documentation/devicetree/bindings/sound/rt1015.txt
Normal file
17
Documentation/devicetree/bindings/sound/rt1015.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
RT1015 Mono Class D Audio Amplifier
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt1015".
|
||||
|
||||
- reg : The I2C address of the device.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
rt1015: codec@28 {
|
||||
compatible = "realtek,rt1015";
|
||||
reg = <0x28>;
|
||||
};
|
||||
|
|
@ -10,6 +10,10 @@ Required properties:
|
|||
|
||||
- interrupts : The CODEC's interrupt output.
|
||||
|
||||
- avdd-supply: Power supply for AVDD, providing 1.8V.
|
||||
|
||||
- cpvdd-supply: Power supply for CPVDD, providing 3.5V.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- hp-detect-gpios:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner SUN8I Thermal Controller Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
- Yangtao Li <tiny.windzz@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun8i-a83t-ths
|
||||
- allwinner,sun8i-h3-ths
|
||||
- allwinner,sun8i-r40-ths
|
||||
- allwinner,sun50i-a64-ths
|
||||
- allwinner,sun50i-h5-ths
|
||||
- allwinner,sun50i-h6-ths
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: bus
|
||||
- const: mod
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
nvmem-cells:
|
||||
maxItems: 1
|
||||
description: Calibration data for thermal sensors
|
||||
|
||||
nvmem-cell-names:
|
||||
const: calibration
|
||||
|
||||
# See ./thermal.txt for details
|
||||
"#thermal-sensor-cells":
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: allwinner,sun50i-h6-ths
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: allwinner,sun8i-h3-ths
|
||||
|
||||
then:
|
||||
properties:
|
||||
"#thermal-sensor-cells":
|
||||
const: 0
|
||||
|
||||
else:
|
||||
properties:
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- const: allwinner,sun8i-h3-ths
|
||||
- const: allwinner,sun8i-r40-ths
|
||||
- const: allwinner,sun50i-a64-ths
|
||||
- const: allwinner,sun50i-h5-ths
|
||||
- const: allwinner,sun50i-h6-ths
|
||||
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- '#thermal-sensor-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
thermal-sensor@1f04000 {
|
||||
compatible = "allwinner,sun8i-a83t-ths";
|
||||
reg = <0x01f04000 0x100>;
|
||||
interrupts = <0 31 0>;
|
||||
nvmem-cells = <&ths_calibration>;
|
||||
nvmem-cell-names = "calibration";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
- |
|
||||
thermal-sensor@1c25000 {
|
||||
compatible = "allwinner,sun8i-h3-ths";
|
||||
reg = <0x01c25000 0x400>;
|
||||
clocks = <&ccu 0>, <&ccu 1>;
|
||||
clock-names = "bus", "mod";
|
||||
resets = <&ccu 2>;
|
||||
interrupts = <0 31 0>;
|
||||
nvmem-cells = <&ths_calibration>;
|
||||
nvmem-cell-names = "calibration";
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
|
||||
- |
|
||||
thermal-sensor@5070400 {
|
||||
compatible = "allwinner,sun50i-h6-ths";
|
||||
reg = <0x05070400 0x100>;
|
||||
clocks = <&ccu 0>;
|
||||
clock-names = "bus";
|
||||
resets = <&ccu 2>;
|
||||
interrupts = <0 15 0>;
|
||||
nvmem-cells = <&ths_calibration>;
|
||||
nvmem-cell-names = "calibration";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/brcm,avs-ro-thermal.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom AVS ring oscillator thermal
|
||||
|
||||
maintainers:
|
||||
- Stefan Wahren <wahrenst@gmx.net>
|
||||
|
||||
description: |+
|
||||
The thermal node should be the child of a syscon node with the
|
||||
required property:
|
||||
|
||||
- compatible: Should be one of the following:
|
||||
"brcm,bcm2711-avs-monitor", "syscon", "simple-mfd"
|
||||
|
||||
Refer to the the bindings described in
|
||||
Documentation/devicetree/bindings/mfd/syscon.txt
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: brcm,bcm2711-thermal
|
||||
|
||||
# See ./thermal.txt for details
|
||||
"#thermal-sensor-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#thermal-sensor-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
avs-monitor@7d5d2000 {
|
||||
compatible = "brcm,bcm2711-avs-monitor",
|
||||
"syscon", "simple-mfd";
|
||||
reg = <0x7d5d2000 0xf00>;
|
||||
|
||||
thermal: thermal {
|
||||
compatible = "brcm,bcm2711-thermal";
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -3,9 +3,13 @@
|
|||
Thermal management core, provided by the AVS TMON hardware block.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "brcm,avs-tmon" and/or "brcm,avs-tmon-bcm7445"
|
||||
- compatible: must be one of:
|
||||
"brcm,avs-tmon-bcm7216"
|
||||
"brcm,avs-tmon-bcm7445"
|
||||
"brcm,avs-tmon"
|
||||
- reg: address range for the AVS TMON registers
|
||||
- interrupts: temperature monitor interrupt, for high/low threshold triggers
|
||||
- interrupts: temperature monitor interrupt, for high/low threshold triggers,
|
||||
required except for "brcm,avs-tmon-bcm7216"
|
||||
- interrupt-names: should be "tmon"
|
||||
|
||||
Example:
|
||||
|
|
|
|||
189
Documentation/driver-api/thermal/cpu-idle-cooling.rst
Normal file
189
Documentation/driver-api/thermal/cpu-idle-cooling.rst
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
|
||||
Situation:
|
||||
----------
|
||||
|
||||
Under certain circumstances a SoC can reach a critical temperature
|
||||
limit and is unable to stabilize the temperature around a temperature
|
||||
control. When the SoC has to stabilize the temperature, the kernel can
|
||||
act on a cooling device to mitigate the dissipated power. When the
|
||||
critical temperature is reached, a decision must be taken to reduce
|
||||
the temperature, that, in turn impacts performance.
|
||||
|
||||
Another situation is when the silicon temperature continues to
|
||||
increase even after the dynamic leakage is reduced to its minimum by
|
||||
clock gating the component. This runaway phenomenon can continue due
|
||||
to the static leakage. The only solution is to power down the
|
||||
component, thus dropping the dynamic and static leakage that will
|
||||
allow the component to cool down.
|
||||
|
||||
Last but not least, the system can ask for a specific power budget but
|
||||
because of the OPP density, we can only choose an OPP with a power
|
||||
budget lower than the requested one and under-utilize the CPU, thus
|
||||
losing performance. In other words, one OPP under-utilizes the CPU
|
||||
with a power less than the requested power budget and the next OPP
|
||||
exceeds the power budget. An intermediate OPP could have been used if
|
||||
it were present.
|
||||
|
||||
Solutions:
|
||||
----------
|
||||
|
||||
If we can remove the static and the dynamic leakage for a specific
|
||||
duration in a controlled period, the SoC temperature will
|
||||
decrease. Acting on the idle state duration or the idle cycle
|
||||
injection period, we can mitigate the temperature by modulating the
|
||||
power budget.
|
||||
|
||||
The Operating Performance Point (OPP) density has a great influence on
|
||||
the control precision of cpufreq, however different vendors have a
|
||||
plethora of OPP density, and some have large power gap between OPPs,
|
||||
that will result in loss of performance during thermal control and
|
||||
loss of power in other scenarios.
|
||||
|
||||
At a specific OPP, we can assume that injecting idle cycle on all CPUs
|
||||
belong to the same cluster, with a duration greater than the cluster
|
||||
idle state target residency, we lead to dropping the static and the
|
||||
dynamic leakage for this period (modulo the energy needed to enter
|
||||
this state). So the sustainable power with idle cycles has a linear
|
||||
relation with the OPP’s sustainable power and can be computed with a
|
||||
coefficient similar to:
|
||||
|
||||
Power(IdleCycle) = Coef x Power(OPP)
|
||||
|
||||
Idle Injection:
|
||||
---------------
|
||||
|
||||
The base concept of the idle injection is to force the CPU to go to an
|
||||
idle state for a specified time each control cycle, it provides
|
||||
another way to control CPU power and heat in addition to
|
||||
cpufreq. Ideally, if all CPUs belonging to the same cluster, inject
|
||||
their idle cycles synchronously, the cluster can reach its power down
|
||||
state with a minimum power consumption and reduce the static leakage
|
||||
to almost zero. However, these idle cycles injection will add extra
|
||||
latencies as the CPUs will have to wakeup from a deep sleep state.
|
||||
|
||||
We use a fixed duration of idle injection that gives an acceptable
|
||||
performance penalty and a fixed latency. Mitigation can be increased
|
||||
or decreased by modulating the duty cycle of the idle injection.
|
||||
|
||||
^
|
||||
|
|
||||
|
|
||||
|------- -------
|
||||
|_______|_______________________|_______|___________
|
||||
|
||||
<------>
|
||||
idle <---------------------->
|
||||
running
|
||||
|
||||
<----------------------------->
|
||||
duty cycle 25%
|
||||
|
||||
|
||||
The implementation of the cooling device bases the number of states on
|
||||
the duty cycle percentage. When no mitigation is happening the cooling
|
||||
device state is zero, meaning the duty cycle is 0%.
|
||||
|
||||
When the mitigation begins, depending on the governor's policy, a
|
||||
starting state is selected. With a fixed idle duration and the duty
|
||||
cycle (aka the cooling device state), the running duration can be
|
||||
computed.
|
||||
|
||||
The governor will change the cooling device state thus the duty cycle
|
||||
and this variation will modulate the cooling effect.
|
||||
|
||||
^
|
||||
|
|
||||
|
|
||||
|------- -------
|
||||
|_______|_______________|_______|___________
|
||||
|
||||
<------>
|
||||
idle <-------------->
|
||||
running
|
||||
|
||||
<----------------------------->
|
||||
duty cycle 33%
|
||||
|
||||
|
||||
^
|
||||
|
|
||||
|
|
||||
|------- -------
|
||||
|_______|_______|_______|___________
|
||||
|
||||
<------>
|
||||
idle <------>
|
||||
running
|
||||
|
||||
<------------->
|
||||
duty cycle 50%
|
||||
|
||||
The idle injection duration value must comply with the constraints:
|
||||
|
||||
- It is less than or equal to the latency we tolerate when the
|
||||
mitigation begins. It is platform dependent and will depend on the
|
||||
user experience, reactivity vs performance trade off we want. This
|
||||
value should be specified.
|
||||
|
||||
- It is greater than the idle state’s target residency we want to go
|
||||
for thermal mitigation, otherwise we end up consuming more energy.
|
||||
|
||||
Power considerations
|
||||
--------------------
|
||||
|
||||
When we reach the thermal trip point, we have to sustain a specified
|
||||
power for a specific temperature but at this time we consume:
|
||||
|
||||
Power = Capacitance x Voltage^2 x Frequency x Utilisation
|
||||
|
||||
... which is more than the sustainable power (or there is something
|
||||
wrong in the system setup). The ‘Capacitance’ and ‘Utilisation’ are a
|
||||
fixed value, ‘Voltage’ and the ‘Frequency’ are fixed artificially
|
||||
because we don’t want to change the OPP. We can group the
|
||||
‘Capacitance’ and the ‘Utilisation’ into a single term which is the
|
||||
‘Dynamic Power Coefficient (Cdyn)’ Simplifying the above, we have:
|
||||
|
||||
Pdyn = Cdyn x Voltage^2 x Frequency
|
||||
|
||||
The power allocator governor will ask us somehow to reduce our power
|
||||
in order to target the sustainable power defined in the device
|
||||
tree. So with the idle injection mechanism, we want an average power
|
||||
(Ptarget) resulting in an amount of time running at full power on a
|
||||
specific OPP and idle another amount of time. That could be put in a
|
||||
equation:
|
||||
|
||||
P(opp)target = ((Trunning x (P(opp)running) + (Tidle x P(opp)idle)) /
|
||||
(Trunning + Tidle)
|
||||
...
|
||||
|
||||
Tidle = Trunning x ((P(opp)running / P(opp)target) - 1)
|
||||
|
||||
At this point if we know the running period for the CPU, that gives us
|
||||
the idle injection we need. Alternatively if we have the idle
|
||||
injection duration, we can compute the running duration with:
|
||||
|
||||
Trunning = Tidle / ((P(opp)running / P(opp)target) - 1)
|
||||
|
||||
Practically, if the running power is less than the targeted power, we
|
||||
end up with a negative time value, so obviously the equation usage is
|
||||
bound to a power reduction, hence a higher OPP is needed to have the
|
||||
running power greater than the targeted power.
|
||||
|
||||
However, in this demonstration we ignore three aspects:
|
||||
|
||||
* The static leakage is not defined here, we can introduce it in the
|
||||
equation but assuming it will be zero most of the time as it is
|
||||
difficult to get the values from the SoC vendors
|
||||
|
||||
* The idle state wake up latency (or entry + exit latency) is not
|
||||
taken into account, it must be added in the equation in order to
|
||||
rigorously compute the idle injection
|
||||
|
||||
* The injected idle duration must be greater than the idle state
|
||||
target residency, otherwise we end up consuming more energy and
|
||||
potentially invert the mitigation effect
|
||||
|
||||
So the final equation is:
|
||||
|
||||
Trunning = (Tidle - Twakeup ) x
|
||||
(((P(opp)dyn + P(opp)static ) - P(opp)target) / P(opp)target )
|
||||
|
|
@ -4,7 +4,7 @@ Kernel driver exynos_tmu
|
|||
|
||||
Supported chips:
|
||||
|
||||
* ARM SAMSUNG EXYNOS4, EXYNOS5 series of SoC
|
||||
* ARM Samsung Exynos4, Exynos5 series of SoC
|
||||
|
||||
Datasheet: Not publicly available
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ Authors: Amit Daniel <amit.daniel@samsung.com>
|
|||
TMU controller Description:
|
||||
---------------------------
|
||||
|
||||
This driver allows to read temperature inside SAMSUNG EXYNOS4/5 series of SoC.
|
||||
This driver allows to read temperature inside Samsung Exynos4/5 series of SoC.
|
||||
|
||||
The chip only exposes the measured 8-bit temperature code value
|
||||
through a register.
|
||||
|
|
@ -43,7 +43,7 @@ The three equations are:
|
|||
Trimming info for 85 degree Celsius (stored at TRIMINFO register)
|
||||
Temperature code measured at 85 degree Celsius which is unchanged
|
||||
|
||||
TMU(Thermal Management Unit) in EXYNOS4/5 generates interrupt
|
||||
TMU(Thermal Management Unit) in Exynos4/5 generates interrupt
|
||||
when temperature exceeds pre-defined levels.
|
||||
The maximum number of configurable threshold is five.
|
||||
The threshold levels are defined as follows::
|
||||
|
|
@ -67,7 +67,7 @@ TMU driver description:
|
|||
The exynos thermal driver is structured as::
|
||||
|
||||
Kernel Core thermal framework
|
||||
(thermal_core.c, step_wise.c, cpu_cooling.c)
|
||||
(thermal_core.c, step_wise.c, cpufreq_cooling.c)
|
||||
^
|
||||
|
|
||||
|
|
||||
|
|
|
|||
|
|
@ -495,7 +495,8 @@ Module for C-Media CMI8338/8738/8768/8770 PCI sound cards.
|
|||
mpu_port
|
||||
port address of MIDI interface (8338 only):
|
||||
0x300,0x310,0x320,0x330 = legacy port,
|
||||
0 = disable (default)
|
||||
1 = integrated PCI port (default on 8738),
|
||||
0 = disable
|
||||
fm_port
|
||||
port address of OPL-3 FM synthesizer (8x38 only):
|
||||
0x388 = legacy port,
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ to details explained in the following section.
|
|||
{
|
||||
struct mychip *chip;
|
||||
int err;
|
||||
static struct snd_device_ops ops = {
|
||||
static const struct snd_device_ops ops = {
|
||||
.dev_free = snd_mychip_dev_free,
|
||||
};
|
||||
|
||||
|
|
@ -675,7 +675,7 @@ low-level device with a specified ``ops``,
|
|||
|
||||
::
|
||||
|
||||
static struct snd_device_ops ops = {
|
||||
static const struct snd_device_ops ops = {
|
||||
.dev_free = snd_mychip_dev_free,
|
||||
};
|
||||
....
|
||||
|
|
@ -761,7 +761,7 @@ destructor and PCI entries. Example code is shown first, below.
|
|||
{
|
||||
struct mychip *chip;
|
||||
int err;
|
||||
static struct snd_device_ops ops = {
|
||||
static const struct snd_device_ops ops = {
|
||||
.dev_free = snd_mychip_dev_free,
|
||||
};
|
||||
|
||||
|
|
@ -3912,7 +3912,7 @@ For a raw-data proc-file, set the attributes as follows:
|
|||
|
||||
::
|
||||
|
||||
static struct snd_info_entry_ops my_file_io_ops = {
|
||||
static const struct snd_info_entry_ops my_file_io_ops = {
|
||||
.read = my_file_io_read,
|
||||
};
|
||||
|
||||
|
|
|
|||
13
MAINTAINERS
13
MAINTAINERS
|
|
@ -694,6 +694,14 @@ L: linux-crypto@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/crypto/allwinner/
|
||||
|
||||
ALLWINNER THERMAL DRIVER
|
||||
M: Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
M: Yangtao Li <tiny.windzz@gmail.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml
|
||||
F: drivers/thermal/sun8i_thermal.c
|
||||
|
||||
ALLWINNER VPU DRIVER
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
|
|
@ -16432,12 +16440,15 @@ F: Documentation/devicetree/bindings/thermal/
|
|||
|
||||
THERMAL/CPU_COOLING
|
||||
M: Amit Daniel Kachhap <amit.kachhap@gmail.com>
|
||||
M: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
M: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
M: Javi Merino <javi.merino@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/driver-api/thermal/cpu-cooling-api.rst
|
||||
F: drivers/thermal/cpu_cooling.c
|
||||
F: Documentation/driver-api/thermal/cpu-idle-cooling.rst
|
||||
F: drivers/thermal/cpufreq_cooling.c
|
||||
F: drivers/thermal/cpuidle_cooling.c
|
||||
F: include/linux/cpu_cooling.h
|
||||
|
||||
THERMAL DRIVER FOR AMLOGIC SOCS
|
||||
|
|
|
|||
|
|
@ -66,6 +66,17 @@
|
|||
IRQ_TYPE_LEVEL_HIGH)>;
|
||||
};
|
||||
|
||||
avs_monitor: avs-monitor@7d5d2000 {
|
||||
compatible = "brcm,bcm2711-avs-monitor",
|
||||
"syscon", "simple-mfd";
|
||||
reg = <0x7d5d2000 0xf00>;
|
||||
|
||||
thermal: thermal {
|
||||
compatible = "brcm,bcm2711-thermal";
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
dma: dma@7e007000 {
|
||||
compatible = "brcm,bcm2835-dma";
|
||||
reg = <0x7e007000 0xb00>;
|
||||
|
|
@ -363,6 +374,7 @@
|
|||
|
||||
&cpu_thermal {
|
||||
coefficients = <(-487) 410040>;
|
||||
thermal-sensors = <&thermal>;
|
||||
};
|
||||
|
||||
&dsi0 {
|
||||
|
|
|
|||
|
|
@ -496,6 +496,7 @@ CONFIG_IMX_THERMAL=y
|
|||
CONFIG_ROCKCHIP_THERMAL=y
|
||||
CONFIG_RCAR_THERMAL=y
|
||||
CONFIG_ARMADA_THERMAL=y
|
||||
CONFIG_BCM2711_THERMAL=m
|
||||
CONFIG_BCM2835_THERMAL=m
|
||||
CONFIG_BRCMSTB_THERMAL=m
|
||||
CONFIG_ST_THERMAL_MEMMAP=y
|
||||
|
|
|
|||
|
|
@ -445,6 +445,7 @@ CONFIG_ROCKCHIP_THERMAL=m
|
|||
CONFIG_RCAR_THERMAL=y
|
||||
CONFIG_RCAR_GEN3_THERMAL=y
|
||||
CONFIG_ARMADA_THERMAL=y
|
||||
CONFIG_BCM2711_THERMAL=m
|
||||
CONFIG_BCM2835_THERMAL=m
|
||||
CONFIG_BRCMSTB_THERMAL=m
|
||||
CONFIG_EXYNOS_THERMAL=y
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ cflags-y += -Wa,-I$(srctree)/arch/$(ARCH)/include
|
|||
#
|
||||
cflags-$(CONFIG_FRAME_POINTER) += -fno-optimize-sibling-calls
|
||||
|
||||
ifeq ($(call cc-option-yn,-mpacked-stack),y)
|
||||
ifeq ($(call cc-option-yn,-mpacked-stack -mbackchain -msoft-float),y)
|
||||
cflags-$(CONFIG_PACK_STACK) += -mpacked-stack -D__PACK_STACK
|
||||
aflags-$(CONFIG_PACK_STACK) += -D__PACK_STACK
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ ENTRY(startup_kdump)
|
|||
.quad .Lduct # cr5: primary-aste origin
|
||||
.quad 0 # cr6: I/O interrupts
|
||||
.quad 0 # cr7: secondary space segment table
|
||||
.quad 0 # cr8: access registers translation
|
||||
.quad 0x0000000000008000 # cr8: access registers translation
|
||||
.quad 0 # cr9: tracing off
|
||||
.quad 0 # cr10: tracing off
|
||||
.quad 0 # cr11: tracing off
|
||||
|
|
|
|||
|
|
@ -10,15 +10,14 @@
|
|||
|
||||
#define __EMIT_BUG(x) do { \
|
||||
asm_inline volatile( \
|
||||
"0: j 0b+2\n" \
|
||||
"1:\n" \
|
||||
"0: mc 0,0\n" \
|
||||
".section .rodata.str,\"aMS\",@progbits,1\n" \
|
||||
"2: .asciz \""__FILE__"\"\n" \
|
||||
"1: .asciz \""__FILE__"\"\n" \
|
||||
".previous\n" \
|
||||
".section __bug_table,\"awM\",@progbits,%2\n" \
|
||||
"3: .long 1b-3b,2b-3b\n" \
|
||||
"2: .long 0b-2b,1b-2b\n" \
|
||||
" .short %0,%1\n" \
|
||||
" .org 3b+%2\n" \
|
||||
" .org 2b+%2\n" \
|
||||
".previous\n" \
|
||||
: : "i" (__LINE__), \
|
||||
"i" (x), \
|
||||
|
|
@ -29,12 +28,11 @@
|
|||
|
||||
#define __EMIT_BUG(x) do { \
|
||||
asm_inline volatile( \
|
||||
"0: j 0b+2\n" \
|
||||
"1:\n" \
|
||||
"0: mc 0,0\n" \
|
||||
".section __bug_table,\"awM\",@progbits,%1\n" \
|
||||
"2: .long 1b-2b\n" \
|
||||
"1: .long 0b-1b\n" \
|
||||
" .short %0\n" \
|
||||
" .org 2b+%1\n" \
|
||||
" .org 1b+%1\n" \
|
||||
".previous\n" \
|
||||
: : "i" (x), \
|
||||
"i" (sizeof(struct bug_entry))); \
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
#define JUMP_LABEL_NOP_SIZE 6
|
||||
#define JUMP_LABEL_NOP_OFFSET 2
|
||||
|
||||
#if __GNUC__ < 9
|
||||
#ifdef CONFIG_CC_IS_CLANG
|
||||
#define JUMP_LABEL_STATIC_KEY_CONSTRAINT "i"
|
||||
#elif __GNUC__ < 9
|
||||
#define JUMP_LABEL_STATIC_KEY_CONSTRAINT "X"
|
||||
#else
|
||||
#define JUMP_LABEL_STATIC_KEY_CONSTRAINT "jdd"
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ void zpci_remove_reserved_devices(void);
|
|||
/* CLP */
|
||||
int clp_scan_pci_devices(void);
|
||||
int clp_rescan_pci_devices(void);
|
||||
int clp_rescan_pci_devices_simple(void);
|
||||
int clp_rescan_pci_devices_simple(u32 *fid);
|
||||
int clp_add_pci_device(u32, u32, int);
|
||||
int clp_enable_fh(struct zpci_dev *, u8);
|
||||
int clp_disable_fh(struct zpci_dev *);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ void specification_exception(struct pt_regs *regs);
|
|||
void transaction_exception(struct pt_regs *regs);
|
||||
void translation_exception(struct pt_regs *regs);
|
||||
void vector_exception(struct pt_regs *regs);
|
||||
void monitor_event_exception(struct pt_regs *regs);
|
||||
|
||||
void do_per_trap(struct pt_regs *regs);
|
||||
void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ ENDPROC(ftrace_stub)
|
|||
#define STACK_PTREGS (STACK_FRAME_OVERHEAD)
|
||||
#define STACK_PTREGS_GPRS (STACK_PTREGS + __PT_GPRS)
|
||||
#define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW)
|
||||
#ifdef __PACK_STACK
|
||||
/* allocate just enough for r14, r15 and backchain */
|
||||
#define TRACED_FUNC_FRAME_SIZE 24
|
||||
#else
|
||||
#define TRACED_FUNC_FRAME_SIZE STACK_FRAME_OVERHEAD
|
||||
#endif
|
||||
|
||||
ENTRY(_mcount)
|
||||
BR_EX %r14
|
||||
|
|
@ -40,9 +46,16 @@ ENTRY(ftrace_caller)
|
|||
#if !(defined(CC_USING_HOTPATCH) || defined(CC_USING_NOP_MCOUNT))
|
||||
aghi %r0,MCOUNT_RETURN_FIXUP
|
||||
#endif
|
||||
aghi %r15,-STACK_FRAME_SIZE
|
||||
# allocate stack frame for ftrace_caller to contain traced function
|
||||
aghi %r15,-TRACED_FUNC_FRAME_SIZE
|
||||
stg %r1,__SF_BACKCHAIN(%r15)
|
||||
stg %r0,(__SF_GPRS+8*8)(%r15)
|
||||
stg %r15,(__SF_GPRS+9*8)(%r15)
|
||||
# allocate pt_regs and stack frame for ftrace_trace_function
|
||||
aghi %r15,-STACK_FRAME_SIZE
|
||||
stg %r1,(STACK_PTREGS_GPRS+15*8)(%r15)
|
||||
aghi %r1,-TRACED_FUNC_FRAME_SIZE
|
||||
stg %r1,__SF_BACKCHAIN(%r15)
|
||||
stg %r0,(STACK_PTREGS_PSW+8)(%r15)
|
||||
stmg %r2,%r14,(STACK_PTREGS_GPRS+2*8)(%r15)
|
||||
#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
|
||||
|
|
|
|||
|
|
@ -1383,7 +1383,8 @@ static void aux_output_end(struct perf_output_handle *handle)
|
|||
te = aux_sdb_trailer(aux, aux->alert_mark);
|
||||
te->flags &= ~SDB_TE_ALERT_REQ_MASK;
|
||||
|
||||
debug_sprintf_event(sfdbg, 6, "%s: collect %#lx SDBs\n", __func__, i);
|
||||
debug_sprintf_event(sfdbg, 6, "%s: SDBs %ld range %ld head %ld\n",
|
||||
__func__, i, range_scan, aux->head);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1416,13 +1417,17 @@ static int aux_output_begin(struct perf_output_handle *handle,
|
|||
* SDBs between aux->head and aux->empty_mark are already ready
|
||||
* for new data. range_scan is num of SDBs not within them.
|
||||
*/
|
||||
debug_sprintf_event(sfdbg, 6,
|
||||
"%s: range %ld head %ld alert %ld empty %ld\n",
|
||||
__func__, range, aux->head, aux->alert_mark,
|
||||
aux->empty_mark);
|
||||
if (range > AUX_SDB_NUM_EMPTY(aux)) {
|
||||
range_scan = range - AUX_SDB_NUM_EMPTY(aux);
|
||||
idx = aux->empty_mark + 1;
|
||||
for (i = 0; i < range_scan; i++, idx++) {
|
||||
te = aux_sdb_trailer(aux, idx);
|
||||
te->flags = te->flags & ~SDB_TE_BUFFER_FULL_MASK;
|
||||
te->flags = te->flags & ~SDB_TE_ALERT_REQ_MASK;
|
||||
te->flags &= ~(SDB_TE_BUFFER_FULL_MASK |
|
||||
SDB_TE_ALERT_REQ_MASK);
|
||||
te->overflow = 0;
|
||||
}
|
||||
/* Save the position of empty SDBs */
|
||||
|
|
@ -1441,15 +1446,11 @@ static int aux_output_begin(struct perf_output_handle *handle,
|
|||
cpuhw->lsctl.tear = base + offset * sizeof(unsigned long);
|
||||
cpuhw->lsctl.dear = aux->sdb_index[head];
|
||||
|
||||
debug_sprintf_event(sfdbg, 6, "%s: "
|
||||
"head->alert_mark->empty_mark (num_alert, range)"
|
||||
"[%#lx -> %#lx -> %#lx] (%#lx, %#lx) "
|
||||
"tear index %#lx, tear %#lx dear %#lx\n", __func__,
|
||||
debug_sprintf_event(sfdbg, 6, "%s: head %ld alert %ld empty %ld "
|
||||
"index %ld tear %#lx dear %#lx\n", __func__,
|
||||
aux->head, aux->alert_mark, aux->empty_mark,
|
||||
AUX_SDB_NUM_ALERT(aux), range,
|
||||
head / CPUM_SF_SDB_PER_TABLE,
|
||||
cpuhw->lsctl.tear,
|
||||
cpuhw->lsctl.dear);
|
||||
cpuhw->lsctl.tear, cpuhw->lsctl.dear);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1469,8 +1470,7 @@ static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index,
|
|||
te = aux_sdb_trailer(aux, alert_index);
|
||||
do {
|
||||
orig_flags = te->flags;
|
||||
orig_overflow = te->overflow;
|
||||
*overflow = orig_overflow;
|
||||
*overflow = orig_overflow = te->overflow;
|
||||
if (orig_flags & SDB_TE_BUFFER_FULL_MASK) {
|
||||
/*
|
||||
* SDB is already set by hardware.
|
||||
|
|
@ -1512,9 +1512,12 @@ static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range,
|
|||
unsigned long long *overflow)
|
||||
{
|
||||
unsigned long long orig_overflow, orig_flags, new_flags;
|
||||
unsigned long i, range_scan, idx;
|
||||
unsigned long i, range_scan, idx, idx_old;
|
||||
struct hws_trailer_entry *te;
|
||||
|
||||
debug_sprintf_event(sfdbg, 6, "%s: range %ld head %ld alert %ld "
|
||||
"empty %ld\n", __func__, range, aux->head,
|
||||
aux->alert_mark, aux->empty_mark);
|
||||
if (range <= AUX_SDB_NUM_EMPTY(aux))
|
||||
/*
|
||||
* No need to scan. All SDBs in range are marked as empty.
|
||||
|
|
@ -1537,7 +1540,7 @@ static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range,
|
|||
* indicator fall into this range, set it.
|
||||
*/
|
||||
range_scan = range - AUX_SDB_NUM_EMPTY(aux);
|
||||
idx = aux->empty_mark + 1;
|
||||
idx_old = idx = aux->empty_mark + 1;
|
||||
for (i = 0; i < range_scan; i++, idx++) {
|
||||
te = aux_sdb_trailer(aux, idx);
|
||||
do {
|
||||
|
|
@ -1557,6 +1560,9 @@ static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range,
|
|||
/* Update empty_mark to new position */
|
||||
aux->empty_mark = aux->head + range - 1;
|
||||
|
||||
debug_sprintf_event(sfdbg, 6, "%s: range_scan %ld idx %ld..%ld "
|
||||
"empty %ld\n", __func__, range_scan, idx_old,
|
||||
idx - 1, aux->empty_mark);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1570,7 +1576,6 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||
unsigned long range = 0, size;
|
||||
unsigned long long overflow = 0;
|
||||
struct perf_output_handle *handle = &cpuhw->handle;
|
||||
unsigned long num_sdb;
|
||||
|
||||
aux = perf_get_aux(handle);
|
||||
if (WARN_ON_ONCE(!aux))
|
||||
|
|
@ -1578,8 +1583,9 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||
|
||||
/* Inform user space new data arrived */
|
||||
size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT;
|
||||
debug_sprintf_event(sfdbg, 6, "%s: #alert %ld\n", __func__,
|
||||
size >> PAGE_SHIFT);
|
||||
perf_aux_output_end(handle, size);
|
||||
num_sdb = aux->sfb.num_sdb;
|
||||
|
||||
while (!done) {
|
||||
/* Get an output handle */
|
||||
|
|
@ -1587,7 +1593,7 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||
if (handle->size == 0) {
|
||||
pr_err("The AUX buffer with %lu pages for the "
|
||||
"diagnostic-sampling mode is full\n",
|
||||
num_sdb);
|
||||
aux->sfb.num_sdb);
|
||||
debug_sprintf_event(sfdbg, 1,
|
||||
"%s: AUX buffer used up\n",
|
||||
__func__);
|
||||
|
|
@ -1612,14 +1618,14 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||
size = range << PAGE_SHIFT;
|
||||
perf_aux_output_end(&cpuhw->handle, size);
|
||||
pr_err("Sample data caused the AUX buffer with %lu "
|
||||
"pages to overflow\n", num_sdb);
|
||||
debug_sprintf_event(sfdbg, 1, "%s: head %#lx range %#lx "
|
||||
"overflow %#llx\n", __func__,
|
||||
"pages to overflow\n", aux->sfb.num_sdb);
|
||||
debug_sprintf_event(sfdbg, 1, "%s: head %ld range %ld "
|
||||
"overflow %lld\n", __func__,
|
||||
aux->head, range, overflow);
|
||||
} else {
|
||||
size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT;
|
||||
perf_aux_output_end(&cpuhw->handle, size);
|
||||
debug_sprintf_event(sfdbg, 6, "%s: head %#lx alert %#lx "
|
||||
debug_sprintf_event(sfdbg, 6, "%s: head %ld alert %ld "
|
||||
"already full, try another\n",
|
||||
__func__,
|
||||
aux->head, aux->alert_mark);
|
||||
|
|
@ -1627,11 +1633,9 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||
}
|
||||
|
||||
if (done)
|
||||
debug_sprintf_event(sfdbg, 6, "%s: aux_reset_buffer "
|
||||
"[%#lx -> %#lx -> %#lx] (%#lx, %#lx)\n",
|
||||
__func__, aux->head, aux->alert_mark,
|
||||
aux->empty_mark, AUX_SDB_NUM_ALERT(aux),
|
||||
range);
|
||||
debug_sprintf_event(sfdbg, 6, "%s: head %ld alert %ld "
|
||||
"empty %ld\n", __func__, aux->head,
|
||||
aux->alert_mark, aux->empty_mark);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1654,8 +1658,7 @@ static void aux_buffer_free(void *data)
|
|||
kfree(aux->sdb_index);
|
||||
kfree(aux);
|
||||
|
||||
debug_sprintf_event(sfdbg, 4, "%s: free "
|
||||
"%lu SDBTs\n", __func__, num_sdbt);
|
||||
debug_sprintf_event(sfdbg, 4, "%s: SDBTs %lu\n", __func__, num_sdbt);
|
||||
}
|
||||
|
||||
static void aux_sdb_init(unsigned long sdb)
|
||||
|
|
@ -1707,13 +1710,13 @@ static void *aux_buffer_setup(struct perf_event *event, void **pages,
|
|||
}
|
||||
|
||||
/* Allocate aux_buffer struct for the event */
|
||||
aux = kmalloc(sizeof(struct aux_buffer), GFP_KERNEL);
|
||||
aux = kzalloc(sizeof(struct aux_buffer), GFP_KERNEL);
|
||||
if (!aux)
|
||||
goto no_aux;
|
||||
sfb = &aux->sfb;
|
||||
|
||||
/* Allocate sdbt_index for fast reference */
|
||||
n_sdbt = (nr_pages + CPUM_SF_SDB_PER_TABLE - 1) / CPUM_SF_SDB_PER_TABLE;
|
||||
n_sdbt = DIV_ROUND_UP(nr_pages, CPUM_SF_SDB_PER_TABLE);
|
||||
aux->sdbt_index = kmalloc_array(n_sdbt, sizeof(void *), GFP_KERNEL);
|
||||
if (!aux->sdbt_index)
|
||||
goto no_sdbt_index;
|
||||
|
|
@ -1763,8 +1766,8 @@ static void *aux_buffer_setup(struct perf_event *event, void **pages,
|
|||
*/
|
||||
aux->empty_mark = sfb->num_sdb - 1;
|
||||
|
||||
debug_sprintf_event(sfdbg, 4, "%s: setup %lu SDBTs and %lu SDBs\n",
|
||||
__func__, sfb->num_sdbt, sfb->num_sdb);
|
||||
debug_sprintf_event(sfdbg, 4, "%s: SDBTs %lu SDBs %lu\n", __func__,
|
||||
sfb->num_sdbt, sfb->num_sdb);
|
||||
|
||||
return aux;
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ PGM_CHECK_DEFAULT /* 3c */
|
|||
PGM_CHECK_DEFAULT /* 3d */
|
||||
PGM_CHECK_DEFAULT /* 3e */
|
||||
PGM_CHECK_DEFAULT /* 3f */
|
||||
PGM_CHECK_DEFAULT /* 40 */
|
||||
PGM_CHECK(monitor_event_exception) /* 40 */
|
||||
PGM_CHECK_DEFAULT /* 41 */
|
||||
PGM_CHECK_DEFAULT /* 42 */
|
||||
PGM_CHECK_DEFAULT /* 43 */
|
||||
|
|
|
|||
|
|
@ -53,11 +53,6 @@ void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
|
|||
if (fixup)
|
||||
regs->psw.addr = extable_fixup(fixup);
|
||||
else {
|
||||
enum bug_trap_type btt;
|
||||
|
||||
btt = report_bug(regs->psw.addr, regs);
|
||||
if (btt == BUG_TRAP_TYPE_WARN)
|
||||
return;
|
||||
die(regs, str);
|
||||
}
|
||||
}
|
||||
|
|
@ -245,6 +240,27 @@ void space_switch_exception(struct pt_regs *regs)
|
|||
do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
|
||||
}
|
||||
|
||||
void monitor_event_exception(struct pt_regs *regs)
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
|
||||
if (user_mode(regs))
|
||||
return;
|
||||
|
||||
switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) {
|
||||
case BUG_TRAP_TYPE_NONE:
|
||||
fixup = s390_search_extables(regs->psw.addr);
|
||||
if (fixup)
|
||||
regs->psw.addr = extable_fixup(fixup);
|
||||
break;
|
||||
case BUG_TRAP_TYPE_WARN:
|
||||
break;
|
||||
case BUG_TRAP_TYPE_BUG:
|
||||
die(regs, "monitor event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_stack_overflow(struct pt_regs *regs)
|
||||
{
|
||||
bust_spinlocks(1);
|
||||
|
|
@ -255,8 +271,23 @@ void kernel_stack_overflow(struct pt_regs *regs)
|
|||
}
|
||||
NOKPROBE_SYMBOL(kernel_stack_overflow);
|
||||
|
||||
static void test_monitor_call(void)
|
||||
{
|
||||
int val = 1;
|
||||
|
||||
asm volatile(
|
||||
" mc 0,0\n"
|
||||
"0: xgr %0,%0\n"
|
||||
"1:\n"
|
||||
EX_TABLE(0b,1b)
|
||||
: "+d" (val));
|
||||
if (!val)
|
||||
panic("Monitor call doesn't work!\n");
|
||||
}
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
sort_extable(__start_dma_ex_table, __stop_dma_ex_table);
|
||||
local_mcck_enable();
|
||||
test_monitor_call();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -939,5 +939,5 @@ subsys_initcall_sync(pci_base_init);
|
|||
void zpci_rescan(void)
|
||||
{
|
||||
if (zpci_is_enabled())
|
||||
clp_rescan_pci_devices_simple();
|
||||
clp_rescan_pci_devices_simple(NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,12 +240,14 @@ error:
|
|||
}
|
||||
|
||||
/*
|
||||
* Enable/Disable a given PCI function defined by its function handle.
|
||||
* Enable/Disable a given PCI function and update its function handle if
|
||||
* necessary
|
||||
*/
|
||||
static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
|
||||
static int clp_set_pci_fn(struct zpci_dev *zdev, u8 nr_dma_as, u8 command)
|
||||
{
|
||||
struct clp_req_rsp_set_pci *rrb;
|
||||
int rc, retries = 100;
|
||||
u32 fid = zdev->fid;
|
||||
|
||||
rrb = clp_alloc_block(GFP_KERNEL);
|
||||
if (!rrb)
|
||||
|
|
@ -256,7 +258,7 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
|
|||
rrb->request.hdr.len = sizeof(rrb->request);
|
||||
rrb->request.hdr.cmd = CLP_SET_PCI_FN;
|
||||
rrb->response.hdr.len = sizeof(rrb->response);
|
||||
rrb->request.fh = *fh;
|
||||
rrb->request.fh = zdev->fh;
|
||||
rrb->request.oc = command;
|
||||
rrb->request.ndas = nr_dma_as;
|
||||
|
||||
|
|
@ -269,12 +271,17 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
|
|||
}
|
||||
} while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
|
||||
|
||||
if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
|
||||
*fh = rrb->response.fh;
|
||||
else {
|
||||
if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
|
||||
zpci_err("Set PCI FN:\n");
|
||||
zpci_err_clp(rrb->response.hdr.rsp, rc);
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
|
||||
zdev->fh = rrb->response.fh;
|
||||
} else if (!rc && rrb->response.hdr.rsp == CLP_RC_SETPCIFN_ALRDY &&
|
||||
rrb->response.fh == 0) {
|
||||
/* Function is already in desired state - update handle */
|
||||
rc = clp_rescan_pci_devices_simple(&fid);
|
||||
}
|
||||
clp_free_block(rrb);
|
||||
return rc;
|
||||
|
|
@ -282,18 +289,17 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
|
|||
|
||||
int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as)
|
||||
{
|
||||
u32 fh = zdev->fh;
|
||||
int rc;
|
||||
|
||||
rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
|
||||
zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, fh, rc);
|
||||
rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
|
||||
zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
zdev->fh = fh;
|
||||
if (zpci_use_mio(zdev)) {
|
||||
rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_MIO);
|
||||
zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n", zdev->fid, fh, rc);
|
||||
rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_MIO);
|
||||
zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n",
|
||||
zdev->fid, zdev->fh, rc);
|
||||
if (rc)
|
||||
clp_disable_fh(zdev);
|
||||
}
|
||||
|
|
@ -309,11 +315,8 @@ int clp_disable_fh(struct zpci_dev *zdev)
|
|||
if (!zdev_enabled(zdev))
|
||||
return 0;
|
||||
|
||||
rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN);
|
||||
rc = clp_set_pci_fn(zdev, 0, CLP_SET_DISABLE_PCI_FN);
|
||||
zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, fh, rc);
|
||||
if (!rc)
|
||||
zdev->fh = fh;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -370,10 +373,14 @@ static void __clp_add(struct clp_fh_list_entry *entry, void *data)
|
|||
static void __clp_update(struct clp_fh_list_entry *entry, void *data)
|
||||
{
|
||||
struct zpci_dev *zdev;
|
||||
u32 *fid = data;
|
||||
|
||||
if (!entry->vendor_id)
|
||||
return;
|
||||
|
||||
if (fid && *fid != entry->fid)
|
||||
return;
|
||||
|
||||
zdev = get_zdev_by_fid(entry->fid);
|
||||
if (!zdev)
|
||||
return;
|
||||
|
|
@ -413,7 +420,10 @@ int clp_rescan_pci_devices(void)
|
|||
return rc;
|
||||
}
|
||||
|
||||
int clp_rescan_pci_devices_simple(void)
|
||||
/* Rescan PCI functions and refresh function handles. If fid is non-NULL only
|
||||
* refresh the handle of the function matching @fid
|
||||
*/
|
||||
int clp_rescan_pci_devices_simple(u32 *fid)
|
||||
{
|
||||
struct clp_req_rsp_list_pci *rrb;
|
||||
int rc;
|
||||
|
|
@ -422,7 +432,7 @@ int clp_rescan_pci_devices_simple(void)
|
|||
if (!rrb)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = clp_list_pci(rrb, NULL, __clp_update);
|
||||
rc = clp_list_pci(rrb, fid, __clp_update);
|
||||
|
||||
clp_free_block(rrb);
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
#include <linux/stat.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../../../drivers/pci/pci.h"
|
||||
|
||||
#include <asm/sclp.h>
|
||||
|
||||
#define zpci_attr(name, fmt, member) \
|
||||
|
|
@ -49,31 +51,50 @@ static DEVICE_ATTR_RO(mio_enabled);
|
|||
static ssize_t recover_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct kernfs_node *kn;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct zpci_dev *zdev = to_zpci(pdev);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!device_remove_file_self(dev, attr))
|
||||
return count;
|
||||
/* Can't use device_remove_self() here as that would lead us to lock
|
||||
* the pci_rescan_remove_lock while holding the device' kernfs lock.
|
||||
* This would create a possible deadlock with disable_slot() which is
|
||||
* not directly protected by the device' kernfs lock but takes it
|
||||
* during the device removal which happens under
|
||||
* pci_rescan_remove_lock.
|
||||
*
|
||||
* This is analogous to sdev_store_delete() in
|
||||
* drivers/scsi/scsi_sysfs.c
|
||||
*/
|
||||
kn = sysfs_break_active_protection(&dev->kobj, &attr->attr);
|
||||
WARN_ON_ONCE(!kn);
|
||||
/* device_remove_file() serializes concurrent calls ignoring all but
|
||||
* the first
|
||||
*/
|
||||
device_remove_file(dev, attr);
|
||||
|
||||
/* A concurrent call to recover_store() may slip between
|
||||
* sysfs_break_active_protection() and the sysfs file removal.
|
||||
* Once it unblocks from pci_lock_rescan_remove() the original pdev
|
||||
* will already be removed.
|
||||
*/
|
||||
pci_lock_rescan_remove();
|
||||
pci_stop_and_remove_bus_device(pdev);
|
||||
ret = zpci_disable_device(zdev);
|
||||
if (ret)
|
||||
goto error;
|
||||
if (pci_dev_is_added(pdev)) {
|
||||
pci_stop_and_remove_bus_device(pdev);
|
||||
ret = zpci_disable_device(zdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = zpci_enable_device(zdev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
pci_rescan_bus(zdev->bus);
|
||||
ret = zpci_enable_device(zdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
pci_rescan_bus(zdev->bus);
|
||||
}
|
||||
out:
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
return count;
|
||||
|
||||
error:
|
||||
pci_unlock_rescan_remove();
|
||||
return ret;
|
||||
if (kn)
|
||||
sysfs_unbreak_active_protection(kn);
|
||||
return ret ? ret : count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(recover);
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,8 @@ config SECCOMP
|
|||
config UML_TIME_TRAVEL_SUPPORT
|
||||
bool
|
||||
prompt "Support time-travel mode (e.g. for test execution)"
|
||||
# inf-cpu mode is incompatible with the benchmarking
|
||||
depends on !RAID6_PQ_BENCHMARK
|
||||
help
|
||||
Enable this option to support time travel inside the UML instance.
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ config UML_NET
|
|||
make use of UML networking.
|
||||
|
||||
config UML_NET_ETHERTAP
|
||||
bool "Ethertap transport"
|
||||
bool "Ethertap transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
The Ethertap User-Mode Linux network transport allows a single
|
||||
|
|
@ -167,14 +167,13 @@ config UML_NET_ETHERTAP
|
|||
has examples of the UML command line to use to enable Ethertap
|
||||
networking.
|
||||
|
||||
If you'd like to set up an IP network with the host and/or the
|
||||
outside world, say Y to this, the Daemon Transport and/or the
|
||||
Slip Transport. You'll need at least one of them, but may choose
|
||||
more than one without conflict. If you don't need UML networking,
|
||||
say N.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_TUNTAP
|
||||
bool "TUN/TAP transport"
|
||||
bool "TUN/TAP transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
The UML TUN/TAP network transport allows a UML instance to exchange
|
||||
|
|
@ -185,8 +184,13 @@ config UML_NET_TUNTAP
|
|||
To use this transport, your host kernel must have support for TUN/TAP
|
||||
devices, either built-in or as a module.
|
||||
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_SLIP
|
||||
bool "SLIP transport"
|
||||
bool "SLIP transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
The slip User-Mode Linux network transport allows a running UML to
|
||||
|
|
@ -201,16 +205,13 @@ config UML_NET_SLIP
|
|||
has examples of the UML command line to use to enable slip
|
||||
networking, and details of a few quirks with it.
|
||||
|
||||
The Ethertap Transport is preferred over slip because of its
|
||||
limitations. If you prefer slip, however, say Y here. Otherwise
|
||||
choose the Multicast transport (to network multiple UMLs on
|
||||
multiple hosts), Ethertap (to network with the host and the
|
||||
outside world), and/or the Daemon transport (to network multiple
|
||||
UMLs on a single host). You may choose more than one without
|
||||
conflict. If you don't need UML networking, say N.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_DAEMON
|
||||
bool "Daemon transport"
|
||||
bool "Daemon transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
This User-Mode Linux network transport allows one or more running
|
||||
|
|
@ -225,13 +226,10 @@ config UML_NET_DAEMON
|
|||
has examples of the UML command line to use to enable Daemon
|
||||
networking.
|
||||
|
||||
If you'd like to set up a network with other UMLs on a single host,
|
||||
say Y. If you need a network between UMLs on multiple physical
|
||||
hosts, choose the Multicast Transport. To set up a network with
|
||||
the host and/or other IP machines, say Y to the Ethertap or Slip
|
||||
transports. You'll need at least one of them, but may choose
|
||||
more than one without conflict. If you don't need UML networking,
|
||||
say N.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_VECTOR
|
||||
bool "Vector I/O high performance network devices"
|
||||
|
|
@ -245,7 +243,7 @@ config UML_NET_VECTOR
|
|||
drivers.
|
||||
|
||||
config UML_NET_VDE
|
||||
bool "VDE transport"
|
||||
bool "VDE transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
This User-Mode Linux network transport allows one or more running
|
||||
|
|
@ -263,11 +261,13 @@ config UML_NET_VDE
|
|||
That site has a good overview of what VDE is and also examples
|
||||
of the UML command line to use to enable VDE networking.
|
||||
|
||||
If you need UML networking with VDE,
|
||||
say Y.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_MCAST
|
||||
bool "Multicast transport"
|
||||
bool "Multicast transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
This Multicast User-Mode Linux network transport allows multiple
|
||||
|
|
@ -284,15 +284,13 @@ config UML_NET_MCAST
|
|||
has examples of the UML command line to use to enable Multicast
|
||||
networking, and notes about the security of this approach.
|
||||
|
||||
If you need UMLs on multiple physical hosts to communicate as if
|
||||
they shared an Ethernet network, say Y. If you need to communicate
|
||||
with other IP machines, make sure you select one of the other
|
||||
transports (possibly in addition to Multicast; they're not
|
||||
exclusive). If you don't need to network UMLs say N to each of
|
||||
the transports.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_PCAP
|
||||
bool "pcap transport"
|
||||
bool "pcap transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
The pcap transport makes a pcap packet stream on the host look
|
||||
|
|
@ -304,11 +302,13 @@ config UML_NET_PCAP
|
|||
<http://user-mode-linux.sourceforge.net/old/networking.html> That site
|
||||
has examples of the UML command line to use to enable this option.
|
||||
|
||||
If you intend to use UML as a network monitor for the host, say
|
||||
Y here. Otherwise, say N.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config UML_NET_SLIRP
|
||||
bool "SLiRP transport"
|
||||
bool "SLiRP transport (obsolete)"
|
||||
depends on UML_NET
|
||||
help
|
||||
The SLiRP User-Mode Linux network transport allows a running UML
|
||||
|
|
@ -328,9 +328,10 @@ config UML_NET_SLIRP
|
|||
that of a host behind a firewall that masquerades all network
|
||||
connections passing through it (but is less secure).
|
||||
|
||||
To use this you should first have slirp compiled somewhere
|
||||
accessible on the host, and have read its documentation. If you
|
||||
don't need UML networking, say N.
|
||||
NOTE: THIS TRANSPORT IS DEPRECATED AND WILL BE REMOVED SOON!!! Please
|
||||
migrate to UML_NET_VECTOR.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
struct chan_opts {
|
||||
void (*const announce)(char *dev_name, int dev);
|
||||
char *xterm_title;
|
||||
const int raw;
|
||||
int raw;
|
||||
};
|
||||
|
||||
struct chan_ops {
|
||||
|
|
|
|||
|
|
@ -196,3 +196,11 @@ static int ssl_chan_setup(char *str)
|
|||
|
||||
__setup("ssl", ssl_chan_setup);
|
||||
__channel_help(ssl_chan_setup, "ssl");
|
||||
|
||||
static int ssl_non_raw_setup(char *str)
|
||||
{
|
||||
opts.raw = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("ssl-non-raw", ssl_non_raw_setup);
|
||||
__channel_help(ssl_non_raw_setup, "set serial lines to non-raw mode");
|
||||
|
|
|
|||
|
|
@ -25,5 +25,4 @@ generic-y += switch_to.h
|
|||
generic-y += topology.h
|
||||
generic-y += trace_clock.h
|
||||
generic-y += word-at-a-time.h
|
||||
generic-y += xor.h
|
||||
generic-y += kprobes.h
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@
|
|||
__preinit_array_end = .;
|
||||
}
|
||||
.init_array : {
|
||||
/* dummy - we call this ourselves */
|
||||
__init_array_start = .;
|
||||
*(.init_array)
|
||||
__init_array_end = .;
|
||||
}
|
||||
.fini_array : {
|
||||
|
|
|
|||
7
arch/um/include/asm/xor.h
Normal file
7
arch/um/include/asm/xor.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <asm-generic/xor.h>
|
||||
#include <shared/timer-internal.h>
|
||||
|
||||
/* pick an arbitrary one - measuring isn't possible with inf-cpu */
|
||||
#define XOR_SELECT_TEMPLATE(x) \
|
||||
(time_travel_mode == TT_MODE_INFCPU ? &xor_block_8regs : NULL)
|
||||
|
|
@ -103,6 +103,7 @@ SECTIONS
|
|||
be empty, which isn't pretty. */
|
||||
. = ALIGN(32 / 8);
|
||||
.preinit_array : { *(.preinit_array) }
|
||||
.init_array : { *(.init_array) }
|
||||
.fini_array : { *(.fini_array) }
|
||||
.data : {
|
||||
INIT_TASK_DATA(KERNEL_STACK_SIZE)
|
||||
|
|
|
|||
|
|
@ -529,17 +529,24 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
|
|||
intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
|
||||
}
|
||||
|
||||
static int intel_config_stream(struct sdw_intel *sdw,
|
||||
static int intel_params_stream(struct sdw_intel *sdw,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai,
|
||||
struct snd_pcm_hw_params *hw_params, int link_id)
|
||||
struct snd_pcm_hw_params *hw_params,
|
||||
int link_id, int alh_stream_id)
|
||||
{
|
||||
struct sdw_intel_link_res *res = sdw->res;
|
||||
struct sdw_intel_stream_params_data params_data;
|
||||
|
||||
if (res->ops && res->ops->config_stream && res->arg)
|
||||
return res->ops->config_stream(res->arg,
|
||||
substream, dai, hw_params, link_id);
|
||||
params_data.substream = substream;
|
||||
params_data.dai = dai;
|
||||
params_data.hw_params = hw_params;
|
||||
params_data.link_id = link_id;
|
||||
params_data.alh_stream_id = alh_stream_id;
|
||||
|
||||
if (res->ops && res->ops->params_stream && res->dev)
|
||||
return res->ops->params_stream(res->dev,
|
||||
¶ms_data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
@ -654,7 +661,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
|
||||
/* Inform DSP about PDI stream number */
|
||||
ret = intel_config_stream(sdw, substream, dai, params,
|
||||
ret = intel_params_stream(sdw, substream, dai, params,
|
||||
sdw->instance,
|
||||
pdi->intel_alh_id);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
|
|
|||
|
|
@ -5,23 +5,26 @@
|
|||
#define __SDW_INTEL_LOCAL_H
|
||||
|
||||
/**
|
||||
* struct sdw_intel_link_res - Soundwire link resources
|
||||
* struct sdw_intel_link_res - Soundwire Intel link resource structure,
|
||||
* typically populated by the controller driver.
|
||||
* @pdev: platform_device
|
||||
* @mmio_base: mmio base of SoundWire registers
|
||||
* @registers: Link IO registers base
|
||||
* @shim: Audio shim pointer
|
||||
* @alh: ALH (Audio Link Hub) pointer
|
||||
* @irq: Interrupt line
|
||||
* @ops: Shim callback ops
|
||||
* @arg: Shim callback ops argument
|
||||
*
|
||||
* This is set as pdata for each link instance.
|
||||
* @dev: device implementing hw_params and free callbacks
|
||||
*/
|
||||
struct sdw_intel_link_res {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *mmio_base; /* not strictly needed, useful for debug */
|
||||
void __iomem *registers;
|
||||
void __iomem *shim;
|
||||
void __iomem *alh;
|
||||
int irq;
|
||||
const struct sdw_intel_ops *ops;
|
||||
void *arg;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
#endif /* __SDW_INTEL_LOCAL_H */
|
||||
|
|
|
|||
|
|
@ -27,19 +27,9 @@ static int link_mask;
|
|||
module_param_named(sdw_link_mask, link_mask, int, 0444);
|
||||
MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
|
||||
|
||||
struct sdw_link_data {
|
||||
struct sdw_intel_link_res res;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
struct sdw_intel_ctx {
|
||||
int count;
|
||||
struct sdw_link_data *links;
|
||||
};
|
||||
|
||||
static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
|
||||
{
|
||||
struct sdw_link_data *link = ctx->links;
|
||||
struct sdw_intel_link_res *link = ctx->links;
|
||||
int i;
|
||||
|
||||
if (!link)
|
||||
|
|
@ -62,7 +52,7 @@ static struct sdw_intel_ctx
|
|||
{
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device *pdev;
|
||||
struct sdw_link_data *link;
|
||||
struct sdw_intel_link_res *link;
|
||||
struct sdw_intel_ctx *ctx;
|
||||
struct acpi_device *adev;
|
||||
int ret, i;
|
||||
|
|
@ -123,14 +113,13 @@ static struct sdw_intel_ctx
|
|||
continue;
|
||||
}
|
||||
|
||||
link->res.irq = res->irq;
|
||||
link->res.registers = res->mmio_base + SDW_LINK_BASE
|
||||
link->registers = res->mmio_base + SDW_LINK_BASE
|
||||
+ (SDW_LINK_SIZE * i);
|
||||
link->res.shim = res->mmio_base + SDW_SHIM_BASE;
|
||||
link->res.alh = res->mmio_base + SDW_ALH_BASE;
|
||||
link->shim = res->mmio_base + SDW_SHIM_BASE;
|
||||
link->alh = res->mmio_base + SDW_ALH_BASE;
|
||||
|
||||
link->res.ops = res->ops;
|
||||
link->res.arg = res->arg;
|
||||
link->ops = res->ops;
|
||||
link->dev = res->dev;
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
|
||||
|
|
@ -138,8 +127,6 @@ static struct sdw_intel_ctx
|
|||
pdevinfo.name = "int-sdw";
|
||||
pdevinfo.id = i;
|
||||
pdevinfo.fwnode = acpi_fwnode_handle(adev);
|
||||
pdevinfo.data = &link->res;
|
||||
pdevinfo.size_data = sizeof(link->res);
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
|
|
@ -216,7 +203,6 @@ void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
|
|||
|
||||
return sdw_intel_add_controller(res);
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_intel_init);
|
||||
|
||||
/**
|
||||
* sdw_intel_exit() - SoundWire Intel exit
|
||||
|
|
@ -224,10 +210,8 @@ EXPORT_SYMBOL(sdw_intel_init);
|
|||
*
|
||||
* Delete the controller instances created and cleanup
|
||||
*/
|
||||
void sdw_intel_exit(void *arg)
|
||||
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
|
||||
{
|
||||
struct sdw_intel_ctx *ctx = arg;
|
||||
|
||||
sdw_intel_cleanup_pdev(ctx);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,8 +151,18 @@ config THERMAL_GOV_POWER_ALLOCATOR
|
|||
|
||||
config CPU_THERMAL
|
||||
bool "Generic cpu cooling support"
|
||||
depends on CPU_FREQ
|
||||
depends on THERMAL_OF
|
||||
help
|
||||
Enable the CPU cooling features. If the system has no active
|
||||
cooling device available, this option allows to use the CPU
|
||||
as a cooling device.
|
||||
|
||||
if CPU_THERMAL
|
||||
|
||||
config CPU_FREQ_THERMAL
|
||||
bool "CPU frequency cooling device"
|
||||
depends on CPU_FREQ
|
||||
default y
|
||||
help
|
||||
This implements the generic cpu cooling mechanism through frequency
|
||||
reduction. An ACPI version of this already exists
|
||||
|
|
@ -160,7 +170,14 @@ config CPU_THERMAL
|
|||
This will be useful for platforms using the generic thermal interface
|
||||
and not the ACPI interface.
|
||||
|
||||
If you want this support, you should say Y here.
|
||||
config CPU_IDLE_THERMAL
|
||||
bool "CPU idle cooling device"
|
||||
depends on IDLE_INJECT
|
||||
help
|
||||
This implements the CPU cooling mechanism through
|
||||
idle injection. This will throttle the CPU by injecting
|
||||
idle cycle.
|
||||
endif
|
||||
|
||||
config CLOCK_THERMAL
|
||||
bool "Generic clock cooling support"
|
||||
|
|
@ -263,6 +280,20 @@ config SPEAR_THERMAL
|
|||
Enable this to plug the SPEAr thermal sensor driver into the Linux
|
||||
thermal framework.
|
||||
|
||||
config SUN8I_THERMAL
|
||||
tristate "Allwinner sun8i thermal driver"
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on NVMEM
|
||||
depends on OF
|
||||
depends on RESET_CONTROLLER
|
||||
help
|
||||
Support for the sun8i thermal sensor driver into the Linux thermal
|
||||
framework.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sun8i-thermal.
|
||||
|
||||
config ROCKCHIP_THERMAL
|
||||
tristate "Rockchip thermal driver"
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
|
|||
thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o
|
||||
|
||||
# cpufreq cooling
|
||||
thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
|
||||
thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpufreq_cooling.o
|
||||
thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o
|
||||
|
||||
# clock cooling
|
||||
thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
|
||||
|
|
@ -31,6 +32,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
|
|||
obj-y += broadcom/
|
||||
obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
|
||||
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
|
||||
obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o
|
||||
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
|
||||
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
|
||||
obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
|
||||
|
|
|
|||
|
|
@ -67,7 +67,11 @@
|
|||
|
||||
/**
|
||||
* struct amlogic_thermal_soc_calib_data
|
||||
* @A, B, m, n: calibration parameters
|
||||
* @A: calibration parameters
|
||||
* @B: calibration parameters
|
||||
* @m: calibration parameters
|
||||
* @n: calibration parameters
|
||||
*
|
||||
* This structure is required for configuration of amlogic thermal driver.
|
||||
*/
|
||||
struct amlogic_thermal_soc_calib_data {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,9 @@ static void armadaxp_init(struct platform_device *pdev,
|
|||
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
reg &= ~PMU_TDC0_SW_RST_MASK;
|
||||
regmap_write(priv->syscon, data->syscon_control1_off, reg);
|
||||
|
||||
/* Enable the sensor */
|
||||
regmap_read(priv->syscon, data->syscon_status_off, ®);
|
||||
reg &= ~PMU_TM_DISABLE_MASK;
|
||||
|
|
@ -578,7 +581,7 @@ static const struct armada_thermal_data armadaxp_data = {
|
|||
.coef_m = 10000000ULL,
|
||||
.coef_div = 13825,
|
||||
.syscon_status_off = 0xb0,
|
||||
.syscon_control1_off = 0xd0,
|
||||
.syscon_control1_off = 0x2d0,
|
||||
};
|
||||
|
||||
static const struct armada_thermal_data armada370_data = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config BCM2711_THERMAL
|
||||
tristate "Broadcom AVS RO thermal sensor driver"
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
depends on THERMAL_OF && MFD_SYSCON
|
||||
help
|
||||
Support for thermal sensors on Broadcom BCM2711 SoCs.
|
||||
|
||||
config BCM2835_THERMAL
|
||||
tristate "Thermal sensors on bcm2835 SoC"
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_BCM2711_THERMAL) += bcm2711_thermal.o
|
||||
obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o
|
||||
obj-$(CONFIG_BRCMSTB_THERMAL) += brcmstb_thermal.o
|
||||
obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o
|
||||
|
|
|
|||
123
drivers/thermal/broadcom/bcm2711_thermal.c
Normal file
123
drivers/thermal/broadcom/bcm2711_thermal.c
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Broadcom AVS RO thermal sensor driver
|
||||
*
|
||||
* based on brcmstb_thermal
|
||||
*
|
||||
* Copyright (C) 2020 Stefan Wahren
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
#define AVS_RO_TEMP_STATUS 0x200
|
||||
#define AVS_RO_TEMP_STATUS_VALID_MSK (BIT(16) | BIT(10))
|
||||
#define AVS_RO_TEMP_STATUS_DATA_MSK GENMASK(9, 0)
|
||||
|
||||
struct bcm2711_thermal_priv {
|
||||
struct regmap *regmap;
|
||||
struct thermal_zone_device *thermal;
|
||||
};
|
||||
|
||||
static int bcm2711_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct bcm2711_thermal_priv *priv = data;
|
||||
int slope = thermal_zone_get_slope(priv->thermal);
|
||||
int offset = thermal_zone_get_offset(priv->thermal);
|
||||
u32 val;
|
||||
int ret;
|
||||
long t;
|
||||
|
||||
ret = regmap_read(priv->regmap, AVS_RO_TEMP_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & AVS_RO_TEMP_STATUS_VALID_MSK))
|
||||
return -EIO;
|
||||
|
||||
val &= AVS_RO_TEMP_STATUS_DATA_MSK;
|
||||
|
||||
/* Convert a HW code to a temperature reading (millidegree celsius) */
|
||||
t = slope * val + offset;
|
||||
|
||||
*temp = t < 0 ? 0 : t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops bcm2711_thermal_of_ops = {
|
||||
.get_temp = bcm2711_get_temp,
|
||||
};
|
||||
|
||||
static const struct of_device_id bcm2711_thermal_id_table[] = {
|
||||
{ .compatible = "brcm,bcm2711-thermal" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm2711_thermal_id_table);
|
||||
|
||||
static int bcm2711_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_zone_device *thermal;
|
||||
struct bcm2711_thermal_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *parent;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* get regmap from syscon node */
|
||||
parent = of_get_parent(dev->of_node); /* parent should be syscon node */
|
||||
regmap = syscon_node_to_regmap(parent);
|
||||
of_node_put(parent);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
dev_err(dev, "failed to get regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
priv->regmap = regmap;
|
||||
|
||||
thermal = devm_thermal_zone_of_sensor_register(dev, 0, priv,
|
||||
&bcm2711_thermal_of_ops);
|
||||
if (IS_ERR(thermal)) {
|
||||
ret = PTR_ERR(thermal);
|
||||
dev_err(dev, "could not register sensor: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->thermal = thermal;
|
||||
|
||||
thermal->tzp->no_hwmon = false;
|
||||
ret = thermal_add_hwmon_sysfs(thermal);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bcm2711_thermal_driver = {
|
||||
.probe = bcm2711_thermal_probe,
|
||||
.driver = {
|
||||
.name = "bcm2711_thermal",
|
||||
.of_match_table = bcm2711_thermal_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(bcm2711_thermal_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Stefan Wahren");
|
||||
MODULE_DESCRIPTION("Broadcom AVS RO thermal sensor driver");
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
#define AVS_TMON_TP_TEST_ENABLE 0x20
|
||||
|
||||
/* Default coefficients */
|
||||
#define AVS_TMON_TEMP_SLOPE -487
|
||||
#define AVS_TMON_TEMP_SLOPE 487
|
||||
#define AVS_TMON_TEMP_OFFSET 410040
|
||||
|
||||
/* HW related temperature constants */
|
||||
|
|
@ -102,29 +102,28 @@ static struct avs_tmon_trip avs_tmon_trips[] = {
|
|||
},
|
||||
};
|
||||
|
||||
struct brcmstb_thermal_params {
|
||||
unsigned int offset;
|
||||
unsigned int mult;
|
||||
const struct thermal_zone_of_device_ops *of_ops;
|
||||
};
|
||||
|
||||
struct brcmstb_thermal_priv {
|
||||
void __iomem *tmon_base;
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *thermal;
|
||||
/* Process specific thermal parameters used for calculations */
|
||||
const struct brcmstb_thermal_params *temp_params;
|
||||
};
|
||||
|
||||
static void avs_tmon_get_coeffs(struct thermal_zone_device *tz, int *slope,
|
||||
int *offset)
|
||||
{
|
||||
*slope = thermal_zone_get_slope(tz);
|
||||
*offset = thermal_zone_get_offset(tz);
|
||||
}
|
||||
|
||||
/* Convert a HW code to a temperature reading (millidegree celsius) */
|
||||
static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz,
|
||||
static inline int avs_tmon_code_to_temp(struct brcmstb_thermal_priv *priv,
|
||||
u32 code)
|
||||
{
|
||||
const int val = code & AVS_TMON_TEMP_MASK;
|
||||
int slope, offset;
|
||||
int offset = priv->temp_params->offset;
|
||||
int mult = priv->temp_params->mult;
|
||||
|
||||
avs_tmon_get_coeffs(tz, &slope, &offset);
|
||||
|
||||
return slope * val + offset;
|
||||
return (offset - (int)((code & AVS_TMON_TEMP_MASK) * mult));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -133,23 +132,22 @@ static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz,
|
|||
* @temp: temperature to convert
|
||||
* @low: if true, round toward the low side
|
||||
*/
|
||||
static inline u32 avs_tmon_temp_to_code(struct thermal_zone_device *tz,
|
||||
static inline u32 avs_tmon_temp_to_code(struct brcmstb_thermal_priv *priv,
|
||||
int temp, bool low)
|
||||
{
|
||||
int slope, offset;
|
||||
int offset = priv->temp_params->offset;
|
||||
int mult = priv->temp_params->mult;
|
||||
|
||||
if (temp < AVS_TMON_TEMP_MIN)
|
||||
return AVS_TMON_TEMP_MAX; /* Maximum code value */
|
||||
|
||||
avs_tmon_get_coeffs(tz, &slope, &offset);
|
||||
return AVS_TMON_TEMP_MAX; /* Maximum code value */
|
||||
|
||||
if (temp >= offset)
|
||||
return 0; /* Minimum code value */
|
||||
|
||||
if (low)
|
||||
return (u32)(DIV_ROUND_UP(offset - temp, abs(slope)));
|
||||
return (u32)(DIV_ROUND_UP(offset - temp, mult));
|
||||
else
|
||||
return (u32)((offset - temp) / abs(slope));
|
||||
return (u32)((offset - temp) / mult);
|
||||
}
|
||||
|
||||
static int brcmstb_get_temp(void *data, int *temp)
|
||||
|
|
@ -167,7 +165,7 @@ static int brcmstb_get_temp(void *data, int *temp)
|
|||
|
||||
val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift;
|
||||
|
||||
t = avs_tmon_code_to_temp(priv->thermal, val);
|
||||
t = avs_tmon_code_to_temp(priv, val);
|
||||
if (t < 0)
|
||||
*temp = 0;
|
||||
else
|
||||
|
|
@ -201,7 +199,7 @@ static int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv,
|
|||
val &= trip->reg_msk;
|
||||
val >>= trip->reg_shift;
|
||||
|
||||
return avs_tmon_code_to_temp(priv->thermal, val);
|
||||
return avs_tmon_code_to_temp(priv, val);
|
||||
}
|
||||
|
||||
static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv,
|
||||
|
|
@ -214,7 +212,7 @@ static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv,
|
|||
dev_dbg(priv->dev, "set temp %d to %d\n", type, temp);
|
||||
|
||||
/* round toward low temp for the low interrupt */
|
||||
val = avs_tmon_temp_to_code(priv->thermal, temp,
|
||||
val = avs_tmon_temp_to_code(priv, temp,
|
||||
type == TMON_TRIP_TYPE_LOW);
|
||||
|
||||
val <<= trip->reg_shift;
|
||||
|
|
@ -231,7 +229,7 @@ static int avs_tmon_get_intr_temp(struct brcmstb_thermal_priv *priv)
|
|||
u32 val;
|
||||
|
||||
val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE);
|
||||
return avs_tmon_code_to_temp(priv->thermal, val);
|
||||
return avs_tmon_code_to_temp(priv, val);
|
||||
}
|
||||
|
||||
static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data)
|
||||
|
|
@ -290,19 +288,37 @@ static int brcmstb_set_trips(void *data, int low, int high)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops of_ops = {
|
||||
static const struct thermal_zone_of_device_ops brcmstb_16nm_of_ops = {
|
||||
.get_temp = brcmstb_get_temp,
|
||||
};
|
||||
|
||||
static const struct brcmstb_thermal_params brcmstb_16nm_params = {
|
||||
.offset = 457829,
|
||||
.mult = 557,
|
||||
.of_ops = &brcmstb_16nm_of_ops,
|
||||
};
|
||||
|
||||
static const struct thermal_zone_of_device_ops brcmstb_28nm_of_ops = {
|
||||
.get_temp = brcmstb_get_temp,
|
||||
.set_trips = brcmstb_set_trips,
|
||||
};
|
||||
|
||||
static const struct brcmstb_thermal_params brcmstb_28nm_params = {
|
||||
.offset = 410040,
|
||||
.mult = 487,
|
||||
.of_ops = &brcmstb_28nm_of_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcmstb_thermal_id_table[] = {
|
||||
{ .compatible = "brcm,avs-tmon" },
|
||||
{ .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params },
|
||||
{ .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table);
|
||||
|
||||
static int brcmstb_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct thermal_zone_of_device_ops *of_ops;
|
||||
struct thermal_zone_device *thermal;
|
||||
struct brcmstb_thermal_priv *priv;
|
||||
struct resource *res;
|
||||
|
|
@ -312,6 +328,10 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->temp_params = of_device_get_match_data(&pdev->dev);
|
||||
if (!priv->temp_params)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->tmon_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->tmon_base))
|
||||
|
|
@ -319,9 +339,10 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
|
|||
|
||||
priv->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
of_ops = priv->temp_params->of_ops;
|
||||
|
||||
thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv,
|
||||
&of_ops);
|
||||
of_ops);
|
||||
if (IS_ERR(thermal)) {
|
||||
ret = PTR_ERR(thermal);
|
||||
dev_err(&pdev->dev, "could not register sensor: %d\n", ret);
|
||||
|
|
@ -331,16 +352,15 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
|
|||
priv->thermal = thermal;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "could not get IRQ\n");
|
||||
return irq;
|
||||
}
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
brcmstb_tmon_irq_thread, IRQF_ONESHOT,
|
||||
DRV_NAME, priv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not request IRQ: %d\n", ret);
|
||||
return ret;
|
||||
if (irq >= 0) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
brcmstb_tmon_irq_thread,
|
||||
IRQF_ONESHOT,
|
||||
DRV_NAME, priv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not request IRQ: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n");
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* Copyright (C) 2013 Texas Instruments Inc.
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* Highly based on cpu_cooling.c.
|
||||
* Highly based on cpufreq_cooling.c.
|
||||
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
|
||||
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* linux/drivers/thermal/cpu_cooling.c
|
||||
* linux/drivers/thermal/cpufreq_cooling.c
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
|
||||
*
|
||||
|
|
@ -63,6 +63,7 @@ struct time_in_idle {
|
|||
* @policy: cpufreq policy.
|
||||
* @node: list_head to link all cpufreq_cooling_device together.
|
||||
* @idle_time: idle time stats
|
||||
* @qos_req: PM QoS contraint to apply
|
||||
*
|
||||
* This structure is required for keeping information of each registered
|
||||
* cpufreq_cooling_device.
|
||||
|
|
@ -620,7 +621,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
|
|||
struct thermal_cooling_device *cdev = NULL;
|
||||
|
||||
if (!np) {
|
||||
pr_err("cpu_cooling: OF node not available for cpu%d\n",
|
||||
pr_err("cpufreq_cooling: OF node not available for cpu%d\n",
|
||||
policy->cpu);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -630,7 +631,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
|
|||
|
||||
cdev = __cpufreq_cooling_register(np, policy, em);
|
||||
if (IS_ERR(cdev)) {
|
||||
pr_err("cpu_cooling: cpu%d failed to register as cooling device: %ld\n",
|
||||
pr_err("cpufreq_cooling: cpu%d failed to register as cooling device: %ld\n",
|
||||
policy->cpu, PTR_ERR(cdev));
|
||||
cdev = NULL;
|
||||
}
|
||||
232
drivers/thermal/cpuidle_cooling.c
Normal file
232
drivers/thermal/cpuidle_cooling.c
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 Linaro Limited.
|
||||
*
|
||||
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
*
|
||||
*/
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/idle_inject.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/**
|
||||
* struct cpuidle_cooling_device - data for the idle cooling device
|
||||
* @ii_dev: an atomic to keep track of the last task exiting the idle cycle
|
||||
* @state: a normalized integer giving the state of the cooling device
|
||||
*/
|
||||
struct cpuidle_cooling_device {
|
||||
struct idle_inject_device *ii_dev;
|
||||
unsigned long state;
|
||||
};
|
||||
|
||||
static DEFINE_IDA(cpuidle_ida);
|
||||
|
||||
/**
|
||||
* cpuidle_cooling_runtime - Running time computation
|
||||
* @idle_duration_us: the idle cooling device
|
||||
* @state: a percentile based number
|
||||
*
|
||||
* The running duration is computed from the idle injection duration
|
||||
* which is fixed. If we reach 100% of idle injection ratio, that
|
||||
* means the running duration is zero. If we have a 50% ratio
|
||||
* injection, that means we have equal duration for idle and for
|
||||
* running duration.
|
||||
*
|
||||
* The formula is deduced as follows:
|
||||
*
|
||||
* running = idle x ((100 / ratio) - 1)
|
||||
*
|
||||
* For precision purpose for integer math, we use the following:
|
||||
*
|
||||
* running = (idle x 100) / ratio - idle
|
||||
*
|
||||
* For example, if we have an injected duration of 50%, then we end up
|
||||
* with 10ms of idle injection and 10ms of running duration.
|
||||
*
|
||||
* Return: An unsigned int for a usec based runtime duration.
|
||||
*/
|
||||
static unsigned int cpuidle_cooling_runtime(unsigned int idle_duration_us,
|
||||
unsigned long state)
|
||||
{
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
return ((idle_duration_us * 100) / state) - idle_duration_us;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuidle_cooling_get_max_state - Get the maximum state
|
||||
* @cdev : the thermal cooling device
|
||||
* @state : a pointer to the state variable to be filled
|
||||
*
|
||||
* The function always returns 100 as the injection ratio. It is
|
||||
* percentile based for consistency accross different platforms.
|
||||
*
|
||||
* Return: The function can not fail, it is always zero
|
||||
*/
|
||||
static int cpuidle_cooling_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
/*
|
||||
* Depending on the configuration or the hardware, the running
|
||||
* cycle and the idle cycle could be different. We want to
|
||||
* unify that to an 0..100 interval, so the set state
|
||||
* interface will be the same whatever the platform is.
|
||||
*
|
||||
* The state 100% will make the cluster 100% ... idle. A 0%
|
||||
* injection ratio means no idle injection at all and 50%
|
||||
* means for 10ms of idle injection, we have 10ms of running
|
||||
* time.
|
||||
*/
|
||||
*state = 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuidle_cooling_get_cur_state - Get the current cooling state
|
||||
* @cdev: the thermal cooling device
|
||||
* @state: a pointer to the state
|
||||
*
|
||||
* The function just copies the state value from the private thermal
|
||||
* cooling device structure, the mapping is 1 <-> 1.
|
||||
*
|
||||
* Return: The function can not fail, it is always zero
|
||||
*/
|
||||
static int cpuidle_cooling_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct cpuidle_cooling_device *idle_cdev = cdev->devdata;
|
||||
|
||||
*state = idle_cdev->state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuidle_cooling_set_cur_state - Set the current cooling state
|
||||
* @cdev: the thermal cooling device
|
||||
* @state: the target state
|
||||
*
|
||||
* The function checks first if we are initiating the mitigation which
|
||||
* in turn wakes up all the idle injection tasks belonging to the idle
|
||||
* cooling device. In any case, it updates the internal state for the
|
||||
* cooling device.
|
||||
*
|
||||
* Return: The function can not fail, it is always zero
|
||||
*/
|
||||
static int cpuidle_cooling_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct cpuidle_cooling_device *idle_cdev = cdev->devdata;
|
||||
struct idle_inject_device *ii_dev = idle_cdev->ii_dev;
|
||||
unsigned long current_state = idle_cdev->state;
|
||||
unsigned int runtime_us, idle_duration_us;
|
||||
|
||||
idle_cdev->state = state;
|
||||
|
||||
idle_inject_get_duration(ii_dev, &runtime_us, &idle_duration_us);
|
||||
|
||||
runtime_us = cpuidle_cooling_runtime(idle_duration_us, state);
|
||||
|
||||
idle_inject_set_duration(ii_dev, runtime_us, idle_duration_us);
|
||||
|
||||
if (current_state == 0 && state > 0) {
|
||||
idle_inject_start(ii_dev);
|
||||
} else if (current_state > 0 && !state) {
|
||||
idle_inject_stop(ii_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuidle_cooling_ops - thermal cooling device ops
|
||||
*/
|
||||
static struct thermal_cooling_device_ops cpuidle_cooling_ops = {
|
||||
.get_max_state = cpuidle_cooling_get_max_state,
|
||||
.get_cur_state = cpuidle_cooling_get_cur_state,
|
||||
.set_cur_state = cpuidle_cooling_set_cur_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* cpuidle_of_cooling_register - Idle cooling device initialization function
|
||||
* @drv: a cpuidle driver structure pointer
|
||||
* @np: a node pointer to a device tree cooling device node
|
||||
*
|
||||
* This function is in charge of creating a cooling device per cpuidle
|
||||
* driver and register it to thermal framework.
|
||||
*
|
||||
* Return: zero on success, or negative value corresponding to the
|
||||
* error detected in the underlying subsystems.
|
||||
*/
|
||||
int cpuidle_of_cooling_register(struct device_node *np,
|
||||
struct cpuidle_driver *drv)
|
||||
{
|
||||
struct idle_inject_device *ii_dev;
|
||||
struct cpuidle_cooling_device *idle_cdev;
|
||||
struct thermal_cooling_device *cdev;
|
||||
char dev_name[THERMAL_NAME_LENGTH];
|
||||
int id, ret;
|
||||
|
||||
idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL);
|
||||
if (!idle_cdev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
id = ida_simple_get(&cpuidle_ida, 0, 0, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
ret = id;
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
ii_dev = idle_inject_register(drv->cpumask);
|
||||
if (!ii_dev) {
|
||||
ret = -EINVAL;
|
||||
goto out_id;
|
||||
}
|
||||
|
||||
idle_inject_set_duration(ii_dev, TICK_USEC, TICK_USEC);
|
||||
|
||||
idle_cdev->ii_dev = ii_dev;
|
||||
|
||||
snprintf(dev_name, sizeof(dev_name), "thermal-idle-%d", id);
|
||||
|
||||
cdev = thermal_of_cooling_device_register(np, dev_name, idle_cdev,
|
||||
&cpuidle_cooling_ops);
|
||||
if (IS_ERR(cdev)) {
|
||||
ret = PTR_ERR(cdev);
|
||||
goto out_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
idle_inject_unregister(ii_dev);
|
||||
out_id:
|
||||
ida_simple_remove(&cpuidle_ida, id);
|
||||
out_kfree:
|
||||
kfree(idle_cdev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuidle_cooling_register - Idle cooling device initialization function
|
||||
* @drv: a cpuidle driver structure pointer
|
||||
*
|
||||
* This function is in charge of creating a cooling device per cpuidle
|
||||
* driver and register it to thermal framework.
|
||||
*
|
||||
* Return: zero on success, or negative value corresponding to the
|
||||
* error detected in the underlying subsystems.
|
||||
*/
|
||||
int cpuidle_cooling_register(struct cpuidle_driver *drv)
|
||||
{
|
||||
return cpuidle_of_cooling_register(NULL, drv);
|
||||
}
|
||||
|
|
@ -152,8 +152,8 @@ static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
|
|||
db8500_thermal_update_config(th, idx, THERMAL_TREND_RAISING,
|
||||
next_low, next_high);
|
||||
|
||||
dev_info(&th->tz->device,
|
||||
"PRCMU set max %ld, min %ld\n", next_high, next_low);
|
||||
dev_dbg(&th->tz->device,
|
||||
"PRCMU set max %ld, min %ld\n", next_high, next_low);
|
||||
} else if (idx == num_points - 1)
|
||||
/* So we roof out 1 degree over the max point */
|
||||
th->interpolated_temp = db8500_thermal_points[idx] + 1;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ static DEFINE_IDA(devfreq_ida);
|
|||
* 'utilization' (which is 'busy_time / 'total_time').
|
||||
* The 'res_util' range is from 100 to (power_table[state] * 100)
|
||||
* for the corresponding 'state'.
|
||||
* @capped_state: index to cooling state with in dynamic power budget
|
||||
*/
|
||||
struct devfreq_cooling_device {
|
||||
int id;
|
||||
|
|
@ -587,7 +588,7 @@ EXPORT_SYMBOL_GPL(devfreq_cooling_register);
|
|||
|
||||
/**
|
||||
* devfreq_cooling_unregister() - Unregister devfreq cooling device.
|
||||
* @dfc: Pointer to devfreq cooling device to unregister.
|
||||
* @cdev: Pointer to devfreq cooling device to unregister.
|
||||
*/
|
||||
void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ static long get_target_state(struct thermal_zone_device *tz,
|
|||
|
||||
/**
|
||||
* fair_share_throttle - throttles devices associated with the given zone
|
||||
* @tz - thermal_zone_device
|
||||
* @trip - trip point index
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: trip point index
|
||||
*
|
||||
* Throttling Logic: This uses three parameters to calculate the new
|
||||
* throttle state of the cooling devices associated with the given zone.
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
|||
|
||||
/**
|
||||
* bang_bang_control - controls devices associated with the given zone
|
||||
* @tz - thermal_zone_device
|
||||
* @trip - the trip point
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: the trip point
|
||||
*
|
||||
* Regulation Logic: a two point regulation, deliver cooling state depending
|
||||
* on the previous state shown in this diagram:
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
/* IceLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_ICL_THERMAL 0x8a03
|
||||
|
||||
/* JasperLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503
|
||||
|
||||
#define DRV_NAME "proc_thermal"
|
||||
|
||||
struct power_config {
|
||||
|
|
@ -724,6 +727,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL),
|
||||
.driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */
|
||||
#define PCH_THERMAL_DID_CNL 0x9Df9 /* CNL PCH */
|
||||
#define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */
|
||||
#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */
|
||||
|
||||
/* Wildcat Point-LP PCH Thermal registers */
|
||||
#define WPT_TEMP 0x0000 /* Temperature */
|
||||
|
|
@ -272,6 +273,7 @@ enum board_ids {
|
|||
board_wpt,
|
||||
board_skl,
|
||||
board_cnl,
|
||||
board_cml,
|
||||
};
|
||||
|
||||
static const struct board_info {
|
||||
|
|
@ -294,6 +296,10 @@ static const struct board_info {
|
|||
.name = "pch_cannonlake",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
[board_cml] = {
|
||||
.name = "pch_cometlake",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
}
|
||||
};
|
||||
|
||||
static int intel_pch_thermal_probe(struct pci_dev *pdev,
|
||||
|
|
@ -365,7 +371,7 @@ static void intel_pch_thermal_remove(struct pci_dev *pdev)
|
|||
thermal_zone_device_unregister(ptd->tzd);
|
||||
iounmap(ptd->hw_base);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
pci_release_region(pdev, 0);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
|
@ -398,6 +404,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = {
|
|||
.driver_data = board_cnl, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
|
||||
.driver_data = board_cnl, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
|
||||
.driver_data = board_cml, },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ struct max77620_therm_info {
|
|||
/**
|
||||
* max77620_thermal_read_temp: Read PMIC die temperatue.
|
||||
* @data: Device specific data.
|
||||
* temp: Temperature in millidegrees Celsius
|
||||
* @temp: Temperature in millidegrees Celsius
|
||||
*
|
||||
* The actual temperature of PMIC die is not available from PMIC.
|
||||
* PMIC only tells the status if it has crossed or not the threshold level
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, };
|
|||
static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 };
|
||||
static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, };
|
||||
|
||||
/**
|
||||
/*
|
||||
* The MT8173 thermal controller has four banks. Each bank can read up to
|
||||
* four temperature sensors simultaneously. The MT8173 has a total of 5
|
||||
* temperature sensors. We use each bank to measure a certain area of the
|
||||
|
|
@ -400,7 +400,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = {
|
|||
.sensor_mux_values = mt8173_mux_values,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The MT2701 thermal controller has one bank, which can read up to
|
||||
* three temperature sensors simultaneously. The MT2701 has a total of 3
|
||||
* temperature sensors.
|
||||
|
|
@ -430,7 +430,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = {
|
|||
.sensor_mux_values = mt2701_mux_values,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The MT2712 thermal controller has one bank, which can read up to
|
||||
* four temperature sensors simultaneously. The MT2712 has a total of 4
|
||||
* temperature sensors.
|
||||
|
|
@ -484,7 +484,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = {
|
|||
.sensor_mux_values = mt7622_mux_values,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The MT8183 thermal controller has one bank for the current SW framework.
|
||||
* The MT8183 has a total of 6 temperature sensors.
|
||||
* There are two thermal controller to control the six sensor.
|
||||
|
|
@ -495,7 +495,6 @@ static const struct mtk_thermal_data mt7622_thermal_data = {
|
|||
* data, and this indeed needs the temperatures of the individual banks
|
||||
* for making better decisions.
|
||||
*/
|
||||
|
||||
static const struct mtk_thermal_data mt8183_thermal_data = {
|
||||
.auxadc_channel = MT8183_TEMP_AUXADC_CHANNEL,
|
||||
.num_banks = MT8183_NUM_SENSORS_PER_ZONE,
|
||||
|
|
@ -519,7 +518,8 @@ static const struct mtk_thermal_data mt8183_thermal_data = {
|
|||
|
||||
/**
|
||||
* raw_to_mcelsius - convert a raw ADC value to mcelsius
|
||||
* @mt: The thermal controller
|
||||
* @mt: The thermal controller
|
||||
* @sensno: sensor number
|
||||
* @raw: raw ADC value
|
||||
*
|
||||
* This converts the raw ADC value to mcelsius using the SoC specific
|
||||
|
|
|
|||
|
|
@ -493,7 +493,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
|
|||
|
||||
if (!dev || !dev->of_node) {
|
||||
of_node_put(np);
|
||||
return ERR_PTR(-EINVAL);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
sensor_np = of_node_get(dev->of_node);
|
||||
|
|
@ -754,7 +754,7 @@ end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* It maps 'enum thermal_trip_type' found in include/linux/thermal.h
|
||||
* into the device tree binding of 'trip', property type.
|
||||
*/
|
||||
|
|
@ -977,7 +977,7 @@ free_tz:
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static inline void of_thermal_free_zone(struct __thermal_zone *tz)
|
||||
static __init void of_thermal_free_zone(struct __thermal_zone *tz)
|
||||
{
|
||||
struct __thermal_bind_params *tbp;
|
||||
int i, j;
|
||||
|
|
@ -998,6 +998,38 @@ static inline void of_thermal_free_zone(struct __thermal_zone *tz)
|
|||
kfree(tz);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_thermal_destroy_zones - remove all zones parsed and allocated resources
|
||||
*
|
||||
* Finds all zones parsed and added to the thermal framework and remove them
|
||||
* from the system, together with their resources.
|
||||
*
|
||||
*/
|
||||
static __init void of_thermal_destroy_zones(void)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np) {
|
||||
pr_debug("unable to find thermal zones\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct thermal_zone_device *zone;
|
||||
|
||||
zone = thermal_zone_get_zone_by_name(child->name);
|
||||
if (IS_ERR(zone))
|
||||
continue;
|
||||
|
||||
thermal_zone_device_unregister(zone);
|
||||
kfree(zone->tzp);
|
||||
kfree(zone->ops);
|
||||
of_thermal_free_zone(zone->devdata);
|
||||
}
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_parse_thermal_zones - parse device tree thermal data
|
||||
*
|
||||
|
|
@ -1087,35 +1119,3 @@ exit_free:
|
|||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_thermal_destroy_zones - remove all zones parsed and allocated resources
|
||||
*
|
||||
* Finds all zones parsed and added to the thermal framework and remove them
|
||||
* from the system, together with their resources.
|
||||
*
|
||||
*/
|
||||
void of_thermal_destroy_zones(void)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np) {
|
||||
pr_debug("unable to find thermal zones\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct thermal_zone_device *zone;
|
||||
|
||||
zone = thermal_zone_get_zone_by_name(child->name);
|
||||
if (IS_ERR(zone))
|
||||
continue;
|
||||
|
||||
thermal_zone_device_unregister(zone);
|
||||
kfree(zone->tzp);
|
||||
kfree(zone->ops);
|
||||
of_thermal_free_zone(zone->devdata);
|
||||
}
|
||||
of_node_put(np);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,12 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
#define SITES_MAX 16
|
||||
#define TMR_DISABLE 0x0
|
||||
|
|
@ -24,129 +27,80 @@
|
|||
#define TMU_VER1 0x1
|
||||
#define TMU_VER2 0x2
|
||||
|
||||
/*
|
||||
* QorIQ TMU Registers
|
||||
*/
|
||||
struct qoriq_tmu_site_regs {
|
||||
u32 tritsr; /* Immediate Temperature Site Register */
|
||||
u32 tratsr; /* Average Temperature Site Register */
|
||||
u8 res0[0x8];
|
||||
};
|
||||
#define REGS_TMR 0x000 /* Mode Register */
|
||||
#define TMR_DISABLE 0x0
|
||||
#define TMR_ME 0x80000000
|
||||
#define TMR_ALPF 0x0c000000
|
||||
#define TMR_MSITE_ALL GENMASK(15, 0)
|
||||
|
||||
struct qoriq_tmu_regs_v1 {
|
||||
u32 tmr; /* Mode Register */
|
||||
u32 tsr; /* Status Register */
|
||||
u32 tmtmir; /* Temperature measurement interval Register */
|
||||
u8 res0[0x14];
|
||||
u32 tier; /* Interrupt Enable Register */
|
||||
u32 tidr; /* Interrupt Detect Register */
|
||||
u32 tiscr; /* Interrupt Site Capture Register */
|
||||
u32 ticscr; /* Interrupt Critical Site Capture Register */
|
||||
u8 res1[0x10];
|
||||
u32 tmhtcrh; /* High Temperature Capture Register */
|
||||
u32 tmhtcrl; /* Low Temperature Capture Register */
|
||||
u8 res2[0x8];
|
||||
u32 tmhtitr; /* High Temperature Immediate Threshold */
|
||||
u32 tmhtatr; /* High Temperature Average Threshold */
|
||||
u32 tmhtactr; /* High Temperature Average Crit Threshold */
|
||||
u8 res3[0x24];
|
||||
u32 ttcfgr; /* Temperature Configuration Register */
|
||||
u32 tscfgr; /* Sensor Configuration Register */
|
||||
u8 res4[0x78];
|
||||
struct qoriq_tmu_site_regs site[SITES_MAX];
|
||||
u8 res5[0x9f8];
|
||||
u32 ipbrr0; /* IP Block Revision Register 0 */
|
||||
u32 ipbrr1; /* IP Block Revision Register 1 */
|
||||
u8 res6[0x310];
|
||||
u32 ttrcr[4]; /* Temperature Range Control Register */
|
||||
};
|
||||
#define REGS_TMTMIR 0x008 /* Temperature measurement interval Register */
|
||||
#define TMTMIR_DEFAULT 0x0000000f
|
||||
|
||||
struct qoriq_tmu_regs_v2 {
|
||||
u32 tmr; /* Mode Register */
|
||||
u32 tsr; /* Status Register */
|
||||
u32 tmsr; /* monitor site register */
|
||||
u32 tmtmir; /* Temperature measurement interval Register */
|
||||
u8 res0[0x10];
|
||||
u32 tier; /* Interrupt Enable Register */
|
||||
u32 tidr; /* Interrupt Detect Register */
|
||||
u8 res1[0x8];
|
||||
u32 tiiscr; /* interrupt immediate site capture register */
|
||||
u32 tiascr; /* interrupt average site capture register */
|
||||
u32 ticscr; /* Interrupt Critical Site Capture Register */
|
||||
u32 res2;
|
||||
u32 tmhtcr; /* monitor high temperature capture register */
|
||||
u32 tmltcr; /* monitor low temperature capture register */
|
||||
u32 tmrtrcr; /* monitor rising temperature rate capture register */
|
||||
u32 tmftrcr; /* monitor falling temperature rate capture register */
|
||||
u32 tmhtitr; /* High Temperature Immediate Threshold */
|
||||
u32 tmhtatr; /* High Temperature Average Threshold */
|
||||
u32 tmhtactr; /* High Temperature Average Crit Threshold */
|
||||
u32 res3;
|
||||
u32 tmltitr; /* monitor low temperature immediate threshold */
|
||||
u32 tmltatr; /* monitor low temperature average threshold register */
|
||||
u32 tmltactr; /* monitor low temperature average critical threshold */
|
||||
u32 res4;
|
||||
u32 tmrtrctr; /* monitor rising temperature rate critical threshold */
|
||||
u32 tmftrctr; /* monitor falling temperature rate critical threshold*/
|
||||
u8 res5[0x8];
|
||||
u32 ttcfgr; /* Temperature Configuration Register */
|
||||
u32 tscfgr; /* Sensor Configuration Register */
|
||||
u8 res6[0x78];
|
||||
struct qoriq_tmu_site_regs site[SITES_MAX];
|
||||
u8 res7[0x9f8];
|
||||
u32 ipbrr0; /* IP Block Revision Register 0 */
|
||||
u32 ipbrr1; /* IP Block Revision Register 1 */
|
||||
u8 res8[0x300];
|
||||
u32 teumr0;
|
||||
u32 teumr1;
|
||||
u32 teumr2;
|
||||
u32 res9;
|
||||
u32 ttrcr[4]; /* Temperature Range Control Register */
|
||||
};
|
||||
#define REGS_V2_TMSR 0x008 /* monitor site register */
|
||||
|
||||
struct qoriq_tmu_data;
|
||||
#define REGS_V2_TMTMIR 0x00c /* Temperature measurement interval Register */
|
||||
|
||||
#define REGS_TIER 0x020 /* Interrupt Enable Register */
|
||||
#define TIER_DISABLE 0x0
|
||||
|
||||
|
||||
#define REGS_TTCFGR 0x080 /* Temperature Configuration Register */
|
||||
#define REGS_TSCFGR 0x084 /* Sensor Configuration Register */
|
||||
|
||||
#define REGS_TRITSR(n) (0x100 + 16 * (n)) /* Immediate Temperature
|
||||
* Site Register
|
||||
*/
|
||||
#define TRITSR_V BIT(31)
|
||||
#define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n
|
||||
* Control Register
|
||||
*/
|
||||
#define REGS_IPBRR(n) (0xbf8 + 4 * (n)) /* IP Block Revision
|
||||
* Register n
|
||||
*/
|
||||
#define REGS_V2_TEUMR(n) (0xf00 + 4 * (n))
|
||||
|
||||
/*
|
||||
* Thermal zone data
|
||||
*/
|
||||
struct qoriq_sensor {
|
||||
struct thermal_zone_device *tzd;
|
||||
struct qoriq_tmu_data *qdata;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct qoriq_tmu_data {
|
||||
int ver;
|
||||
struct qoriq_tmu_regs_v1 __iomem *regs;
|
||||
struct qoriq_tmu_regs_v2 __iomem *regs_v2;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
bool little_endian;
|
||||
struct qoriq_sensor *sensor[SITES_MAX];
|
||||
struct qoriq_sensor sensor[SITES_MAX];
|
||||
};
|
||||
|
||||
static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
|
||||
static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s)
|
||||
{
|
||||
if (p->little_endian)
|
||||
iowrite32(val, addr);
|
||||
else
|
||||
iowrite32be(val, addr);
|
||||
}
|
||||
|
||||
static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
|
||||
{
|
||||
if (p->little_endian)
|
||||
return ioread32(addr);
|
||||
else
|
||||
return ioread32be(addr);
|
||||
return container_of(s, struct qoriq_tmu_data, sensor[s->id]);
|
||||
}
|
||||
|
||||
static int tmu_get_temp(void *p, int *temp)
|
||||
{
|
||||
struct qoriq_sensor *qsensor = p;
|
||||
struct qoriq_tmu_data *qdata = qsensor->qdata;
|
||||
struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor);
|
||||
u32 val;
|
||||
/*
|
||||
* REGS_TRITSR(id) has the following layout:
|
||||
*
|
||||
* 31 ... 7 6 5 4 3 2 1 0
|
||||
* V TEMP
|
||||
*
|
||||
* Where V bit signifies if the measurement is ready and is
|
||||
* within sensor range. TEMP is an 8 bit value representing
|
||||
* temperature in C.
|
||||
*/
|
||||
if (regmap_read_poll_timeout(qdata->regmap,
|
||||
REGS_TRITSR(qsensor->id),
|
||||
val,
|
||||
val & TRITSR_V,
|
||||
USEC_PER_MSEC,
|
||||
10 * USEC_PER_MSEC))
|
||||
return -ENODATA;
|
||||
|
||||
val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr);
|
||||
*temp = (val & 0xff) * 1000;
|
||||
|
||||
return 0;
|
||||
|
|
@ -156,84 +110,82 @@ static const struct thermal_zone_of_device_ops tmu_tz_ops = {
|
|||
.get_temp = tmu_get_temp,
|
||||
};
|
||||
|
||||
static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
|
||||
static int qoriq_tmu_register_tmu_zone(struct device *dev,
|
||||
struct qoriq_tmu_data *qdata)
|
||||
{
|
||||
struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev);
|
||||
int id, sites = 0;
|
||||
int id;
|
||||
|
||||
for (id = 0; id < SITES_MAX; id++) {
|
||||
qdata->sensor[id] = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct qoriq_sensor), GFP_KERNEL);
|
||||
if (!qdata->sensor[id])
|
||||
return -ENOMEM;
|
||||
|
||||
qdata->sensor[id]->id = id;
|
||||
qdata->sensor[id]->qdata = qdata;
|
||||
qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register(
|
||||
&pdev->dev, id, qdata->sensor[id], &tmu_tz_ops);
|
||||
if (IS_ERR(qdata->sensor[id]->tzd)) {
|
||||
if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV)
|
||||
continue;
|
||||
else
|
||||
return PTR_ERR(qdata->sensor[id]->tzd);
|
||||
}
|
||||
|
||||
if (qdata->ver == TMU_VER1)
|
||||
sites |= 0x1 << (15 - id);
|
||||
else
|
||||
sites |= 0x1 << id;
|
||||
if (qdata->ver == TMU_VER1) {
|
||||
regmap_write(qdata->regmap, REGS_TMR,
|
||||
TMR_MSITE_ALL | TMR_ME | TMR_ALPF);
|
||||
} else {
|
||||
regmap_write(qdata->regmap, REGS_V2_TMSR, TMR_MSITE_ALL);
|
||||
regmap_write(qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF_V2);
|
||||
}
|
||||
|
||||
/* Enable monitoring */
|
||||
if (sites != 0) {
|
||||
if (qdata->ver == TMU_VER1) {
|
||||
tmu_write(qdata, sites | TMR_ME | TMR_ALPF,
|
||||
&qdata->regs->tmr);
|
||||
} else {
|
||||
tmu_write(qdata, sites, &qdata->regs_v2->tmsr);
|
||||
tmu_write(qdata, TMR_ME | TMR_ALPF_V2,
|
||||
&qdata->regs_v2->tmr);
|
||||
for (id = 0; id < SITES_MAX; id++) {
|
||||
struct thermal_zone_device *tzd;
|
||||
struct qoriq_sensor *sensor = &qdata->sensor[id];
|
||||
int ret;
|
||||
|
||||
sensor->id = id;
|
||||
|
||||
tzd = devm_thermal_zone_of_sensor_register(dev, id,
|
||||
sensor,
|
||||
&tmu_tz_ops);
|
||||
ret = PTR_ERR_OR_ZERO(tzd);
|
||||
if (ret) {
|
||||
if (ret == -ENODEV)
|
||||
continue;
|
||||
|
||||
regmap_write(qdata->regmap, REGS_TMR, TMR_DISABLE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (devm_thermal_add_hwmon_sysfs(tzd))
|
||||
dev_warn(dev,
|
||||
"Failed to add hwmon sysfs attributes\n");
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qoriq_tmu_calibration(struct platform_device *pdev)
|
||||
static int qoriq_tmu_calibration(struct device *dev,
|
||||
struct qoriq_tmu_data *data)
|
||||
{
|
||||
int i, val, len;
|
||||
u32 range[4];
|
||||
const u32 *calibration;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
len = of_property_count_u32_elems(np, "fsl,tmu-range");
|
||||
if (len < 0 || len > 4) {
|
||||
dev_err(&pdev->dev, "invalid range data.\n");
|
||||
dev_err(dev, "invalid range data.\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
val = of_property_read_u32_array(np, "fsl,tmu-range", range, len);
|
||||
if (val != 0) {
|
||||
dev_err(&pdev->dev, "failed to read range data.\n");
|
||||
dev_err(dev, "failed to read range data.\n");
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Init temperature range registers */
|
||||
for (i = 0; i < len; i++)
|
||||
tmu_write(data, range[i], &data->regs->ttrcr[i]);
|
||||
regmap_write(data->regmap, REGS_TTRnCR(i), range[i]);
|
||||
|
||||
calibration = of_get_property(np, "fsl,tmu-calibration", &len);
|
||||
if (calibration == NULL || len % 8) {
|
||||
dev_err(&pdev->dev, "invalid calibration data.\n");
|
||||
dev_err(dev, "invalid calibration data.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += 8, calibration += 2) {
|
||||
val = of_read_number(calibration, 1);
|
||||
tmu_write(data, val, &data->regs->ttcfgr);
|
||||
regmap_write(data->regmap, REGS_TTCFGR, val);
|
||||
val = of_read_number(calibration + 1, 1);
|
||||
tmu_write(data, val, &data->regs->tscfgr);
|
||||
regmap_write(data->regmap, REGS_TSCFGR, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -242,76 +194,117 @@ static int qoriq_tmu_calibration(struct platform_device *pdev)
|
|||
static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
|
||||
{
|
||||
/* Disable interrupt, using polling instead */
|
||||
tmu_write(data, TIER_DISABLE, &data->regs->tier);
|
||||
regmap_write(data->regmap, REGS_TIER, TIER_DISABLE);
|
||||
|
||||
/* Set update_interval */
|
||||
|
||||
if (data->ver == TMU_VER1) {
|
||||
tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
|
||||
regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT);
|
||||
} else {
|
||||
tmu_write(data, TMTMIR_DEFAULT, &data->regs_v2->tmtmir);
|
||||
tmu_write(data, TEUMR0_V2, &data->regs_v2->teumr0);
|
||||
regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT);
|
||||
regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2);
|
||||
}
|
||||
|
||||
/* Disable monitoring */
|
||||
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
|
||||
regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
|
||||
}
|
||||
|
||||
static const struct regmap_range qoriq_yes_ranges[] = {
|
||||
regmap_reg_range(REGS_TMR, REGS_TSCFGR),
|
||||
regmap_reg_range(REGS_TTRnCR(0), REGS_TTRnCR(3)),
|
||||
regmap_reg_range(REGS_V2_TEUMR(0), REGS_V2_TEUMR(2)),
|
||||
regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)),
|
||||
/* Read only registers below */
|
||||
regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table qoriq_wr_table = {
|
||||
.yes_ranges = qoriq_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges) - 1,
|
||||
};
|
||||
|
||||
static const struct regmap_access_table qoriq_rd_table = {
|
||||
.yes_ranges = qoriq_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges),
|
||||
};
|
||||
|
||||
static int qoriq_tmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
u32 ver;
|
||||
struct qoriq_tmu_data *data;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
const bool little_endian = of_property_read_bool(np, "little-endian");
|
||||
const enum regmap_endian format_endian =
|
||||
little_endian ? REGMAP_ENDIAN_LITTLE : REGMAP_ENDIAN_BIG;
|
||||
const struct regmap_config regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.rd_table = &qoriq_rd_table,
|
||||
.wr_table = &qoriq_wr_table,
|
||||
.val_format_endian = format_endian,
|
||||
.max_register = SZ_4K,
|
||||
};
|
||||
void __iomem *base;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
|
||||
data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
data->little_endian = of_property_read_bool(np, "little-endian");
|
||||
|
||||
data->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(data->regs)) {
|
||||
dev_err(&pdev->dev, "Failed to get memory region\n");
|
||||
return PTR_ERR(data->regs);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
ret = PTR_ERR_OR_ZERO(base);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get memory region\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
data->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
|
||||
ret = PTR_ERR_OR_ZERO(data->regmap);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to init regmap (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->clk = devm_clk_get_optional(dev, NULL);
|
||||
if (IS_ERR(data->clk))
|
||||
return PTR_ERR(data->clk);
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable clock\n");
|
||||
dev_err(dev, "Failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* version register offset at: 0xbf8 on both v1 and v2 */
|
||||
ver = tmu_read(data, &data->regs->ipbrr0);
|
||||
ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to read IP block version\n");
|
||||
return ret;
|
||||
}
|
||||
data->ver = (ver >> 8) & 0xff;
|
||||
if (data->ver == TMU_VER2)
|
||||
data->regs_v2 = (void __iomem *)data->regs;
|
||||
|
||||
qoriq_tmu_init_device(data); /* TMU initialization */
|
||||
|
||||
ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
|
||||
ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = qoriq_tmu_register_tmu_zone(pdev);
|
||||
ret = qoriq_tmu_register_tmu_zone(dev, data);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register sensors\n");
|
||||
dev_err(dev, "Failed to register sensors\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(data->clk);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -321,24 +314,21 @@ static int qoriq_tmu_remove(struct platform_device *pdev)
|
|||
struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
/* Disable monitoring */
|
||||
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
|
||||
regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused qoriq_tmu_suspend(struct device *dev)
|
||||
{
|
||||
u32 tmr;
|
||||
struct qoriq_tmu_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Disable monitoring */
|
||||
tmr = tmu_read(data, &data->regs->tmr);
|
||||
tmr &= ~TMR_ME;
|
||||
tmu_write(data, tmr, &data->regs->tmr);
|
||||
ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
|
|
@ -347,7 +337,6 @@ static int __maybe_unused qoriq_tmu_suspend(struct device *dev)
|
|||
|
||||
static int __maybe_unused qoriq_tmu_resume(struct device *dev)
|
||||
{
|
||||
u32 tmr;
|
||||
int ret;
|
||||
struct qoriq_tmu_data *data = dev_get_drvdata(dev);
|
||||
|
||||
|
|
@ -356,11 +345,7 @@ static int __maybe_unused qoriq_tmu_resume(struct device *dev)
|
|||
return ret;
|
||||
|
||||
/* Enable monitoring */
|
||||
tmr = tmu_read(data, &data->regs->tmr);
|
||||
tmr |= TMR_ME;
|
||||
tmu_write(data, tmr, &data->regs->tmr);
|
||||
|
||||
return 0;
|
||||
return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
|
||||
|
|
|
|||
|
|
@ -182,9 +182,7 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
|
|||
tsc->coef.a2);
|
||||
mcelsius = FIXPT_TO_MCELSIUS(val);
|
||||
|
||||
/* Make sure we are inside specifications */
|
||||
if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125)))
|
||||
return -EIO;
|
||||
/* Guaranteed operating range is -40C to 125C. */
|
||||
|
||||
/* Round value to device granularity setting */
|
||||
*temp = rcar_gen3_thermal_round(mcelsius);
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
|
|||
* to get stable temperature.
|
||||
* see "Usage Notes" on datasheet
|
||||
*/
|
||||
udelay(300);
|
||||
usleep_range(300, 400);
|
||||
|
||||
new = rcar_thermal_read(priv, THSSR) & CTEMP;
|
||||
if (new == old) {
|
||||
|
|
@ -275,12 +275,7 @@ static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
|
|||
tmp = MCELSIUS((priv->ctemp * 5) - 60);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
if ((tmp < MCELSIUS(-45)) || (tmp > MCELSIUS(125))) {
|
||||
struct device *dev = rcar_priv_to_dev(priv);
|
||||
|
||||
dev_err(dev, "it couldn't measure temperature correctly\n");
|
||||
return -EIO;
|
||||
}
|
||||
/* Guaranteed operating range is -45C to 125C. */
|
||||
|
||||
*temp = tmp;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/**
|
||||
/*
|
||||
* If the temperature over a period of time High,
|
||||
* the resulting TSHUT gave CRU module,let it reset the entire chip,
|
||||
* or via GPIO give PMIC.
|
||||
|
|
@ -29,7 +29,7 @@ enum tshut_mode {
|
|||
TSHUT_MODE_GPIO,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The system Temperature Sensors tshut(tshut) polarity
|
||||
* the bit 8 is tshut polarity.
|
||||
* 0: low active, 1: high active
|
||||
|
|
@ -39,7 +39,7 @@ enum tshut_polarity {
|
|||
TSHUT_HIGH_ACTIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The system has two Temperature Sensors.
|
||||
* sensor0 is for CPU, and sensor1 is for GPU.
|
||||
*/
|
||||
|
|
@ -48,7 +48,7 @@ enum sensor_id {
|
|||
SENSOR_GPU,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The conversion table has the adc value and temperature.
|
||||
* ADC_DECREMENT: the adc value is of diminishing.(e.g. rk3288_code_table)
|
||||
* ADC_INCREMENT: the adc value is incremental.(e.g. rk3368_code_table)
|
||||
|
|
@ -58,6 +58,8 @@ enum adc_sort_mode {
|
|||
ADC_INCREMENT,
|
||||
};
|
||||
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
/**
|
||||
* The max sensors is two in rockchip SoCs.
|
||||
* Two sensors: CPU and GPU sensor.
|
||||
|
|
@ -80,13 +82,14 @@ struct chip_tsadc_table {
|
|||
|
||||
/**
|
||||
* struct rockchip_tsadc_chip - hold the private data of tsadc chip
|
||||
* @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel
|
||||
* @chn_id: array of sensor ids of chip corresponding to the channel
|
||||
* @chn_num: the channel number of tsadc chip
|
||||
* @tshut_temp: the hardware-controlled shutdown temperature value
|
||||
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
|
||||
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
|
||||
* @initialize: SoC special initialize tsadc controller method
|
||||
* @irq_ack: clear the interrupt
|
||||
* @control: enable/disable method for the tsadc controller
|
||||
* @get_temp: get the temperature
|
||||
* @set_alarm_temp: set the high temperature interrupt
|
||||
* @set_tshut_temp: set the hardware-controlled shutdown temperature
|
||||
|
|
@ -139,7 +142,7 @@ struct rockchip_thermal_sensor {
|
|||
* @chip: pointer to the platform/configuration data
|
||||
* @pdev: platform device of thermal
|
||||
* @reset: the reset controller of tsadc
|
||||
* @sensors[SOC_MAX_SENSORS]: the thermal sensor
|
||||
* @sensors: array of thermal sensors
|
||||
* @clk: the controller clock is divided by the exteral 24MHz
|
||||
* @pclk: the advanced peripherals bus clock
|
||||
* @grf: the general register file will be used to do static set by software
|
||||
|
|
@ -590,6 +593,9 @@ static int rk_tsadcv2_code_to_temp(const struct chip_tsadc_table *table,
|
|||
|
||||
/**
|
||||
* rk_tsadcv2_initialize - initialize TASDC Controller.
|
||||
* @grf: the general register file will be used to do static set by software
|
||||
* @regs: the base address of tsadc controller
|
||||
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
|
||||
*
|
||||
* (1) Set TSADC_V2_AUTO_PERIOD:
|
||||
* Configure the interleave between every two accessing of
|
||||
|
|
@ -624,6 +630,9 @@ static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs,
|
|||
|
||||
/**
|
||||
* rk_tsadcv3_initialize - initialize TASDC Controller.
|
||||
* @grf: the general register file will be used to do static set by software
|
||||
* @regs: the base address of tsadc controller
|
||||
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
|
||||
*
|
||||
* (1) The tsadc control power sequence.
|
||||
*
|
||||
|
|
@ -723,6 +732,8 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable)
|
|||
|
||||
/**
|
||||
* rk_tsadcv3_control - the tsadc controller is enabled or disabled.
|
||||
* @regs: the base address of tsadc controller
|
||||
* @enable: boolean flag to enable the controller
|
||||
*
|
||||
* NOTE: TSADC controller works at auto mode, and some SoCs need set the
|
||||
* tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output
|
||||
|
|
@ -1206,6 +1217,7 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
|
|||
|
||||
/**
|
||||
* Reset TSADC Controller, reset all tsadc registers.
|
||||
* @reset: the reset controller of tsadc
|
||||
*/
|
||||
static void rockchip_thermal_reset_controller(struct reset_control *reset)
|
||||
{
|
||||
|
|
@ -1321,8 +1333,15 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
|||
|
||||
thermal->chip->control(thermal->regs, true);
|
||||
|
||||
for (i = 0; i < thermal->chip->chn_num; i++)
|
||||
for (i = 0; i < thermal->chip->chn_num; i++) {
|
||||
rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
|
||||
thermal->sensors[i].tzd->tzp->no_hwmon = false;
|
||||
error = thermal_add_hwmon_sysfs(thermal->sensors[i].tzd);
|
||||
if (error)
|
||||
dev_warn(&pdev->dev,
|
||||
"failed to register sensor %d with hwmon: %d\n",
|
||||
i, error);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, thermal);
|
||||
|
||||
|
|
@ -1344,6 +1363,7 @@ static int rockchip_thermal_remove(struct platform_device *pdev)
|
|||
for (i = 0; i < thermal->chip->chn_num; i++) {
|
||||
struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
|
||||
|
||||
thermal_remove_hwmon_sysfs(sensor->tzd);
|
||||
rockchip_thermal_toggle_sensor(sensor, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ config EXYNOS_THERMAL
|
|||
depends on HAS_IOMEM
|
||||
help
|
||||
If you say yes here you get support for the TMU (Thermal Management
|
||||
Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises
|
||||
Unit) driver for Samsung Exynos series of SoCs. This driver initialises
|
||||
the TMU, reports temperature and handles cooling action if defined.
|
||||
This driver uses the Exynos core thermal APIs and TMU configuration
|
||||
data from the supported SoCs.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit)
|
||||
* exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit)
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
|
||||
|
|
@ -138,7 +138,7 @@ enum soc_type {
|
|||
|
||||
/**
|
||||
* struct exynos_tmu_data : A structure to hold the private data of the TMU
|
||||
driver
|
||||
* driver
|
||||
* @id: identifier of the one instance of the TMU controller.
|
||||
* @base: base address of the single instance of the TMU controller.
|
||||
* @base_second: base address of the common registers of the TMU controller.
|
||||
|
|
@ -162,8 +162,11 @@ enum soc_type {
|
|||
* 0 < reference_voltage <= 31
|
||||
* @regulator: pointer to the TMU regulator structure.
|
||||
* @reg_conf: pointer to structure to register with core thermal.
|
||||
* @tzd: pointer to thermal_zone_device structure
|
||||
* @ntrip: number of supported trip points.
|
||||
* @enabled: current status of TMU device
|
||||
* @tmu_set_trip_temp: SoC specific method to set trip (rising threshold)
|
||||
* @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold)
|
||||
* @tmu_initialize: SoC specific TMU initialization method
|
||||
* @tmu_control: SoC specific TMU control method
|
||||
* @tmu_read: SoC specific TMU temperature read method
|
||||
|
|
@ -1183,7 +1186,7 @@ static struct platform_driver exynos_tmu_driver = {
|
|||
|
||||
module_platform_driver(exynos_tmu_driver);
|
||||
|
||||
MODULE_DESCRIPTION("EXYNOS TMU Driver");
|
||||
MODULE_DESCRIPTION("Exynos TMU Driver");
|
||||
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:exynos-tmu");
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
#define DTS_DR_OFFSET 0x1C
|
||||
#define DTS_SR_OFFSET 0x20
|
||||
#define DTS_ITENR_OFFSET 0x24
|
||||
#define DTS_CIFR_OFFSET 0x28
|
||||
#define DTS_ICIFR_OFFSET 0x28
|
||||
|
||||
/* DTS_CFGR1 register mask definitions */
|
||||
#define HSREF_CLK_DIV_MASK GENMASK(30, 24)
|
||||
|
|
@ -51,10 +51,16 @@
|
|||
/* DTS_DR register mask definitions */
|
||||
#define TS1_MFREQ_MASK GENMASK(15, 0)
|
||||
|
||||
/* DTS_ITENR register mask definitions */
|
||||
#define ITENR_MASK (GENMASK(2, 0) | GENMASK(6, 4))
|
||||
|
||||
/* DTS_ICIFR register mask definitions */
|
||||
#define ICIFR_MASK (GENMASK(2, 0) | GENMASK(6, 4))
|
||||
|
||||
/* Less significant bit position definitions */
|
||||
#define TS1_T0_POS 16
|
||||
#define TS1_SMP_TIME_POS 16
|
||||
#define TS1_HITTHD_POS 16
|
||||
#define TS1_LITTHD_POS 0
|
||||
#define HSREF_CLK_DIV_POS 24
|
||||
|
||||
/* DTS_CFGR1 bit definitions */
|
||||
|
|
@ -76,59 +82,60 @@
|
|||
#define ONE_MHZ 1000000
|
||||
#define POLL_TIMEOUT 5000
|
||||
#define STARTUP_TIME 40
|
||||
#define TS1_T0_VAL0 30
|
||||
#define TS1_T0_VAL1 130
|
||||
#define TS1_T0_VAL0 30000 /* 30 celsius */
|
||||
#define TS1_T0_VAL1 130000 /* 130 celsius */
|
||||
#define NO_HW_TRIG 0
|
||||
|
||||
/* The Thermal Framework expects millidegrees */
|
||||
#define mcelsius(temp) ((temp) * 1000)
|
||||
|
||||
/* The Sensor expects oC degrees */
|
||||
#define celsius(temp) ((temp) / 1000)
|
||||
#define SAMPLING_TIME 15
|
||||
|
||||
struct stm_thermal_sensor {
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *th_dev;
|
||||
enum thermal_device_mode mode;
|
||||
struct clk *clk;
|
||||
int high_temp;
|
||||
int low_temp;
|
||||
int temp_critical;
|
||||
int temp_passive;
|
||||
unsigned int low_temp_enabled;
|
||||
int num_trips;
|
||||
unsigned int high_temp_enabled;
|
||||
int irq;
|
||||
unsigned int irq_enabled;
|
||||
void __iomem *base;
|
||||
int t0, fmt0, ramp_coeff;
|
||||
};
|
||||
|
||||
static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = sdata;
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
sensor->irq_enabled = false;
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata)
|
||||
static int stm_enable_irq(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
dev_dbg(sensor->dev, "low:%d high:%d\n", sensor->low_temp_enabled,
|
||||
sensor->high_temp_enabled);
|
||||
|
||||
/* Disable IT generation for low and high thresholds */
|
||||
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
||||
value &= ~(LOW_THRESHOLD | HIGH_THRESHOLD);
|
||||
|
||||
if (sensor->low_temp_enabled)
|
||||
value |= HIGH_THRESHOLD;
|
||||
|
||||
if (sensor->high_temp_enabled)
|
||||
value |= LOW_THRESHOLD;
|
||||
|
||||
/* Enable interrupts */
|
||||
writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t stm_thermal_irq_handler(int irq, void *sdata)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = sdata;
|
||||
|
||||
/* read IT reason in SR and clear flags */
|
||||
value = readl_relaxed(sensor->base + DTS_SR_OFFSET);
|
||||
|
||||
if ((value & LOW_THRESHOLD) == LOW_THRESHOLD)
|
||||
writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD)
|
||||
writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
dev_dbg(sensor->dev, "sr:%d\n",
|
||||
readl_relaxed(sensor->base + DTS_SR_OFFSET));
|
||||
|
||||
thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
stm_enable_irq(sensor);
|
||||
|
||||
/* Acknoledge all DTS irqs */
|
||||
writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +167,8 @@ static int stm_sensor_power_on(struct stm_thermal_sensor *sensor)
|
|||
writel_relaxed(value, sensor->base +
|
||||
DTS_CFGR1_OFFSET);
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +176,8 @@ static int stm_sensor_power_off(struct stm_thermal_sensor *sensor)
|
|||
{
|
||||
u32 value;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
/* Stop measuring */
|
||||
value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
|
||||
value &= ~TS1_START;
|
||||
|
|
@ -263,60 +274,17 @@ static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor,
|
|||
int temp, u32 *th)
|
||||
{
|
||||
int freqM;
|
||||
u32 sampling_time;
|
||||
|
||||
/* Retrieve the number of periods to sample */
|
||||
sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
|
||||
TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
|
||||
|
||||
/* Figure out the CLK_PTAT frequency for a given temperature */
|
||||
freqM = ((temp - sensor->t0) * sensor->ramp_coeff)
|
||||
+ sensor->fmt0;
|
||||
|
||||
dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz",
|
||||
__func__, freqM);
|
||||
freqM = ((temp - sensor->t0) * sensor->ramp_coeff) / 1000 +
|
||||
sensor->fmt0;
|
||||
|
||||
/* Figure out the threshold sample number */
|
||||
*th = clk_get_rate(sensor->clk);
|
||||
*th = clk_get_rate(sensor->clk) * SAMPLING_TIME / freqM;
|
||||
if (!*th)
|
||||
return -EINVAL;
|
||||
|
||||
*th = *th / freqM;
|
||||
|
||||
*th *= sampling_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value, th;
|
||||
int ret;
|
||||
|
||||
value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET);
|
||||
|
||||
/* Erase threshold content */
|
||||
value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK);
|
||||
|
||||
/* Retrieve the sample threshold number th for a given temperature */
|
||||
ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
value |= th & TS1_LITTHD_MASK;
|
||||
|
||||
if (sensor->low_temp_enabled) {
|
||||
/* Retrieve the sample threshold */
|
||||
ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp,
|
||||
&th);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS));
|
||||
}
|
||||
|
||||
/* Write value on the Low interrupt threshold */
|
||||
writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET);
|
||||
dev_dbg(sensor->dev, "freqM=%d Hz, threshold=0x%x", freqM, *th);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -326,77 +294,57 @@ static int stm_disable_irq(struct stm_thermal_sensor *sensor)
|
|||
{
|
||||
u32 value;
|
||||
|
||||
/* Disable IT generation for low and high thresholds */
|
||||
/* Disable IT generation */
|
||||
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
||||
writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD),
|
||||
sensor->base + DTS_ITENR_OFFSET);
|
||||
|
||||
dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enable temperature interrupt */
|
||||
static int stm_enable_irq(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/*
|
||||
* Code below enables High temperature threshold using a low threshold
|
||||
* sampling value
|
||||
*/
|
||||
|
||||
/* Make sure LOW_THRESHOLD IT is clear before enabling */
|
||||
writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
/* Enable IT generation for low threshold */
|
||||
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
||||
value |= LOW_THRESHOLD;
|
||||
|
||||
/* Enable the low temperature threshold if needed */
|
||||
if (sensor->low_temp_enabled) {
|
||||
/* Make sure HIGH_THRESHOLD IT is clear before enabling */
|
||||
writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
/* Enable IT generation for high threshold */
|
||||
value |= HIGH_THRESHOLD;
|
||||
}
|
||||
|
||||
/* Enable thresholds */
|
||||
value &= ~ITENR_MASK;
|
||||
writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
|
||||
|
||||
dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor)
|
||||
static int stm_thermal_set_trips(void *data, int low, int high)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = data;
|
||||
u32 itr1, th;
|
||||
int ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_DISABLED;
|
||||
dev_dbg(sensor->dev, "set trips %d <--> %d\n", low, high);
|
||||
|
||||
ret = stm_sensor_power_off(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Erase threshold content */
|
||||
itr1 = readl_relaxed(sensor->base + DTS_ITR1_OFFSET);
|
||||
itr1 &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK);
|
||||
|
||||
ret = stm_disable_irq(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Disable low-temp if "low" is too small. As per thermal framework
|
||||
* API, we use -INT_MAX rather than INT_MIN.
|
||||
*/
|
||||
|
||||
ret = stm_thermal_set_threshold(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (low > -INT_MAX) {
|
||||
sensor->low_temp_enabled = 1;
|
||||
/* add 0.5 of hysteresis due to measurement error */
|
||||
ret = stm_thermal_calculate_threshold(sensor, low - 500, &th);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm_enable_irq(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
itr1 |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS));
|
||||
} else {
|
||||
sensor->low_temp_enabled = 0;
|
||||
}
|
||||
|
||||
ret = stm_sensor_power_on(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Disable high-temp if "high" is too big. */
|
||||
if (high < INT_MAX) {
|
||||
sensor->high_temp_enabled = 1;
|
||||
ret = stm_thermal_calculate_threshold(sensor, high, &th);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
itr1 |= (TS1_LITTHD_MASK & (th << TS1_LITTHD_POS));
|
||||
} else {
|
||||
sensor->high_temp_enabled = 0;
|
||||
}
|
||||
|
||||
/* Write new threshod values*/
|
||||
writel_relaxed(itr1, sensor->base + DTS_ITR1_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -405,76 +353,26 @@ static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor)
|
|||
static int stm_thermal_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = data;
|
||||
u32 sampling_time;
|
||||
u32 periods;
|
||||
int freqM, ret;
|
||||
|
||||
if (sensor->mode != THERMAL_DEVICE_ENABLED)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Retrieve the number of samples */
|
||||
ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM,
|
||||
(freqM & TS1_MFREQ_MASK), STARTUP_TIME,
|
||||
POLL_TIMEOUT);
|
||||
|
||||
/* Retrieve the number of periods sampled */
|
||||
ret = readl_relaxed_poll_timeout(sensor->base + DTS_DR_OFFSET, periods,
|
||||
(periods & TS1_MFREQ_MASK),
|
||||
STARTUP_TIME, POLL_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!freqM)
|
||||
return -ENODATA;
|
||||
|
||||
/* Retrieve the number of periods sampled */
|
||||
sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
|
||||
TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
|
||||
|
||||
/* Figure out the number of samples per period */
|
||||
freqM /= sampling_time;
|
||||
|
||||
/* Figure out the CLK_PTAT frequency */
|
||||
freqM = clk_get_rate(sensor->clk) / freqM;
|
||||
freqM = (clk_get_rate(sensor->clk) * SAMPLING_TIME) / periods;
|
||||
if (!freqM)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM);
|
||||
|
||||
/* Figure out the temperature in mili celsius */
|
||||
*temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) /
|
||||
sensor->ramp_coeff));
|
||||
|
||||
dev_dbg(sensor->dev, "%s: temperature = %d millicelsius",
|
||||
__func__, *temp);
|
||||
|
||||
/* Update thresholds */
|
||||
if (sensor->num_trips > 1) {
|
||||
/* Update alarm threshold value to next higher trip point */
|
||||
if (sensor->high_temp == sensor->temp_passive &&
|
||||
celsius(*temp) >= sensor->temp_passive) {
|
||||
sensor->high_temp = sensor->temp_critical;
|
||||
sensor->low_temp = sensor->temp_passive;
|
||||
sensor->low_temp_enabled = true;
|
||||
ret = stm_thermal_update_threshold(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sensor->high_temp == sensor->temp_critical &&
|
||||
celsius(*temp) < sensor->temp_passive) {
|
||||
sensor->high_temp = sensor->temp_passive;
|
||||
sensor->low_temp_enabled = false;
|
||||
ret = stm_thermal_update_threshold(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-enable alarm IRQ if temperature below critical
|
||||
* temperature
|
||||
*/
|
||||
if (!sensor->irq_enabled &&
|
||||
(celsius(*temp) < sensor->temp_critical)) {
|
||||
sensor->irq_enabled = true;
|
||||
enable_irq(sensor->irq);
|
||||
}
|
||||
}
|
||||
*temp = (freqM - sensor->fmt0) * 1000 / sensor->ramp_coeff + sensor->t0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -493,8 +391,8 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor)
|
|||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, sensor->irq,
|
||||
stm_thermal_alarm_irq,
|
||||
stm_thermal_alarm_irq_thread,
|
||||
NULL,
|
||||
stm_thermal_irq_handler,
|
||||
IRQF_ONESHOT,
|
||||
dev->driver->name, sensor);
|
||||
if (ret) {
|
||||
|
|
@ -503,8 +401,6 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor)
|
|||
return ret;
|
||||
}
|
||||
|
||||
sensor->irq_enabled = true;
|
||||
|
||||
dev_dbg(dev, "%s: thermal IRQ registered", __func__);
|
||||
|
||||
return 0;
|
||||
|
|
@ -514,6 +410,8 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor)
|
|||
{
|
||||
int ret;
|
||||
|
||||
stm_disable_irq(sensor);
|
||||
|
||||
ret = stm_sensor_power_off(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -526,7 +424,6 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor)
|
|||
static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = sensor->dev;
|
||||
|
||||
ret = clk_prepare_enable(sensor->clk);
|
||||
if (ret)
|
||||
|
|
@ -540,26 +437,8 @@ static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
|
|||
if (ret)
|
||||
goto thermal_unprepare;
|
||||
|
||||
/* Set threshold(s) for IRQ */
|
||||
ret = stm_thermal_set_threshold(sensor);
|
||||
if (ret)
|
||||
goto thermal_unprepare;
|
||||
|
||||
ret = stm_enable_irq(sensor);
|
||||
if (ret)
|
||||
goto thermal_unprepare;
|
||||
|
||||
ret = stm_sensor_power_on(sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: failed to power on sensor\n", __func__);
|
||||
goto irq_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
irq_disable:
|
||||
stm_disable_irq(sensor);
|
||||
|
||||
thermal_unprepare:
|
||||
clk_disable_unprepare(sensor->clk);
|
||||
|
||||
|
|
@ -576,8 +455,6 @@ static int stm_thermal_suspend(struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -590,7 +467,12 @@ static int stm_thermal_resume(struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
ret = stm_sensor_power_on(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
|
||||
stm_enable_irq(sensor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -600,6 +482,7 @@ SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume);
|
|||
|
||||
static const struct thermal_zone_of_device_ops stm_tz_ops = {
|
||||
.get_temp = stm_thermal_get_temp,
|
||||
.set_trips = stm_thermal_set_trips,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm_thermal_of_match[] = {
|
||||
|
|
@ -612,9 +495,8 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct stm_thermal_sensor *sensor;
|
||||
struct resource *res;
|
||||
const struct thermal_trip *trip;
|
||||
void __iomem *base;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
dev_err(&pdev->dev, "%s: device tree node not found\n",
|
||||
|
|
@ -645,10 +527,23 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(sensor->clk);
|
||||
}
|
||||
|
||||
/* Register IRQ into GIC */
|
||||
ret = stm_register_irq(sensor);
|
||||
if (ret)
|
||||
stm_disable_irq(sensor);
|
||||
|
||||
/* Clear irq flags */
|
||||
writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET);
|
||||
|
||||
/* Configure and enable HW sensor */
|
||||
ret = stm_thermal_prepare(sensor);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error preprare sensor: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm_sensor_power_on(sensor);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error power on sensor: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
|
||||
sensor,
|
||||
|
|
@ -661,53 +556,12 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!sensor->th_dev->ops->get_crit_temp) {
|
||||
/* Critical point must be provided */
|
||||
ret = -EINVAL;
|
||||
/* Register IRQ into GIC */
|
||||
ret = stm_register_irq(sensor);
|
||||
if (ret)
|
||||
goto err_tz;
|
||||
}
|
||||
|
||||
ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev,
|
||||
&sensor->temp_critical);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Not able to read critical_temp: %d\n", ret);
|
||||
goto err_tz;
|
||||
}
|
||||
|
||||
sensor->temp_critical = celsius(sensor->temp_critical);
|
||||
|
||||
/* Set thresholds for IRQ */
|
||||
sensor->high_temp = sensor->temp_critical;
|
||||
|
||||
trip = of_thermal_get_trip_points(sensor->th_dev);
|
||||
sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev);
|
||||
|
||||
/* Find out passive temperature if it exists */
|
||||
for (i = (sensor->num_trips - 1); i >= 0; i--) {
|
||||
if (trip[i].type == THERMAL_TRIP_PASSIVE) {
|
||||
sensor->temp_passive = celsius(trip[i].temperature);
|
||||
/* Update high temperature threshold */
|
||||
sensor->high_temp = sensor->temp_passive;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure low_temp_enabled flag is disabled.
|
||||
* By disabling low_temp_enabled, low threshold IT will not be
|
||||
* configured neither enabled because it is not needed as high
|
||||
* threshold is set on the lowest temperature trip point after
|
||||
* probe.
|
||||
*/
|
||||
sensor->low_temp_enabled = false;
|
||||
|
||||
/* Configure and enable HW sensor */
|
||||
ret = stm_thermal_prepare(sensor);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Not able to enable sensor: %d\n", ret);
|
||||
goto err_tz;
|
||||
}
|
||||
stm_enable_irq(sensor);
|
||||
|
||||
/*
|
||||
* Thermal_zone doesn't enable hwmon as default,
|
||||
|
|
@ -718,8 +572,6 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_tz;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
dev_info(&pdev->dev, "%s: Driver initialized successfully\n",
|
||||
__func__);
|
||||
|
||||
|
|
|
|||
|
|
@ -174,8 +174,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
|||
|
||||
/**
|
||||
* step_wise_throttle - throttles devices associated with the given zone
|
||||
* @tz - thermal_zone_device
|
||||
* @trip - trip point index
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: trip point index
|
||||
*
|
||||
* Throttling Logic: This uses the trend of the thermal zone to throttle.
|
||||
* If the thermal zone is 'heating up' this throttles all the cooling
|
||||
|
|
|
|||
639
drivers/thermal/sun8i_thermal.c
Normal file
639
drivers/thermal/sun8i_thermal.c
Normal file
|
|
@ -0,0 +1,639 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Thermal sensor driver for Allwinner SOC
|
||||
* Copyright (C) 2019 Yangtao Li
|
||||
*
|
||||
* Based on the work of Icenowy Zheng <icenowy@aosc.io>
|
||||
* Based on the work of Ondrej Jirman <megous@megous.com>
|
||||
* Based on the work of Josef Gajdusek <atx@atx.name>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
#define MAX_SENSOR_NUM 4
|
||||
|
||||
#define FT_TEMP_MASK GENMASK(11, 0)
|
||||
#define TEMP_CALIB_MASK GENMASK(11, 0)
|
||||
#define CALIBRATE_DEFAULT 0x800
|
||||
|
||||
#define SUN8I_THS_CTRL0 0x00
|
||||
#define SUN8I_THS_CTRL2 0x40
|
||||
#define SUN8I_THS_IC 0x44
|
||||
#define SUN8I_THS_IS 0x48
|
||||
#define SUN8I_THS_MFC 0x70
|
||||
#define SUN8I_THS_TEMP_CALIB 0x74
|
||||
#define SUN8I_THS_TEMP_DATA 0x80
|
||||
|
||||
#define SUN50I_THS_CTRL0 0x00
|
||||
#define SUN50I_H6_THS_ENABLE 0x04
|
||||
#define SUN50I_H6_THS_PC 0x08
|
||||
#define SUN50I_H6_THS_DIC 0x10
|
||||
#define SUN50I_H6_THS_DIS 0x20
|
||||
#define SUN50I_H6_THS_MFC 0x30
|
||||
#define SUN50I_H6_THS_TEMP_CALIB 0xa0
|
||||
#define SUN50I_H6_THS_TEMP_DATA 0xc0
|
||||
|
||||
#define SUN8I_THS_CTRL0_T_ACQ0(x) (GENMASK(15, 0) & (x))
|
||||
#define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16)
|
||||
#define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8)
|
||||
|
||||
#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16)
|
||||
#define SUN50I_THS_FILTER_EN BIT(2)
|
||||
#define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
|
||||
#define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12)
|
||||
#define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x)
|
||||
|
||||
/* millidegree celsius */
|
||||
|
||||
struct tsensor {
|
||||
struct ths_device *tmdev;
|
||||
struct thermal_zone_device *tzd;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct ths_thermal_chip {
|
||||
bool has_mod_clk;
|
||||
bool has_bus_clk_reset;
|
||||
int sensor_num;
|
||||
int offset;
|
||||
int scale;
|
||||
int ft_deviation;
|
||||
int temp_data_base;
|
||||
int (*calibrate)(struct ths_device *tmdev,
|
||||
u16 *caldata, int callen);
|
||||
int (*init)(struct ths_device *tmdev);
|
||||
int (*irq_ack)(struct ths_device *tmdev);
|
||||
int (*calc_temp)(struct ths_device *tmdev,
|
||||
int id, int reg);
|
||||
};
|
||||
|
||||
struct ths_device {
|
||||
const struct ths_thermal_chip *chip;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct reset_control *reset;
|
||||
struct clk *bus_clk;
|
||||
struct clk *mod_clk;
|
||||
struct tsensor sensor[MAX_SENSOR_NUM];
|
||||
};
|
||||
|
||||
/* Temp Unit: millidegree Celsius */
|
||||
static int sun8i_ths_calc_temp(struct ths_device *tmdev,
|
||||
int id, int reg)
|
||||
{
|
||||
return tmdev->chip->offset - (reg * tmdev->chip->scale / 10);
|
||||
}
|
||||
|
||||
static int sun50i_h5_calc_temp(struct ths_device *tmdev,
|
||||
int id, int reg)
|
||||
{
|
||||
if (reg >= 0x500)
|
||||
return -1191 * reg / 10 + 223000;
|
||||
else if (!id)
|
||||
return -1452 * reg / 10 + 259000;
|
||||
else
|
||||
return -1590 * reg / 10 + 276000;
|
||||
}
|
||||
|
||||
static int sun8i_ths_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct tsensor *s = data;
|
||||
struct ths_device *tmdev = s->tmdev;
|
||||
int val = 0;
|
||||
|
||||
regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
|
||||
0x4 * s->id, &val);
|
||||
|
||||
/* ths have no data yet */
|
||||
if (!val)
|
||||
return -EAGAIN;
|
||||
|
||||
*temp = tmdev->chip->calc_temp(tmdev, s->id, val);
|
||||
/*
|
||||
* According to the original sdk, there are some platforms(rarely)
|
||||
* that add a fixed offset value after calculating the temperature
|
||||
* value. We can't simply put it on the formula for calculating the
|
||||
* temperature above, because the formula for calculating the
|
||||
* temperature above is also used when the sensor is calibrated. If
|
||||
* do this, the correct calibration formula is hard to know.
|
||||
*/
|
||||
*temp += tmdev->chip->ft_deviation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops ths_ops = {
|
||||
.get_temp = sun8i_ths_get_temp,
|
||||
};
|
||||
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
.max_register = 0xfc,
|
||||
};
|
||||
|
||||
static int sun8i_h3_irq_ack(struct ths_device *tmdev)
|
||||
{
|
||||
int i, state, ret = 0;
|
||||
|
||||
regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
|
||||
|
||||
for (i = 0; i < tmdev->chip->sensor_num; i++) {
|
||||
if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
|
||||
regmap_write(tmdev->regmap, SUN8I_THS_IS,
|
||||
SUN8I_THS_DATA_IRQ_STS(i));
|
||||
ret |= BIT(i);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun50i_h6_irq_ack(struct ths_device *tmdev)
|
||||
{
|
||||
int i, state, ret = 0;
|
||||
|
||||
regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
|
||||
|
||||
for (i = 0; i < tmdev->chip->sensor_num; i++) {
|
||||
if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
|
||||
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
|
||||
SUN50I_H6_THS_DATA_IRQ_STS(i));
|
||||
ret |= BIT(i);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t sun8i_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct ths_device *tmdev = data;
|
||||
int i, state;
|
||||
|
||||
state = tmdev->chip->irq_ack(tmdev);
|
||||
|
||||
for (i = 0; i < tmdev->chip->sensor_num; i++) {
|
||||
if (state & BIT(i))
|
||||
thermal_zone_device_update(tmdev->sensor[i].tzd,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
|
||||
u16 *caldata, int callen)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < tmdev->chip->sensor_num; i++) {
|
||||
int offset = (i % 2) << 4;
|
||||
|
||||
regmap_update_bits(tmdev->regmap,
|
||||
SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
|
||||
0xfff << offset,
|
||||
caldata[i] << offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
|
||||
u16 *caldata, int callen)
|
||||
{
|
||||
struct device *dev = tmdev->dev;
|
||||
int i, ft_temp;
|
||||
|
||||
if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* efuse layout:
|
||||
*
|
||||
* 0 11 16 32
|
||||
* +-------+-------+-------+
|
||||
* |temp| |sensor0|sensor1|
|
||||
* +-------+-------+-------+
|
||||
*
|
||||
* The calibration data on the H6 is the ambient temperature and
|
||||
* sensor values that are filled during the factory test stage.
|
||||
*
|
||||
* The unit of stored FT temperature is 0.1 degreee celusis.
|
||||
*
|
||||
* We need to calculate a delta between measured and caluclated
|
||||
* register values and this will become a calibration offset.
|
||||
*/
|
||||
ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
|
||||
|
||||
for (i = 0; i < tmdev->chip->sensor_num; i++) {
|
||||
int sensor_reg = caldata[i + 1];
|
||||
int cdata, offset;
|
||||
int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
|
||||
|
||||
/*
|
||||
* Calibration data is CALIBRATE_DEFAULT - (calculated
|
||||
* temperature from sensor reading at factory temperature
|
||||
* minus actual factory temperature) * 14.88 (scale from
|
||||
* temperature to register values)
|
||||
*/
|
||||
cdata = CALIBRATE_DEFAULT -
|
||||
((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
|
||||
if (cdata & ~TEMP_CALIB_MASK) {
|
||||
/*
|
||||
* Calibration value more than 12-bit, but calibration
|
||||
* register is 12-bit. In this case, ths hardware can
|
||||
* still work without calibration, although the data
|
||||
* won't be so accurate.
|
||||
*/
|
||||
dev_warn(dev, "sensor%d is not calibrated.\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
offset = (i % 2) * 16;
|
||||
regmap_update_bits(tmdev->regmap,
|
||||
SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
|
||||
0xfff << offset,
|
||||
cdata << offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ths_calibrate(struct ths_device *tmdev)
|
||||
{
|
||||
struct nvmem_cell *calcell;
|
||||
struct device *dev = tmdev->dev;
|
||||
u16 *caldata;
|
||||
size_t callen;
|
||||
int ret = 0;
|
||||
|
||||
calcell = devm_nvmem_cell_get(dev, "calibration");
|
||||
if (IS_ERR(calcell)) {
|
||||
if (PTR_ERR(calcell) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
/*
|
||||
* Even if the external calibration data stored in sid is
|
||||
* not accessible, the THS hardware can still work, although
|
||||
* the data won't be so accurate.
|
||||
*
|
||||
* The default value of calibration register is 0x800 for
|
||||
* every sensor, and the calibration value is usually 0x7xx
|
||||
* or 0x8xx, so they won't be away from the default value
|
||||
* for a lot.
|
||||
*
|
||||
* So here we do not return error if the calibartion data is
|
||||
* not available, except the probe needs deferring.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
caldata = nvmem_cell_read(calcell, &callen);
|
||||
if (IS_ERR(caldata)) {
|
||||
ret = PTR_ERR(caldata);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmdev->chip->calibrate(tmdev, caldata, callen);
|
||||
|
||||
kfree(caldata);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun8i_ths_resource_init(struct ths_device *tmdev)
|
||||
{
|
||||
struct device *dev = tmdev->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
|
||||
if (IS_ERR(tmdev->regmap))
|
||||
return PTR_ERR(tmdev->regmap);
|
||||
|
||||
if (tmdev->chip->has_bus_clk_reset) {
|
||||
tmdev->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(tmdev->reset))
|
||||
return PTR_ERR(tmdev->reset);
|
||||
|
||||
tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
||||
if (IS_ERR(tmdev->bus_clk))
|
||||
return PTR_ERR(tmdev->bus_clk);
|
||||
}
|
||||
|
||||
if (tmdev->chip->has_mod_clk) {
|
||||
tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod");
|
||||
if (IS_ERR(tmdev->mod_clk))
|
||||
return PTR_ERR(tmdev->mod_clk);
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(tmdev->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(tmdev->bus_clk);
|
||||
if (ret)
|
||||
goto assert_reset;
|
||||
|
||||
ret = clk_set_rate(tmdev->mod_clk, 24000000);
|
||||
if (ret)
|
||||
goto bus_disable;
|
||||
|
||||
ret = clk_prepare_enable(tmdev->mod_clk);
|
||||
if (ret)
|
||||
goto bus_disable;
|
||||
|
||||
ret = sun8i_ths_calibrate(tmdev);
|
||||
if (ret)
|
||||
goto mod_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
mod_disable:
|
||||
clk_disable_unprepare(tmdev->mod_clk);
|
||||
bus_disable:
|
||||
clk_disable_unprepare(tmdev->bus_clk);
|
||||
assert_reset:
|
||||
reset_control_assert(tmdev->reset);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun8i_h3_thermal_init(struct ths_device *tmdev)
|
||||
{
|
||||
int val;
|
||||
|
||||
/* average over 4 samples */
|
||||
regmap_write(tmdev->regmap, SUN8I_THS_MFC,
|
||||
SUN50I_THS_FILTER_EN |
|
||||
SUN50I_THS_FILTER_TYPE(1));
|
||||
/*
|
||||
* clkin = 24MHz
|
||||
* filter_samples = 4
|
||||
* period = 0.25s
|
||||
*
|
||||
* x = period * clkin / 4096 / filter_samples - 1
|
||||
* = 365
|
||||
*/
|
||||
val = GENMASK(7 + tmdev->chip->sensor_num, 8);
|
||||
regmap_write(tmdev->regmap, SUN8I_THS_IC,
|
||||
SUN50I_H6_THS_PC_TEMP_PERIOD(365) | val);
|
||||
/*
|
||||
* T_acq = 20us
|
||||
* clkin = 24MHz
|
||||
*
|
||||
* x = T_acq * clkin - 1
|
||||
* = 479
|
||||
*/
|
||||
regmap_write(tmdev->regmap, SUN8I_THS_CTRL0,
|
||||
SUN8I_THS_CTRL0_T_ACQ0(479));
|
||||
val = GENMASK(tmdev->chip->sensor_num - 1, 0);
|
||||
regmap_write(tmdev->regmap, SUN8I_THS_CTRL2,
|
||||
SUN8I_THS_CTRL2_T_ACQ1(479) | val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Without this undocummented value, the returned temperatures would
|
||||
* be higher than real ones by about 20C.
|
||||
*/
|
||||
#define SUN50I_H6_CTRL0_UNK 0x0000002f
|
||||
|
||||
static int sun50i_h6_thermal_init(struct ths_device *tmdev)
|
||||
{
|
||||
int val;
|
||||
|
||||
/*
|
||||
* T_acq = 20us
|
||||
* clkin = 24MHz
|
||||
*
|
||||
* x = T_acq * clkin - 1
|
||||
* = 479
|
||||
*/
|
||||
regmap_write(tmdev->regmap, SUN50I_THS_CTRL0,
|
||||
SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479));
|
||||
/* average over 4 samples */
|
||||
regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC,
|
||||
SUN50I_THS_FILTER_EN |
|
||||
SUN50I_THS_FILTER_TYPE(1));
|
||||
/*
|
||||
* clkin = 24MHz
|
||||
* filter_samples = 4
|
||||
* period = 0.25s
|
||||
*
|
||||
* x = period * clkin / 4096 / filter_samples - 1
|
||||
* = 365
|
||||
*/
|
||||
regmap_write(tmdev->regmap, SUN50I_H6_THS_PC,
|
||||
SUN50I_H6_THS_PC_TEMP_PERIOD(365));
|
||||
/* enable sensor */
|
||||
val = GENMASK(tmdev->chip->sensor_num - 1, 0);
|
||||
regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val);
|
||||
/* thermal data interrupt enable */
|
||||
val = GENMASK(tmdev->chip->sensor_num - 1, 0);
|
||||
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ths_register(struct ths_device *tmdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tmdev->chip->sensor_num; i++) {
|
||||
tmdev->sensor[i].tmdev = tmdev;
|
||||
tmdev->sensor[i].id = i;
|
||||
tmdev->sensor[i].tzd =
|
||||
devm_thermal_zone_of_sensor_register(tmdev->dev,
|
||||
i,
|
||||
&tmdev->sensor[i],
|
||||
&ths_ops);
|
||||
if (IS_ERR(tmdev->sensor[i].tzd))
|
||||
return PTR_ERR(tmdev->sensor[i].tzd);
|
||||
|
||||
if (devm_thermal_add_hwmon_sysfs(tmdev->sensor[i].tzd))
|
||||
dev_warn(tmdev->dev,
|
||||
"Failed to add hwmon sysfs attributes\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ths_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ths_device *tmdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret, irq;
|
||||
|
||||
tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
|
||||
if (!tmdev)
|
||||
return -ENOMEM;
|
||||
|
||||
tmdev->dev = dev;
|
||||
tmdev->chip = of_device_get_match_data(&pdev->dev);
|
||||
if (!tmdev->chip)
|
||||
return -EINVAL;
|
||||
|
||||
platform_set_drvdata(pdev, tmdev);
|
||||
|
||||
ret = sun8i_ths_resource_init(tmdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = tmdev->chip->init(tmdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sun8i_ths_register(tmdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Avoid entering the interrupt handler, the thermal device is not
|
||||
* registered yet, we deffer the registration of the interrupt to
|
||||
* the end.
|
||||
*/
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
sun8i_irq_thread,
|
||||
IRQF_ONESHOT, "ths", tmdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ths_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ths_device *tmdev = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(tmdev->mod_clk);
|
||||
clk_disable_unprepare(tmdev->bus_clk);
|
||||
reset_control_assert(tmdev->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ths_thermal_chip sun8i_a83t_ths = {
|
||||
.sensor_num = 3,
|
||||
.scale = 705,
|
||||
.offset = 191668,
|
||||
.temp_data_base = SUN8I_THS_TEMP_DATA,
|
||||
.calibrate = sun8i_h3_ths_calibrate,
|
||||
.init = sun8i_h3_thermal_init,
|
||||
.irq_ack = sun8i_h3_irq_ack,
|
||||
.calc_temp = sun8i_ths_calc_temp,
|
||||
};
|
||||
|
||||
static const struct ths_thermal_chip sun8i_h3_ths = {
|
||||
.sensor_num = 1,
|
||||
.scale = 1211,
|
||||
.offset = 217000,
|
||||
.has_mod_clk = true,
|
||||
.has_bus_clk_reset = true,
|
||||
.temp_data_base = SUN8I_THS_TEMP_DATA,
|
||||
.calibrate = sun8i_h3_ths_calibrate,
|
||||
.init = sun8i_h3_thermal_init,
|
||||
.irq_ack = sun8i_h3_irq_ack,
|
||||
.calc_temp = sun8i_ths_calc_temp,
|
||||
};
|
||||
|
||||
static const struct ths_thermal_chip sun8i_r40_ths = {
|
||||
.sensor_num = 2,
|
||||
.offset = 251086,
|
||||
.scale = 1130,
|
||||
.has_mod_clk = true,
|
||||
.has_bus_clk_reset = true,
|
||||
.temp_data_base = SUN8I_THS_TEMP_DATA,
|
||||
.calibrate = sun8i_h3_ths_calibrate,
|
||||
.init = sun8i_h3_thermal_init,
|
||||
.irq_ack = sun8i_h3_irq_ack,
|
||||
.calc_temp = sun8i_ths_calc_temp,
|
||||
};
|
||||
|
||||
static const struct ths_thermal_chip sun50i_a64_ths = {
|
||||
.sensor_num = 3,
|
||||
.offset = 260890,
|
||||
.scale = 1170,
|
||||
.has_mod_clk = true,
|
||||
.has_bus_clk_reset = true,
|
||||
.temp_data_base = SUN8I_THS_TEMP_DATA,
|
||||
.calibrate = sun8i_h3_ths_calibrate,
|
||||
.init = sun8i_h3_thermal_init,
|
||||
.irq_ack = sun8i_h3_irq_ack,
|
||||
.calc_temp = sun8i_ths_calc_temp,
|
||||
};
|
||||
|
||||
static const struct ths_thermal_chip sun50i_h5_ths = {
|
||||
.sensor_num = 2,
|
||||
.has_mod_clk = true,
|
||||
.has_bus_clk_reset = true,
|
||||
.temp_data_base = SUN8I_THS_TEMP_DATA,
|
||||
.calibrate = sun8i_h3_ths_calibrate,
|
||||
.init = sun8i_h3_thermal_init,
|
||||
.irq_ack = sun8i_h3_irq_ack,
|
||||
.calc_temp = sun50i_h5_calc_temp,
|
||||
};
|
||||
|
||||
static const struct ths_thermal_chip sun50i_h6_ths = {
|
||||
.sensor_num = 2,
|
||||
.has_bus_clk_reset = true,
|
||||
.ft_deviation = 7000,
|
||||
.offset = 187744,
|
||||
.scale = 672,
|
||||
.temp_data_base = SUN50I_H6_THS_TEMP_DATA,
|
||||
.calibrate = sun50i_h6_ths_calibrate,
|
||||
.init = sun50i_h6_thermal_init,
|
||||
.irq_ack = sun50i_h6_irq_ack,
|
||||
.calc_temp = sun8i_ths_calc_temp,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_ths_match[] = {
|
||||
{ .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
|
||||
{ .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
|
||||
{ .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
|
||||
{ .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
|
||||
{ .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
|
||||
{ .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_ths_match);
|
||||
|
||||
static struct platform_driver ths_driver = {
|
||||
.probe = sun8i_ths_probe,
|
||||
.remove = sun8i_ths_remove,
|
||||
.driver = {
|
||||
.name = "sun8i-thermal",
|
||||
.of_match_table = of_ths_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ths_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -360,7 +360,7 @@ static struct soctherm_oc_irq_chip_data soc_irq_cdata;
|
|||
/**
|
||||
* ccroc_writel() - writes a value to a CCROC register
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @v: the value to write
|
||||
* @value: the value to write
|
||||
* @reg: the register offset
|
||||
*
|
||||
* Writes @v to @reg. No return value.
|
||||
|
|
@ -435,6 +435,7 @@ static int tegra_thermctl_get_temp(void *data, int *out_temp)
|
|||
|
||||
/**
|
||||
* enforce_temp_range() - check and enforce temperature range [min, max]
|
||||
* @dev: struct device * of the SOC_THERM instance
|
||||
* @trip_temp: the trip temperature to check
|
||||
*
|
||||
* Checks and enforces the permitted temperature range that SOC_THERM
|
||||
|
|
@ -747,6 +748,8 @@ static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp)
|
|||
/**
|
||||
* tegra_soctherm_set_hwtrips() - set HW trip point from DT data
|
||||
* @dev: struct device * of the SOC_THERM instance
|
||||
* @sg: pointer to the sensor group to set the thermtrip temperature for
|
||||
* @tz: struct thermal_zone_device *
|
||||
*
|
||||
* Configure the SOC_THERM HW trip points, setting "THERMTRIP"
|
||||
* "THROTTLE" trip points , using "thermtrips", "critical" or "hot"
|
||||
|
|
@ -931,6 +934,7 @@ static irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id)
|
|||
|
||||
/**
|
||||
* soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @alarm: The soctherm throttle id
|
||||
* @enable: Flag indicating enable the soctherm over-current
|
||||
* interrupt or disable it
|
||||
|
|
@ -1156,7 +1160,7 @@ static void soctherm_oc_irq_enable(struct irq_data *data)
|
|||
|
||||
/**
|
||||
* soctherm_oc_irq_disable() - Disables overcurrent interrupt requests
|
||||
* @irq_data: The interrupt request information
|
||||
* @data: The interrupt request information
|
||||
*
|
||||
* Clears the interrupt request enable bit of the overcurrent
|
||||
* interrupt request chip data.
|
||||
|
|
@ -1206,6 +1210,7 @@ static int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq,
|
|||
/**
|
||||
* soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts
|
||||
* @d: Interrupt request domain
|
||||
* @ctrlr: Controller device tree node
|
||||
* @intspec: Array of u32s from DTs "interrupt" property
|
||||
* @intsize: Number of values inside the intspec array
|
||||
* @out_hwirq: HW IRQ value associated with this interrupt
|
||||
|
|
@ -1681,6 +1686,7 @@ err:
|
|||
/**
|
||||
* soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations
|
||||
* and register them as cooling devices.
|
||||
* @pdev: Pointer to platform_device struct
|
||||
*/
|
||||
static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
|
||||
{
|
||||
|
|
@ -1751,6 +1757,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
|
|||
|
||||
/**
|
||||
* throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @level: describing the level LOW/MED/HIGH of throttling
|
||||
*
|
||||
* It's necessary to set up the CPU-local CCROC NV_THERM instance with
|
||||
|
|
@ -1798,6 +1805,7 @@ static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level)
|
|||
|
||||
/**
|
||||
* throttlectl_cpu_level_select() - program CPU pulse skipper config
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @throt: the LIGHT/HEAVY of throttle event id
|
||||
*
|
||||
* Pulse skippers are used to throttle clock frequencies. This
|
||||
|
|
@ -1841,6 +1849,7 @@ static void throttlectl_cpu_level_select(struct tegra_soctherm *ts,
|
|||
|
||||
/**
|
||||
* throttlectl_cpu_mn() - program CPU pulse skipper configuration
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @throt: the LIGHT/HEAVY of throttle event id
|
||||
*
|
||||
* Pulse skippers are used to throttle clock frequencies. This
|
||||
|
|
@ -1874,6 +1883,7 @@ static void throttlectl_cpu_mn(struct tegra_soctherm *ts,
|
|||
|
||||
/**
|
||||
* throttlectl_gpu_level_select() - selects throttling level for GPU
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @throt: the LIGHT/HEAVY of throttle event id
|
||||
*
|
||||
* This function programs soctherm's interface to GK20a NV_THERM to select
|
||||
|
|
@ -1918,6 +1928,7 @@ static int soctherm_oc_cfg_program(struct tegra_soctherm *ts,
|
|||
|
||||
/**
|
||||
* soctherm_throttle_program() - programs pulse skippers' configuration
|
||||
* @ts: pointer to a struct tegra_soctherm
|
||||
* @throt: the LIGHT/HEAVY of the throttle event id.
|
||||
*
|
||||
* Pulse skippers are used to throttle clock frequencies.
|
||||
|
|
|
|||
|
|
@ -76,13 +76,17 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev,
|
|||
struct gadc_thermal_info *gti)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
enum iio_chan_type chan_type;
|
||||
int ntable;
|
||||
int ret;
|
||||
|
||||
ntable = of_property_count_elems_of_size(np, "temperature-lookup-table",
|
||||
sizeof(u32));
|
||||
if (ntable <= 0) {
|
||||
dev_notice(dev, "no lookup table, assuming DAC channel returns milliCelcius\n");
|
||||
ret = iio_get_channel_type(gti->channel, &chan_type);
|
||||
if (ret || chan_type != IIO_TEMP)
|
||||
dev_notice(dev,
|
||||
"no lookup table, assuming DAC channel returns milliCelcius\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -124,13 +128,6 @@ static int gadc_thermal_probe(struct platform_device *pdev)
|
|||
if (!gti)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gti->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, gti);
|
||||
|
||||
gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel");
|
||||
if (IS_ERR(gti->channel)) {
|
||||
ret = PTR_ERR(gti->channel);
|
||||
|
|
@ -139,6 +136,13 @@ static int gadc_thermal_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gti->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, gti);
|
||||
|
||||
gti->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, gti,
|
||||
&gadc_thermal_ops);
|
||||
if (IS_ERR(gti->tz_dev)) {
|
||||
|
|
|
|||
|
|
@ -92,14 +92,12 @@ thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
|||
/* device tree support */
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
int of_parse_thermal_zones(void);
|
||||
void of_thermal_destroy_zones(void);
|
||||
int of_thermal_get_ntrips(struct thermal_zone_device *);
|
||||
bool of_thermal_is_trip_valid(struct thermal_zone_device *, int);
|
||||
const struct thermal_trip *
|
||||
of_thermal_get_trip_points(struct thermal_zone_device *);
|
||||
#else
|
||||
static inline int of_parse_thermal_zones(void) { return 0; }
|
||||
static inline void of_thermal_destroy_zones(void) { }
|
||||
static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -248,3 +248,31 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
|
|||
kfree(hwmon);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs);
|
||||
|
||||
static void devm_thermal_hwmon_release(struct device *dev, void *res)
|
||||
{
|
||||
thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res);
|
||||
}
|
||||
|
||||
int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_zone_device **ptr;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = thermal_add_hwmon_sysfs(tz);
|
||||
if (ret) {
|
||||
devres_free(ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ptr = tz;
|
||||
devres_add(&tz->device, ptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#ifdef CONFIG_THERMAL_HWMON
|
||||
int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz);
|
||||
int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz);
|
||||
void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz);
|
||||
#else
|
||||
static inline int
|
||||
|
|
@ -25,6 +26,12 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
/**
|
||||
* notify_user_space - Notifies user space about thermal events
|
||||
* @tz - thermal_zone_device
|
||||
* @trip - trip point index
|
||||
* @tz: thermal_zone_device
|
||||
* @trip: trip point index
|
||||
*
|
||||
* This function notifies the user space through UEvents.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
* @clk_topcrm: topcrm clk structure
|
||||
* @clk_apb: apb clk structure
|
||||
* @regs: pointer to base address of the thermal sensor
|
||||
* @dev: struct device pointer
|
||||
*/
|
||||
|
||||
struct zx2967_thermal_priv {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* thermal_exynos.h - Samsung EXYNOS TMU device tree definitions
|
||||
* thermal_exynos.h - Samsung Exynos TMU device tree definitions
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Lukasz Majewski <l.majewski@samsung.com>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* Copyright (C) 2013 Texas Instruments Inc.
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* Highly based on cpu_cooling.c.
|
||||
* Highly based on cpufreq_cooling.c.
|
||||
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
|
||||
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
struct cpufreq_policy;
|
||||
|
||||
#ifdef CONFIG_CPU_THERMAL
|
||||
#ifdef CONFIG_CPU_FREQ_THERMAL
|
||||
/**
|
||||
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* @policy: cpufreq policy.
|
||||
|
|
@ -40,7 +40,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
|
|||
struct thermal_cooling_device *
|
||||
of_cpufreq_cooling_register(struct cpufreq_policy *policy);
|
||||
|
||||
#else /* !CONFIG_CPU_THERMAL */
|
||||
#else /* !CONFIG_CPU_FREQ_THERMAL */
|
||||
static inline struct thermal_cooling_device *
|
||||
cpufreq_cooling_register(struct cpufreq_policy *policy)
|
||||
{
|
||||
|
|
@ -58,6 +58,24 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_CPU_THERMAL */
|
||||
#endif /* CONFIG_CPU_FREQ_THERMAL */
|
||||
|
||||
struct cpuidle_driver;
|
||||
|
||||
#ifdef CONFIG_CPU_IDLE_THERMAL
|
||||
int cpuidle_cooling_register(struct cpuidle_driver *drv);
|
||||
int cpuidle_of_cooling_register(struct device_node *np,
|
||||
struct cpuidle_driver *drv);
|
||||
#else /* CONFIG_CPU_IDLE_THERMAL */
|
||||
static inline int cpuidle_cooling_register(struct cpuidle_driver *drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int cpuidle_of_cooling_register(struct device_node *np,
|
||||
struct cpuidle_driver *drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_CPU_IDLE_THERMAL */
|
||||
|
||||
#endif /* __CPU_COOLING_H__ */
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
|
|||
extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
|
||||
enum kernel_read_file_id id);
|
||||
extern void ima_post_path_mknod(struct dentry *dentry);
|
||||
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
|
||||
extern void ima_kexec_cmdline(const void *buf, int size);
|
||||
|
||||
#ifdef CONFIG_IMA_KEXEC
|
||||
|
|
@ -91,6 +92,11 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
|
|||
return;
|
||||
}
|
||||
|
||||
static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void ima_kexec_cmdline(const void *buf, int size) {}
|
||||
#endif /* CONFIG_IMA */
|
||||
|
||||
|
|
@ -101,6 +107,20 @@ static inline void ima_add_kexec_buffer(struct kimage *image)
|
|||
{}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
|
||||
extern void ima_post_key_create_or_update(struct key *keyring,
|
||||
struct key *key,
|
||||
const void *payload, size_t plen,
|
||||
unsigned long flags, bool create);
|
||||
#else
|
||||
static inline void ima_post_key_create_or_update(struct key *keyring,
|
||||
struct key *key,
|
||||
const void *payload,
|
||||
size_t plen,
|
||||
unsigned long flags,
|
||||
bool create) {}
|
||||
#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
extern bool is_ima_appraise_enabled(void);
|
||||
extern void ima_inode_post_setattr(struct dentry *dentry);
|
||||
|
|
|
|||
|
|
@ -547,6 +547,20 @@ struct sdw_slave_ops {
|
|||
* @node: node for bus list
|
||||
* @port_ready: Port ready completion flag for each Slave port
|
||||
* @dev_num: Device Number assigned by Bus
|
||||
* @probed: boolean tracking driver state
|
||||
* @probe_complete: completion utility to control potential races
|
||||
* on startup between driver probe/initialization and SoundWire
|
||||
* Slave state changes/implementation-defined interrupts
|
||||
* @enumeration_complete: completion utility to control potential races
|
||||
* on startup between device enumeration and read/write access to the
|
||||
* Slave device
|
||||
* @initialization_complete: completion utility to control potential races
|
||||
* on startup between device enumeration and settings being restored
|
||||
* @unattach_request: mask field to keep track why the Slave re-attached and
|
||||
* was re-initialized. This is useful to deal with potential race conditions
|
||||
* between the Master suspending and the codec resuming, and make sure that
|
||||
* when the Master triggered a reset the Slave is properly enumerated and
|
||||
* initialized
|
||||
*/
|
||||
struct sdw_slave {
|
||||
struct sdw_slave_id id;
|
||||
|
|
@ -561,6 +575,11 @@ struct sdw_slave {
|
|||
struct list_head node;
|
||||
struct completion *port_ready;
|
||||
u16 dev_num;
|
||||
bool probed;
|
||||
struct completion probe_complete;
|
||||
struct completion enumeration_complete;
|
||||
struct completion initialization_complete;
|
||||
u32 unattach_request;
|
||||
};
|
||||
|
||||
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
|
||||
|
|
|
|||
|
|
@ -4,36 +4,174 @@
|
|||
#ifndef __SDW_INTEL_H
|
||||
#define __SDW_INTEL_H
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ops: Intel audio driver callback ops
|
||||
*
|
||||
* @config_stream: configure the stream with the hw_params
|
||||
* the first argument containing the context is mandatory
|
||||
* struct sdw_intel_stream_params_data: configuration passed during
|
||||
* the @params_stream callback, e.g. for interaction with DSP
|
||||
* firmware.
|
||||
*/
|
||||
struct sdw_intel_ops {
|
||||
int (*config_stream)(void *arg, void *substream,
|
||||
void *dai, void *hw_params, int stream_num);
|
||||
struct sdw_intel_stream_params_data {
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_soc_dai *dai;
|
||||
struct snd_pcm_hw_params *hw_params;
|
||||
int link_id;
|
||||
int alh_stream_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_res - Soundwire Intel resource structure
|
||||
* struct sdw_intel_stream_free_data: configuration passed during
|
||||
* the @free_stream callback, e.g. for interaction with DSP
|
||||
* firmware.
|
||||
*/
|
||||
struct sdw_intel_stream_free_data {
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_soc_dai *dai;
|
||||
int link_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ops: Intel audio driver callback ops
|
||||
*
|
||||
*/
|
||||
struct sdw_intel_ops {
|
||||
int (*params_stream)(struct device *dev,
|
||||
struct sdw_intel_stream_params_data *params_data);
|
||||
int (*free_stream)(struct device *dev,
|
||||
struct sdw_intel_stream_free_data *free_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_acpi_info - Soundwire Intel information found in ACPI tables
|
||||
* @handle: ACPI controller handle
|
||||
* @count: link count found with "sdw-master-count" property
|
||||
* @link_mask: bit-wise mask listing links enabled by BIOS menu
|
||||
*
|
||||
* this structure could be expanded to e.g. provide all the _ADR
|
||||
* information in case the link_mask is not sufficient to identify
|
||||
* platform capabilities.
|
||||
*/
|
||||
struct sdw_intel_acpi_info {
|
||||
acpi_handle handle;
|
||||
int count;
|
||||
u32 link_mask;
|
||||
};
|
||||
|
||||
struct sdw_intel_link_res;
|
||||
|
||||
/* Intel clock-stop/pm_runtime quirk definitions */
|
||||
|
||||
/*
|
||||
* Force the clock to remain on during pm_runtime suspend. This might
|
||||
* be needed if Slave devices do not have an alternate clock source or
|
||||
* if the latency requirements are very strict.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_NOT_ALLOWED BIT(0)
|
||||
|
||||
/*
|
||||
* Stop the bus during pm_runtime suspend. If set, a complete bus
|
||||
* reset and re-enumeration will be performed when the bus
|
||||
* restarts. This mode shall not be used if Slave devices can generate
|
||||
* in-band wakes.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_TEARDOWN BIT(1)
|
||||
|
||||
/*
|
||||
* Stop the bus during pm_suspend if Slaves are not wake capable
|
||||
* (e.g. speaker amplifiers). The clock-stop mode is typically
|
||||
* slightly higher power than when the IP is completely powered-off.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_WAKE_CAPABLE_ONLY BIT(2)
|
||||
|
||||
/*
|
||||
* Require a bus reset (and complete re-enumeration) when exiting
|
||||
* clock stop modes. This may be needed if the controller power was
|
||||
* turned off and all context lost. This quirk shall not be used if a
|
||||
* Slave device needs to remain enumerated and keep its context,
|
||||
* e.g. to provide the reasons for the wake, report acoustic events or
|
||||
* pass a history buffer.
|
||||
*/
|
||||
#define SDW_INTEL_CLK_STOP_BUS_RESET BIT(3)
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ctx - context allocated by the controller
|
||||
* driver probe
|
||||
* @count: link count
|
||||
* @mmio_base: mmio base of SoundWire registers, only used to check
|
||||
* hardware capabilities after all power dependencies are settled.
|
||||
* @link_mask: bit-wise mask listing SoundWire links reported by the
|
||||
* Controller
|
||||
* @handle: ACPI parent handle
|
||||
* @links: information for each link (controller-specific and kept
|
||||
* opaque here)
|
||||
* @link_list: list to handle interrupts across all links
|
||||
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
|
||||
*/
|
||||
struct sdw_intel_ctx {
|
||||
int count;
|
||||
void __iomem *mmio_base;
|
||||
u32 link_mask;
|
||||
acpi_handle handle;
|
||||
struct sdw_intel_link_res *links;
|
||||
struct list_head link_list;
|
||||
struct mutex shim_lock; /* lock for access to shared SHIM registers */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdw_intel_res - Soundwire Intel global resource structure,
|
||||
* typically populated by the DSP driver
|
||||
*
|
||||
* @count: link count
|
||||
* @mmio_base: mmio base of SoundWire registers
|
||||
* @irq: interrupt number
|
||||
* @handle: ACPI parent handle
|
||||
* @parent: parent device
|
||||
* @ops: callback ops
|
||||
* @arg: callback arg
|
||||
* @dev: device implementing hwparams and free callbacks
|
||||
* @link_mask: bit-wise mask listing links selected by the DSP driver
|
||||
* This mask may be a subset of the one reported by the controller since
|
||||
* machine-specific quirks are handled in the DSP driver.
|
||||
* @clock_stop_quirks: mask array of possible behaviors requested by the
|
||||
* DSP driver. The quirks are common for all links for now.
|
||||
*/
|
||||
struct sdw_intel_res {
|
||||
int count;
|
||||
void __iomem *mmio_base;
|
||||
int irq;
|
||||
acpi_handle handle;
|
||||
struct device *parent;
|
||||
const struct sdw_intel_ops *ops;
|
||||
void *arg;
|
||||
struct device *dev;
|
||||
u32 link_mask;
|
||||
u32 clock_stop_quirks;
|
||||
};
|
||||
|
||||
void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res);
|
||||
void sdw_intel_exit(void *arg);
|
||||
/*
|
||||
* On Intel platforms, the SoundWire IP has dependencies on power
|
||||
* rails shared with the DSP, and the initialization steps are split
|
||||
* in three. First an ACPI scan to check what the firmware describes
|
||||
* in DSDT tables, then an allocation step (with no hardware
|
||||
* configuration but with all the relevant devices created) and last
|
||||
* the actual hardware configuration. The final stage is a global
|
||||
* interrupt enable which is controlled by the DSP driver. Splitting
|
||||
* these phases helps simplify the boot flow and make early decisions
|
||||
* on e.g. which machine driver to select (I2S mode, HDaudio or
|
||||
* SoundWire).
|
||||
*/
|
||||
int sdw_intel_acpi_scan(acpi_handle *parent_handle,
|
||||
struct sdw_intel_acpi_info *info);
|
||||
|
||||
void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx);
|
||||
|
||||
struct sdw_intel_ctx *
|
||||
sdw_intel_probe(struct sdw_intel_res *res);
|
||||
|
||||
int sdw_intel_startup(struct sdw_intel_ctx *ctx);
|
||||
|
||||
void sdw_intel_exit(struct sdw_intel_ctx *ctx);
|
||||
|
||||
void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable);
|
||||
|
||||
irqreturn_t sdw_intel_thread(int irq, void *dev_id);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ struct snd_ac97_bus_ops {
|
|||
|
||||
struct snd_ac97_bus {
|
||||
/* -- lowlevel (hardware) driver specific -- */
|
||||
struct snd_ac97_bus_ops *ops;
|
||||
const struct snd_ac97_bus_ops *ops;
|
||||
void *private_data;
|
||||
void (*private_free) (struct snd_ac97_bus *bus);
|
||||
/* --- */
|
||||
|
|
@ -310,7 +310,8 @@ static inline int ac97_can_spdif(struct snd_ac97 * ac97)
|
|||
|
||||
/* functions */
|
||||
/* create new AC97 bus */
|
||||
int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
|
||||
int snd_ac97_bus(struct snd_card *card, int num,
|
||||
const struct snd_ac97_bus_ops *ops,
|
||||
void *private_data, struct snd_ac97_bus **rbus);
|
||||
/* create mixer controls */
|
||||
int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,16 @@ typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,
|
|||
unsigned int size,
|
||||
unsigned int __user *tlv);
|
||||
|
||||
/* internal flag for skipping validations */
|
||||
#ifdef CONFIG_SND_CTL_VALIDATION
|
||||
#define SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK (1 << 27)
|
||||
#define snd_ctl_skip_validation(info) \
|
||||
((info)->access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK)
|
||||
#else
|
||||
#define SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK 0
|
||||
#define snd_ctl_skip_validation(info) true
|
||||
#endif
|
||||
|
||||
enum {
|
||||
SNDRV_CTL_TLV_OP_READ = 0,
|
||||
SNDRV_CTL_TLV_OP_WRITE = 1,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ struct snd_device {
|
|||
enum snd_device_state state; /* state of the device */
|
||||
enum snd_device_type type; /* device type */
|
||||
void *device_data; /* device structure */
|
||||
struct snd_device_ops *ops; /* operations */
|
||||
const struct snd_device_ops *ops; /* operations */
|
||||
};
|
||||
|
||||
#define snd_device(n) list_entry(n, struct snd_device, list)
|
||||
|
|
@ -120,6 +120,9 @@ struct snd_card {
|
|||
int sync_irq; /* assigned irq, used for PCM sync */
|
||||
wait_queue_head_t remove_sleep;
|
||||
|
||||
size_t total_pcm_alloc_bytes; /* total amount of allocated buffers */
|
||||
struct mutex memory_mutex; /* protection for the above */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_state; /* power state */
|
||||
wait_queue_head_t power_sleep;
|
||||
|
|
@ -256,7 +259,7 @@ static inline void snd_card_unref(struct snd_card *card)
|
|||
/* device.c */
|
||||
|
||||
int snd_device_new(struct snd_card *card, enum snd_device_type type,
|
||||
void *device_data, struct snd_device_ops *ops);
|
||||
void *device_data, const struct snd_device_ops *ops);
|
||||
int snd_device_register(struct snd_card *card, void *device_data);
|
||||
int snd_device_register_all(struct snd_card *card);
|
||||
void snd_device_disconnect(struct snd_card *card, void *device_data);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ struct hda_bus {
|
|||
DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
|
||||
|
||||
/* misc op flags */
|
||||
unsigned int needs_damn_long_delay :1;
|
||||
unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
|
||||
/* status for codec/controller */
|
||||
unsigned int shutdown :1; /* being unloaded */
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
|
|||
unsigned int val);
|
||||
int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
|
||||
unsigned int mask, unsigned int val);
|
||||
int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
|
||||
unsigned int mask, unsigned int val);
|
||||
void snd_hdac_regmap_sync(struct hdac_device *codec);
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_encode_verb - encode the verb to a pseudo register
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/timecounter.h>
|
||||
#include <sound/core.h>
|
||||
|
|
@ -86,6 +87,7 @@ struct hdac_device {
|
|||
|
||||
/* regmap */
|
||||
struct regmap *regmap;
|
||||
struct mutex regmap_lock;
|
||||
struct snd_array vendor_verbs;
|
||||
bool lazy_cache:1; /* don't wake up for writes */
|
||||
bool caps_overwriting:1; /* caps overwrite being in process */
|
||||
|
|
@ -317,6 +319,7 @@ struct hdac_bus {
|
|||
struct hdac_rb corb;
|
||||
struct hdac_rb rirb;
|
||||
unsigned int last_cmd[HDA_MAX_CODECS]; /* last sent command */
|
||||
wait_queue_head_t rirb_wq;
|
||||
|
||||
/* CORB/RIRB and position buffers */
|
||||
struct snd_dma_buffer rb;
|
||||
|
|
@ -330,6 +333,7 @@ struct hdac_bus {
|
|||
bool chip_init:1; /* h/w initialized */
|
||||
|
||||
/* behavior flags */
|
||||
bool aligned_mmio:1; /* aligned MMIO access */
|
||||
bool sync_write:1; /* sync after verb write */
|
||||
bool use_posbuf:1; /* use position buffer */
|
||||
bool snoop:1; /* enable snooping */
|
||||
|
|
@ -337,6 +341,7 @@ struct hdac_bus {
|
|||
bool reverse_assign:1; /* assign devices in reverse order */
|
||||
bool corbrp_self_clear:1; /* CORBRP clears itself after reset */
|
||||
bool polling_mode:1;
|
||||
bool needs_damn_long_delay:1;
|
||||
|
||||
int poll_count;
|
||||
|
||||
|
|
@ -405,34 +410,61 @@ void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus);
|
|||
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask);
|
||||
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
|
||||
unsigned int mask);
|
||||
#define snd_hdac_reg_writeb(v, addr) snd_hdac_aligned_write(v, addr, 0xff)
|
||||
#define snd_hdac_reg_writew(v, addr) snd_hdac_aligned_write(v, addr, 0xffff)
|
||||
#define snd_hdac_reg_readb(addr) snd_hdac_aligned_read(addr, 0xff)
|
||||
#define snd_hdac_reg_readw(addr) snd_hdac_aligned_read(addr, 0xffff)
|
||||
#else /* CONFIG_SND_HDA_ALIGNED_MMIO */
|
||||
#define snd_hdac_reg_writeb(val, addr) writeb(val, addr)
|
||||
#define snd_hdac_reg_writew(val, addr) writew(val, addr)
|
||||
#define snd_hdac_reg_readb(addr) readb(addr)
|
||||
#define snd_hdac_reg_readw(addr) readw(addr)
|
||||
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
|
||||
#define snd_hdac_reg_writel(val, addr) writel(val, addr)
|
||||
#define snd_hdac_reg_readl(addr) readl(addr)
|
||||
#define snd_hdac_aligned_mmio(bus) (bus)->aligned_mmio
|
||||
#else
|
||||
#define snd_hdac_aligned_mmio(bus) false
|
||||
#define snd_hdac_aligned_read(addr, mask) 0
|
||||
#define snd_hdac_aligned_write(val, addr, mask) do {} while (0)
|
||||
#endif
|
||||
|
||||
static inline void snd_hdac_reg_writeb(struct hdac_bus *bus, void __iomem *addr,
|
||||
u8 val)
|
||||
{
|
||||
if (snd_hdac_aligned_mmio(bus))
|
||||
snd_hdac_aligned_write(val, addr, 0xff);
|
||||
else
|
||||
writeb(val, addr);
|
||||
}
|
||||
|
||||
static inline void snd_hdac_reg_writew(struct hdac_bus *bus, void __iomem *addr,
|
||||
u16 val)
|
||||
{
|
||||
if (snd_hdac_aligned_mmio(bus))
|
||||
snd_hdac_aligned_write(val, addr, 0xffff);
|
||||
else
|
||||
writew(val, addr);
|
||||
}
|
||||
|
||||
static inline u8 snd_hdac_reg_readb(struct hdac_bus *bus, void __iomem *addr)
|
||||
{
|
||||
return snd_hdac_aligned_mmio(bus) ?
|
||||
snd_hdac_aligned_read(addr, 0xff) : readb(addr);
|
||||
}
|
||||
|
||||
static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
|
||||
{
|
||||
return snd_hdac_aligned_mmio(bus) ?
|
||||
snd_hdac_aligned_read(addr, 0xffff) : readw(addr);
|
||||
}
|
||||
|
||||
#define snd_hdac_reg_writel(bus, addr, val) writel(val, addr)
|
||||
#define snd_hdac_reg_readl(bus, addr) readl(addr)
|
||||
|
||||
/*
|
||||
* macros for easy use
|
||||
*/
|
||||
#define _snd_hdac_chip_writeb(chip, reg, value) \
|
||||
snd_hdac_reg_writeb(value, (chip)->remap_addr + (reg))
|
||||
snd_hdac_reg_writeb(chip, (chip)->remap_addr + (reg), value)
|
||||
#define _snd_hdac_chip_readb(chip, reg) \
|
||||
snd_hdac_reg_readb((chip)->remap_addr + (reg))
|
||||
snd_hdac_reg_readb(chip, (chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_writew(chip, reg, value) \
|
||||
snd_hdac_reg_writew(value, (chip)->remap_addr + (reg))
|
||||
snd_hdac_reg_writew(chip, (chip)->remap_addr + (reg), value)
|
||||
#define _snd_hdac_chip_readw(chip, reg) \
|
||||
snd_hdac_reg_readw((chip)->remap_addr + (reg))
|
||||
snd_hdac_reg_readw(chip, (chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_writel(chip, reg, value) \
|
||||
snd_hdac_reg_writel(value, (chip)->remap_addr + (reg))
|
||||
snd_hdac_reg_writel(chip, (chip)->remap_addr + (reg), value)
|
||||
#define _snd_hdac_chip_readl(chip, reg) \
|
||||
snd_hdac_reg_readl((chip)->remap_addr + (reg))
|
||||
snd_hdac_reg_readl(chip, (chip)->remap_addr + (reg))
|
||||
|
||||
/* read/write a register, pass without AZX_REG_ prefix */
|
||||
#define snd_hdac_chip_writel(chip, reg, value) \
|
||||
|
|
@ -540,17 +572,17 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
|
|||
*/
|
||||
/* read/write a register, pass without AZX_REG_ prefix */
|
||||
#define snd_hdac_stream_writel(dev, reg, value) \
|
||||
snd_hdac_reg_writel(value, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
snd_hdac_reg_writel((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg, value)
|
||||
#define snd_hdac_stream_writew(dev, reg, value) \
|
||||
snd_hdac_reg_writew(value, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
snd_hdac_reg_writew((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg, value)
|
||||
#define snd_hdac_stream_writeb(dev, reg, value) \
|
||||
snd_hdac_reg_writeb(value, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
snd_hdac_reg_writeb((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg, value)
|
||||
#define snd_hdac_stream_readl(dev, reg) \
|
||||
snd_hdac_reg_readl((dev)->sd_addr + AZX_REG_ ## reg)
|
||||
snd_hdac_reg_readl((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_readw(dev, reg) \
|
||||
snd_hdac_reg_readw((dev)->sd_addr + AZX_REG_ ## reg)
|
||||
snd_hdac_reg_readw((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_readb(dev, reg) \
|
||||
snd_hdac_reg_readb((dev)->sd_addr + AZX_REG_ ## reg)
|
||||
snd_hdac_reg_readb((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
|
||||
/* update a register, pass without AZX_REG_ prefix */
|
||||
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct snd_info_entry {
|
|||
unsigned short content;
|
||||
union {
|
||||
struct snd_info_entry_text text;
|
||||
struct snd_info_entry_ops *ops;
|
||||
const struct snd_info_entry_ops *ops;
|
||||
} c;
|
||||
struct snd_info_entry *parent;
|
||||
struct module *module;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
#define SNDRV_DEFAULT_PTR SNDRV_DEFAULT_STR
|
||||
|
||||
#ifdef SNDRV_LEGACY_FIND_FREE_IOPORT
|
||||
static long snd_legacy_find_free_ioport(long *port_table, long size)
|
||||
static long snd_legacy_find_free_ioport(const long *port_table, long size)
|
||||
{
|
||||
while (*port_table != -1) {
|
||||
if (request_region(*port_table, size, "ALSA test")) {
|
||||
|
|
@ -58,7 +58,7 @@ static irqreturn_t snd_legacy_empty_irq_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int snd_legacy_find_free_irq(int *irq_table)
|
||||
static int snd_legacy_find_free_irq(const int *irq_table)
|
||||
{
|
||||
while (*irq_table != -1) {
|
||||
if (!request_irq(*irq_table, snd_legacy_empty_irq_handler,
|
||||
|
|
@ -74,7 +74,7 @@ static int snd_legacy_find_free_irq(int *irq_table)
|
|||
#endif
|
||||
|
||||
#ifdef SNDRV_LEGACY_FIND_FREE_DMA
|
||||
static int snd_legacy_find_free_dma(int *dma_table)
|
||||
static int snd_legacy_find_free_dma(const int *dma_table)
|
||||
{
|
||||
while (*dma_table != -1) {
|
||||
if (!request_dma(*dma_table, "ALSA Test DMA")) {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ struct snd_pcm_hardware {
|
|||
size_t fifo_size; /* fifo size in bytes */
|
||||
};
|
||||
|
||||
struct snd_pcm_status64;
|
||||
struct snd_pcm_substream;
|
||||
|
||||
struct snd_pcm_audio_tstamp_config; /* definitions further down */
|
||||
|
|
@ -62,7 +63,7 @@ struct snd_pcm_ops {
|
|||
int (*sync_stop)(struct snd_pcm_substream *substream);
|
||||
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
|
||||
int (*get_time_info)(struct snd_pcm_substream *substream,
|
||||
struct timespec *system_ts, struct timespec *audio_ts,
|
||||
struct timespec64 *system_ts, struct timespec64 *audio_ts,
|
||||
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
|
||||
struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
|
||||
int (*fill_silence)(struct snd_pcm_substream *substream, int channel,
|
||||
|
|
@ -343,7 +344,7 @@ static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy
|
|||
struct snd_pcm_runtime {
|
||||
/* -- Status -- */
|
||||
struct snd_pcm_substream *trigger_master;
|
||||
struct timespec trigger_tstamp; /* trigger timestamp */
|
||||
struct timespec64 trigger_tstamp; /* trigger timestamp */
|
||||
bool trigger_tstamp_latched; /* trigger timestamp latched in low-level driver/hardware */
|
||||
int overrange;
|
||||
snd_pcm_uframes_t avail_max;
|
||||
|
|
@ -421,7 +422,7 @@ struct snd_pcm_runtime {
|
|||
/* -- audio timestamp config -- */
|
||||
struct snd_pcm_audio_tstamp_config audio_tstamp_config;
|
||||
struct snd_pcm_audio_tstamp_report audio_tstamp_report;
|
||||
struct timespec driver_tstamp;
|
||||
struct timespec64 driver_tstamp;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
|
||||
/* -- OSS things -- */
|
||||
|
|
@ -559,8 +560,8 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree);
|
|||
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info);
|
||||
int snd_pcm_info_user(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_info __user *info);
|
||||
int snd_pcm_status(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_status *status);
|
||||
int snd_pcm_status64(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_status64 *status);
|
||||
int snd_pcm_start(struct snd_pcm_substream *substream);
|
||||
int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
|
||||
int snd_pcm_drain_done(struct snd_pcm_substream *substream);
|
||||
|
|
@ -1156,22 +1157,22 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea
|
|||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_gettime - Fill the timespec depending on the timestamp mode
|
||||
* snd_pcm_gettime - Fill the timespec64 depending on the timestamp mode
|
||||
* @runtime: PCM runtime instance
|
||||
* @tv: timespec to fill
|
||||
* @tv: timespec64 to fill
|
||||
*/
|
||||
static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
|
||||
struct timespec *tv)
|
||||
struct timespec64 *tv)
|
||||
{
|
||||
switch (runtime->tstamp_type) {
|
||||
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
|
||||
ktime_get_ts(tv);
|
||||
ktime_get_ts64(tv);
|
||||
break;
|
||||
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
|
||||
getrawmonotonic(tv);
|
||||
ktime_get_raw_ts64(tv);
|
||||
break;
|
||||
default:
|
||||
getnstimeofday(tv);
|
||||
ktime_get_real_ts64(tv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1423,4 +1424,55 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
|
|||
#define pcm_dbg(pcm, fmt, args...) \
|
||||
dev_dbg((pcm)->card->dev, fmt, ##args)
|
||||
|
||||
struct snd_pcm_status64 {
|
||||
snd_pcm_state_t state; /* stream state */
|
||||
u8 rsvd[4];
|
||||
s64 trigger_tstamp_sec; /* time when stream was started/stopped/paused */
|
||||
s64 trigger_tstamp_nsec;
|
||||
s64 tstamp_sec; /* reference timestamp */
|
||||
s64 tstamp_nsec;
|
||||
snd_pcm_uframes_t appl_ptr; /* appl ptr */
|
||||
snd_pcm_uframes_t hw_ptr; /* hw ptr */
|
||||
snd_pcm_sframes_t delay; /* current delay in frames */
|
||||
snd_pcm_uframes_t avail; /* number of frames available */
|
||||
snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */
|
||||
snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */
|
||||
snd_pcm_state_t suspended_state; /* suspended stream state */
|
||||
__u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */
|
||||
s64 audio_tstamp_sec; /* sample counter, wall clock, PHC or on-demand sync'ed */
|
||||
s64 audio_tstamp_nsec;
|
||||
s64 driver_tstamp_sec; /* useful in case reference system tstamp is reported with delay */
|
||||
s64 driver_tstamp_nsec;
|
||||
__u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
|
||||
unsigned char reserved[52-4*sizeof(s64)]; /* must be filled with zero */
|
||||
};
|
||||
|
||||
#define SNDRV_PCM_IOCTL_STATUS64 _IOR('A', 0x20, struct snd_pcm_status64)
|
||||
#define SNDRV_PCM_IOCTL_STATUS_EXT64 _IOWR('A', 0x24, struct snd_pcm_status64)
|
||||
|
||||
struct snd_pcm_status32 {
|
||||
s32 state; /* stream state */
|
||||
s32 trigger_tstamp_sec; /* time when stream was started/stopped/paused */
|
||||
s32 trigger_tstamp_nsec;
|
||||
s32 tstamp_sec; /* reference timestamp */
|
||||
s32 tstamp_nsec;
|
||||
u32 appl_ptr; /* appl ptr */
|
||||
u32 hw_ptr; /* hw ptr */
|
||||
s32 delay; /* current delay in frames */
|
||||
u32 avail; /* number of frames available */
|
||||
u32 avail_max; /* max frames available on hw since last status */
|
||||
u32 overrange; /* count of ADC (capture) overrange detections from last status */
|
||||
s32 suspended_state; /* suspended stream state */
|
||||
u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */
|
||||
s32 audio_tstamp_sec; /* sample counter, wall clock, PHC or on-demand sync'ed */
|
||||
s32 audio_tstamp_nsec;
|
||||
s32 driver_tstamp_sec; /* useful in case reference system tstamp is reported with delay */
|
||||
s32 driver_tstamp_nsec;
|
||||
u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
|
||||
unsigned char reserved[52-4*sizeof(s32)]; /* must be filled with zero */
|
||||
};
|
||||
|
||||
#define SNDRV_PCM_IOCTL_STATUS32 _IOR('A', 0x20, struct snd_pcm_status32)
|
||||
#define SNDRV_PCM_IOCTL_STATUS_EXT32 _IOWR('A', 0x24, struct snd_pcm_status32)
|
||||
|
||||
#endif /* __SOUND_PCM_H */
|
||||
|
|
|
|||
|
|
@ -174,7 +174,8 @@ enum {
|
|||
};
|
||||
|
||||
/* Prototypes for midi_process.c */
|
||||
void snd_midi_process_event(struct snd_midi_op *ops, struct snd_seq_event *ev,
|
||||
void snd_midi_process_event(const struct snd_midi_op *ops,
|
||||
struct snd_seq_event *ev,
|
||||
struct snd_midi_channel_set *chanset);
|
||||
void snd_midi_channel_set_clear(struct snd_midi_channel_set *chset);
|
||||
struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
|
|||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[];
|
||||
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[];
|
||||
|
||||
/*
|
||||
* generic table used for HDA codec-based platforms, possibly with
|
||||
* additional ACPI-enumerated codecs
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue