Commit fb3da48a authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 5ecc9d15 163b00cd
# 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>;
};
...
* 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>;
};
# 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>;
};
...
......@@ -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)
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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)
{
......
......@@ -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)
{
......
......@@ -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)
{
......
......@@ -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);
......@@ -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
......
......@@ -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
......
......@@ -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);
......@@ -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
......
......@@ -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
......
......@@ -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)
{
......
......@@ -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"
......
......@@ -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
// 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");
This diff is collapsed.
......@@ -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);
......
This diff is collapsed.
......@@ -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),
......
......@@ -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,
};
......@@ -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),
......
......@@ -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);
......
This diff is collapsed.
......@@ -14,6 +14,15 @@
#include "thermal_core.h"
#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);
}
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 */
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 */
......
......@@ -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,
......
......@@ -134,6 +134,7 @@ 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);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "IIO channel not found: %d\n", ret);
return ret;
}
......@@ -142,7 +143,9 @@ 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",
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Thermal zone sensor register failed: %d\n",
ret);
return ret;
}
......
......@@ -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);
......@@ -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),
},
};
......
......@@ -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__ */
......@@ -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) { }
......
......@@ -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__ */
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment