Merge branch 'thermal/next' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux

Pull thermal management updates from Zhang Rui:

 - Fix a deadlock regression in thermal core framework, which was
   introduced in 5.3 (Wei Wang)

 - Initialize thermal control framework earlier to enable thermal
   mitigation during boot (Amit Kucheria)

 - Convert the Intelligent Power Allocator (IPA) thermal governor to
   follow the generic PM_EM instead of its own Energy Model (Quentin
   Perret)

 - Introduce a new Amlogic soc thermal driver (Guillaume La Roque)

 - Add interrupt support for tsens thermal driver (Amit Kucheria)

 - Add support for MSM8956/8976 in tsens thermal driver
   (AngeloGioacchino Del Regno)

 - Add support for r8a774b1 in rcar thermal driver (Biju Das)

 - Add support for Thermal Monitor Unit v2 in qoriq thermal driver
   (Yuantian Tang)

 - Some other fixes/cleanups on thermal core framework and soc thermal
   drivers (Colin Ian King, Daniel Lezcano, Hsin-Yi Wang, Tian Tao)

* 'thermal/next' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (32 commits)
  thermal: Fix deadlock in thermal thermal_zone_device_check
  thermal: cpu_cooling: Migrate to using the EM framework
  thermal: cpu_cooling: Make the power-related code depend on IPA
  PM / EM: Declare EM data types unconditionally
  arm64: defconfig: Enable CONFIG_ENERGY_MODEL
  drivers: thermal: tsens: fix potential integer overflow on multiply
  thermal: cpu_cooling: Reorder the header file
  thermal: cpu_cooling: Remove pointless dependency on CONFIG_OF
  thermal: no need to set .owner when using module_platform_driver
  thermal: qcom: tsens-v1: Fix kfree of a non-pointer value
  cpufreq: qcom-hw: Move driver initialization earlier
  clk: qcom: Initialize clock drivers earlier
  cpufreq: Initialize cpufreq-dt driver earlier
  cpufreq: Initialize the governors in core_initcall
  thermal: Initialize thermal subsystem earlier
  thermal: Remove netlink support
  dt: thermal: tsens: Document compatible for MSM8976/56
  thermal: qcom: tsens-v1: Add support for MSM8956 and MSM8976
  MAINTAINERS: add entry for Amlogic Thermal driver
  thermal: amlogic: Add thermal driver to support G12 SoCs
  ...
This commit is contained in:
Linus Torvalds 2019-12-05 11:21:24 -08:00
commit fb3da48a86
37 changed files with 1895 additions and 606 deletions

View File

@ -0,0 +1,54 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/amlogic,thermal.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic Thermal
maintainers:
- Guillaume La Roque <glaroque@baylibre.com>
description: Binding for Amlogic Thermal
properties:
compatible:
items:
- enum:
- amlogic,g12a-cpu-thermal
- amlogic,g12a-ddr-thermal
- const: amlogic,g12a-thermal
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
amlogic,ao-secure:
description: phandle to the ao-secure syscon
$ref: '/schemas/types.yaml#/definitions/phandle'
required:
- compatible
- reg
- interrupts
- clocks
- amlogic,ao-secure
examples:
- |
cpu_temp: temperature-sensor@ff634800 {
compatible = "amlogic,g12a-cpu-thermal",
"amlogic,g12a-thermal";
reg = <0xff634800 0x50>;
interrupts = <0x0 0x24 0x0>;
clocks = <&clk 164>;
#thermal-sensor-cells = <0>;
amlogic,ao-secure = <&sec_AO>;
};
...

View File

@ -1,55 +0,0 @@
* QCOM SoC Temperature Sensor (TSENS)
Required properties:
- compatible:
Must be one of the following:
- "qcom,msm8916-tsens" (MSM8916)
- "qcom,msm8974-tsens" (MSM8974)
- "qcom,msm8996-tsens" (MSM8996)
- "qcom,qcs404-tsens", "qcom,tsens-v1" (QCS404)
- "qcom,msm8998-tsens", "qcom,tsens-v2" (MSM8998)
- "qcom,sdm845-tsens", "qcom,tsens-v2" (SDM845)
The generic "qcom,tsens-v2" property must be used as a fallback for any SoC
with version 2 of the TSENS IP. MSM8996 is the only exception because the
generic property did not exist when support was added.
Similarly, the generic "qcom,tsens-v1" property must be used as a fallback for
any SoC with version 1 of the TSENS IP.
- reg: Address range of the thermal registers.
New platforms containing v2.x.y of the TSENS IP must specify the SROT and TM
register spaces separately, with order being TM before SROT.
See Example 2, below.
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
- #qcom,sensors: Number of sensors in tsens block
- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
nvmem cells
Example 1 (legacy support before a fallback tsens-v2 property was introduced):
tsens: thermal-sensor@900000 {
compatible = "qcom,msm8916-tsens";
reg = <0x4a8000 0x2000>;
nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
nvmem-cell-names = "caldata", "calsel";
#thermal-sensor-cells = <1>;
};
Example 2 (for any platform containing v2 of the TSENS IP):
tsens0: thermal-sensor@c263000 {
compatible = "qcom,sdm845-tsens", "qcom,tsens-v2";
reg = <0xc263000 0x1ff>, /* TM */
<0xc222000 0x1ff>; /* SROT */
#qcom,sensors = <13>;
#thermal-sensor-cells = <1>;
};
Example 3 (for any platform containing v1 of the TSENS IP):
tsens: thermal-sensor@4a9000 {
compatible = "qcom,qcs404-tsens", "qcom,tsens-v1";
reg = <0x004a9000 0x1000>, /* TM */
<0x004a8000 0x1000>; /* SROT */
nvmem-cells = <&tsens_caldata>;
nvmem-cell-names = "calib";
#qcom,sensors = <10>;
#thermal-sensor-cells = <1>;
};

View File

@ -0,0 +1,170 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
# Copyright 2019 Linaro Ltd.
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/qcom-tsens.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: QCOM SoC Temperature Sensor (TSENS)
maintainers:
- Amit Kucheria <amit.kucheria@linaro.org>
description: |
QCOM SoCs have TSENS IP to allow temperature measurement. There are currently
three distinct major versions of the IP that is supported by a single driver.
The IP versions are named v0.1, v1 and v2 in the driver, where v0.1 captures
everything before v1 when there was no versioning information.
properties:
compatible:
oneOf:
- description: v0.1 of TSENS
items:
- enum:
- qcom,msm8916-tsens
- qcom,msm8974-tsens
- const: qcom,tsens-v0_1
- description: v1 of TSENS
items:
- enum:
- qcom,msm8976-tsens
- qcom,qcs404-tsens
- const: qcom,tsens-v1
- description: v2 of TSENS
items:
- enum:
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,sdm845-tsens
- const: qcom,tsens-v2
reg:
maxItems: 2
items:
- description: TM registers
- description: SROT registers
nvmem-cells:
minItems: 1
maxItems: 2
description:
Reference to an nvmem node for the calibration data
nvmem-cells-names:
minItems: 1
maxItems: 2
items:
- enum:
- caldata
- calsel
"#qcom,sensors":
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- minimum: 1
- maximum: 16
description:
Number of sensors enabled on this platform
"#thermal-sensor-cells":
const: 1
description:
Number of cells required to uniquely identify the thermal sensors. Since
we have multiple sensors this is set to 1
allOf:
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8916-tsens
- qcom,msm8974-tsens
- qcom,msm8976-tsens
- qcom,qcs404-tsens
- qcom,tsens-v0_1
- qcom,tsens-v1
then:
properties:
interrupts:
items:
- description: Combined interrupt if upper or lower threshold crossed
interrupt-names:
items:
- const: uplow
else:
properties:
interrupts:
items:
- description: Combined interrupt if upper or lower threshold crossed
- description: Interrupt if critical threshold crossed
interrupt-names:
items:
- const: uplow
- const: critical
required:
- compatible
- reg
- "#qcom,sensors"
- interrupts
- interrupt-names
- "#thermal-sensor-cells"
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
// Example 1 (legacy: for pre v1 IP):
tsens1: thermal-sensor@900000 {
compatible = "qcom,msm8916-tsens", "qcom,tsens-v0_1";
reg = <0x4a9000 0x1000>, /* TM */
<0x4a8000 0x1000>; /* SROT */
nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
nvmem-cell-names = "caldata", "calsel";
interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "uplow";
#qcom,sensors = <5>;
#thermal-sensor-cells = <1>;
};
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
// Example 2 (for any platform containing v1 of the TSENS IP):
tsens2: thermal-sensor@4a9000 {
compatible = "qcom,qcs404-tsens", "qcom,tsens-v1";
reg = <0x004a9000 0x1000>, /* TM */
<0x004a8000 0x1000>; /* SROT */
nvmem-cells = <&tsens_caldata>;
nvmem-cell-names = "calib";
interrupts = <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "uplow";
#qcom,sensors = <10>;
#thermal-sensor-cells = <1>;
};
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
// Example 3 (for any platform containing v2 of the TSENS IP):
tsens3: thermal-sensor@c263000 {
compatible = "qcom,sdm845-tsens", "qcom,tsens-v2";
reg = <0xc263000 0x1ff>,
<0xc222000 0x1ff>;
interrupts = <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "uplow", "critical";
#qcom,sensors = <13>;
#thermal-sensor-cells = <1>;
};
...

View File

@ -8,6 +8,7 @@ Required properties:
- compatible : "renesas,<soctype>-thermal",
Examples with soctypes are:
- "renesas,r8a774a1-thermal" (RZ/G2M)
- "renesas,r8a774b1-thermal" (RZ/G2N)
- "renesas,r8a7795-thermal" (R-Car H3)
- "renesas,r8a7796-thermal" (R-Car M3-W)
- "renesas,r8a77965-thermal" (R-Car M3-N)

View File

@ -725,24 +725,10 @@ method, the sys I/F structure will be built like this::
|---temp1_input: 37000
|---temp1_crit: 100000
4. Event Notification
4. Export Symbol APIs
=====================
The framework includes a simple notification mechanism, in the form of a
netlink event. Netlink socket initialization is done during the _init_
of the framework. Drivers which intend to use the notification mechanism
just need to call thermal_generate_netlink_event() with two arguments viz
(originator, event). The originator is a pointer to struct thermal_zone_device
from where the event has been originated. An integer which represents the
thermal zone device will be used in the message to identify the zone. The
event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL,
THERMAL_DEV_FAULT}. Notification can be sent when the current temperature
crosses any of the configured thresholds.
5. Export Symbol APIs
=====================
5.1. get_tz_trend
4.1. get_tz_trend
-----------------
This function returns the trend of a thermal zone, i.e the rate of change
@ -751,14 +737,14 @@ are supposed to implement the callback. If they don't, the thermal
framework calculated the trend by comparing the previous and the current
temperature values.
5.2. get_thermal_instance
4.2. get_thermal_instance
-------------------------
This function returns the thermal_instance corresponding to a given
{thermal_zone, cooling_device, trip_point} combination. Returns NULL
if such an instance does not exist.
5.3. thermal_notify_framework
4.3. thermal_notify_framework
-----------------------------
This function handles the trip events from sensor drivers. It starts
@ -768,14 +754,14 @@ and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
The throttling policy is based on the configured platform data; if no
platform data is provided, this uses the step_wise throttling policy.
5.4. thermal_cdev_update
4.4. thermal_cdev_update
------------------------
This function serves as an arbitrator to set the state of a cooling
device. It sets the cooling device to the deepest cooling state if
possible.
6. thermal_emergency_poweroff
5. thermal_emergency_poweroff
=============================
On an event of critical trip temperature crossing. Thermal framework

View File

@ -13688,6 +13688,7 @@ L: linux-pm@vger.kernel.org
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/thermal/qcom/
F: Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
QUALCOMM VENUS VIDEO ACCELERATOR DRIVER
M: Stanimir Varbanov <stanimir.varbanov@linaro.org>
@ -16310,6 +16311,15 @@ F: Documentation/driver-api/thermal/cpu-cooling-api.rst
F: drivers/thermal/cpu_cooling.c
F: include/linux/cpu_cooling.h
THERMAL DRIVER FOR AMLOGIC SOCS
M: Guillaume La Roque <glaroque@baylibre.com>
L: linux-pm@vger.kernel.org
L: linux-amlogic@lists.infradead.org
W: http://linux-meson.com/
S: Supported
F: drivers/thermal/amlogic_thermal.c
F: Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
THINKPAD ACPI EXTRAS DRIVER
M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
L: ibm-acpi-devel@lists.sourceforge.net

View File

@ -71,6 +71,7 @@ CONFIG_COMPAT=y
CONFIG_RANDOMIZE_BASE=y
CONFIG_HIBERNATION=y
CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
CONFIG_ENERGY_MODEL=y
CONFIG_ARM_CPUIDLE=y
CONFIG_ARM_PSCI_CPUIDLE=y
CONFIG_CPU_FREQ=y

View File

@ -500,7 +500,7 @@ static int __init clk_rpmh_init(void)
{
return platform_driver_register(&clk_rpmh_driver);
}
subsys_initcall(clk_rpmh_init);
core_initcall(clk_rpmh_init);
static void __exit clk_rpmh_exit(void)
{

View File

@ -2855,7 +2855,7 @@ static int __init gcc_qcs404_init(void)
{
return platform_driver_register(&gcc_qcs404_driver);
}
subsys_initcall(gcc_qcs404_init);
core_initcall(gcc_qcs404_init);
static void __exit gcc_qcs404_exit(void)
{

View File

@ -3628,7 +3628,7 @@ static int __init gcc_sdm845_init(void)
{
return platform_driver_register(&gcc_sdm845_driver);
}
subsys_initcall(gcc_sdm845_init);
core_initcall(gcc_sdm845_init);
static void __exit gcc_sdm845_exit(void)
{

View File

@ -180,4 +180,4 @@ static int __init cpufreq_dt_platdev_init(void)
-1, data,
sizeof(struct cpufreq_dt_platform_data)));
}
device_initcall(cpufreq_dt_platdev_init);
core_initcall(cpufreq_dt_platdev_init);

View File

@ -346,7 +346,7 @@ struct cpufreq_governor *cpufreq_default_governor(void)
return CPU_FREQ_GOV_CONSERVATIVE;
}
fs_initcall(cpufreq_gov_dbs_init);
core_initcall(cpufreq_gov_dbs_init);
#else
module_init(cpufreq_gov_dbs_init);
#endif

View File

@ -483,7 +483,7 @@ struct cpufreq_governor *cpufreq_default_governor(void)
return CPU_FREQ_GOV_ONDEMAND;
}
fs_initcall(cpufreq_gov_dbs_init);
core_initcall(cpufreq_gov_dbs_init);
#else
module_init(cpufreq_gov_dbs_init);
#endif

View File

@ -50,5 +50,5 @@ MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq policy governor 'performance'");
MODULE_LICENSE("GPL");
fs_initcall(cpufreq_gov_performance_init);
core_initcall(cpufreq_gov_performance_init);
module_exit(cpufreq_gov_performance_exit);

View File

@ -43,7 +43,7 @@ struct cpufreq_governor *cpufreq_default_governor(void)
return &cpufreq_gov_powersave;
}
fs_initcall(cpufreq_gov_powersave_init);
core_initcall(cpufreq_gov_powersave_init);
#else
module_init(cpufreq_gov_powersave_init);
#endif

View File

@ -147,7 +147,7 @@ struct cpufreq_governor *cpufreq_default_governor(void)
return &cpufreq_gov_userspace;
}
fs_initcall(cpufreq_gov_userspace_init);
core_initcall(cpufreq_gov_userspace_init);
#else
module_init(cpufreq_gov_userspace_init);
#endif

View File

@ -334,7 +334,7 @@ static int __init qcom_cpufreq_hw_init(void)
{
return platform_driver_register(&qcom_cpufreq_hw_driver);
}
device_initcall(qcom_cpufreq_hw_init);
postcore_initcall(qcom_cpufreq_hw_init);
static void __exit qcom_cpufreq_hw_exit(void)
{

View File

@ -144,6 +144,7 @@ config THERMAL_GOV_USER_SPACE
config THERMAL_GOV_POWER_ALLOCATOR
bool "Power allocator thermal governor"
depends on ENERGY_MODEL
help
Enable this to manage platform thermals by dynamically
allocating and limiting power to devices.
@ -348,6 +349,17 @@ config MTK_THERMAL
Enable this option if you want to have support for thermal management
controller present in Mediatek SoCs
config AMLOGIC_THERMAL
tristate "Amlogic Thermal Support"
default ARCH_MESON
depends on OF && ARCH_MESON
help
If you say yes here you get support for Amlogic Thermal
for G12 SoC Family.
This driver can also be built as a module. If so, the module will
be called amlogic_thermal.
menu "Intel thermal drivers"
depends on X86 || X86_INTEL_QUARK || COMPILE_TEST
source "drivers/thermal/intel/Kconfig"

View File

@ -54,3 +54,4 @@ obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o

View File

@ -0,0 +1,333 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Amlogic Thermal Sensor Driver
*
* Copyright (C) 2017 Huan Biao <huan.biao@amlogic.com>
* Copyright (C) 2019 Guillaume La Roque <glaroque@baylibre.com>
*
* Register value to celsius temperature formulas:
* Read_Val m * U
* U = ---------, Uptat = ---------
* 2^16 1 + n * U
*
* Temperature = A * ( Uptat + u_efuse / 2^16 )- B
*
* A B m n : calibration parameters
* u_efuse : fused calibration value, it's a signed 16 bits value
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include "thermal_core.h"
#define TSENSOR_CFG_REG1 0x4
#define TSENSOR_CFG_REG1_RSET_VBG BIT(12)
#define TSENSOR_CFG_REG1_RSET_ADC BIT(11)
#define TSENSOR_CFG_REG1_VCM_EN BIT(10)
#define TSENSOR_CFG_REG1_VBG_EN BIT(9)
#define TSENSOR_CFG_REG1_OUT_CTL BIT(6)
#define TSENSOR_CFG_REG1_FILTER_EN BIT(5)
#define TSENSOR_CFG_REG1_DEM_EN BIT(3)
#define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0)
#define TSENSOR_CFG_REG1_ENABLE \
(TSENSOR_CFG_REG1_FILTER_EN | \
TSENSOR_CFG_REG1_VCM_EN | \
TSENSOR_CFG_REG1_VBG_EN | \
TSENSOR_CFG_REG1_DEM_EN | \
TSENSOR_CFG_REG1_CH_SEL)
#define TSENSOR_STAT0 0x40
#define TSENSOR_STAT9 0x64
#define TSENSOR_READ_TEMP_MASK GENMASK(15, 0)
#define TSENSOR_TEMP_MASK GENMASK(11, 0)
#define TSENSOR_TRIM_SIGN_MASK BIT(15)
#define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0)
#define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24)
#define TSENSOR_TRIM_VERSION(_version) \
FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version)
#define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7))
#define TSENSOR_CALIB_OFFSET 1
#define TSENSOR_CALIB_SHIFT 4
/**
* struct amlogic_thermal_soc_calib_data
* @A, B, m, n: calibration parameters
* This structure is required for configuration of amlogic thermal driver.
*/
struct amlogic_thermal_soc_calib_data {
int A;
int B;
int m;
int n;
};
/**
* struct amlogic_thermal_data
* @u_efuse_off: register offset to read fused calibration value
* @calibration_parameters: calibration parameters structure pointer
* @regmap_config: regmap config for the device
* This structure is required for configuration of amlogic thermal driver.
*/
struct amlogic_thermal_data {
int u_efuse_off;
const struct amlogic_thermal_soc_calib_data *calibration_parameters;
const struct regmap_config *regmap_config;
};
struct amlogic_thermal {
struct platform_device *pdev;
const struct amlogic_thermal_data *data;
struct regmap *regmap;
struct regmap *sec_ao_map;
struct clk *clk;
struct thermal_zone_device *tzd;
u32 trim_info;
};
/*
* Calculate a temperature value from a temperature code.
* The unit of the temperature is degree milliCelsius.
*/
static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata,
int temp_code)
{
const struct amlogic_thermal_soc_calib_data *param =
pdata->data->calibration_parameters;
int temp;
s64 factor, Uptat, uefuse;
uefuse = pdata->trim_info & TSENSOR_TRIM_SIGN_MASK ?
~(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK) + 1 :
(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK);
factor = param->n * temp_code;
factor = div_s64(factor, 100);
Uptat = temp_code * param->m;
Uptat = div_s64(Uptat, 100);
Uptat = Uptat * BIT(16);
Uptat = div_s64(Uptat, BIT(16) + factor);
temp = (Uptat + uefuse) * param->A;
temp = div_s64(temp, BIT(16));
temp = (temp - param->B) * 100;
return temp;
}
static int amlogic_thermal_initialize(struct amlogic_thermal *pdata)
{
int ret = 0;
int ver;
regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off,
&pdata->trim_info);
ver = TSENSOR_TRIM_VERSION(pdata->trim_info);
if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) {
ret = -EINVAL;
dev_err(&pdata->pdev->dev,
"tsensor thermal calibration not supported: 0x%x!\n",
ver);
}
return ret;
}
static int amlogic_thermal_enable(struct amlogic_thermal *data)
{
int ret;
ret = clk_prepare_enable(data->clk);
if (ret)
return ret;
regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
TSENSOR_CFG_REG1_ENABLE, TSENSOR_CFG_REG1_ENABLE);
return 0;
}
static int amlogic_thermal_disable(struct amlogic_thermal *data)
{
regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
TSENSOR_CFG_REG1_ENABLE, 0);
clk_disable_unprepare(data->clk);
return 0;
}
static int amlogic_thermal_get_temp(void *data, int *temp)
{
unsigned int tval;
struct amlogic_thermal *pdata = data;
if (!data)
return -EINVAL;
regmap_read(pdata->regmap, TSENSOR_STAT0, &tval);
*temp =
amlogic_thermal_code_to_millicelsius(pdata,
tval & TSENSOR_READ_TEMP_MASK);
return 0;
}
static const struct thermal_zone_of_device_ops amlogic_thermal_ops = {
.get_temp = amlogic_thermal_get_temp,
};
static const struct regmap_config amlogic_thermal_regmap_config_g12a = {
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
.max_register = TSENSOR_STAT9,
};
static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a = {
.A = 9411,
.B = 3159,
.m = 424,
.n = 324,
};
static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param = {
.u_efuse_off = 0x128,
.calibration_parameters = &amlogic_thermal_g12a,
.regmap_config = &amlogic_thermal_regmap_config_g12a,
};
static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = {
.u_efuse_off = 0xf0,
.calibration_parameters = &amlogic_thermal_g12a,
.regmap_config = &amlogic_thermal_regmap_config_g12a,
};
static const struct of_device_id of_amlogic_thermal_match[] = {
{
.compatible = "amlogic,g12a-ddr-thermal",
.data = &amlogic_thermal_g12a_ddr_param,
},
{
.compatible = "amlogic,g12a-cpu-thermal",
.data = &amlogic_thermal_g12a_cpu_param,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match);
static int amlogic_thermal_probe(struct platform_device *pdev)
{
struct amlogic_thermal *pdata;
struct device *dev = &pdev->dev;
void __iomem *base;
int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->data = of_device_get_match_data(dev);
pdata->pdev = pdev;
platform_set_drvdata(pdev, pdata);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
dev_err(dev, "failed to get io address\n");
return PTR_ERR(base);
}
pdata->regmap = devm_regmap_init_mmio(dev, base,
pdata->data->regmap_config);
if (IS_ERR(pdata->regmap))
return PTR_ERR(pdata->regmap);
pdata->clk = devm_clk_get(dev, NULL);
if (IS_ERR(pdata->clk)) {
if (PTR_ERR(pdata->clk) != -EPROBE_DEFER)
dev_err(dev, "failed to get clock\n");
return PTR_ERR(pdata->clk);
}
pdata->sec_ao_map = syscon_regmap_lookup_by_phandle
(pdev->dev.of_node, "amlogic,ao-secure");
if (IS_ERR(pdata->sec_ao_map)) {
dev_err(dev, "syscon regmap lookup failed.\n");
return PTR_ERR(pdata->sec_ao_map);
}
pdata->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
0,
pdata,
&amlogic_thermal_ops);
if (IS_ERR(pdata->tzd)) {
ret = PTR_ERR(pdata->tzd);
dev_err(dev, "Failed to register tsensor: %d\n", ret);
return ret;
}
ret = amlogic_thermal_initialize(pdata);
if (ret)
return ret;
ret = amlogic_thermal_enable(pdata);
return ret;
}
static int amlogic_thermal_remove(struct platform_device *pdev)
{
struct amlogic_thermal *data = platform_get_drvdata(pdev);
return amlogic_thermal_disable(data);
}
static int __maybe_unused amlogic_thermal_suspend(struct device *dev)
{
struct amlogic_thermal *data = dev_get_drvdata(dev);
return amlogic_thermal_disable(data);
}
static int __maybe_unused amlogic_thermal_resume(struct device *dev)
{
struct amlogic_thermal *data = dev_get_drvdata(dev);
return amlogic_thermal_enable(data);
}
static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops,
amlogic_thermal_suspend, amlogic_thermal_resume);
static struct platform_driver amlogic_thermal_driver = {
.driver = {
.name = "amlogic_thermal",
.pm = &amlogic_thermal_pm_ops,
.of_match_table = of_amlogic_thermal_match,
},
.probe = amlogic_thermal_probe,
.remove = amlogic_thermal_remove,
};
module_platform_driver(amlogic_thermal_driver);
MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
MODULE_DESCRIPTION("Amlogic thermal driver");
MODULE_LICENSE("GPL v2");

View File

@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/cpu_cooling.h>
#include <linux/energy_model.h>
#include <trace/events/thermal.h>
@ -37,19 +38,6 @@
* ...
*/
/**
* struct freq_table - frequency table along with power entries
* @frequency: frequency in KHz
* @power: power in mW
*
* This structure is built when the cooling device registers and helps
* in translating frequency to power and vice versa.
*/
struct freq_table {
u32 frequency;
u32 power;
};
/**
* struct time_in_idle - Idle time stats
* @time: previous reading of the absolute time that this cpu was idle
@ -69,7 +57,7 @@ struct time_in_idle {
* cooling devices.
* @max_level: maximum cooling level. One less than total number of valid
* cpufreq frequencies.
* @freq_table: Freq table in descending order of frequencies
* @em: Reference on the Energy Model of the device
* @cdev: thermal_cooling_device pointer to keep track of the
* registered cooling device.
* @policy: cpufreq policy.
@ -84,7 +72,7 @@ struct cpufreq_cooling_device {
u32 last_load;
unsigned int cpufreq_state;
unsigned int max_level;
struct freq_table *freq_table; /* In descending order */
struct em_perf_domain *em;
struct cpufreq_policy *policy;
struct list_head node;
struct time_in_idle *idle_time;
@ -95,8 +83,7 @@ static DEFINE_IDA(cpufreq_ida);
static DEFINE_MUTEX(cooling_list_lock);
static LIST_HEAD(cpufreq_cdev_list);
/* Below code defines functions to be used for cpufreq as cooling device */
#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
/**
* get_level: Find the level for a particular frequency
* @cpufreq_cdev: cpufreq_cdev for which the property is required
@ -107,114 +94,40 @@ static LIST_HEAD(cpufreq_cdev_list);
static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev,
unsigned int freq)
{
struct freq_table *freq_table = cpufreq_cdev->freq_table;
unsigned long level;
int i;
for (level = 1; level <= cpufreq_cdev->max_level; level++)
if (freq > freq_table[level].frequency)
for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) {
if (freq > cpufreq_cdev->em->table[i].frequency)
break;
return level - 1;
}
/**
* update_freq_table() - Update the freq table with power numbers
* @cpufreq_cdev: the cpufreq cooling device in which to update the table
* @capacitance: dynamic power coefficient for these cpus
*
* Update the freq table with power numbers. This table will be used in
* cpu_power_to_freq() and cpu_freq_to_power() to convert between power and
* frequency efficiently. Power is stored in mW, frequency in KHz. The
* resulting table is in descending order.
*
* Return: 0 on success, -EINVAL if there are no OPPs for any CPUs,
* or -ENOMEM if we run out of memory.
*/
static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev,
u32 capacitance)
{
struct freq_table *freq_table = cpufreq_cdev->freq_table;
struct dev_pm_opp *opp;
struct device *dev = NULL;
int num_opps = 0, cpu = cpufreq_cdev->policy->cpu, i;
dev = get_cpu_device(cpu);
if (unlikely(!dev)) {
pr_warn("No cpu device for cpu %d\n", cpu);
return -ENODEV;
}
num_opps = dev_pm_opp_get_opp_count(dev);
if (num_opps < 0)
return num_opps;
/*
* The cpufreq table is also built from the OPP table and so the count
* should match.
*/
if (num_opps != cpufreq_cdev->max_level + 1) {
dev_warn(dev, "Number of OPPs not matching with max_levels\n");
return -EINVAL;
}
for (i = 0; i <= cpufreq_cdev->max_level; i++) {
unsigned long freq = freq_table[i].frequency * 1000;
u32 freq_mhz = freq_table[i].frequency / 1000;
u64 power;
u32 voltage_mv;
/*
* Find ceil frequency as 'freq' may be slightly lower than OPP
* freq due to truncation while converting to kHz.
*/
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp)) {
dev_err(dev, "failed to get opp for %lu frequency\n",
freq);
return -EINVAL;
}
voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
dev_pm_opp_put(opp);
/*
* Do the multiplication with MHz and millivolt so as
* to not overflow.
*/
power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
do_div(power, 1000000000);
/* power is stored in mW */
freq_table[i].power = power;
}
return 0;
return cpufreq_cdev->max_level - i - 1;
}
static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
u32 freq)
{
int i;
struct freq_table *freq_table = cpufreq_cdev->freq_table;
for (i = 1; i <= cpufreq_cdev->max_level; i++)
if (freq > freq_table[i].frequency)
for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) {
if (freq > cpufreq_cdev->em->table[i].frequency)
break;
}
return freq_table[i - 1].power;
return cpufreq_cdev->em->table[i + 1].power;
}
static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev,
u32 power)
{
int i;
struct freq_table *freq_table = cpufreq_cdev->freq_table;
for (i = 1; i <= cpufreq_cdev->max_level; i++)
if (power > freq_table[i].power)
for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) {
if (power > cpufreq_cdev->em->table[i].power)
break;
}
return freq_table[i - 1].frequency;
return cpufreq_cdev->em->table[i + 1].frequency;
}
/**
@ -265,76 +178,6 @@ static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev,
return (raw_cpu_power * cpufreq_cdev->last_load) / 100;
}
/* cpufreq cooling device callback functions are defined below */
/**
* cpufreq_get_max_state - callback function to get the max cooling state.
* @cdev: thermal cooling device pointer.
* @state: fill this variable with the max cooling state.
*
* Callback for the thermal cooling device to return the cpufreq
* max cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
*state = cpufreq_cdev->max_level;
return 0;
}
/**
* cpufreq_get_cur_state - callback function to get the current cooling state.
* @cdev: thermal cooling device pointer.
* @state: fill this variable with the current cooling state.
*
* Callback for the thermal cooling device to return the cpufreq
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
*state = cpufreq_cdev->cpufreq_state;
return 0;
}
/**
* cpufreq_set_cur_state - callback function to set the current cooling state.
* @cdev: thermal cooling device pointer.
* @state: set this variable to the current cooling state.
*
* Callback for the thermal cooling device to change the cpufreq
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
/* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_cdev->max_level))
return -EINVAL;
/* Check if the old cooling action is same as new cooling action */
if (cpufreq_cdev->cpufreq_state == state)
return 0;
cpufreq_cdev->cpufreq_state = state;
return freq_qos_update_request(&cpufreq_cdev->qos_req,
cpufreq_cdev->freq_table[state].frequency);
}
/**
* cpufreq_get_requested_power() - get the current power
* @cdev: &thermal_cooling_device pointer
@ -425,7 +268,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz,
unsigned long state, u32 *power)
{
unsigned int freq, num_cpus;
unsigned int freq, num_cpus, idx;
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
/* Request state should be less than max_level */
@ -434,7 +277,8 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev,
num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
freq = cpufreq_cdev->freq_table[state].frequency;
idx = cpufreq_cdev->max_level - state;
freq = cpufreq_cdev->em->table[idx].frequency;
*power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus;
return 0;
@ -479,43 +323,142 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev,
return 0;
}
static inline bool em_is_sane(struct cpufreq_cooling_device *cpufreq_cdev,
struct em_perf_domain *em) {
struct cpufreq_policy *policy;
unsigned int nr_levels;
if (!em)
return false;
policy = cpufreq_cdev->policy;
if (!cpumask_equal(policy->related_cpus, to_cpumask(em->cpus))) {
pr_err("The span of pd %*pbl is misaligned with cpufreq policy %*pbl\n",
cpumask_pr_args(to_cpumask(em->cpus)),
cpumask_pr_args(policy->related_cpus));
return false;
}
nr_levels = cpufreq_cdev->max_level + 1;
if (em->nr_cap_states != nr_levels) {
pr_err("The number of cap states in pd %*pbl (%u) doesn't match the number of cooling levels (%u)\n",
cpumask_pr_args(to_cpumask(em->cpus)),
em->nr_cap_states, nr_levels);
return false;
}
return true;
}
#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
static unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev,
unsigned long state)
{
struct cpufreq_policy *policy;
unsigned long idx;
#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
/* Use the Energy Model table if available */
if (cpufreq_cdev->em) {
idx = cpufreq_cdev->max_level - state;
return cpufreq_cdev->em->table[idx].frequency;
}
#endif
/* Otherwise, fallback on the CPUFreq table */
policy = cpufreq_cdev->policy;
if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING)
idx = cpufreq_cdev->max_level - state;
else
idx = state;
return policy->freq_table[idx].frequency;
}
/* cpufreq cooling device callback functions are defined below */
/**
* cpufreq_get_max_state - callback function to get the max cooling state.
* @cdev: thermal cooling device pointer.
* @state: fill this variable with the max cooling state.
*
* Callback for the thermal cooling device to return the cpufreq
* max cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
*state = cpufreq_cdev->max_level;
return 0;
}
/**
* cpufreq_get_cur_state - callback function to get the current cooling state.
* @cdev: thermal cooling device pointer.
* @state: fill this variable with the current cooling state.
*
* Callback for the thermal cooling device to return the cpufreq
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
*state = cpufreq_cdev->cpufreq_state;
return 0;
}
/**
* cpufreq_set_cur_state - callback function to set the current cooling state.
* @cdev: thermal cooling device pointer.
* @state: set this variable to the current cooling state.
*
* Callback for the thermal cooling device to change the cpufreq
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
/* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_cdev->max_level))
return -EINVAL;
/* Check if the old cooling action is same as new cooling action */
if (cpufreq_cdev->cpufreq_state == state)
return 0;
cpufreq_cdev->cpufreq_state = state;
return freq_qos_update_request(&cpufreq_cdev->qos_req,
get_state_freq(cpufreq_cdev, state));
}
/* Bind cpufreq callbacks to thermal cooling device ops */
static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
.get_max_state = cpufreq_get_max_state,
.get_cur_state = cpufreq_get_cur_state,
.set_cur_state = cpufreq_set_cur_state,
};
static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = {
.get_max_state = cpufreq_get_max_state,
.get_cur_state = cpufreq_get_cur_state,
.set_cur_state = cpufreq_set_cur_state,
.get_requested_power = cpufreq_get_requested_power,
.state2power = cpufreq_state2power,
.power2state = cpufreq_power2state,
};
static unsigned int find_next_max(struct cpufreq_frequency_table *table,
unsigned int prev_max)
{
struct cpufreq_frequency_table *pos;
unsigned int max = 0;
cpufreq_for_each_valid_entry(pos, table) {
if (pos->frequency > max && pos->frequency < prev_max)
max = pos->frequency;
}
return max;
}
/**
* __cpufreq_cooling_register - helper function to create cpufreq cooling device
* @np: a valid struct device_node to the cooling device device tree node
* @policy: cpufreq policy
* Normally this should be same as cpufreq policy->related_cpus.
* @capacitance: dynamic power coefficient for these cpus
* @em: Energy Model of the cpufreq policy
*
* This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
@ -527,12 +470,13 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
*/
static struct thermal_cooling_device *
__cpufreq_cooling_register(struct device_node *np,
struct cpufreq_policy *policy, u32 capacitance)
struct cpufreq_policy *policy,
struct em_perf_domain *em)
{
struct thermal_cooling_device *cdev;
struct cpufreq_cooling_device *cpufreq_cdev;
char dev_name[THERMAL_NAME_LENGTH];
unsigned int freq, i, num_cpus;
unsigned int i, num_cpus;
struct device *dev;
int ret;
struct thermal_cooling_device_ops *cooling_ops;
@ -573,51 +517,36 @@ __cpufreq_cooling_register(struct device_node *np,
/* max_level is an index, not a counter */
cpufreq_cdev->max_level = i - 1;
cpufreq_cdev->freq_table = kmalloc_array(i,
sizeof(*cpufreq_cdev->freq_table),
GFP_KERNEL);
if (!cpufreq_cdev->freq_table) {
cdev = ERR_PTR(-ENOMEM);
goto free_idle_time;
}
ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL);
if (ret < 0) {
cdev = ERR_PTR(ret);
goto free_table;
goto free_idle_time;
}
cpufreq_cdev->id = ret;
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
cpufreq_cdev->id);
/* Fill freq-table in descending order of frequencies */
for (i = 0, freq = -1; i <= cpufreq_cdev->max_level; i++) {
freq = find_next_max(policy->freq_table, freq);
cpufreq_cdev->freq_table[i].frequency = freq;
cooling_ops = &cpufreq_cooling_ops;
/* Warn for duplicate entries */
if (!freq)
pr_warn("%s: table has duplicate entries\n", __func__);
else
pr_debug("%s: freq:%u KHz\n", __func__, freq);
}
if (capacitance) {
ret = update_freq_table(cpufreq_cdev, capacitance);
if (ret) {
cdev = ERR_PTR(ret);
goto remove_ida;
}
cooling_ops = &cpufreq_power_cooling_ops;
} else {
cooling_ops = &cpufreq_cooling_ops;
#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
if (em_is_sane(cpufreq_cdev, em)) {
cpufreq_cdev->em = em;
cooling_ops->get_requested_power = cpufreq_get_requested_power;
cooling_ops->state2power = cpufreq_state2power;
cooling_ops->power2state = cpufreq_power2state;
} else
#endif
if (policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED) {
pr_err("%s: unsorted frequency tables are not supported\n",
__func__);
cdev = ERR_PTR(-EINVAL);
goto remove_ida;
}
ret = freq_qos_add_request(&policy->constraints,
&cpufreq_cdev->qos_req, FREQ_QOS_MAX,
cpufreq_cdev->freq_table[0].frequency);
get_state_freq(cpufreq_cdev, 0));
if (ret < 0) {
pr_err("%s: Failed to add freq constraint (%d)\n", __func__,
ret);
@ -640,8 +569,6 @@ __cpufreq_cooling_register(struct device_node *np,
freq_qos_remove_request(&cpufreq_cdev->qos_req);
remove_ida:
ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id);
free_table:
kfree(cpufreq_cdev->freq_table);
free_idle_time:
kfree(cpufreq_cdev->idle_time);
free_cdev:
@ -663,7 +590,7 @@ __cpufreq_cooling_register(struct device_node *np,
struct thermal_cooling_device *
cpufreq_cooling_register(struct cpufreq_policy *policy)
{
return __cpufreq_cooling_register(NULL, policy, 0);
return __cpufreq_cooling_register(NULL, policy, NULL);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
@ -691,7 +618,6 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
{
struct device_node *np = of_get_cpu_node(policy->cpu, NULL);
struct thermal_cooling_device *cdev = NULL;
u32 capacitance = 0;
if (!np) {
pr_err("cpu_cooling: OF node not available for cpu%d\n",
@ -700,10 +626,9 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
}
if (of_find_property(np, "#cooling-cells", NULL)) {
of_property_read_u32(np, "dynamic-power-coefficient",
&capacitance);
struct em_perf_domain *em = em_cpu_get(policy->cpu);
cdev = __cpufreq_cooling_register(np, policy, capacitance);
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",
policy->cpu, PTR_ERR(cdev));
@ -739,7 +664,6 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
freq_qos_remove_request(&cpufreq_cdev->qos_req);
ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id);
kfree(cpufreq_cdev->idle_time);
kfree(cpufreq_cdev->freq_table);
kfree(cpufreq_cdev);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister);

View File

@ -245,11 +245,11 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
return adc_code * slope + offset;
}
static int get_temp_8960(struct tsens_priv *priv, int id, int *temp)
static int get_temp_8960(struct tsens_sensor *s, int *temp)
{
int ret;
u32 code, trdy;
const struct tsens_sensor *s = &priv->sensor[id];
struct tsens_priv *priv = s->priv;
unsigned long timeout;
timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);

View File

@ -3,6 +3,7 @@
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/nvmem-consumer.h>
@ -12,6 +13,31 @@
#include <linux/regmap.h>
#include "tsens.h"
/**
* struct tsens_irq_data - IRQ status and temperature violations
* @up_viol: upper threshold violated
* @up_thresh: upper threshold temperature value
* @up_irq_mask: mask register for upper threshold irqs
* @up_irq_clear: clear register for uppper threshold irqs
* @low_viol: lower threshold violated
* @low_thresh: lower threshold temperature value
* @low_irq_mask: mask register for lower threshold irqs
* @low_irq_clear: clear register for lower threshold irqs
*
* Structure containing data about temperature threshold settings and
* irq status if they were violated.
*/
struct tsens_irq_data {
u32 up_viol;
int up_thresh;
u32 up_irq_mask;
u32 up_irq_clear;
u32 low_viol;
int low_thresh;
u32 low_irq_mask;
u32 low_irq_clear;
};
char *qfprom_read(struct device *dev, const char *cname)
{
struct nvmem_cell *cell;
@ -42,8 +68,8 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
for (i = 0; i < priv->num_sensors; i++) {
dev_dbg(priv->dev,
"sensor%d - data_point1:%#x data_point2:%#x\n",
i, p1[i], p2[i]);
"%s: sensor%d - data_point1:%#x data_point2:%#x\n",
__func__, i, p1[i], p2[i]);
priv->sensor[i].slope = SLOPE_DEFAULT;
if (mode == TWO_PT_CALIB) {
@ -60,10 +86,18 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
(CAL_DEGC_PT1 *
priv->sensor[i].slope);
dev_dbg(priv->dev, "offset:%d\n", priv->sensor[i].offset);
dev_dbg(priv->dev, "%s: offset:%d\n", __func__, priv->sensor[i].offset);
}
}
static inline u32 degc_to_code(int degc, const struct tsens_sensor *s)
{
u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR);
pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc);
return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE);
}
static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
{
int degc, num, den;
@ -83,12 +117,353 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
return degc;
}
int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp)
/**
* tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
* @s: Pointer to sensor struct
* @field: Index into regmap_field array pointing to temperature data
*
* This function handles temperature returned in ADC code or deciCelsius
* depending on IP version.
*
* Return: Temperature in milliCelsius on success, a negative errno will
* be returned in error cases
*/
static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
{
struct tsens_sensor *s = &priv->sensor[i];
u32 temp_idx = LAST_TEMP_0 + s->hw_id;
u32 valid_idx = VALID_0 + s->hw_id;
u32 last_temp = 0, valid, mask;
struct tsens_priv *priv = s->priv;
u32 resolution;
u32 temp = 0;
int ret;
resolution = priv->fields[LAST_TEMP_0].msb -
priv->fields[LAST_TEMP_0].lsb;
ret = regmap_field_read(priv->rf[field], &temp);
if (ret)
return ret;
/* Convert temperature from ADC code to milliCelsius */
if (priv->feat->adc)
return code_to_degc(temp, s) * 1000;
/* deciCelsius -> milliCelsius along with sign extension */
return sign_extend32(temp, resolution) * 100;
}
/**
* tsens_mC_to_hw - Convert temperature to hardware register value
* @s: Pointer to sensor struct
* @temp: temperature in milliCelsius to be programmed to hardware
*
* This function outputs the value to be written to hardware in ADC code
* or deciCelsius depending on IP version.
*
* Return: ADC code or temperature in deciCelsius.
*/
static int tsens_mC_to_hw(struct tsens_sensor *s, int temp)
{
struct tsens_priv *priv = s->priv;
/* milliC to adc code */
if (priv->feat->adc)
return degc_to_code(temp / 1000, s);
/* milliC to deciC */
return temp / 100;
}
static inline enum tsens_ver tsens_version(struct tsens_priv *priv)
{
return priv->feat->ver_major;
}
static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
enum tsens_irq_type irq_type, bool enable)
{
u32 index = 0;
switch (irq_type) {
case UPPER:
index = UP_INT_CLEAR_0 + hw_id;
break;
case LOWER:
index = LOW_INT_CLEAR_0 + hw_id;
break;
}
regmap_field_write(priv->rf[index], enable ? 0 : 1);
}
static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
enum tsens_irq_type irq_type, bool enable)
{
u32 index_mask = 0, index_clear = 0;
/*
* To enable the interrupt flag for a sensor:
* - clear the mask bit
* To disable the interrupt flag for a sensor:
* - Mask further interrupts for this sensor
* - Write 1 followed by 0 to clear the interrupt
*/
switch (irq_type) {
case UPPER:
index_mask = UP_INT_MASK_0 + hw_id;
index_clear = UP_INT_CLEAR_0 + hw_id;
break;
case LOWER:
index_mask = LOW_INT_MASK_0 + hw_id;
index_clear = LOW_INT_CLEAR_0 + hw_id;
break;
}
if (enable) {
regmap_field_write(priv->rf[index_mask], 0);
} else {
regmap_field_write(priv->rf[index_mask], 1);
regmap_field_write(priv->rf[index_clear], 1);
regmap_field_write(priv->rf[index_clear], 0);
}
}
/**
* tsens_set_interrupt - Set state of an interrupt
* @priv: Pointer to tsens controller private data
* @hw_id: Hardware ID aka. sensor number
* @irq_type: irq_type from enum tsens_irq_type
* @enable: false = disable, true = enable
*
* Call IP-specific function to set state of an interrupt
*
* Return: void
*/
static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
enum tsens_irq_type irq_type, bool enable)
{
dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
enable ? "en" : "dis");
if (tsens_version(priv) > VER_1_X)
tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
else
tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
}
/**
* tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
* @priv: Pointer to tsens controller private data
* @hw_id: Hardware ID aka. sensor number
* @d: Pointer to irq state data
*
* Return: 0 if threshold was not violated, 1 if it was violated and negative
* errno in case of errors
*/
static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
struct tsens_irq_data *d)
{
int ret;
ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
if (ret)
return ret;
ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
if (ret)
return ret;
if (d->up_viol || d->low_viol)
return 1;
return 0;
}
static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
struct tsens_sensor *s, struct tsens_irq_data *d)
{
int ret;
ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
if (ret)
return ret;
ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
if (ret)
return ret;
if (tsens_version(priv) > VER_1_X) {
ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
if (ret)
return ret;
ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
if (ret)
return ret;
} else {
/* No mask register on older TSENS */
d->up_irq_mask = 0;
d->low_irq_mask = 0;
}
d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u) | clr(%u|%u) | mask(%u|%u)\n",
hw_id, __func__, (d->up_viol || d->low_viol) ? "(V)" : "",
d->low_viol, d->up_viol, d->low_irq_clear, d->up_irq_clear,
d->low_irq_mask, d->up_irq_mask);
dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d)\n", hw_id, __func__,
(d->up_viol || d->low_viol) ? "(violation)" : "",
d->low_thresh, d->up_thresh);
return 0;
}
static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
{
if (ver > VER_1_X)
return mask & (1 << hw_id);
/* v1, v0.1 don't have a irq mask register */
return 0;
}
/**
* tsens_irq_thread - Threaded interrupt handler for uplow interrupts
* @irq: irq number
* @data: tsens controller private data
*
* Check all sensors to find ones that violated their threshold limits. If the
* temperature is still outside the limits, call thermal_zone_device_update() to
* update the thresholds, else re-enable the interrupts.
*
* The level-triggered interrupt might deassert if the temperature returned to
* within the threshold limits by the time the handler got scheduled. We
* consider the irq to have been handled in that case.
*
* Return: IRQ_HANDLED
*/
irqreturn_t tsens_irq_thread(int irq, void *data)
{
struct tsens_priv *priv = data;
struct tsens_irq_data d;
bool enable = true, disable = false;
unsigned long flags;
int temp, ret, i;
for (i = 0; i < priv->num_sensors; i++) {
bool trigger = false;
struct tsens_sensor *s = &priv->sensor[i];
u32 hw_id = s->hw_id;
if (IS_ERR(priv->sensor[i].tzd))
continue;
if (!tsens_threshold_violated(priv, hw_id, &d))
continue;
ret = get_temp_tsens_valid(s, &temp);
if (ret) {
dev_err(priv->dev, "[%u] %s: error reading sensor\n", hw_id, __func__);
continue;
}
spin_lock_irqsave(&priv->ul_lock, flags);
tsens_read_irq_state(priv, hw_id, s, &d);
if (d.up_viol &&
!masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) {
tsens_set_interrupt(priv, hw_id, UPPER, disable);
if (d.up_thresh > temp) {
dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
priv->sensor[i].hw_id, __func__);
tsens_set_interrupt(priv, hw_id, UPPER, enable);
} else {
trigger = true;
/* Keep irq masked */
}
} else if (d.low_viol &&
!masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) {
tsens_set_interrupt(priv, hw_id, LOWER, disable);
if (d.low_thresh < temp) {
dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
priv->sensor[i].hw_id, __func__);
tsens_set_interrupt(priv, hw_id, LOWER, enable);
} else {
trigger = true;
/* Keep irq masked */
}
}
spin_unlock_irqrestore(&priv->ul_lock, flags);
if (trigger) {
dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
hw_id, __func__, temp);
thermal_zone_device_update(priv->sensor[i].tzd,
THERMAL_EVENT_UNSPECIFIED);
} else {
dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
hw_id, __func__, temp);
}
}
return IRQ_HANDLED;
}
int tsens_set_trips(void *_sensor, int low, int high)
{
struct tsens_sensor *s = _sensor;
struct tsens_priv *priv = s->priv;
struct device *dev = priv->dev;
struct tsens_irq_data d;
unsigned long flags;
int high_val, low_val, cl_high, cl_low;
u32 hw_id = s->hw_id;
dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
hw_id, __func__, low, high);
cl_high = clamp_val(high, -40000, 120000);
cl_low = clamp_val(low, -40000, 120000);
high_val = tsens_mC_to_hw(s, cl_high);
low_val = tsens_mC_to_hw(s, cl_low);
spin_lock_irqsave(&priv->ul_lock, flags);
tsens_read_irq_state(priv, hw_id, s, &d);
/* Write the new thresholds and clear the status */
regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
tsens_set_interrupt(priv, hw_id, LOWER, true);
tsens_set_interrupt(priv, hw_id, UPPER, true);
spin_unlock_irqrestore(&priv->ul_lock, flags);
dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
s->hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
return 0;
}
int tsens_enable_irq(struct tsens_priv *priv)
{
int ret;
int val = tsens_version(priv) > VER_1_X ? 7 : 1;
ret = regmap_field_write(priv->rf[INT_EN], val);
if (ret < 0)
dev_err(priv->dev, "%s: failed to enable interrupts\n", __func__);
return ret;
}
void tsens_disable_irq(struct tsens_priv *priv)
{
regmap_field_write(priv->rf[INT_EN], 0);
}
int get_temp_tsens_valid(struct tsens_sensor *s, int *temp)
{
struct tsens_priv *priv = s->priv;
int hw_id = s->hw_id;
u32 temp_idx = LAST_TEMP_0 + hw_id;
u32 valid_idx = VALID_0 + hw_id;
u32 valid;
int ret;
ret = regmap_field_read(priv->rf[valid_idx], &valid);
@ -106,29 +481,18 @@ int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp)
}
/* Valid bit is set, OK to read the temperature */
ret = regmap_field_read(priv->rf[temp_idx], &last_temp);
if (ret)
return ret;
if (priv->feat->adc) {
/* Convert temperature from ADC code to milliCelsius */
*temp = code_to_degc(last_temp, s) * 1000;
} else {
mask = GENMASK(priv->fields[LAST_TEMP_0].msb,
priv->fields[LAST_TEMP_0].lsb);
/* Convert temperature from deciCelsius to milliCelsius */
*temp = sign_extend32(last_temp, fls(mask) - 1) * 100;
}
*temp = tsens_hw_to_mC(s, temp_idx);
return 0;
}
int get_temp_common(struct tsens_priv *priv, int i, int *temp)
int get_temp_common(struct tsens_sensor *s, int *temp)
{
struct tsens_sensor *s = &priv->sensor[i];
struct tsens_priv *priv = s->priv;
int hw_id = s->hw_id;
int last_temp = 0, ret;
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp);
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
if (ret)
return ret;
@ -137,6 +501,77 @@ int get_temp_common(struct tsens_priv *priv, int i, int *temp)
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int dbg_sensors_show(struct seq_file *s, void *data)
{
struct platform_device *pdev = s->private;
struct tsens_priv *priv = platform_get_drvdata(pdev);
int i;
seq_printf(s, "max: %2d\nnum: %2d\n\n",
priv->feat->max_sensors, priv->num_sensors);
seq_puts(s, " id slope offset\n--------------------------\n");
for (i = 0; i < priv->num_sensors; i++) {
seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id,
priv->sensor[i].slope, priv->sensor[i].offset);
}
return 0;
}
static int dbg_version_show(struct seq_file *s, void *data)
{
struct platform_device *pdev = s->private;
struct tsens_priv *priv = platform_get_drvdata(pdev);
u32 maj_ver, min_ver, step_ver;
int ret;
if (tsens_version(priv) > VER_0_1) {
ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver);
if (ret)
return ret;
ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver);
if (ret)
return ret;
ret = regmap_field_read(priv->rf[VER_STEP], &step_ver);
if (ret)
return ret;
seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
} else {
seq_puts(s, "0.1.0\n");
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(dbg_version);
DEFINE_SHOW_ATTRIBUTE(dbg_sensors);
static void tsens_debug_init(struct platform_device *pdev)
{
struct tsens_priv *priv = platform_get_drvdata(pdev);
struct dentry *root, *file;
root = debugfs_lookup("tsens", NULL);
if (!root)
priv->debug_root = debugfs_create_dir("tsens", NULL);
else
priv->debug_root = root;
file = debugfs_lookup("version", priv->debug_root);
if (!file)
debugfs_create_file("version", 0444, priv->debug_root,
pdev, &dbg_version_fops);
/* A directory for each instance of the TSENS IP */
priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
}
#else
static inline void tsens_debug_init(struct platform_device *pdev) {}
#endif
static const struct regmap_config tsens_config = {
.name = "tm",
.reg_bits = 32,
@ -197,6 +632,15 @@ int __init init_common(struct tsens_priv *priv)
goto err_put_device;
}
if (tsens_version(priv) > VER_0_1) {
for (i = VER_MAJOR; i <= VER_STEP; i++) {
priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[i]);
if (IS_ERR(priv->rf[i]))
return PTR_ERR(priv->rf[i]);
}
}
priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[TSENS_EN]);
if (IS_ERR(priv->rf[TSENS_EN])) {
@ -207,7 +651,7 @@ int __init init_common(struct tsens_priv *priv)
if (ret)
goto err_put_device;
if (!enabled) {
dev_err(dev, "tsens device is not enabled\n");
dev_err(dev, "%s: device not enabled\n", __func__);
ret = -ENODEV;
goto err_put_device;
}
@ -218,24 +662,31 @@ int __init init_common(struct tsens_priv *priv)
ret = PTR_ERR(priv->rf[SENSOR_EN]);
goto err_put_device;
}
/* now alloc regmap_fields in tm_map */
for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) {
priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map,
priv->fields[j]);
if (IS_ERR(priv->rf[j])) {
ret = PTR_ERR(priv->rf[j]);
goto err_put_device;
}
}
for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) {
priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map,
priv->fields[j]);
if (IS_ERR(priv->rf[j])) {
ret = PTR_ERR(priv->rf[j]);
goto err_put_device;
priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
priv->fields[INT_EN]);
if (IS_ERR(priv->rf[INT_EN])) {
ret = PTR_ERR(priv->rf[INT_EN]);
goto err_put_device;
}
/* This loop might need changes if enum regfield_ids is reordered */
for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
for (i = 0; i < priv->feat->max_sensors; i++) {
int idx = j + i;
priv->rf[idx] = devm_regmap_field_alloc(dev, priv->tm_map,
priv->fields[idx]);
if (IS_ERR(priv->rf[idx])) {
ret = PTR_ERR(priv->rf[idx]);
goto err_put_device;
}
}
}
spin_lock_init(&priv->ul_lock);
tsens_enable_irq(priv);
tsens_debug_init(op);
return 0;
err_put_device:

View File

@ -347,9 +347,20 @@ static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = {
/* INTERRUPT ENABLE */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
/* UPPER/LOWER TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR11(LOW_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 0, 9),
REG_FIELD_FOR_EACH_SENSOR11(UP_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 10, 19),
/* UPPER/LOWER INTERRUPTS [CLEAR/STATUS] */
REG_FIELD_FOR_EACH_SENSOR11(LOW_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 20, 20),
REG_FIELD_FOR_EACH_SENSOR11(UP_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 21, 21),
/* NO CRITICAL INTERRUPT SUPPORT on v0.1 */
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
/* No VALID field on v0.1 */
/* xxx_STATUS bits: 1 == threshold violated */
REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10),
REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11),
REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12),

View File

@ -6,6 +6,7 @@
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "tsens.h"
/* ----- SROT ------ */
@ -17,6 +18,70 @@
#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
#define TM_Sn_STATUS_OFF 0x0044
#define TM_TRDY_OFF 0x0084
#define TM_HIGH_LOW_INT_STATUS_OFF 0x0088
#define TM_HIGH_LOW_Sn_INT_THRESHOLD_OFF 0x0090
/* eeprom layout data for msm8956/76 (v1) */
#define MSM8976_BASE0_MASK 0xff
#define MSM8976_BASE1_MASK 0xff
#define MSM8976_BASE1_SHIFT 8
#define MSM8976_S0_P1_MASK 0x3f00
#define MSM8976_S1_P1_MASK 0x3f00000
#define MSM8976_S2_P1_MASK 0x3f
#define MSM8976_S3_P1_MASK 0x3f000
#define MSM8976_S4_P1_MASK 0x3f00
#define MSM8976_S5_P1_MASK 0x3f00000
#define MSM8976_S6_P1_MASK 0x3f
#define MSM8976_S7_P1_MASK 0x3f000
#define MSM8976_S8_P1_MASK 0x1f8
#define MSM8976_S9_P1_MASK 0x1f8000
#define MSM8976_S10_P1_MASK 0xf8000000
#define MSM8976_S10_P1_MASK_1 0x1
#define MSM8976_S0_P2_MASK 0xfc000
#define MSM8976_S1_P2_MASK 0xfc000000
#define MSM8976_S2_P2_MASK 0xfc0
#define MSM8976_S3_P2_MASK 0xfc0000
#define MSM8976_S4_P2_MASK 0xfc000
#define MSM8976_S5_P2_MASK 0xfc000000
#define MSM8976_S6_P2_MASK 0xfc0
#define MSM8976_S7_P2_MASK 0xfc0000
#define MSM8976_S8_P2_MASK 0x7e00
#define MSM8976_S9_P2_MASK 0x7e00000
#define MSM8976_S10_P2_MASK 0x7e
#define MSM8976_S0_P1_SHIFT 8
#define MSM8976_S1_P1_SHIFT 20
#define MSM8976_S2_P1_SHIFT 0
#define MSM8976_S3_P1_SHIFT 12
#define MSM8976_S4_P1_SHIFT 8
#define MSM8976_S5_P1_SHIFT 20
#define MSM8976_S6_P1_SHIFT 0
#define MSM8976_S7_P1_SHIFT 12
#define MSM8976_S8_P1_SHIFT 3
#define MSM8976_S9_P1_SHIFT 15
#define MSM8976_S10_P1_SHIFT 27
#define MSM8976_S10_P1_SHIFT_1 0
#define MSM8976_S0_P2_SHIFT 14
#define MSM8976_S1_P2_SHIFT 26
#define MSM8976_S2_P2_SHIFT 6
#define MSM8976_S3_P2_SHIFT 18
#define MSM8976_S4_P2_SHIFT 14
#define MSM8976_S5_P2_SHIFT 26
#define MSM8976_S6_P2_SHIFT 6
#define MSM8976_S7_P2_SHIFT 18
#define MSM8976_S8_P2_SHIFT 9
#define MSM8976_S9_P2_SHIFT 21
#define MSM8976_S10_P2_SHIFT 1
#define MSM8976_CAL_SEL_MASK 0x3
#define MSM8976_CAL_DEGC_PT1 30
#define MSM8976_CAL_DEGC_PT2 120
#define MSM8976_SLOPE_FACTOR 1000
#define MSM8976_SLOPE_DEFAULT 3200
/* eeprom layout data for qcs404/405 (v1) */
#define BASE0_MASK 0x000007f8
@ -77,6 +142,30 @@
#define CAL_SEL_MASK 7
#define CAL_SEL_SHIFT 0
static void compute_intercept_slope_8976(struct tsens_priv *priv,
u32 *p1, u32 *p2, u32 mode)
{
int i;
priv->sensor[0].slope = 3313;
priv->sensor[1].slope = 3275;
priv->sensor[2].slope = 3320;
priv->sensor[3].slope = 3246;
priv->sensor[4].slope = 3279;
priv->sensor[5].slope = 3257;
priv->sensor[6].slope = 3234;
priv->sensor[7].slope = 3269;
priv->sensor[8].slope = 3255;
priv->sensor[9].slope = 3239;
priv->sensor[10].slope = 3286;
for (i = 0; i < priv->num_sensors; i++) {
priv->sensor[i].offset = (p1[i] * MSM8976_SLOPE_FACTOR) -
(MSM8976_CAL_DEGC_PT1 *
priv->sensor[i].slope);
}
}
static int calibrate_v1(struct tsens_priv *priv)
{
u32 base0 = 0, base1 = 0;
@ -143,7 +232,72 @@ static int calibrate_v1(struct tsens_priv *priv)
return 0;
}
/* v1.x: qcs404,405 */
static int calibrate_8976(struct tsens_priv *priv)
{
int base0 = 0, base1 = 0, i;
u32 p1[11], p2[11];
int mode = 0, tmp = 0;
u32 *qfprom_cdata;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
mode = (qfprom_cdata[4] & MSM8976_CAL_SEL_MASK);
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base1 = (qfprom_cdata[2] & MSM8976_BASE1_MASK) >> MSM8976_BASE1_SHIFT;
p2[0] = (qfprom_cdata[0] & MSM8976_S0_P2_MASK) >> MSM8976_S0_P2_SHIFT;
p2[1] = (qfprom_cdata[0] & MSM8976_S1_P2_MASK) >> MSM8976_S1_P2_SHIFT;
p2[2] = (qfprom_cdata[1] & MSM8976_S2_P2_MASK) >> MSM8976_S2_P2_SHIFT;
p2[3] = (qfprom_cdata[1] & MSM8976_S3_P2_MASK) >> MSM8976_S3_P2_SHIFT;
p2[4] = (qfprom_cdata[2] & MSM8976_S4_P2_MASK) >> MSM8976_S4_P2_SHIFT;
p2[5] = (qfprom_cdata[2] & MSM8976_S5_P2_MASK) >> MSM8976_S5_P2_SHIFT;
p2[6] = (qfprom_cdata[3] & MSM8976_S6_P2_MASK) >> MSM8976_S6_P2_SHIFT;
p2[7] = (qfprom_cdata[3] & MSM8976_S7_P2_MASK) >> MSM8976_S7_P2_SHIFT;
p2[8] = (qfprom_cdata[4] & MSM8976_S8_P2_MASK) >> MSM8976_S8_P2_SHIFT;
p2[9] = (qfprom_cdata[4] & MSM8976_S9_P2_MASK) >> MSM8976_S9_P2_SHIFT;
p2[10] = (qfprom_cdata[5] & MSM8976_S10_P2_MASK) >> MSM8976_S10_P2_SHIFT;
for (i = 0; i < priv->num_sensors; i++)
p2[i] = ((base1 + p2[i]) << 2);
/* Fall through */
case ONE_PT_CALIB2:
base0 = qfprom_cdata[0] & MSM8976_BASE0_MASK;
p1[0] = (qfprom_cdata[0] & MSM8976_S0_P1_MASK) >> MSM8976_S0_P1_SHIFT;
p1[1] = (qfprom_cdata[0] & MSM8976_S1_P1_MASK) >> MSM8976_S1_P1_SHIFT;
p1[2] = (qfprom_cdata[1] & MSM8976_S2_P1_MASK) >> MSM8976_S2_P1_SHIFT;
p1[3] = (qfprom_cdata[1] & MSM8976_S3_P1_MASK) >> MSM8976_S3_P1_SHIFT;
p1[4] = (qfprom_cdata[2] & MSM8976_S4_P1_MASK) >> MSM8976_S4_P1_SHIFT;
p1[5] = (qfprom_cdata[2] & MSM8976_S5_P1_MASK) >> MSM8976_S5_P1_SHIFT;
p1[6] = (qfprom_cdata[3] & MSM8976_S6_P1_MASK) >> MSM8976_S6_P1_SHIFT;
p1[7] = (qfprom_cdata[3] & MSM8976_S7_P1_MASK) >> MSM8976_S7_P1_SHIFT;
p1[8] = (qfprom_cdata[4] & MSM8976_S8_P1_MASK) >> MSM8976_S8_P1_SHIFT;
p1[9] = (qfprom_cdata[4] & MSM8976_S9_P1_MASK) >> MSM8976_S9_P1_SHIFT;
p1[10] = (qfprom_cdata[4] & MSM8976_S10_P1_MASK) >> MSM8976_S10_P1_SHIFT;
tmp = (qfprom_cdata[5] & MSM8976_S10_P1_MASK_1) << MSM8976_S10_P1_SHIFT_1;
p1[10] |= tmp;
for (i = 0; i < priv->num_sensors; i++)
p1[i] = (((base0) + p1[i]) << 2);
break;
default:
for (i = 0; i < priv->num_sensors; i++) {
p1[i] = 500;
p2[i] = 780;
}
break;
}
compute_intercept_slope_8976(priv, p1, p2, mode);
kfree(qfprom_cdata);
return 0;
}
/* v1.x: msm8956,8976,qcs404,405 */
static const struct tsens_features tsens_v1_feat = {
.ver_major = VER_1_X,
@ -168,9 +322,36 @@ static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
/* INTERRUPT ENABLE */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
/* UPPER/LOWER TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR11(LOW_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 0, 9),
REG_FIELD_FOR_EACH_SENSOR11(UP_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 10, 19),
/* UPPER/LOWER INTERRUPTS [CLEAR/STATUS] */
REG_FIELD_FOR_EACH_SENSOR11(LOW_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 20, 20),
REG_FIELD_FOR_EACH_SENSOR11(UP_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 21, 21),
[LOW_INT_STATUS_0] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 0, 0),
[LOW_INT_STATUS_1] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 1, 1),
[LOW_INT_STATUS_2] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 2, 2),
[LOW_INT_STATUS_3] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 3, 3),
[LOW_INT_STATUS_4] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 4, 4),
[LOW_INT_STATUS_5] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 5, 5),
[LOW_INT_STATUS_6] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 6, 6),
[LOW_INT_STATUS_7] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 7, 7),
[UP_INT_STATUS_0] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 8, 8),
[UP_INT_STATUS_1] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 9, 9),
[UP_INT_STATUS_2] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 10, 10),
[UP_INT_STATUS_3] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 11, 11),
[UP_INT_STATUS_4] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 12, 12),
[UP_INT_STATUS_5] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 13, 13),
[UP_INT_STATUS_6] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 14, 14),
[UP_INT_STATUS_7] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 15, 15),
/* NO CRITICAL INTERRUPT SUPPORT on v1 */
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
REG_FIELD_FOR_EACH_SENSOR11(VALID, TM_Sn_STATUS_OFF, 14, 14),
/* xxx_STATUS bits: 1 == threshold violated */
REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10),
REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11),
REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12),
@ -192,3 +373,18 @@ const struct tsens_plat_data data_tsens_v1 = {
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
static const struct tsens_ops ops_8976 = {
.init = init_common,
.calibrate = calibrate_8976,
.get_temp = get_temp_tsens_valid,
};
/* Valid for both MSM8956 and MSM8976. Sensor ID 3 is unused. */
const struct tsens_plat_data data_8976 = {
.num_sensors = 11,
.ops = &ops_8976,
.hw_ids = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10},
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};

View File

@ -50,9 +50,22 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
/* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2),
/* TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11),
REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12, 23),
/* INTERRUPTS [CLEAR/STATUS/MASK] */
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF),
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_CLEAR, TM_UPPER_LOWER_INT_CLEAR_OFF),
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_MASK, TM_UPPER_LOWER_INT_MASK_OFF),
REG_FIELD_SPLIT_BITS_16_31(UP_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF),
REG_FIELD_SPLIT_BITS_16_31(UP_INT_CLEAR, TM_UPPER_LOWER_INT_CLEAR_OFF),
REG_FIELD_SPLIT_BITS_16_31(UP_INT_MASK, TM_UPPER_LOWER_INT_MASK_OFF),
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11),
REG_FIELD_FOR_EACH_SENSOR16(VALID, TM_Sn_STATUS_OFF, 21, 21),
/* xxx_STATUS bits: 1 == threshold violated */
REG_FIELD_FOR_EACH_SENSOR16(MIN_STATUS, TM_Sn_STATUS_OFF, 16, 16),
REG_FIELD_FOR_EACH_SENSOR16(LOWER_STATUS, TM_Sn_STATUS_OFF, 17, 17),
REG_FIELD_FOR_EACH_SENSOR16(UPPER_STATUS, TM_Sn_STATUS_OFF, 18, 18),

View File

@ -3,9 +3,11 @@
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
@ -14,19 +16,19 @@
static int tsens_get_temp(void *data, int *temp)
{
const struct tsens_sensor *s = data;
struct tsens_sensor *s = data;
struct tsens_priv *priv = s->priv;
return priv->ops->get_temp(priv, s->id, temp);
return priv->ops->get_temp(s, temp);
}
static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
{
const struct tsens_sensor *s = data;
struct tsens_sensor *s = data;
struct tsens_priv *priv = s->priv;
if (priv->ops->get_trend)
return priv->ops->get_trend(priv, s->id, trend);
return priv->ops->get_trend(s, trend);
return -ENOTSUPP;
}
@ -60,6 +62,9 @@ static const struct of_device_id tsens_table[] = {
}, {
.compatible = "qcom,msm8974-tsens",
.data = &data_8974,
}, {
.compatible = "qcom,msm8976-tsens",
.data = &data_8976,
}, {
.compatible = "qcom,msm8996-tsens",
.data = &data_8996,
@ -77,17 +82,18 @@ MODULE_DEVICE_TABLE(of, tsens_table);
static const struct thermal_zone_of_device_ops tsens_of_ops = {
.get_temp = tsens_get_temp,
.get_trend = tsens_get_trend,
.set_trips = tsens_set_trips,
};
static int tsens_register(struct tsens_priv *priv)
{
int i;
int i, ret, irq;
struct thermal_zone_device *tzd;
struct platform_device *pdev;
for (i = 0; i < priv->num_sensors; i++) {
priv->sensor[i].priv = priv;
priv->sensor[i].id = i;
tzd = devm_thermal_zone_of_sensor_register(priv->dev, i,
tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id,
&priv->sensor[i],
&tsens_of_ops);
if (IS_ERR(tzd))
@ -96,7 +102,31 @@ static int tsens_register(struct tsens_priv *priv)
if (priv->ops->enable)
priv->ops->enable(priv, i);
}
return 0;
pdev = of_find_device_by_node(priv->dev->of_node);
if (!pdev)
return -ENODEV;
irq = platform_get_irq_byname(pdev, "uplow");
if (irq < 0) {
ret = irq;
goto err_put_device;
}
ret = devm_request_threaded_irq(&pdev->dev, irq,
NULL, tsens_irq_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
dev_name(&pdev->dev), priv);
if (ret) {
dev_err(&pdev->dev, "%s: failed to get irq\n", __func__);
goto err_put_device;
}
enable_irq_wake(irq);
err_put_device:
put_device(&pdev->dev);
return ret;
}
static int tsens_probe(struct platform_device *pdev)
@ -128,7 +158,7 @@ static int tsens_probe(struct platform_device *pdev)
of_property_read_u32(np, "#qcom,sensors", &num_sensors);
if (num_sensors <= 0) {
dev_err(dev, "invalid number of sensors\n");
dev_err(dev, "%s: invalid number of sensors\n", __func__);
return -EINVAL;
}
@ -150,12 +180,14 @@ static int tsens_probe(struct platform_device *pdev)
priv->feat = data->feat;
priv->fields = data->fields;
platform_set_drvdata(pdev, priv);
if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
return -EINVAL;
ret = priv->ops->init(priv);
if (ret < 0) {
dev_err(dev, "tsens init failed\n");
dev_err(dev, "%s: init failed\n", __func__);
return ret;
}
@ -163,22 +195,20 @@ static int tsens_probe(struct platform_device *pdev)
ret = priv->ops->calibrate(priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "tsens calibration failed\n");
dev_err(dev, "%s: calibration failed\n", __func__);
return ret;
}
}
ret = tsens_register(priv);
platform_set_drvdata(pdev, priv);
return ret;
return tsens_register(priv);
}
static int tsens_remove(struct platform_device *pdev)
{
struct tsens_priv *priv = platform_get_drvdata(pdev);
debugfs_remove_recursive(priv->debug_root);
tsens_disable_irq(priv);
if (priv->ops->disable)
priv->ops->disable(priv);

View File

@ -13,8 +13,10 @@
#define CAL_DEGC_PT2 120
#define SLOPE_FACTOR 1000
#define SLOPE_DEFAULT 3200
#define THRESHOLD_MAX_ADC_CODE 0x3ff
#define THRESHOLD_MIN_ADC_CODE 0x0
#include <linux/interrupt.h>
#include <linux/thermal.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@ -27,12 +29,16 @@ enum tsens_ver {
VER_2_X,
};
enum tsens_irq_type {
LOWER,
UPPER,
};
/**
* struct tsens_sensor - data for each sensor connected to the tsens device
* @priv: tsens device instance that this sensor is connected to
* @tzd: pointer to the thermal zone that this sensor is in
* @offset: offset of temperature adjustment curve
* @id: Sensor ID
* @hw_id: HW ID can be used in case of platform-specific IDs
* @slope: slope of temperature adjustment curve
* @status: 8960-specific variable to track 8960 and 8660 status register offset
@ -41,7 +47,6 @@ struct tsens_sensor {
struct tsens_priv *priv;
struct thermal_zone_device *tzd;
int offset;
unsigned int id;
unsigned int hw_id;
int slope;
u32 status;
@ -62,13 +67,13 @@ struct tsens_ops {
/* mandatory callbacks */
int (*init)(struct tsens_priv *priv);
int (*calibrate)(struct tsens_priv *priv);
int (*get_temp)(struct tsens_priv *priv, int i, int *temp);
int (*get_temp)(struct tsens_sensor *s, int *temp);
/* optional callbacks */
int (*enable)(struct tsens_priv *priv, int i);
void (*disable)(struct tsens_priv *priv);
int (*suspend)(struct tsens_priv *priv);
int (*resume)(struct tsens_priv *priv);
int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend);
int (*get_trend)(struct tsens_sensor *s, enum thermal_trend *trend);
};
#define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \
@ -102,22 +107,66 @@ struct tsens_ops {
[_name##_##14] = REG_FIELD(_offset + 56, _startbit, _stopbit), \
[_name##_##15] = REG_FIELD(_offset + 60, _startbit, _stopbit)
/* reg_field IDs to use as an index into an array */
#define REG_FIELD_SPLIT_BITS_0_15(_name, _offset) \
[_name##_##0] = REG_FIELD(_offset, 0, 0), \
[_name##_##1] = REG_FIELD(_offset, 1, 1), \
[_name##_##2] = REG_FIELD(_offset, 2, 2), \
[_name##_##3] = REG_FIELD(_offset, 3, 3), \
[_name##_##4] = REG_FIELD(_offset, 4, 4), \
[_name##_##5] = REG_FIELD(_offset, 5, 5), \
[_name##_##6] = REG_FIELD(_offset, 6, 6), \
[_name##_##7] = REG_FIELD(_offset, 7, 7), \
[_name##_##8] = REG_FIELD(_offset, 8, 8), \
[_name##_##9] = REG_FIELD(_offset, 9, 9), \
[_name##_##10] = REG_FIELD(_offset, 10, 10), \
[_name##_##11] = REG_FIELD(_offset, 11, 11), \
[_name##_##12] = REG_FIELD(_offset, 12, 12), \
[_name##_##13] = REG_FIELD(_offset, 13, 13), \
[_name##_##14] = REG_FIELD(_offset, 14, 14), \
[_name##_##15] = REG_FIELD(_offset, 15, 15)
#define REG_FIELD_SPLIT_BITS_16_31(_name, _offset) \
[_name##_##0] = REG_FIELD(_offset, 16, 16), \
[_name##_##1] = REG_FIELD(_offset, 17, 17), \
[_name##_##2] = REG_FIELD(_offset, 18, 18), \
[_name##_##3] = REG_FIELD(_offset, 19, 19), \
[_name##_##4] = REG_FIELD(_offset, 20, 20), \
[_name##_##5] = REG_FIELD(_offset, 21, 21), \
[_name##_##6] = REG_FIELD(_offset, 22, 22), \
[_name##_##7] = REG_FIELD(_offset, 23, 23), \
[_name##_##8] = REG_FIELD(_offset, 24, 24), \
[_name##_##9] = REG_FIELD(_offset, 25, 25), \
[_name##_##10] = REG_FIELD(_offset, 26, 26), \
[_name##_##11] = REG_FIELD(_offset, 27, 27), \
[_name##_##12] = REG_FIELD(_offset, 28, 28), \
[_name##_##13] = REG_FIELD(_offset, 29, 29), \
[_name##_##14] = REG_FIELD(_offset, 30, 30), \
[_name##_##15] = REG_FIELD(_offset, 31, 31)
/*
* reg_field IDs to use as an index into an array
* If you change the order of the entries, check the devm_regmap_field_alloc()
* calls in init_common()
*/
enum regfield_ids {
/* ----- SROT ------ */
/* HW_VER */
VER_MAJOR = 0,
VER_MAJOR,
VER_MINOR,
VER_STEP,
/* CTRL_OFFSET */
TSENS_EN = 3,
TSENS_EN,
TSENS_SW_RST,
SENSOR_EN,
CODE_OR_TEMP,
/* ----- TM ------ */
/* TRDY */
TRDY,
/* INTERRUPT ENABLE */
INT_EN, /* v2+ has separate enables for crit, upper and lower irq */
/* STATUS */
LAST_TEMP_0 = 7, /* Last temperature reading */
LAST_TEMP_0, /* Last temperature reading */
LAST_TEMP_1,
LAST_TEMP_2,
LAST_TEMP_3,
@ -133,7 +182,7 @@ enum regfield_ids {
LAST_TEMP_13,
LAST_TEMP_14,
LAST_TEMP_15,
VALID_0 = 23, /* VALID reading or not */
VALID_0, /* VALID reading or not */
VALID_1,
VALID_2,
VALID_3,
@ -149,6 +198,182 @@ enum regfield_ids {
VALID_13,
VALID_14,
VALID_15,
LOWER_STATUS_0, /* LOWER threshold violated */
LOWER_STATUS_1,
LOWER_STATUS_2,
LOWER_STATUS_3,
LOWER_STATUS_4,
LOWER_STATUS_5,
LOWER_STATUS_6,
LOWER_STATUS_7,
LOWER_STATUS_8,
LOWER_STATUS_9,
LOWER_STATUS_10,
LOWER_STATUS_11,
LOWER_STATUS_12,
LOWER_STATUS_13,
LOWER_STATUS_14,
LOWER_STATUS_15,
LOW_INT_STATUS_0, /* LOWER interrupt status */
LOW_INT_STATUS_1,
LOW_INT_STATUS_2,
LOW_INT_STATUS_3,
LOW_INT_STATUS_4,
LOW_INT_STATUS_5,
LOW_INT_STATUS_6,
LOW_INT_STATUS_7,
LOW_INT_STATUS_8,
LOW_INT_STATUS_9,
LOW_INT_STATUS_10,
LOW_INT_STATUS_11,
LOW_INT_STATUS_12,
LOW_INT_STATUS_13,
LOW_INT_STATUS_14,
LOW_INT_STATUS_15,
LOW_INT_CLEAR_0, /* LOWER interrupt clear */
LOW_INT_CLEAR_1,
LOW_INT_CLEAR_2,
LOW_INT_CLEAR_3,
LOW_INT_CLEAR_4,
LOW_INT_CLEAR_5,
LOW_INT_CLEAR_6,
LOW_INT_CLEAR_7,
LOW_INT_CLEAR_8,
LOW_INT_CLEAR_9,
LOW_INT_CLEAR_10,
LOW_INT_CLEAR_11,
LOW_INT_CLEAR_12,
LOW_INT_CLEAR_13,
LOW_INT_CLEAR_14,
LOW_INT_CLEAR_15,
LOW_INT_MASK_0, /* LOWER interrupt mask */
LOW_INT_MASK_1,
LOW_INT_MASK_2,
LOW_INT_MASK_3,
LOW_INT_MASK_4,
LOW_INT_MASK_5,
LOW_INT_MASK_6,
LOW_INT_MASK_7,
LOW_INT_MASK_8,
LOW_INT_MASK_9,
LOW_INT_MASK_10,
LOW_INT_MASK_11,
LOW_INT_MASK_12,
LOW_INT_MASK_13,
LOW_INT_MASK_14,
LOW_INT_MASK_15,
LOW_THRESH_0, /* LOWER threshold values */
LOW_THRESH_1,
LOW_THRESH_2,
LOW_THRESH_3,
LOW_THRESH_4,
LOW_THRESH_5,
LOW_THRESH_6,
LOW_THRESH_7,
LOW_THRESH_8,
LOW_THRESH_9,
LOW_THRESH_10,
LOW_THRESH_11,
LOW_THRESH_12,
LOW_THRESH_13,
LOW_THRESH_14,
LOW_THRESH_15,
UPPER_STATUS_0, /* UPPER threshold violated */
UPPER_STATUS_1,
UPPER_STATUS_2,
UPPER_STATUS_3,
UPPER_STATUS_4,
UPPER_STATUS_5,
UPPER_STATUS_6,
UPPER_STATUS_7,
UPPER_STATUS_8,
UPPER_STATUS_9,
UPPER_STATUS_10,
UPPER_STATUS_11,
UPPER_STATUS_12,
UPPER_STATUS_13,
UPPER_STATUS_14,
UPPER_STATUS_15,
UP_INT_STATUS_0, /* UPPER interrupt status */
UP_INT_STATUS_1,
UP_INT_STATUS_2,
UP_INT_STATUS_3,
UP_INT_STATUS_4,
UP_INT_STATUS_5,
UP_INT_STATUS_6,
UP_INT_STATUS_7,
UP_INT_STATUS_8,
UP_INT_STATUS_9,
UP_INT_STATUS_10,
UP_INT_STATUS_11,
UP_INT_STATUS_12,
UP_INT_STATUS_13,
UP_INT_STATUS_14,
UP_INT_STATUS_15,
UP_INT_CLEAR_0, /* UPPER interrupt clear */
UP_INT_CLEAR_1,
UP_INT_CLEAR_2,
UP_INT_CLEAR_3,
UP_INT_CLEAR_4,
UP_INT_CLEAR_5,
UP_INT_CLEAR_6,
UP_INT_CLEAR_7,
UP_INT_CLEAR_8,
UP_INT_CLEAR_9,
UP_INT_CLEAR_10,
UP_INT_CLEAR_11,
UP_INT_CLEAR_12,
UP_INT_CLEAR_13,
UP_INT_CLEAR_14,
UP_INT_CLEAR_15,
UP_INT_MASK_0, /* UPPER interrupt mask */
UP_INT_MASK_1,
UP_INT_MASK_2,
UP_INT_MASK_3,
UP_INT_MASK_4,
UP_INT_MASK_5,
UP_INT_MASK_6,
UP_INT_MASK_7,
UP_INT_MASK_8,
UP_INT_MASK_9,
UP_INT_MASK_10,
UP_INT_MASK_11,
UP_INT_MASK_12,
UP_INT_MASK_13,
UP_INT_MASK_14,
UP_INT_MASK_15,
UP_THRESH_0, /* UPPER threshold values */
UP_THRESH_1,
UP_THRESH_2,
UP_THRESH_3,
UP_THRESH_4,
UP_THRESH_5,
UP_THRESH_6,
UP_THRESH_7,
UP_THRESH_8,
UP_THRESH_9,
UP_THRESH_10,
UP_THRESH_11,
UP_THRESH_12,
UP_THRESH_13,
UP_THRESH_14,
UP_THRESH_15,
CRITICAL_STATUS_0, /* CRITICAL threshold violated */
CRITICAL_STATUS_1,
CRITICAL_STATUS_2,
CRITICAL_STATUS_3,
CRITICAL_STATUS_4,
CRITICAL_STATUS_5,
CRITICAL_STATUS_6,
CRITICAL_STATUS_7,
CRITICAL_STATUS_8,
CRITICAL_STATUS_9,
CRITICAL_STATUS_10,
CRITICAL_STATUS_11,
CRITICAL_STATUS_12,
CRITICAL_STATUS_13,
CRITICAL_STATUS_14,
CRITICAL_STATUS_15,
MIN_STATUS_0, /* MIN threshold violated */
MIN_STATUS_1,
MIN_STATUS_2,
@ -181,61 +406,6 @@ enum regfield_ids {
MAX_STATUS_13,
MAX_STATUS_14,
MAX_STATUS_15,
LOWER_STATUS_0, /* LOWER threshold violated */
LOWER_STATUS_1,
LOWER_STATUS_2,
LOWER_STATUS_3,
LOWER_STATUS_4,
LOWER_STATUS_5,
LOWER_STATUS_6,
LOWER_STATUS_7,
LOWER_STATUS_8,
LOWER_STATUS_9,
LOWER_STATUS_10,
LOWER_STATUS_11,
LOWER_STATUS_12,
LOWER_STATUS_13,
LOWER_STATUS_14,
LOWER_STATUS_15,
UPPER_STATUS_0, /* UPPER threshold violated */
UPPER_STATUS_1,
UPPER_STATUS_2,
UPPER_STATUS_3,
UPPER_STATUS_4,
UPPER_STATUS_5,
UPPER_STATUS_6,
UPPER_STATUS_7,
UPPER_STATUS_8,
UPPER_STATUS_9,
UPPER_STATUS_10,
UPPER_STATUS_11,
UPPER_STATUS_12,
UPPER_STATUS_13,
UPPER_STATUS_14,
UPPER_STATUS_15,
CRITICAL_STATUS_0, /* CRITICAL threshold violated */
CRITICAL_STATUS_1,
CRITICAL_STATUS_2,
CRITICAL_STATUS_3,
CRITICAL_STATUS_4,
CRITICAL_STATUS_5,
CRITICAL_STATUS_6,
CRITICAL_STATUS_7,
CRITICAL_STATUS_8,
CRITICAL_STATUS_9,
CRITICAL_STATUS_10,
CRITICAL_STATUS_11,
CRITICAL_STATUS_12,
CRITICAL_STATUS_13,
CRITICAL_STATUS_14,
CRITICAL_STATUS_15,
/* TRDY */
TRDY,
/* INTERRUPT ENABLE */
INT_EN, /* Pre-V1, V1.x */
LOW_INT_EN, /* V2.x */
UP_INT_EN, /* V2.x */
CRIT_INT_EN, /* V2.x */
/* Keep last */
MAX_REGFIELDS
@ -295,6 +465,8 @@ struct tsens_context {
* @feat: features of the IP
* @fields: bitfield locations
* @ops: pointer to list of callbacks supported by this device
* @debug_root: pointer to debugfs dentry for all tsens
* @debug: pointer to debugfs dentry for tsens controller
* @sensor: list of sensors attached to this device
*/
struct tsens_priv {
@ -303,19 +475,31 @@ struct tsens_priv {
struct regmap *tm_map;
struct regmap *srot_map;
u32 tm_offset;
/* lock for upper/lower threshold interrupts */
spinlock_t ul_lock;
struct regmap_field *rf[MAX_REGFIELDS];
struct tsens_context ctx;
const struct tsens_features *feat;
const struct reg_field *fields;
const struct tsens_ops *ops;
struct dentry *debug_root;
struct dentry *debug;
struct tsens_sensor sensor[0];
};
char *qfprom_read(struct device *dev, const char *cname);
void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode);
int init_common(struct tsens_priv *priv);
int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp);
int get_temp_common(struct tsens_priv *priv, int i, int *temp);
int get_temp_tsens_valid(struct tsens_sensor *s, int *temp);
int get_temp_common(struct tsens_sensor *s, int *temp);
int tsens_enable_irq(struct tsens_priv *priv);
void tsens_disable_irq(struct tsens_priv *priv);
int tsens_set_trips(void *_sensor, int low, int high);
irqreturn_t tsens_irq_thread(int irq, void *data);
/* TSENS target */
extern const struct tsens_plat_data data_8960;
@ -324,7 +508,7 @@ extern const struct tsens_plat_data data_8960;
extern const struct tsens_plat_data data_8916, data_8974;
/* TSENS v1 targets */
extern const struct tsens_plat_data data_tsens_v1;
extern const struct tsens_plat_data data_tsens_v1, data_8976;
/* TSENS v2 targets */
extern const struct tsens_plat_data data_8996, data_tsens_v2;

View File

@ -13,7 +13,16 @@
#include "thermal_core.h"
#define SITES_MAX 16
#define SITES_MAX 16
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
#define TMR_ALPF 0x0c000000
#define TMR_ALPF_V2 0x03000000
#define TMTMIR_DEFAULT 0x0000000f
#define TIER_DISABLE 0x0
#define TEUMR0_V2 0x51009c00
#define TMU_VER1 0x1
#define TMU_VER2 0x2
/*
* QorIQ TMU Registers
@ -24,17 +33,12 @@ struct qoriq_tmu_site_regs {
u8 res0[0x8];
};
struct qoriq_tmu_regs {
struct qoriq_tmu_regs_v1 {
u32 tmr; /* Mode Register */
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
#define TMR_ALPF 0x0c000000
u32 tsr; /* Status Register */
u32 tmtmir; /* Temperature measurement interval Register */
#define TMTMIR_DEFAULT 0x0000000f
u8 res0[0x14];
u32 tier; /* Interrupt Enable Register */
#define TIER_DISABLE 0x0
u32 tidr; /* Interrupt Detect Register */
u32 tiscr; /* Interrupt Site Capture Register */
u32 ticscr; /* Interrupt Critical Site Capture Register */
@ -54,10 +58,50 @@ struct qoriq_tmu_regs {
u32 ipbrr0; /* IP Block Revision Register 0 */
u32 ipbrr1; /* IP Block Revision Register 1 */
u8 res6[0x310];
u32 ttr0cr; /* Temperature Range 0 Control Register */
u32 ttr1cr; /* Temperature Range 1 Control Register */
u32 ttr2cr; /* Temperature Range 2 Control Register */
u32 ttr3cr; /* Temperature Range 3 Control Register */
u32 ttrcr[4]; /* Temperature Range Control Register */
};
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 */
};
struct qoriq_tmu_data;
@ -72,7 +116,9 @@ struct qoriq_sensor {
};
struct qoriq_tmu_data {
struct qoriq_tmu_regs __iomem *regs;
int ver;
struct qoriq_tmu_regs_v1 __iomem *regs;
struct qoriq_tmu_regs_v2 __iomem *regs_v2;
struct clk *clk;
bool little_endian;
struct qoriq_sensor *sensor[SITES_MAX];
@ -132,12 +178,23 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
return PTR_ERR(qdata->sensor[id]->tzd);
}
sites |= 0x1 << (15 - id);
if (qdata->ver == TMU_VER1)
sites |= 0x1 << (15 - id);
else
sites |= 0x1 << id;
}
/* Enable monitoring */
if (sites != 0)
tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr);
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);
}
}
return 0;
}
@ -150,16 +207,21 @@ static int qoriq_tmu_calibration(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
dev_err(&pdev->dev, "missing calibration range.\n");
return -ENODEV;
len = of_property_count_u32_elems(np, "fsl,tmu-range");
if (len < 0 || len > 4) {
dev_err(&pdev->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");
return val;
}
/* Init temperature range registers */
tmu_write(data, range[0], &data->regs->ttr0cr);
tmu_write(data, range[1], &data->regs->ttr1cr);
tmu_write(data, range[2], &data->regs->ttr2cr);
tmu_write(data, range[3], &data->regs->ttr3cr);
for (i = 0; i < len; i++)
tmu_write(data, range[i], &data->regs->ttrcr[i]);
calibration = of_get_property(np, "fsl,tmu-calibration", &len);
if (calibration == NULL || len % 8) {
@ -183,7 +245,12 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
tmu_write(data, TIER_DISABLE, &data->regs->tier);
/* Set update_interval */
tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
if (data->ver == TMU_VER1) {
tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
} else {
tmu_write(data, TMTMIR_DEFAULT, &data->regs_v2->tmtmir);
tmu_write(data, TEUMR0_V2, &data->regs_v2->teumr0);
}
/* Disable monitoring */
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
@ -192,6 +259,7 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
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;
@ -220,6 +288,12 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
return ret;
}
/* version register offset at: 0xbf8 on both v1 and v2 */
ver = tmu_read(data, &data->regs->ipbrr0);
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 */

View File

@ -314,6 +314,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
.compatible = "renesas,r8a774a1-thermal",
.data = &rcar_gen3_ths_tj_1_m3_w,
},
{
.compatible = "renesas,r8a774b1-thermal",
.data = &rcar_gen3_ths_tj_1,
},
{
.compatible = "renesas,r8a7795-thermal",
.data = &rcar_gen3_ths_tj_1,

View File

@ -134,7 +134,8 @@ static int gadc_thermal_probe(struct platform_device *pdev)
gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel");
if (IS_ERR(gti->channel)) {
ret = PTR_ERR(gti->channel);
dev_err(&pdev->dev, "IIO channel not found: %d\n", ret);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "IIO channel not found: %d\n", ret);
return ret;
}
@ -142,8 +143,10 @@ static int gadc_thermal_probe(struct platform_device *pdev)
&gadc_thermal_ops);
if (IS_ERR(gti->tz_dev)) {
ret = PTR_ERR(gti->tz_dev);
dev_err(&pdev->dev, "Thermal zone sensor register failed: %d\n",
ret);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Thermal zone sensor register failed: %d\n",
ret);
return ret;
}

View File

@ -19,8 +19,6 @@
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/of.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <linux/suspend.h>
#define CREATE_TRACE_POINTS
@ -304,7 +302,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
&tz->poll_queue,
msecs_to_jiffies(delay));
else
cancel_delayed_work_sync(&tz->poll_queue);
cancel_delayed_work(&tz->poll_queue);
}
static void monitor_thermal_zone(struct thermal_zone_device *tz)
@ -1414,7 +1412,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
mutex_unlock(&thermal_list_lock);
thermal_zone_device_set_polling(tz, 0);
cancel_delayed_work_sync(&tz->poll_queue);
thermal_set_governor(tz, NULL);
@ -1464,97 +1462,6 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
}
EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
#ifdef CONFIG_NET
static const struct genl_multicast_group thermal_event_mcgrps[] = {
{ .name = THERMAL_GENL_MCAST_GROUP_NAME, },
};
static struct genl_family thermal_event_genl_family __ro_after_init = {
.module = THIS_MODULE,
.name = THERMAL_GENL_FAMILY_NAME,
.version = THERMAL_GENL_VERSION,
.maxattr = THERMAL_GENL_ATTR_MAX,
.mcgrps = thermal_event_mcgrps,
.n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps),
};
int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event)
{
struct sk_buff *skb;
struct nlattr *attr;
struct thermal_genl_event *thermal_event;
void *msg_header;
int size;
int result;
static unsigned int thermal_event_seqnum;
if (!tz)
return -EINVAL;
/* allocate memory */
size = nla_total_size(sizeof(struct thermal_genl_event)) +
nla_total_size(0);
skb = genlmsg_new(size, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
/* add the genetlink message header */
msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
&thermal_event_genl_family, 0,
THERMAL_GENL_CMD_EVENT);
if (!msg_header) {
nlmsg_free(skb);
return -ENOMEM;
}
/* fill the data */
attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
sizeof(struct thermal_genl_event));
if (!attr) {
nlmsg_free(skb);
return -EINVAL;
}
thermal_event = nla_data(attr);
if (!thermal_event) {
nlmsg_free(skb);
return -EINVAL;
}
memset(thermal_event, 0, sizeof(struct thermal_genl_event));
thermal_event->orig = tz->id;
thermal_event->event = event;
/* send multicast genetlink message */
genlmsg_end(skb, msg_header);
result = genlmsg_multicast(&thermal_event_genl_family, skb, 0,
0, GFP_ATOMIC);
if (result)
dev_err(&tz->device, "Failed to send netlink event:%d", result);
return result;
}
EXPORT_SYMBOL_GPL(thermal_generate_netlink_event);
static int __init genetlink_init(void)
{
return genl_register_family(&thermal_event_genl_family);
}
static void genetlink_exit(void)
{
genl_unregister_family(&thermal_event_genl_family);
}
#else /* !CONFIG_NET */
static inline int genetlink_init(void) { return 0; }
static inline void genetlink_exit(void) {}
#endif /* !CONFIG_NET */
static int thermal_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
@ -1607,13 +1514,9 @@ static int __init thermal_init(void)
if (result)
goto unregister_governors;
result = genetlink_init();
if (result)
goto unregister_class;
result = of_parse_thermal_zones();
if (result)
goto exit_netlink;
goto unregister_class;
result = register_pm_notifier(&thermal_pm_nb);
if (result)
@ -1622,8 +1525,6 @@ static int __init thermal_init(void)
return 0;
exit_netlink:
genetlink_exit();
unregister_class:
class_unregister(&thermal_class);
unregister_governors:
@ -1636,4 +1537,4 @@ static int __init thermal_init(void)
mutex_destroy(&poweroff_lock);
return result;
}
fs_initcall(thermal_init);
core_initcall(thermal_init);

View File

@ -110,7 +110,6 @@ static struct platform_driver thermal_mmio_driver = {
.probe = thermal_mmio_probe,
.driver = {
.name = "thermal-mmio",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(thermal_mmio_id_table),
},
};

View File

@ -33,6 +33,13 @@ cpufreq_cooling_register(struct cpufreq_policy *policy);
*/
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
/**
* of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
* @policy: cpufreq policy.
*/
struct thermal_cooling_device *
of_cpufreq_cooling_register(struct cpufreq_policy *policy);
#else /* !CONFIG_CPU_THERMAL */
static inline struct thermal_cooling_device *
cpufreq_cooling_register(struct cpufreq_policy *policy)
@ -45,21 +52,12 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
return;
}
#endif /* CONFIG_CPU_THERMAL */
#if defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL)
/**
* of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
* @policy: cpufreq policy.
*/
struct thermal_cooling_device *
of_cpufreq_cooling_register(struct cpufreq_policy *policy);
#else
static inline struct thermal_cooling_device *
of_cpufreq_cooling_register(struct cpufreq_policy *policy)
{
return NULL;
}
#endif /* defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL) */
#endif /* CONFIG_CPU_THERMAL */
#endif /* __CPU_COOLING_H__ */

View File

@ -9,7 +9,6 @@
#include <linux/sched/topology.h>
#include <linux/types.h>
#ifdef CONFIG_ENERGY_MODEL
/**
* em_cap_state - Capacity state of a performance domain
* @frequency: The CPU frequency in KHz, for consistency with CPUFreq
@ -40,6 +39,7 @@ struct em_perf_domain {
unsigned long cpus[0];
};
#ifdef CONFIG_ENERGY_MODEL
#define EM_CPU_MAX_POWER 0xFFFF
struct em_data_callback {
@ -160,7 +160,6 @@ static inline int em_pd_nr_cap_states(struct em_perf_domain *pd)
}
#else
struct em_perf_domain {};
struct em_data_callback {};
#define EM_DATA_CB(_active_power_cb) { }

View File

@ -544,15 +544,4 @@ static inline void thermal_notify_framework(struct thermal_zone_device *tz,
{ }
#endif /* CONFIG_THERMAL */
#if defined(CONFIG_NET) && IS_ENABLED(CONFIG_THERMAL)
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event);
#else
static inline int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event)
{
return 0;
}
#endif
#endif /* __THERMAL_H__ */

View File

@ -915,7 +915,7 @@ static int __init sugov_register(void)
{
return cpufreq_register_governor(&schedutil_gov);
}
fs_initcall(sugov_register);
core_initcall(sugov_register);
#ifdef CONFIG_ENERGY_MODEL
extern bool sched_energy_update;