1
0
Fork 0

Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal

Pull thermal soc updates from Eduardo Valentin:

 - thermal core has a new devm_* API for registering cooling devices. I
   took the entire series, that is why you see changes on drivers/hwmon
   in this pull (Guenter Roeck)

 - rockchip thermal driver gains support to PX30 SoC (Elaine Zhang)

 - the generic-adc thermal driver now considers the lookup table DT
   property as optional (Jean-Francois Dagenais)

 - Refactoring of tsens thermal driver (Amit Kucheria)

 - Cleanups on cpu cooling driver (Daniel Lezcano)

 - broadcom thermal driver dropped support to ACPI (Srinath Mannam)

 - tegra thermal driver gains support to OC hw throttle and GPU throtle
   (Wei Ni)

 - Fixes in several thermal drivers.

* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (59 commits)
  hwmon: (pwm-fan) Use devm_thermal_of_cooling_device_register
  hwmon: (npcm750-pwm-fan) Use devm_thermal_of_cooling_device_register
  hwmon: (mlxreg-fan) Use devm_thermal_of_cooling_device_register
  hwmon: (gpio-fan) Use devm_thermal_of_cooling_device_register
  hwmon: (aspeed-pwm-tacho) Use devm_thermal_of_cooling_device_register
  thermal: rcar_gen3_thermal: Fix to show correct trip points number
  thermal: rcar_thermal: update calculation formula for R-Car Gen3 SoCs
  thermal: cpu_cooling: Actually trace CPU load in thermal_power_cpu_get_power
  thermal: rockchip: Support the PX30 SoC in thermal driver
  dt-bindings: rockchip-thermal: Support the PX30 SoC compatible
  thermal: rockchip: fix up the tsadc pinctrl setting error
  thermal: broadcom: Remove ACPI support
  thermal: Fix build error of missing devm_ioremap_resource on UM
  thermal/drivers/cpu_cooling: Remove pointless field
  thermal/drivers/cpu_cooling: Add Software Package Data Exchange (SPDX)
  thermal/drivers/cpu_cooling: Fixup the header and copyright
  thermal/drivers/cpu_cooling: Remove pointless test in power2state()
  thermal: rcar_gen3_thermal: disable interrupt in .remove
  thermal: rcar_gen3_thermal: fix interrupt type
  thermal: Introduce devm_thermal_of_cooling_device_register
  ...
hifive-unleashed-5.2
Linus Torvalds 2019-05-16 07:56:57 -07:00
commit a455eda33f
42 changed files with 2344 additions and 610 deletions

View File

@ -0,0 +1,33 @@
Amazon's Annapurna Labs Thermal Sensor
Simple thermal device that allows temperature reading by a single MMIO
transaction.
Required properties:
- compatible: "amazon,al-thermal".
- reg: The physical base address and length of the sensor's registers.
- #thermal-sensor-cells: Must be 1. See ./thermal.txt for a description.
Example:
thermal: thermal {
compatible = "amazon,al-thermal";
reg = <0x0 0x05002860 0x0 0x1>;
#thermal-sensor-cells = <0x1>;
};
thermal-zones {
thermal-z0 {
polling-delay-passive = <250>;
polling-delay = <1000>;
thermal-sensors = <&thermal 0>;
trips {
critical {
temperature = <105000>;
hysteresis = <2000>;
type = "critical";
};
};
};
};

View File

@ -52,13 +52,47 @@ Required properties :
Must set as following values:
TEGRA_SOCTHERM_THROT_LEVEL_LOW, TEGRA_SOCTHERM_THROT_LEVEL_MED
TEGRA_SOCTHERM_THROT_LEVEL_HIGH, TEGRA_SOCTHERM_THROT_LEVEL_NONE
- nvidia,gpu-throt-level: This property is for Tegra124 and Tegra210.
It is the level of pulse skippers, which used to throttle clock
frequencies. It indicates gpu clock throttling depth and can be
programmed to any of the following values which represent a throttling
percentage:
TEGRA_SOCTHERM_THROT_LEVEL_NONE (0%)
TEGRA_SOCTHERM_THROT_LEVEL_LOW (50%),
TEGRA_SOCTHERM_THROT_LEVEL_MED (75%),
TEGRA_SOCTHERM_THROT_LEVEL_HIGH (85%).
- #cooling-cells: Should be 1. This cooling device only support on/off state.
See ./thermal.txt for a description of this property.
Optional properties: The following properties are T210 specific and
valid only for OCx throttle events.
- nvidia,count-threshold: Specifies the number of OC events that are
required for triggering an interrupt. Interrupts are not triggered if
the property is missing. A value of 0 will interrupt on every OC alarm.
- nvidia,polarity-active-low: Configures the polarity of the OC alaram
signal. If present, this means assert low, otherwise assert high.
- nvidia,alarm-filter: Number of clocks to filter event. When the filter
expires (which means the OC event has not occurred for a long time),
the counter is cleared and filter is rearmed. Default value is 0.
- nvidia,throttle-period-us: Specifies the number of uSec for which
throttling is engaged after the OC event is deasserted. Default value
is 0.
Optional properties:
- nvidia,thermtrips : When present, this property specifies the temperature at
which the soctherm hardware will assert the thermal trigger signal to the
Power Management IC, which can be configured to reset or shutdown the device.
It is an array of pairs where each pair represents a tsensor id followed by a
temperature in milli Celcius. In the absence of this property the critical
trip point will be used for thermtrip temperature.
Note:
- the "critical" type trip points will be set to SOC_THERM hardware as the
shut down temperature. Once the temperature of this thermal zone is higher
than it, the system will be shutdown or reset by hardware.
- the "critical" type trip points will be used to set the temperature at which
the SOC_THERM hardware will assert a thermal trigger if the "nvidia,thermtrips"
property is missing. When the thermtrips property is present, the breach of a
critical trip point is reported back to the thermal framework to implement
software shutdown.
- the "hot" type trip points will be set to SOC_THERM hardware as the throttle
temperature. Once the the temperature of this thermal zone is higher
than it, it will trigger the HW throttle event.
@ -79,25 +113,32 @@ Example :
#thermal-sensor-cells = <1>;
nvidia,thermtrips = <TEGRA124_SOCTHERM_SENSOR_CPU 102500
TEGRA124_SOCTHERM_SENSOR_GPU 103000>;
throttle-cfgs {
/*
* When the "heavy" cooling device triggered,
* the HW will skip cpu clock's pulse in 85% depth
* the HW will skip cpu clock's pulse in 85% depth,
* skip gpu clock's pulse in 85% level
*/
throttle_heavy: heavy {
nvidia,priority = <100>;
nvidia,cpu-throt-percent = <85>;
nvidia,gpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>;
#cooling-cells = <1>;
};
/*
* When the "light" cooling device triggered,
* the HW will skip cpu clock's pulse in 50% depth
* the HW will skip cpu clock's pulse in 50% depth,
* skip gpu clock's pulse in 50% level
*/
throttle_light: light {
nvidia,priority = <80>;
nvidia,cpu-throt-percent = <50>;
nvidia,gpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_LOW>;
#cooling-cells = <1>;
};
@ -107,6 +148,17 @@ Example :
* arbiter will select the highest priority as the final throttle
* settings to skip cpu pulse.
*/
throttle_oc1: oc1 {
nvidia,priority = <50>;
nvidia,polarity-active-low;
nvidia,count-threshold = <100>;
nvidia,alarm-filter = <5100000>;
nvidia,throttle-period-us = <0>;
nvidia,cpu-throt-percent = <75>;
nvidia,gpu-throt-level =
<TEGRA_SOCTHERM_THROT_LEVEL_MED>;
};
};
};

View File

@ -6,11 +6,14 @@ Required properties:
- "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
@ -39,3 +42,14 @@ tsens0: thermal-sensor@c263000 {
#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

@ -2,6 +2,7 @@
Required properties:
- compatible : should be "rockchip,<name>-tsadc"
"rockchip,px30-tsadc": found on PX30 SoCs
"rockchip,rv1108-tsadc": found on RV1108 SoCs
"rockchip,rk3228-tsadc": found on RK3228 SoCs
"rockchip,rk3288-tsadc": found on RK3288 SoCs

View File

@ -8,16 +8,22 @@ temperature using voltage-temperature lookup table.
Required properties:
===================
- compatible: Must be "generic-adc-thermal".
- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description
of this property.
Optional properties:
===================
- temperature-lookup-table: Two dimensional array of Integer; lookup table
to map the relation between ADC value and
temperature. When ADC is read, the value is
looked up on the table to get the equivalent
temperature.
The first value of the each row of array is the
temperature in milliCelsius and second value of
the each row of array is the ADC read value.
- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description
of this property.
If not specified, driver assumes the ADC channel
gives milliCelsius directly.
Example :
#include <dt-bindings/thermal/thermal.h>

View File

@ -742,6 +742,12 @@ F: drivers/tty/serial/altera_jtaguart.c
F: include/linux/altera_uart.h
F: include/linux/altera_jtaguart.h
AMAZON ANNAPURNA LABS THERMAL MMIO DRIVER
M: Talel Shenhar <talel@amazon.com>
S: Maintained
F: Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt
F: drivers/thermal/thermal_mmio.c
AMAZON ETHERNET DRIVERS
M: Netanel Belgazal <netanel@amazon.com>
R: Saeed Bishara <saeedb@amazon.com>

View File

@ -830,10 +830,8 @@ static int aspeed_create_pwm_cooling(struct device *dev,
}
snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port);
cdev->tcdev = thermal_of_cooling_device_register(child,
cdev->name,
cdev,
&aspeed_pwm_cool_ops);
cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child,
cdev->name, cdev, &aspeed_pwm_cool_ops);
if (IS_ERR(cdev->tcdev))
return PTR_ERR(cdev->tcdev);

View File

@ -498,6 +498,11 @@ static const struct of_device_id of_gpio_fan_match[] = {
};
MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
static void gpio_fan_stop(void *data)
{
set_fan_speed(data, 0);
}
static int gpio_fan_probe(struct platform_device *pdev)
{
int err;
@ -532,6 +537,7 @@ static int gpio_fan_probe(struct platform_device *pdev)
err = fan_ctrl_init(fan_data);
if (err)
return err;
devm_add_action_or_reset(dev, gpio_fan_stop, fan_data);
}
/* Make this driver part of hwmon class. */
@ -543,32 +549,20 @@ static int gpio_fan_probe(struct platform_device *pdev)
return PTR_ERR(fan_data->hwmon_dev);
/* Optional cooling device register for Device tree platforms */
fan_data->cdev = thermal_of_cooling_device_register(np,
"gpio-fan",
fan_data,
&gpio_fan_cool_ops);
fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
"gpio-fan", fan_data, &gpio_fan_cool_ops);
dev_info(dev, "GPIO fan initialized\n");
return 0;
}
static int gpio_fan_remove(struct platform_device *pdev)
static void gpio_fan_shutdown(struct platform_device *pdev)
{
struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
if (!IS_ERR(fan_data->cdev))
thermal_cooling_device_unregister(fan_data->cdev);
if (fan_data->gpios)
set_fan_speed(fan_data, 0);
return 0;
}
static void gpio_fan_shutdown(struct platform_device *pdev)
{
gpio_fan_remove(pdev);
}
#ifdef CONFIG_PM_SLEEP
@ -602,7 +596,6 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
.remove = gpio_fan_remove,
.shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",

View File

@ -465,42 +465,42 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
static int mlxreg_fan_probe(struct platform_device *pdev)
{
struct mlxreg_core_platform_data *pdata;
struct device *dev = &pdev->dev;
struct mlxreg_fan *fan;
struct device *hwm;
int err;
pdata = dev_get_platdata(&pdev->dev);
pdata = dev_get_platdata(dev);
if (!pdata) {
dev_err(&pdev->dev, "Failed to get platform data.\n");
dev_err(dev, "Failed to get platform data.\n");
return -EINVAL;
}
fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL);
if (!fan)
return -ENOMEM;
fan->dev = &pdev->dev;
fan->dev = dev;
fan->regmap = pdata->regmap;
platform_set_drvdata(pdev, fan);
err = mlxreg_fan_config(fan, pdata);
if (err)
return err;
hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan",
hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan",
fan,
&mlxreg_fan_hwmon_chip_info,
NULL);
if (IS_ERR(hwm)) {
dev_err(&pdev->dev, "Failed to register hwmon device\n");
dev_err(dev, "Failed to register hwmon device\n");
return PTR_ERR(hwm);
}
if (IS_REACHABLE(CONFIG_THERMAL)) {
fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan,
&mlxreg_fan_cooling_ops);
fan->cdev = devm_thermal_of_cooling_device_register(dev,
NULL, "mlxreg_fan", fan, &mlxreg_fan_cooling_ops);
if (IS_ERR(fan->cdev)) {
dev_err(&pdev->dev, "Failed to register cooling device\n");
dev_err(dev, "Failed to register cooling device\n");
return PTR_ERR(fan->cdev);
}
}
@ -508,22 +508,11 @@ static int mlxreg_fan_probe(struct platform_device *pdev)
return 0;
}
static int mlxreg_fan_remove(struct platform_device *pdev)
{
struct mlxreg_fan *fan = platform_get_drvdata(pdev);
if (IS_REACHABLE(CONFIG_THERMAL))
thermal_cooling_device_unregister(fan->cdev);
return 0;
}
static struct platform_driver mlxreg_fan_driver = {
.driver = {
.name = "mlxreg-fan",
},
.probe = mlxreg_fan_probe,
.remove = mlxreg_fan_remove,
};
module_platform_driver(mlxreg_fan_driver);

View File

@ -846,10 +846,8 @@ static int npcm7xx_create_pwm_cooling(struct device *dev,
snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child,
pwm_port);
cdev->tcdev = thermal_of_cooling_device_register(child,
cdev->name,
cdev,
&npcm7xx_pwm_cool_ops);
cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child,
cdev->name, cdev, &npcm7xx_pwm_cool_ops);
if (IS_ERR(cdev->tcdev))
return PTR_ERR(cdev->tcdev);

View File

@ -273,27 +273,40 @@ static int pwm_fan_of_get_cooling_data(struct device *dev,
return 0;
}
static void pwm_fan_regulator_disable(void *data)
{
regulator_disable(data);
}
static void pwm_fan_pwm_disable(void *__ctx)
{
struct pwm_fan_ctx *ctx = __ctx;
pwm_disable(ctx->pwm);
del_timer_sync(&ctx->rpm_timer);
}
static int pwm_fan_probe(struct platform_device *pdev)
{
struct thermal_cooling_device *cdev;
struct device *dev = &pdev->dev;
struct pwm_fan_ctx *ctx;
struct device *hwmon;
int ret;
struct pwm_state state = { };
u32 ppr = 2;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mutex_init(&ctx->lock);
ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL);
if (IS_ERR(ctx->pwm)) {
ret = PTR_ERR(ctx->pwm);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Could not get PWM: %d\n", ret);
dev_err(dev, "Could not get PWM: %d\n", ret);
return ret;
}
@ -304,7 +317,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (ctx->irq == -EPROBE_DEFER)
return ctx->irq;
ctx->reg_en = devm_regulator_get_optional(&pdev->dev, "fan");
ctx->reg_en = devm_regulator_get_optional(dev, "fan");
if (IS_ERR(ctx->reg_en)) {
if (PTR_ERR(ctx->reg_en) != -ENODEV)
return PTR_ERR(ctx->reg_en);
@ -313,10 +326,11 @@ static int pwm_fan_probe(struct platform_device *pdev)
} else {
ret = regulator_enable(ctx->reg_en);
if (ret) {
dev_err(&pdev->dev,
"Failed to enable fan supply: %d\n", ret);
dev_err(dev, "Failed to enable fan supply: %d\n", ret);
return ret;
}
devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
ctx->reg_en);
}
ctx->pwm_value = MAX_PWM;
@ -328,91 +342,57 @@ static int pwm_fan_probe(struct platform_device *pdev)
ret = pwm_apply_state(ctx->pwm, &state);
if (ret) {
dev_err(&pdev->dev, "Failed to configure PWM: %d\n", ret);
goto err_reg_disable;
dev_err(dev, "Failed to configure PWM: %d\n", ret);
return ret;
}
timer_setup(&ctx->rpm_timer, sample_timer, 0);
devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx);
of_property_read_u32(pdev->dev.of_node, "pulses-per-revolution", &ppr);
of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr);
ctx->pulses_per_revolution = ppr;
if (!ctx->pulses_per_revolution) {
dev_err(&pdev->dev, "pulses-per-revolution can't be zero.\n");
ret = -EINVAL;
goto err_pwm_disable;
dev_err(dev, "pulses-per-revolution can't be zero.\n");
return -EINVAL;
}
if (ctx->irq > 0) {
ret = devm_request_irq(&pdev->dev, ctx->irq, pulse_handler, 0,
ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
pdev->name, ctx);
if (ret) {
dev_err(&pdev->dev,
"Failed to request interrupt: %d\n", ret);
goto err_pwm_disable;
dev_err(dev, "Failed to request interrupt: %d\n", ret);
return ret;
}
ctx->sample_start = ktime_get();
mod_timer(&ctx->rpm_timer, jiffies + HZ);
}
hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan",
ctx, pwm_fan_groups);
if (IS_ERR(hwmon)) {
ret = PTR_ERR(hwmon);
dev_err(&pdev->dev,
"Failed to register hwmon device: %d\n", ret);
goto err_del_timer;
dev_err(dev, "Failed to register hwmon device\n");
return PTR_ERR(hwmon);
}
ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
ret = pwm_fan_of_get_cooling_data(dev, ctx);
if (ret)
goto err_del_timer;
return ret;
ctx->pwm_fan_state = ctx->pwm_fan_max_state;
if (IS_ENABLED(CONFIG_THERMAL)) {
cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
"pwm-fan", ctx,
&pwm_fan_cooling_ops);
cdev = devm_thermal_of_cooling_device_register(dev,
dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops);
if (IS_ERR(cdev)) {
ret = PTR_ERR(cdev);
dev_err(&pdev->dev,
dev_err(dev,
"Failed to register pwm-fan as cooling device: %d\n",
ret);
goto err_del_timer;
return ret;
}
ctx->cdev = cdev;
thermal_cdev_update(cdev);
}
return 0;
err_del_timer:
del_timer_sync(&ctx->rpm_timer);
err_pwm_disable:
state.enabled = false;
pwm_apply_state(ctx->pwm, &state);
err_reg_disable:
if (ctx->reg_en)
regulator_disable(ctx->reg_en);
return ret;
}
static int pwm_fan_remove(struct platform_device *pdev)
{
struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
thermal_cooling_device_unregister(ctx->cdev);
del_timer_sync(&ctx->rpm_timer);
if (ctx->pwm_value)
pwm_disable(ctx->pwm);
if (ctx->reg_en)
regulator_disable(ctx->reg_en);
return 0;
}
#ifdef CONFIG_PM_SLEEP
@ -480,7 +460,6 @@ MODULE_DEVICE_TABLE(of, of_pwm_fan_match);
static struct platform_driver pwm_fan_driver = {
.probe = pwm_fan_probe,
.remove = pwm_fan_remove,
.driver = {
.name = "pwm-fan",
.pm = &pwm_fan_pm,

View File

@ -200,6 +200,17 @@ config THERMAL_EMULATION
because userland can easily disable the thermal policy by simply
flooding this sysfs node with low temperature values.
config THERMAL_MMIO
tristate "Generic Thermal MMIO driver"
depends on OF || COMPILE_TEST
depends on HAS_IOMEM
help
This option enables the generic thermal MMIO driver that will use
memory-mapped reads to get the temperature. Any HW/System that
allows temperature reading by a single memory-mapped reading, be it
register or shared memory, is a potential candidate to work with this
driver.
config HISI_THERMAL
tristate "Hisilicon thermal driver"
depends on ARCH_HISI || COMPILE_TEST

View File

@ -29,6 +29,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
# platform thermal drivers
obj-y += broadcom/
obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o

View File

@ -3,7 +3,6 @@
* Copyright (C) 2018 Broadcom
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@ -100,18 +99,11 @@ static const struct of_device_id sr_thermal_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sr_thermal_of_match);
static const struct acpi_device_id sr_thermal_acpi_ids[] = {
{ .id = "BRCM0500" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, sr_thermal_acpi_ids);
static struct platform_driver sr_thermal_driver = {
.probe = sr_thermal_probe,
.driver = {
.name = "sr-thermal",
.of_match_table = sr_thermal_of_match,
.acpi_match_table = ACPI_PTR(sr_thermal_acpi_ids),
},
};
module_platform_driver(sr_thermal_driver);

View File

@ -1,26 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/drivers/thermal/cpu_cooling.c
*
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
*
* Copyright (C) 2014 Viresh Kumar <viresh.kumar@linaro.org>
* Copyright (C) 2012-2018 Linaro Limited.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
* Authors: Amit Daniel <amit.kachhap@linaro.org>
* Viresh Kumar <viresh.kumar@linaro.org>
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/module.h>
#include <linux/thermal.h>
@ -99,7 +87,6 @@ struct cpufreq_cooling_device {
unsigned int clipped_freq;
unsigned int max_level;
struct freq_table *freq_table; /* In descending order */
struct thermal_cooling_device *cdev;
struct cpufreq_policy *policy;
struct list_head node;
struct time_in_idle *idle_time;
@ -207,8 +194,7 @@ static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev,
dev = get_cpu_device(cpu);
if (unlikely(!dev)) {
dev_warn(&cpufreq_cdev->cdev->device,
"No cpu device for cpu %d\n", cpu);
pr_warn("No cpu device for cpu %d\n", cpu);
return -ENODEV;
}
@ -458,7 +444,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev,
load = 0;
total_load += load;
if (trace_thermal_power_cpu_limit_enabled() && load_cpu)
if (load_cpu)
load_cpu[i] = load;
i++;
@ -541,7 +527,6 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev,
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
struct cpufreq_policy *policy = cpufreq_cdev->policy;
power = power > 0 ? power : 0;
last_load = cpufreq_cdev->last_load ?: 1;
normalised_power = (power * 100) / last_load;
target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power);
@ -692,7 +677,6 @@ __cpufreq_cooling_register(struct device_node *np,
goto remove_ida;
cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency;
cpufreq_cdev->cdev = cdev;
mutex_lock(&cooling_list_lock);
/* Register the notifier for first cpufreq cooling device */
@ -810,7 +794,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
thermal_cooling_device_unregister(cpufreq_cdev->cdev);
thermal_cooling_device_unregister(cdev);
ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id);
kfree(cpufreq_cdev->idle_time);
kfree(cpufreq_cdev->freq_table);

View File

@ -5,6 +5,9 @@
* Copyright (C) 2013 Texas Instruments
* Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/thermal.h>
#include <linux/slab.h>
#include <linux/types.h>

View File

@ -1,3 +1,5 @@
obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-v2.o
qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \
tsens-8960.o tsens-v2.o tsens-v1.o
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o

View File

@ -1,105 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <linux/platform_device.h>
#include "tsens.h"
/* eeprom layout data for 8916 */
#define BASE0_MASK 0x0000007f
#define BASE1_MASK 0xfe000000
#define BASE0_SHIFT 0
#define BASE1_SHIFT 25
#define S0_P1_MASK 0x00000f80
#define S1_P1_MASK 0x003e0000
#define S2_P1_MASK 0xf8000000
#define S3_P1_MASK 0x000003e0
#define S4_P1_MASK 0x000f8000
#define S0_P2_MASK 0x0001f000
#define S1_P2_MASK 0x07c00000
#define S2_P2_MASK 0x0000001f
#define S3_P2_MASK 0x00007c00
#define S4_P2_MASK 0x01f00000
#define S0_P1_SHIFT 7
#define S1_P1_SHIFT 17
#define S2_P1_SHIFT 27
#define S3_P1_SHIFT 5
#define S4_P1_SHIFT 15
#define S0_P2_SHIFT 12
#define S1_P2_SHIFT 22
#define S2_P2_SHIFT 0
#define S3_P2_SHIFT 10
#define S4_P2_SHIFT 20
#define CAL_SEL_MASK 0xe0000000
#define CAL_SEL_SHIFT 29
static int calibrate_8916(struct tsens_device *tmdev)
{
int base0 = 0, base1 = 0, i;
u32 p1[5], p2[5];
int mode = 0;
u32 *qfprom_cdata, *qfprom_csel;
qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel");
if (IS_ERR(qfprom_csel))
return PTR_ERR(qfprom_csel);
mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
for (i = 0; i < tmdev->num_sensors; i++)
p2[i] = ((base1 + p2[i]) << 3);
/* Fall through */
case ONE_PT_CALIB2:
base0 = (qfprom_cdata[0] & BASE0_MASK);
p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
for (i = 0; i < tmdev->num_sensors; i++)
p1[i] = (((base0) + p1[i]) << 3);
break;
default:
for (i = 0; i < tmdev->num_sensors; i++) {
p1[i] = 500;
p2[i] = 780;
}
break;
}
compute_intercept_slope(tmdev, p1, p2, mode);
return 0;
}
static const struct tsens_ops ops_8916 = {
.init = init_common,
.calibrate = calibrate_8916,
.get_temp = get_temp_common,
};
const struct tsens_data data_8916 = {
.num_sensors = 5,
.ops = &ops_8916,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 },
.hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
};

View File

@ -56,21 +56,21 @@
#define TRDY_MASK BIT(7)
#define TIMEOUT_US 100
static int suspend_8960(struct tsens_device *tmdev)
static int suspend_8960(struct tsens_priv *priv)
{
int ret;
unsigned int mask;
struct regmap *map = tmdev->tm_map;
struct regmap *map = priv->tm_map;
ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold);
if (ret)
return ret;
ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control);
if (ret)
return ret;
if (tmdev->num_sensors > 1)
if (priv->num_sensors > 1)
mask = SLP_CLK_ENA | EN;
else
mask = SLP_CLK_ENA_8660 | EN;
@ -82,10 +82,10 @@ static int suspend_8960(struct tsens_device *tmdev)
return 0;
}
static int resume_8960(struct tsens_device *tmdev)
static int resume_8960(struct tsens_priv *priv)
{
int ret;
struct regmap *map = tmdev->tm_map;
struct regmap *map = priv->tm_map;
ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
if (ret)
@ -95,80 +95,80 @@ static int resume_8960(struct tsens_device *tmdev)
* Separate CONFIG restore is not needed only for 8660 as
* config is part of CTRL Addr and its restored as such
*/
if (tmdev->num_sensors > 1) {
if (priv->num_sensors > 1) {
ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
if (ret)
return ret;
}
ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold);
if (ret)
return ret;
ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
ret = regmap_write(map, CNTL_ADDR, priv->ctx.control);
if (ret)
return ret;
return 0;
}
static int enable_8960(struct tsens_device *tmdev, int id)
static int enable_8960(struct tsens_priv *priv, int id)
{
int ret;
u32 reg, mask;
ret = regmap_read(tmdev->tm_map, CNTL_ADDR, &reg);
ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg);
if (ret)
return ret;
mask = BIT(id + SENSOR0_SHIFT);
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg | SW_RST);
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
if (ret)
return ret;
if (tmdev->num_sensors > 1)
if (priv->num_sensors > 1)
reg |= mask | SLP_CLK_ENA | EN;
else
reg |= mask | SLP_CLK_ENA_8660 | EN;
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg);
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg);
if (ret)
return ret;
return 0;
}
static void disable_8960(struct tsens_device *tmdev)
static void disable_8960(struct tsens_priv *priv)
{
int ret;
u32 reg_cntl;
u32 mask;
mask = GENMASK(tmdev->num_sensors - 1, 0);
mask = GENMASK(priv->num_sensors - 1, 0);
mask <<= SENSOR0_SHIFT;
mask |= EN;
ret = regmap_read(tmdev->tm_map, CNTL_ADDR, &reg_cntl);
ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
if (ret)
return;
reg_cntl &= ~mask;
if (tmdev->num_sensors > 1)
if (priv->num_sensors > 1)
reg_cntl &= ~SLP_CLK_ENA;
else
reg_cntl &= ~SLP_CLK_ENA_8660;
regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
}
static int init_8960(struct tsens_device *tmdev)
static int init_8960(struct tsens_priv *priv)
{
int ret, i;
u32 reg_cntl;
tmdev->tm_map = dev_get_regmap(tmdev->dev, NULL);
if (!tmdev->tm_map)
priv->tm_map = dev_get_regmap(priv->dev, NULL);
if (!priv->tm_map)
return -ENODEV;
/*
@ -177,21 +177,21 @@ static int init_8960(struct tsens_device *tmdev)
* but the control registers stay in the same place, i.e
* directly after the first 5 status registers.
*/
for (i = 0; i < tmdev->num_sensors; i++) {
for (i = 0; i < priv->num_sensors; i++) {
if (i >= 5)
tmdev->sensor[i].status = S0_STATUS_ADDR + 40;
tmdev->sensor[i].status += i * 4;
priv->sensor[i].status = S0_STATUS_ADDR + 40;
priv->sensor[i].status += i * 4;
}
reg_cntl = SW_RST;
ret = regmap_update_bits(tmdev->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
if (ret)
return ret;
if (tmdev->num_sensors > 1) {
if (priv->num_sensors > 1) {
reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
reg_cntl &= ~SW_RST;
ret = regmap_update_bits(tmdev->tm_map, CONFIG_ADDR,
ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR,
CONFIG_MASK, CONFIG);
} else {
reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
@ -199,30 +199,30 @@ static int init_8960(struct tsens_device *tmdev)
reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
}
reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT;
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
if (ret)
return ret;
reg_cntl |= EN;
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
if (ret)
return ret;
return 0;
}
static int calibrate_8960(struct tsens_device *tmdev)
static int calibrate_8960(struct tsens_priv *priv)
{
int i;
char *data;
ssize_t num_read = tmdev->num_sensors;
struct tsens_sensor *s = tmdev->sensor;
ssize_t num_read = priv->num_sensors;
struct tsens_sensor *s = priv->sensor;
data = qfprom_read(tmdev->dev, "calib");
data = qfprom_read(priv->dev, "calib");
if (IS_ERR(data))
data = qfprom_read(tmdev->dev, "calib_backup");
data = qfprom_read(priv->dev, "calib_backup");
if (IS_ERR(data))
return PTR_ERR(data);
@ -243,21 +243,21 @@ 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_device *tmdev, int id, int *temp)
static int get_temp_8960(struct tsens_priv *priv, int id, int *temp)
{
int ret;
u32 code, trdy;
const struct tsens_sensor *s = &tmdev->sensor[id];
const struct tsens_sensor *s = &priv->sensor[id];
unsigned long timeout;
timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
do {
ret = regmap_read(tmdev->tm_map, INT_STATUS_ADDR, &trdy);
ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy);
if (ret)
return ret;
if (!(trdy & TRDY_MASK))
continue;
ret = regmap_read(tmdev->tm_map, s->status, &code);
ret = regmap_read(priv->tm_map, s->status, &code);
if (ret)
return ret;
*temp = code_to_mdegC(code, s);
@ -277,7 +277,7 @@ static const struct tsens_ops ops_8960 = {
.resume = resume_8960,
};
const struct tsens_data data_8960 = {
const struct tsens_plat_data data_8960 = {
.num_sensors = 11,
.ops = &ops_8960,
};

View File

@ -12,18 +12,6 @@
#include <linux/regmap.h>
#include "tsens.h"
/* SROT */
#define TSENS_EN BIT(0)
/* TM */
#define STATUS_OFFSET 0x30
#define SN_ADDR_OFFSET 0x4
#define SN_ST_TEMP_MASK 0x3ff
#define CAL_DEGC_PT1 30
#define CAL_DEGC_PT2 120
#define SLOPE_FACTOR 1000
#define SLOPE_DEFAULT 3200
char *qfprom_read(struct device *dev, const char *cname)
{
struct nvmem_cell *cell;
@ -46,18 +34,18 @@ char *qfprom_read(struct device *dev, const char *cname)
* and offset values are derived from tz->tzp->slope and tz->tzp->offset
* resp.
*/
void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
u32 *p2, u32 mode)
{
int i;
int num, den;
for (i = 0; i < tmdev->num_sensors; i++) {
dev_dbg(tmdev->dev,
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]);
tmdev->sensor[i].slope = SLOPE_DEFAULT;
priv->sensor[i].slope = SLOPE_DEFAULT;
if (mode == TWO_PT_CALIB) {
/*
* slope (m) = adc_code2 - adc_code1 (y2 - y1)/
@ -66,16 +54,30 @@ void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
num = p2[i] - p1[i];
num *= SLOPE_FACTOR;
den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
tmdev->sensor[i].slope = num / den;
priv->sensor[i].slope = num / den;
}
tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
(CAL_DEGC_PT1 *
tmdev->sensor[i].slope);
dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
priv->sensor[i].slope);
dev_dbg(priv->dev, "offset:%d\n", priv->sensor[i].offset);
}
}
bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id)
{
u32 val;
int ret;
if ((hw_id > (priv->num_sensors - 1)) || (hw_id < 0))
return -EINVAL;
ret = regmap_field_read(priv->rf[SENSOR_EN], &val);
if (ret)
return ret;
return val & (1 << hw_id);
}
static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
{
int degc, num, den;
@ -95,18 +97,54 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
return degc;
}
int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp)
{
struct tsens_sensor *s = &tmdev->sensor[id];
u32 code;
unsigned int status_reg;
int last_temp = 0, ret;
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;
int ret;
status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET;
ret = regmap_read(tmdev->tm_map, status_reg, &code);
ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
while (!valid) {
/* Valid bit is 0 for 6 AHB clock cycles.
* At 19.2MHz, 1 AHB clock is ~60ns.
* We should enter this loop very, very rarely.
*/
ndelay(400);
ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
}
/* 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;
}
return 0;
}
int get_temp_common(struct tsens_priv *priv, int i, int *temp)
{
struct tsens_sensor *s = &priv->sensor[i];
int last_temp = 0, ret;
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp);
if (ret)
return ret;
last_temp = code & SN_ST_TEMP_MASK;
*temp = code_to_degc(last_temp, s) * 1000;
@ -127,21 +165,21 @@ static const struct regmap_config tsens_srot_config = {
.reg_stride = 4,
};
int __init init_common(struct tsens_device *tmdev)
int __init init_common(struct tsens_priv *priv)
{
void __iomem *tm_base, *srot_base;
struct device *dev = priv->dev;
struct resource *res;
u32 code;
int ret;
struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node);
u16 ctrl_offset = tmdev->reg_offsets[SROT_CTRL_OFFSET];
u32 enabled;
int ret, i, j;
struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
if (!op)
return -EINVAL;
if (op->num_resources > 1) {
/* DT with separate SROT and TM address space */
tmdev->tm_offset = 0;
priv->tm_offset = 0;
res = platform_get_resource(op, IORESOURCE_MEM, 1);
srot_base = devm_ioremap_resource(&op->dev, res);
if (IS_ERR(srot_base)) {
@ -149,16 +187,15 @@ int __init init_common(struct tsens_device *tmdev)
goto err_put_device;
}
tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base,
priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
&tsens_srot_config);
if (IS_ERR(tmdev->srot_map)) {
ret = PTR_ERR(tmdev->srot_map);
if (IS_ERR(priv->srot_map)) {
ret = PTR_ERR(priv->srot_map);
goto err_put_device;
}
} else {
/* old DTs where SROT and TM were in a contiguous 2K block */
tmdev->tm_offset = 0x1000;
priv->tm_offset = 0x1000;
}
res = platform_get_resource(op, IORESOURCE_MEM, 0);
@ -168,19 +205,47 @@ int __init init_common(struct tsens_device *tmdev)
goto err_put_device;
}
tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config);
if (IS_ERR(tmdev->tm_map)) {
ret = PTR_ERR(tmdev->tm_map);
priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
if (IS_ERR(priv->tm_map)) {
ret = PTR_ERR(priv->tm_map);
goto err_put_device;
}
if (tmdev->srot_map) {
ret = regmap_read(tmdev->srot_map, ctrl_offset, &code);
if (ret)
priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[TSENS_EN]);
if (IS_ERR(priv->rf[TSENS_EN])) {
ret = PTR_ERR(priv->rf[TSENS_EN]);
goto err_put_device;
}
ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
if (ret)
goto err_put_device;
if (!enabled) {
dev_err(dev, "tsens device is not enabled\n");
ret = -ENODEV;
goto err_put_device;
}
priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[SENSOR_EN]);
if (IS_ERR(priv->rf[SENSOR_EN])) {
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;
if (!(code & TSENS_EN)) {
dev_err(tmdev->dev, "tsens device is not enabled\n");
ret = -ENODEV;
}
}
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;
}
}

View File

@ -6,6 +6,48 @@
#include <linux/platform_device.h>
#include "tsens.h"
/* ----- SROT ------ */
#define SROT_CTRL_OFF 0x0000
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0000
#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
#define TM_Sn_STATUS_OFF 0x0030
#define TM_TRDY_OFF 0x005c
/* eeprom layout data for 8916 */
#define MSM8916_BASE0_MASK 0x0000007f
#define MSM8916_BASE1_MASK 0xfe000000
#define MSM8916_BASE0_SHIFT 0
#define MSM8916_BASE1_SHIFT 25
#define MSM8916_S0_P1_MASK 0x00000f80
#define MSM8916_S1_P1_MASK 0x003e0000
#define MSM8916_S2_P1_MASK 0xf8000000
#define MSM8916_S3_P1_MASK 0x000003e0
#define MSM8916_S4_P1_MASK 0x000f8000
#define MSM8916_S0_P2_MASK 0x0001f000
#define MSM8916_S1_P2_MASK 0x07c00000
#define MSM8916_S2_P2_MASK 0x0000001f
#define MSM8916_S3_P2_MASK 0x00007c00
#define MSM8916_S4_P2_MASK 0x01f00000
#define MSM8916_S0_P1_SHIFT 7
#define MSM8916_S1_P1_SHIFT 17
#define MSM8916_S2_P1_SHIFT 27
#define MSM8916_S3_P1_SHIFT 5
#define MSM8916_S4_P1_SHIFT 15
#define MSM8916_S0_P2_SHIFT 12
#define MSM8916_S1_P2_SHIFT 22
#define MSM8916_S2_P2_SHIFT 0
#define MSM8916_S3_P2_SHIFT 10
#define MSM8916_S4_P2_SHIFT 20
#define MSM8916_CAL_SEL_MASK 0xe0000000
#define MSM8916_CAL_SEL_SHIFT 29
/* eeprom layout data for 8974 */
#define BASE1_MASK 0xff
#define S0_P1_MASK 0x3f00
@ -91,7 +133,59 @@
#define BIT_APPEND 0x3
static int calibrate_8974(struct tsens_device *tmdev)
static int calibrate_8916(struct tsens_priv *priv)
{
int base0 = 0, base1 = 0, i;
u32 p1[5], p2[5];
int mode = 0;
u32 *qfprom_cdata, *qfprom_csel;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
qfprom_csel = (u32 *)qfprom_read(priv->dev, "calib_sel");
if (IS_ERR(qfprom_csel))
return PTR_ERR(qfprom_csel);
mode = (qfprom_csel[0] & MSM8916_CAL_SEL_MASK) >> MSM8916_CAL_SEL_SHIFT;
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base1 = (qfprom_cdata[1] & MSM8916_BASE1_MASK) >> MSM8916_BASE1_SHIFT;
p2[0] = (qfprom_cdata[0] & MSM8916_S0_P2_MASK) >> MSM8916_S0_P2_SHIFT;
p2[1] = (qfprom_cdata[0] & MSM8916_S1_P2_MASK) >> MSM8916_S1_P2_SHIFT;
p2[2] = (qfprom_cdata[1] & MSM8916_S2_P2_MASK) >> MSM8916_S2_P2_SHIFT;
p2[3] = (qfprom_cdata[1] & MSM8916_S3_P2_MASK) >> MSM8916_S3_P2_SHIFT;
p2[4] = (qfprom_cdata[1] & MSM8916_S4_P2_MASK) >> MSM8916_S4_P2_SHIFT;
for (i = 0; i < priv->num_sensors; i++)
p2[i] = ((base1 + p2[i]) << 3);
/* Fall through */
case ONE_PT_CALIB2:
base0 = (qfprom_cdata[0] & MSM8916_BASE0_MASK);
p1[0] = (qfprom_cdata[0] & MSM8916_S0_P1_MASK) >> MSM8916_S0_P1_SHIFT;
p1[1] = (qfprom_cdata[0] & MSM8916_S1_P1_MASK) >> MSM8916_S1_P1_SHIFT;
p1[2] = (qfprom_cdata[0] & MSM8916_S2_P1_MASK) >> MSM8916_S2_P1_SHIFT;
p1[3] = (qfprom_cdata[1] & MSM8916_S3_P1_MASK) >> MSM8916_S3_P1_SHIFT;
p1[4] = (qfprom_cdata[1] & MSM8916_S4_P1_MASK) >> MSM8916_S4_P1_SHIFT;
for (i = 0; i < priv->num_sensors; i++)
p1[i] = (((base0) + p1[i]) << 3);
break;
default:
for (i = 0; i < priv->num_sensors; i++) {
p1[i] = 500;
p2[i] = 780;
}
break;
}
compute_intercept_slope(priv, p1, p2, mode);
return 0;
}
static int calibrate_8974(struct tsens_priv *priv)
{
int base1 = 0, base2 = 0, i;
u32 p1[11], p2[11];
@ -99,11 +193,11 @@ static int calibrate_8974(struct tsens_device *tmdev)
u32 *calib, *bkp;
u32 calib_redun_sel;
calib = (u32 *)qfprom_read(tmdev->dev, "calib");
calib = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(calib))
return PTR_ERR(calib);
bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup");
bkp = (u32 *)qfprom_read(priv->dev, "calib_backup");
if (IS_ERR(bkp))
return PTR_ERR(bkp);
@ -184,25 +278,25 @@ static int calibrate_8974(struct tsens_device *tmdev)
switch (mode) {
case ONE_PT_CALIB:
for (i = 0; i < tmdev->num_sensors; i++)
for (i = 0; i < priv->num_sensors; i++)
p1[i] += (base1 << 2) | BIT_APPEND;
break;
case TWO_PT_CALIB:
for (i = 0; i < tmdev->num_sensors; i++) {
for (i = 0; i < priv->num_sensors; i++) {
p2[i] += base2;
p2[i] <<= 2;
p2[i] |= BIT_APPEND;
}
/* Fall through */
case ONE_PT_CALIB2:
for (i = 0; i < tmdev->num_sensors; i++) {
for (i = 0; i < priv->num_sensors; i++) {
p1[i] += base1;
p1[i] <<= 2;
p1[i] |= BIT_APPEND;
}
break;
default:
for (i = 0; i < tmdev->num_sensors; i++)
for (i = 0; i < priv->num_sensors; i++)
p2[i] = 780;
p1[0] = 502;
p1[1] = 509;
@ -218,19 +312,71 @@ static int calibrate_8974(struct tsens_device *tmdev)
break;
}
compute_intercept_slope(tmdev, p1, p2, mode);
compute_intercept_slope(priv, p1, p2, mode);
return 0;
}
/* v0.1: 8916, 8974 */
static const struct tsens_features tsens_v0_1_feat = {
.ver_major = VER_0_1,
.crit_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
};
static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* No VERSION information */
/* CTRL_OFFSET */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
[SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
/* No VALID field on v0.1 */
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),
/* No CRITICAL field on v0.1 */
REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
static const struct tsens_ops ops_8916 = {
.init = init_common,
.calibrate = calibrate_8916,
.get_temp = get_temp_common,
};
const struct tsens_plat_data data_8916 = {
.num_sensors = 5,
.ops = &ops_8916,
.hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
static const struct tsens_ops ops_8974 = {
.init = init_common,
.calibrate = calibrate_8974,
.get_temp = get_temp_common,
};
const struct tsens_data data_8974 = {
const struct tsens_plat_data data_8974 = {
.num_sensors = 11,
.ops = &ops_8974,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 },
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};

View File

@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019, Linaro Limited
*/
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include "tsens.h"
/* ----- SROT ------ */
#define SROT_HW_VER_OFF 0x0000
#define SROT_CTRL_OFF 0x0004
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0000
#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
#define TM_Sn_STATUS_OFF 0x0044
#define TM_TRDY_OFF 0x0084
/* eeprom layout data for qcs404/405 (v1) */
#define BASE0_MASK 0x000007f8
#define BASE1_MASK 0x0007f800
#define BASE0_SHIFT 3
#define BASE1_SHIFT 11
#define S0_P1_MASK 0x0000003f
#define S1_P1_MASK 0x0003f000
#define S2_P1_MASK 0x3f000000
#define S3_P1_MASK 0x000003f0
#define S4_P1_MASK 0x003f0000
#define S5_P1_MASK 0x0000003f
#define S6_P1_MASK 0x0003f000
#define S7_P1_MASK 0x3f000000
#define S8_P1_MASK 0x000003f0
#define S9_P1_MASK 0x003f0000
#define S0_P2_MASK 0x00000fc0
#define S1_P2_MASK 0x00fc0000
#define S2_P2_MASK_1_0 0xc0000000
#define S2_P2_MASK_5_2 0x0000000f
#define S3_P2_MASK 0x0000fc00
#define S4_P2_MASK 0x0fc00000
#define S5_P2_MASK 0x00000fc0
#define S6_P2_MASK 0x00fc0000
#define S7_P2_MASK_1_0 0xc0000000
#define S7_P2_MASK_5_2 0x0000000f
#define S8_P2_MASK 0x0000fc00
#define S9_P2_MASK 0x0fc00000
#define S0_P1_SHIFT 0
#define S0_P2_SHIFT 6
#define S1_P1_SHIFT 12
#define S1_P2_SHIFT 18
#define S2_P1_SHIFT 24
#define S2_P2_SHIFT_1_0 30
#define S2_P2_SHIFT_5_2 0
#define S3_P1_SHIFT 4
#define S3_P2_SHIFT 10
#define S4_P1_SHIFT 16
#define S4_P2_SHIFT 22
#define S5_P1_SHIFT 0
#define S5_P2_SHIFT 6
#define S6_P1_SHIFT 12
#define S6_P2_SHIFT 18
#define S7_P1_SHIFT 24
#define S7_P2_SHIFT_1_0 30
#define S7_P2_SHIFT_5_2 0
#define S8_P1_SHIFT 4
#define S8_P2_SHIFT 10
#define S9_P1_SHIFT 16
#define S9_P2_SHIFT 22
#define CAL_SEL_MASK 7
#define CAL_SEL_SHIFT 0
static int calibrate_v1(struct tsens_priv *priv)
{
u32 base0 = 0, base1 = 0;
u32 p1[10], p2[10];
u32 mode = 0, lsb = 0, msb = 0;
u32 *qfprom_cdata;
int i;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
mode = (qfprom_cdata[4] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base1 = (qfprom_cdata[4] & BASE1_MASK) >> BASE1_SHIFT;
p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
/* This value is split over two registers, 2 bits and 4 bits */
lsb = (qfprom_cdata[0] & S2_P2_MASK_1_0) >> S2_P2_SHIFT_1_0;
msb = (qfprom_cdata[1] & S2_P2_MASK_5_2) >> S2_P2_SHIFT_5_2;
p2[2] = msb << 2 | lsb;
p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
p2[5] = (qfprom_cdata[2] & S5_P2_MASK) >> S5_P2_SHIFT;
p2[6] = (qfprom_cdata[2] & S6_P2_MASK) >> S6_P2_SHIFT;
/* This value is split over two registers, 2 bits and 4 bits */
lsb = (qfprom_cdata[2] & S7_P2_MASK_1_0) >> S7_P2_SHIFT_1_0;
msb = (qfprom_cdata[3] & S7_P2_MASK_5_2) >> S7_P2_SHIFT_5_2;
p2[7] = msb << 2 | lsb;
p2[8] = (qfprom_cdata[3] & S8_P2_MASK) >> S8_P2_SHIFT;
p2[9] = (qfprom_cdata[3] & S9_P2_MASK) >> S9_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[4] & BASE0_MASK) >> BASE0_SHIFT;
p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
p1[5] = (qfprom_cdata[2] & S5_P1_MASK) >> S5_P1_SHIFT;
p1[6] = (qfprom_cdata[2] & S6_P1_MASK) >> S6_P1_SHIFT;
p1[7] = (qfprom_cdata[2] & S7_P1_MASK) >> S7_P1_SHIFT;
p1[8] = (qfprom_cdata[3] & S8_P1_MASK) >> S8_P1_SHIFT;
p1[9] = (qfprom_cdata[3] & S9_P1_MASK) >> S9_P1_SHIFT;
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(priv, p1, p2, mode);
return 0;
}
/* v1.x: qcs404,405 */
static const struct tsens_features tsens_v1_feat = {
.ver_major = VER_1_X,
.crit_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
};
static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* VERSION */
[VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31),
[VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27),
[VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15),
/* CTRL_OFFSET */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
[SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
/* 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),
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),
/* No CRITICAL field on v1.x */
REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
static const struct tsens_ops ops_generic_v1 = {
.init = init_common,
.calibrate = calibrate_v1,
.get_temp = get_temp_tsens_valid,
};
const struct tsens_plat_data data_tsens_v1 = {
.ops = &ops_generic_v1,
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};

View File

@ -4,76 +4,81 @@
* Copyright (c) 2018, Linaro Limited
*/
#include <linux/regmap.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include "tsens.h"
#define STATUS_OFFSET 0xa0
#define LAST_TEMP_MASK 0xfff
#define STATUS_VALID_BIT BIT(21)
/* ----- SROT ------ */
#define SROT_HW_VER_OFF 0x0000
#define SROT_CTRL_OFF 0x0004
static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
{
struct tsens_sensor *s = &tmdev->sensor[id];
u32 code;
unsigned int status_reg;
u32 last_temp = 0, last_temp2 = 0, last_temp3 = 0;
int ret;
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0004
#define TM_UPPER_LOWER_INT_STATUS_OFF 0x0008
#define TM_UPPER_LOWER_INT_CLEAR_OFF 0x000c
#define TM_UPPER_LOWER_INT_MASK_OFF 0x0010
#define TM_CRITICAL_INT_STATUS_OFF 0x0014
#define TM_CRITICAL_INT_CLEAR_OFF 0x0018
#define TM_CRITICAL_INT_MASK_OFF 0x001c
#define TM_Sn_UPPER_LOWER_THRESHOLD_OFF 0x0020
#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060
#define TM_Sn_STATUS_OFF 0x00a0
#define TM_TRDY_OFF 0x00e4
status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4;
ret = regmap_read(tmdev->tm_map, status_reg, &code);
if (ret)
return ret;
last_temp = code & LAST_TEMP_MASK;
if (code & STATUS_VALID_BIT)
goto done;
/* v2.x: 8996, 8998, sdm845 */
/* Try a second time */
ret = regmap_read(tmdev->tm_map, status_reg, &code);
if (ret)
return ret;
if (code & STATUS_VALID_BIT) {
last_temp = code & LAST_TEMP_MASK;
goto done;
} else {
last_temp2 = code & LAST_TEMP_MASK;
}
static const struct tsens_features tsens_v2_feat = {
.ver_major = VER_2_X,
.crit_int = 1,
.adc = 0,
.srot_split = 1,
.max_sensors = 16,
};
/* Try a third/last time */
ret = regmap_read(tmdev->tm_map, status_reg, &code);
if (ret)
return ret;
if (code & STATUS_VALID_BIT) {
last_temp = code & LAST_TEMP_MASK;
goto done;
} else {
last_temp3 = code & LAST_TEMP_MASK;
}
static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* VERSION */
[VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31),
[VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27),
[VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15),
/* CTRL_OFF */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
[SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 18),
if (last_temp == last_temp2)
last_temp = last_temp2;
else if (last_temp2 == last_temp3)
last_temp = last_temp3;
done:
/* Convert temperature from deciCelsius to milliCelsius */
*temp = sign_extend32(last_temp, fls(LAST_TEMP_MASK) - 1) * 100;
/* ----- TM ------ */
/* INTERRUPT ENABLE */
/* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2),
return 0;
}
/* 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),
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),
REG_FIELD_FOR_EACH_SENSOR16(CRITICAL_STATUS, TM_Sn_STATUS_OFF, 19, 19),
REG_FIELD_FOR_EACH_SENSOR16(MAX_STATUS, TM_Sn_STATUS_OFF, 20, 20),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
static const struct tsens_ops ops_generic_v2 = {
.init = init_common,
.get_temp = get_temp_tsens_v2,
.get_temp = get_temp_tsens_valid,
};
const struct tsens_data data_tsens_v2 = {
.ops = &ops_generic_v2,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 },
const struct tsens_plat_data data_tsens_v2 = {
.ops = &ops_generic_v2,
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
};
/* Kept around for backward compatibility with old msm8996.dtsi */
const struct tsens_data data_8996 = {
const struct tsens_plat_data data_8996 = {
.num_sensors = 13,
.ops = &ops_generic_v2,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 },
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
};

View File

@ -15,38 +15,38 @@
static int tsens_get_temp(void *data, int *temp)
{
const struct tsens_sensor *s = data;
struct tsens_device *tmdev = s->tmdev;
struct tsens_priv *priv = s->priv;
return tmdev->ops->get_temp(tmdev, s->id, temp);
return priv->ops->get_temp(priv, s->id, temp);
}
static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
{
const struct tsens_sensor *s = p;
struct tsens_device *tmdev = s->tmdev;
const struct tsens_sensor *s = data;
struct tsens_priv *priv = s->priv;
if (tmdev->ops->get_trend)
return tmdev->ops->get_trend(tmdev, s->id, trend);
if (priv->ops->get_trend)
return priv->ops->get_trend(priv, s->id, trend);
return -ENOTSUPP;
}
static int __maybe_unused tsens_suspend(struct device *dev)
{
struct tsens_device *tmdev = dev_get_drvdata(dev);
struct tsens_priv *priv = dev_get_drvdata(dev);
if (tmdev->ops && tmdev->ops->suspend)
return tmdev->ops->suspend(tmdev);
if (priv->ops && priv->ops->suspend)
return priv->ops->suspend(priv);
return 0;
}
static int __maybe_unused tsens_resume(struct device *dev)
{
struct tsens_device *tmdev = dev_get_drvdata(dev);
struct tsens_priv *priv = dev_get_drvdata(dev);
if (tmdev->ops && tmdev->ops->resume)
return tmdev->ops->resume(tmdev);
if (priv->ops && priv->ops->resume)
return priv->ops->resume(priv);
return 0;
}
@ -63,6 +63,9 @@ static const struct of_device_id tsens_table[] = {
}, {
.compatible = "qcom,msm8996-tsens",
.data = &data_8996,
}, {
.compatible = "qcom,tsens-v1",
.data = &data_tsens_v1,
}, {
.compatible = "qcom,tsens-v2",
.data = &data_tsens_v2,
@ -76,22 +79,27 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = {
.get_trend = tsens_get_trend,
};
static int tsens_register(struct tsens_device *tmdev)
static int tsens_register(struct tsens_priv *priv)
{
int i;
struct thermal_zone_device *tzd;
for (i = 0; i < tmdev->num_sensors; i++) {
tmdev->sensor[i].tmdev = tmdev;
tmdev->sensor[i].id = i;
tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
&tmdev->sensor[i],
for (i = 0; i < priv->num_sensors; i++) {
if (!is_sensor_enabled(priv, priv->sensor[i].hw_id)) {
dev_err(priv->dev, "sensor %d: disabled\n",
priv->sensor[i].hw_id);
continue;
}
priv->sensor[i].priv = priv;
priv->sensor[i].id = i;
tzd = devm_thermal_zone_of_sensor_register(priv->dev, i,
&priv->sensor[i],
&tsens_of_ops);
if (IS_ERR(tzd))
continue;
tmdev->sensor[i].tzd = tzd;
if (tmdev->ops->enable)
tmdev->ops->enable(tmdev, i);
priv->sensor[i].tzd = tzd;
if (priv->ops->enable)
priv->ops->enable(priv, i);
}
return 0;
}
@ -101,8 +109,8 @@ static int tsens_probe(struct platform_device *pdev)
int ret, i;
struct device *dev;
struct device_node *np;
struct tsens_device *tmdev;
const struct tsens_data *data;
struct tsens_priv *priv;
const struct tsens_plat_data *data;
const struct of_device_id *id;
u32 num_sensors;
@ -129,55 +137,55 @@ static int tsens_probe(struct platform_device *pdev)
return -EINVAL;
}
tmdev = devm_kzalloc(dev,
struct_size(tmdev, sensor, num_sensors),
priv = devm_kzalloc(dev,
struct_size(priv, sensor, num_sensors),
GFP_KERNEL);
if (!tmdev)
if (!priv)
return -ENOMEM;
tmdev->dev = dev;
tmdev->num_sensors = num_sensors;
tmdev->ops = data->ops;
for (i = 0; i < tmdev->num_sensors; i++) {
priv->dev = dev;
priv->num_sensors = num_sensors;
priv->ops = data->ops;
for (i = 0; i < priv->num_sensors; i++) {
if (data->hw_ids)
tmdev->sensor[i].hw_id = data->hw_ids[i];
priv->sensor[i].hw_id = data->hw_ids[i];
else
tmdev->sensor[i].hw_id = i;
}
for (i = 0; i < REG_ARRAY_SIZE; i++) {
tmdev->reg_offsets[i] = data->reg_offsets[i];
priv->sensor[i].hw_id = i;
}
priv->feat = data->feat;
priv->fields = data->fields;
if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
return -EINVAL;
ret = tmdev->ops->init(tmdev);
ret = priv->ops->init(priv);
if (ret < 0) {
dev_err(dev, "tsens init failed\n");
return ret;
}
if (tmdev->ops->calibrate) {
ret = tmdev->ops->calibrate(tmdev);
if (priv->ops->calibrate) {
ret = priv->ops->calibrate(priv);
if (ret < 0) {
dev_err(dev, "tsens calibration failed\n");
if (ret != -EPROBE_DEFER)
dev_err(dev, "tsens calibration failed\n");
return ret;
}
}
ret = tsens_register(tmdev);
ret = tsens_register(priv);
platform_set_drvdata(pdev, tmdev);
platform_set_drvdata(pdev, priv);
return ret;
}
static int tsens_remove(struct platform_device *pdev)
{
struct tsens_device *tmdev = platform_get_drvdata(pdev);
struct tsens_priv *priv = platform_get_drvdata(pdev);
if (tmdev->ops->disable)
tmdev->ops->disable(tmdev);
if (priv->ops->disable)
priv->ops->disable(priv);
return 0;
}

View File

@ -9,17 +9,39 @@
#define ONE_PT_CALIB 0x1
#define ONE_PT_CALIB2 0x2
#define TWO_PT_CALIB 0x3
#define CAL_DEGC_PT1 30
#define CAL_DEGC_PT2 120
#define SLOPE_FACTOR 1000
#define SLOPE_DEFAULT 3200
#include <linux/thermal.h>
#include <linux/regmap.h>
struct tsens_device;
struct tsens_priv;
enum tsens_ver {
VER_0_1 = 0,
VER_1_X,
VER_2_X,
};
/**
* 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
*/
struct tsens_sensor {
struct tsens_device *tmdev;
struct tsens_priv *priv;
struct thermal_zone_device *tzd;
int offset;
int id;
int hw_id;
unsigned int id;
unsigned int hw_id;
int slope;
u32 status;
};
@ -37,63 +59,274 @@ struct tsens_sensor {
*/
struct tsens_ops {
/* mandatory callbacks */
int (*init)(struct tsens_device *);
int (*calibrate)(struct tsens_device *);
int (*get_temp)(struct tsens_device *, int, int *);
int (*init)(struct tsens_priv *priv);
int (*calibrate)(struct tsens_priv *priv);
int (*get_temp)(struct tsens_priv *priv, int i, int *temp);
/* optional callbacks */
int (*enable)(struct tsens_device *, int);
void (*disable)(struct tsens_device *);
int (*suspend)(struct tsens_device *);
int (*resume)(struct tsens_device *);
int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
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);
};
enum reg_list {
SROT_CTRL_OFFSET,
#define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \
[_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \
[_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \
[_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \
[_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \
[_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \
[_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \
[_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \
[_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \
[_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \
[_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \
[_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit)
REG_ARRAY_SIZE,
#define REG_FIELD_FOR_EACH_SENSOR16(_name, _offset, _startbit, _stopbit) \
[_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \
[_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \
[_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \
[_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \
[_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \
[_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \
[_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \
[_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \
[_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \
[_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \
[_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit), \
[_name##_##11] = REG_FIELD(_offset + 44, _startbit, _stopbit), \
[_name##_##12] = REG_FIELD(_offset + 48, _startbit, _stopbit), \
[_name##_##13] = REG_FIELD(_offset + 52, _startbit, _stopbit), \
[_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 */
enum regfield_ids {
/* ----- SROT ------ */
/* HW_VER */
VER_MAJOR = 0,
VER_MINOR,
VER_STEP,
/* CTRL_OFFSET */
TSENS_EN = 3,
TSENS_SW_RST,
SENSOR_EN,
CODE_OR_TEMP,
/* ----- TM ------ */
/* STATUS */
LAST_TEMP_0 = 7, /* Last temperature reading */
LAST_TEMP_1,
LAST_TEMP_2,
LAST_TEMP_3,
LAST_TEMP_4,
LAST_TEMP_5,
LAST_TEMP_6,
LAST_TEMP_7,
LAST_TEMP_8,
LAST_TEMP_9,
LAST_TEMP_10,
LAST_TEMP_11,
LAST_TEMP_12,
LAST_TEMP_13,
LAST_TEMP_14,
LAST_TEMP_15,
VALID_0 = 23, /* VALID reading or not */
VALID_1,
VALID_2,
VALID_3,
VALID_4,
VALID_5,
VALID_6,
VALID_7,
VALID_8,
VALID_9,
VALID_10,
VALID_11,
VALID_12,
VALID_13,
VALID_14,
VALID_15,
MIN_STATUS_0, /* MIN threshold violated */
MIN_STATUS_1,
MIN_STATUS_2,
MIN_STATUS_3,
MIN_STATUS_4,
MIN_STATUS_5,
MIN_STATUS_6,
MIN_STATUS_7,
MIN_STATUS_8,
MIN_STATUS_9,
MIN_STATUS_10,
MIN_STATUS_11,
MIN_STATUS_12,
MIN_STATUS_13,
MIN_STATUS_14,
MIN_STATUS_15,
MAX_STATUS_0, /* MAX threshold violated */
MAX_STATUS_1,
MAX_STATUS_2,
MAX_STATUS_3,
MAX_STATUS_4,
MAX_STATUS_5,
MAX_STATUS_6,
MAX_STATUS_7,
MAX_STATUS_8,
MAX_STATUS_9,
MAX_STATUS_10,
MAX_STATUS_11,
MAX_STATUS_12,
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
};
/**
* struct tsens_data - tsens instance specific data
* @num_sensors: Max number of sensors supported by platform
* @ops: operations the tsens instance supports
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
* @reg_offsets: Register offsets for commonly used registers
* struct tsens_features - Features supported by the IP
* @ver_major: Major number of IP version
* @crit_int: does the IP support critical interrupts?
* @adc: do the sensors only output adc code (instead of temperature)?
* @srot_split: does the IP neatly splits the register space into SROT and TM,
* with SROT only being available to secure boot firmware?
* @max_sensors: maximum sensors supported by this version of the IP
*/
struct tsens_data {
const u32 num_sensors;
const struct tsens_ops *ops;
const u16 reg_offsets[REG_ARRAY_SIZE];
unsigned int *hw_ids;
struct tsens_features {
unsigned int ver_major;
unsigned int crit_int:1;
unsigned int adc:1;
unsigned int srot_split:1;
unsigned int max_sensors;
};
/* Registers to be saved/restored across a context loss */
/**
* struct tsens_plat_data - tsens compile-time platform data
* @num_sensors: Number of sensors supported by platform
* @ops: operations the tsens instance supports
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
* @feat: features of the IP
* @fields: bitfield locations
*/
struct tsens_plat_data {
const u32 num_sensors;
const struct tsens_ops *ops;
unsigned int *hw_ids;
const struct tsens_features *feat;
const struct reg_field *fields;
};
/**
* struct tsens_context - Registers to be saved/restored across a context loss
*/
struct tsens_context {
int threshold;
int control;
};
struct tsens_device {
/**
* struct tsens_priv - private data for each instance of the tsens IP
* @dev: pointer to struct device
* @num_sensors: number of sensors enabled on this device
* @tm_map: pointer to TM register address space
* @srot_map: pointer to SROT register address space
* @tm_offset: deal with old device trees that don't address TM and SROT
* address space separately
* @rf: array of regmap_fields used to store value of the field
* @ctx: registers to be saved and restored during suspend/resume
* @feat: features of the IP
* @fields: bitfield locations
* @ops: pointer to list of callbacks supported by this device
* @sensor: list of sensors attached to this device
*/
struct tsens_priv {
struct device *dev;
u32 num_sensors;
struct regmap *tm_map;
struct regmap *srot_map;
u32 tm_offset;
u16 reg_offsets[REG_ARRAY_SIZE];
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 tsens_sensor sensor[0];
};
char *qfprom_read(struct device *, const char *);
void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
int init_common(struct tsens_device *);
int get_temp_common(struct tsens_device *, int, int *);
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);
bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id);
/* TSENS target */
extern const struct tsens_plat_data data_8960;
/* TSENS v0.1 targets */
extern const struct tsens_plat_data data_8916, data_8974;
/* TSENS v1 targets */
extern const struct tsens_data data_8916, data_8974, data_8960;
extern const struct tsens_plat_data data_tsens_v1;
/* TSENS v2 targets */
extern const struct tsens_data data_8996, data_tsens_v2;
extern const struct tsens_plat_data data_8996, data_tsens_v2;
#endif /* __QCOM_TSENS_H__ */

View File

@ -193,11 +193,6 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
struct qoriq_tmu_data *data;
struct device_node *np = pdev->dev.of_node;
if (!np) {
dev_err(&pdev->dev, "Device OF-Node is NULL");
return -ENODEV;
}
data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
GFP_KERNEL);
if (!data)

View File

@ -14,7 +14,6 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <linux/sys_soc.h>
#include <linux/thermal.h>
@ -82,7 +81,6 @@ struct rcar_gen3_thermal_tsc {
struct rcar_gen3_thermal_priv {
struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
unsigned int num_tscs;
spinlock_t lock; /* Protect interrupts on and off */
void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc);
};
@ -232,38 +230,16 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
{
struct rcar_gen3_thermal_priv *priv = data;
u32 status;
int i, ret = IRQ_HANDLED;
int i;
spin_lock(&priv->lock);
for (i = 0; i < priv->num_tscs; i++) {
status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
if (status)
ret = IRQ_WAKE_THREAD;
thermal_zone_device_update(priv->tscs[i]->zone,
THERMAL_EVENT_UNSPECIFIED);
}
if (ret == IRQ_WAKE_THREAD)
rcar_thermal_irq_set(priv, false);
spin_unlock(&priv->lock);
return ret;
}
static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data)
{
struct rcar_gen3_thermal_priv *priv = data;
unsigned long flags;
int i;
for (i = 0; i < priv->num_tscs; i++)
thermal_zone_device_update(priv->tscs[i]->zone,
THERMAL_EVENT_UNSPECIFIED);
spin_lock_irqsave(&priv->lock, flags);
rcar_thermal_irq_set(priv, true);
spin_unlock_irqrestore(&priv->lock, flags);
return IRQ_HANDLED;
}
@ -307,7 +283,7 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
usleep_range(1000, 2000);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2);
@ -331,6 +307,9 @@ MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
static int rcar_gen3_thermal_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
rcar_thermal_irq_set(priv, false);
pm_runtime_put(dev);
pm_runtime_disable(dev);
@ -371,8 +350,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
if (soc_device_match(r8a7795es1))
priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1;
spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);
/*
@ -390,9 +367,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
if (!irqname)
return -ENOMEM;
ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq,
rcar_gen3_thermal_irq_thread,
IRQF_SHARED, irqname, priv);
ret = devm_request_threaded_irq(dev, irq, NULL,
rcar_gen3_thermal_irq,
IRQF_ONESHOT, irqname, priv);
if (ret)
return ret;
}
@ -433,10 +410,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
}
tsc->zone = zone;
ret = of_thermal_get_ntrips(tsc->zone);
if (ret < 0)
goto error_unregister;
tsc->zone->tzp->no_hwmon = false;
ret = thermal_add_hwmon_sysfs(tsc->zone);
if (ret)
@ -448,6 +421,10 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
goto error_unregister;
}
ret = of_thermal_get_ntrips(tsc->zone);
if (ret < 0)
goto error_unregister;
dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
}

View File

@ -52,6 +52,7 @@ struct rcar_thermal_chip {
unsigned int irq_per_ch : 1;
unsigned int needs_suspend_resume : 1;
unsigned int nirqs;
unsigned int ctemp_bands;
};
static const struct rcar_thermal_chip rcar_thermal = {
@ -60,6 +61,7 @@ static const struct rcar_thermal_chip rcar_thermal = {
.irq_per_ch = 0,
.needs_suspend_resume = 0,
.nirqs = 1,
.ctemp_bands = 1,
};
static const struct rcar_thermal_chip rcar_gen2_thermal = {
@ -68,6 +70,7 @@ static const struct rcar_thermal_chip rcar_gen2_thermal = {
.irq_per_ch = 0,
.needs_suspend_resume = 0,
.nirqs = 1,
.ctemp_bands = 1,
};
static const struct rcar_thermal_chip rcar_gen3_thermal = {
@ -80,6 +83,7 @@ static const struct rcar_thermal_chip rcar_gen3_thermal = {
* interrupts to detect a temperature change, rise or fall.
*/
.nirqs = 2,
.ctemp_bands = 2,
};
struct rcar_thermal_priv {
@ -263,7 +267,12 @@ static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
return ret;
mutex_lock(&priv->lock);
tmp = MCELSIUS((priv->ctemp * 5) - 65);
if (priv->chip->ctemp_bands == 1)
tmp = MCELSIUS((priv->ctemp * 5) - 65);
else if (priv->ctemp < 24)
tmp = MCELSIUS(((priv->ctemp * 55) - 720) / 10);
else
tmp = MCELSIUS((priv->ctemp * 5) - 60);
mutex_unlock(&priv->lock);
if ((tmp < MCELSIUS(-45)) || (tmp > MCELSIUS(125))) {

View File

@ -172,6 +172,9 @@ struct rockchip_thermal_data {
int tshut_temp;
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
struct pinctrl *pinctrl;
struct pinctrl_state *gpio_state;
struct pinctrl_state *otp_state;
};
/**
@ -222,11 +225,15 @@ struct rockchip_thermal_data {
#define GRF_TSADC_TESTBIT_L 0x0e648
#define GRF_TSADC_TESTBIT_H 0x0e64c
#define PX30_GRF_SOC_CON2 0x0408
#define GRF_SARADC_TESTBIT_ON (0x10001 << 2)
#define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2)
#define GRF_TSADC_VCM_EN_L (0x10001 << 7)
#define GRF_TSADC_VCM_EN_H (0x10001 << 7)
#define GRF_CON_TSADC_CH_INV (0x10001 << 1)
/**
* struct tsadc_table - code to temperature conversion table
* @code: the value of adc channel
@ -689,6 +696,13 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
regs + TSADCV2_AUTO_CON);
}
static void rk_tsadcv4_initialize(struct regmap *grf, void __iomem *regs,
enum tshut_polarity tshut_polarity)
{
rk_tsadcv2_initialize(grf, regs, tshut_polarity);
regmap_write(grf, PX30_GRF_SOC_CON2, GRF_CON_TSADC_CH_INV);
}
static void rk_tsadcv2_irq_ack(void __iomem *regs)
{
u32 val;
@ -818,6 +832,30 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs,
writel_relaxed(val, regs + TSADCV2_INT_EN);
}
static const struct rockchip_tsadc_chip px30_tsadc_data = {
.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
.chn_num = 2, /* 2 channels for tsadc */
.tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
.tshut_temp = 95000,
.initialize = rk_tsadcv4_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
.get_temp = rk_tsadcv2_get_temp,
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3328_code_table,
.length = ARRAY_SIZE(rk3328_code_table),
.data_mask = TSADCV2_DATA_MASK,
.mode = ADC_INCREMENT,
},
};
static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
.chn_num = 1, /* one channel for tsadc */
@ -990,6 +1028,9 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
};
static const struct of_device_id of_rockchip_thermal_match[] = {
{ .compatible = "rockchip,px30-tsadc",
.data = (void *)&px30_tsadc_data,
},
{
.compatible = "rockchip,rv1108-tsadc",
.data = (void *)&rv1108_tsadc_data,
@ -1242,6 +1283,8 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
return error;
}
thermal->chip->control(thermal->regs, false);
error = clk_prepare_enable(thermal->clk);
if (error) {
dev_err(&pdev->dev, "failed to enable converter clock: %d\n",
@ -1267,6 +1310,30 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
thermal->chip->initialize(thermal->grf, thermal->regs,
thermal->tshut_polarity);
if (thermal->tshut_mode == TSHUT_MODE_GPIO) {
thermal->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(thermal->pinctrl)) {
dev_err(&pdev->dev, "failed to find thermal pinctrl\n");
return PTR_ERR(thermal->pinctrl);
}
thermal->gpio_state = pinctrl_lookup_state(thermal->pinctrl,
"gpio");
if (IS_ERR_OR_NULL(thermal->gpio_state)) {
dev_err(&pdev->dev, "failed to find thermal gpio state\n");
return -EINVAL;
}
thermal->otp_state = pinctrl_lookup_state(thermal->pinctrl,
"otpout");
if (IS_ERR_OR_NULL(thermal->otp_state)) {
dev_err(&pdev->dev, "failed to find thermal otpout state\n");
return -EINVAL;
}
pinctrl_select_state(thermal->pinctrl, thermal->otp_state);
}
for (i = 0; i < thermal->chip->chn_num; i++) {
error = rockchip_thermal_register_sensor(pdev, thermal,
&thermal->sensors[i],
@ -1337,8 +1404,8 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
clk_disable(thermal->pclk);
clk_disable(thermal->clk);
pinctrl_pm_select_sleep_state(dev);
if (thermal->tshut_mode == TSHUT_MODE_GPIO)
pinctrl_select_state(thermal->pinctrl, thermal->gpio_state);
return 0;
}
@ -1383,7 +1450,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
for (i = 0; i < thermal->chip->chn_num; i++)
rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
pinctrl_pm_select_default_state(dev);
if (thermal->tshut_mode == TSHUT_MODE_GPIO)
pinctrl_select_state(thermal->pinctrl, thermal->otp_state);
return 0;
}

View File

@ -3,9 +3,9 @@
#
config ST_THERMAL
tristate "Thermal sensors on STMicroelectronics STi series of SoCs"
help
Support for thermal sensors on STMicroelectronics STi series of SoCs.
tristate "Thermal sensors on STMicroelectronics STi series of SoCs"
help
Support for thermal sensors on STMicroelectronics STi series of SoCs.
config ST_THERMAL_SYSCFG
select ST_THERMAL
@ -16,11 +16,11 @@ config ST_THERMAL_MEMMAP
tristate "STi series memory mapped access based thermal sensors"
config STM32_THERMAL
tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs"
depends on MACH_STM32MP157
default y
help
Support for thermal framework on STMicroelectronics STM32 series of
SoCs. This thermal driver allows to access to general thermal framework
functionalities and to acces to SoC sensor functionalities. This
configuration is fully dependent of MACH_STM32MP157.
tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs"
depends on MACH_STM32MP157
default y
help
Support for thermal framework on STMicroelectronics STM32 series of
SoCs. This thermal driver allows to access to general thermal framework
functionalities and to acces to SoC sensor functionalities. This
configuration is fully dependent of MACH_STM32MP157.

View File

@ -570,8 +570,7 @@ thermal_unprepare:
static int stm_thermal_suspend(struct device *dev)
{
int ret;
struct platform_device *pdev = to_platform_device(dev);
struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
struct stm_thermal_sensor *sensor = dev_get_drvdata(dev);
ret = stm_thermal_sensor_off(sensor);
if (ret)
@ -585,8 +584,7 @@ static int stm_thermal_suspend(struct device *dev)
static int stm_thermal_resume(struct device *dev)
{
int ret;
struct platform_device *pdev = to_platform_device(dev);
struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
struct stm_thermal_sensor *sensor = dev_get_drvdata(dev);
ret = stm_thermal_prepare(sensor);
if (ret)

View File

@ -14,7 +14,7 @@ config TEGRA_BPMP_THERMAL
tristate "Tegra BPMP thermal sensing"
depends on TEGRA_BPMP || COMPILE_TEST
help
Enable this option for support for sensing system temperature of NVIDIA
Tegra systems-on-chip with the BPMP coprocessor (Tegra186).
Enable this option for support for sensing system temperature of NVIDIA
Tegra systems-on-chip with the BPMP coprocessor (Tegra186).
endmenu

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
*
@ -29,6 +30,14 @@
#define THERMCTL_THERMTRIP_CTL 0x80
/* BITs are defined in device file */
#define THERMCTL_INTR_ENABLE 0x88
#define THERMCTL_INTR_DISABLE 0x8c
#define TH_INTR_UP_DN_EN 0x3
#define THERM_IRQ_MEM_MASK (TH_INTR_UP_DN_EN << 24)
#define THERM_IRQ_GPU_MASK (TH_INTR_UP_DN_EN << 16)
#define THERM_IRQ_CPU_MASK (TH_INTR_UP_DN_EN << 8)
#define THERM_IRQ_TSENSE_MASK (TH_INTR_UP_DN_EN << 0)
#define SENSOR_PDIV 0x1c0
#define SENSOR_PDIV_CPU_MASK (0xf << 12)
#define SENSOR_PDIV_GPU_MASK (0xf << 8)
@ -70,6 +79,7 @@ struct tegra_tsensor_group {
u32 thermtrip_enable_mask;
u32 thermtrip_any_en_mask;
u32 thermtrip_threshold_mask;
u32 thermctl_isr_mask;
u16 thermctl_lvl0_offset;
u32 thermctl_lvl0_up_thresh_mask;
u32 thermctl_lvl0_dn_thresh_mask;
@ -92,6 +102,11 @@ struct tegra_tsensor {
const struct tegra_tsensor_group *group;
};
struct tsensor_group_thermtrips {
u8 id;
u32 temp;
};
struct tegra_soctherm_fuse {
u32 fuse_base_cp_mask, fuse_base_cp_shift;
u32 fuse_base_ft_mask, fuse_base_ft_shift;
@ -113,6 +128,7 @@ struct tegra_soctherm_soc {
const int thresh_grain;
const unsigned int bptt;
const bool use_ccroc;
struct tsensor_group_thermtrips *thermtrips;
};
int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -55,6 +56,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = {
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
@ -73,6 +75,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
@ -89,6 +92,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
@ -107,6 +111,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
.thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -55,6 +56,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = {
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
@ -73,6 +75,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
@ -89,6 +92,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
@ -107,6 +111,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
.thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -56,6 +57,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = {
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
@ -74,6 +76,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
@ -90,6 +93,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
@ -108,6 +112,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
@ -203,6 +208,13 @@ static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = {
.fuse_spare_realignment = 0,
};
struct tsensor_group_thermtrips tegra210_tsensor_thermtrips[] = {
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
{.id = TEGRA124_SOCTHERM_SENSOR_NUM},
};
const struct tegra_soctherm_soc tegra210_soctherm = {
.tsensors = tegra210_tsensors,
.num_tsensors = ARRAY_SIZE(tegra210_tsensors),
@ -212,4 +224,5 @@ const struct tegra_soctherm_soc tegra210_soctherm = {
.thresh_grain = TEGRA210_THRESH_GRAIN,
.bptt = TEGRA210_BPTT,
.use_ccroc = false,
.thermtrips = tegra210_tsensor_thermtrips,
};

View File

@ -29,6 +29,9 @@ static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val)
int temp, temp_hi, temp_lo, adc_hi, adc_lo;
int i;
if (!gti->lookup_table)
return val;
for (i = 0; i < gti->nlookup_table; i++) {
if (val >= gti->lookup_table[2 * i + 1])
break;
@ -81,9 +84,9 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev,
ntable = of_property_count_elems_of_size(np, "temperature-lookup-table",
sizeof(u32));
if (ntable < 0) {
dev_err(dev, "Lookup table is not provided\n");
return ntable;
if (ntable <= 0) {
dev_notice(dev, "no lookup table, assuming DAC channel returns milliCelcius\n");
return 0;
}
if (ntable % 2) {

View File

@ -1046,6 +1046,55 @@ thermal_of_cooling_device_register(struct device_node *np,
}
EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
static void thermal_cooling_device_release(struct device *dev, void *res)
{
thermal_cooling_device_unregister(
*(struct thermal_cooling_device **)res);
}
/**
* devm_thermal_of_cooling_device_register() - register an OF thermal cooling
* device
* @dev: a valid struct device pointer of a sensor device.
* @np: a pointer to a device tree node.
* @type: the thermal cooling device type.
* @devdata: device private data.
* @ops: standard thermal cooling devices callbacks.
*
* This function will register a cooling device with device tree node reference.
* This interface function adds a new thermal cooling device (fan/processor/...)
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
* to all the thermal zone devices registered at the same time.
*
* Return: a pointer to the created struct thermal_cooling_device or an
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
*/
struct thermal_cooling_device *
devm_thermal_of_cooling_device_register(struct device *dev,
struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
{
struct thermal_cooling_device **ptr, *tcd;
ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
tcd = __thermal_cooling_device_register(np, type, devdata, ops);
if (IS_ERR(tcd)) {
devres_free(ptr);
return tcd;
}
*ptr = tcd;
devres_add(dev, ptr);
return tcd;
}
EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register);
static void __unbind(struct thermal_zone_device *tz, int mask,
struct thermal_cooling_device *cdev)
{

View File

@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*/
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
struct thermal_mmio {
void __iomem *mmio_base;
u32 (*read_mmio)(void __iomem *mmio_base);
u32 mask;
int factor;
};
static u32 thermal_mmio_readb(void __iomem *mmio_base)
{
return readb(mmio_base);
}
static int thermal_mmio_get_temperature(void *private, int *temp)
{
int t;
struct thermal_mmio *sensor =
(struct thermal_mmio *)private;
t = sensor->read_mmio(sensor->mmio_base) & sensor->mask;
t *= sensor->factor;
*temp = t;
return 0;
}
static struct thermal_zone_of_device_ops thermal_mmio_ops = {
.get_temp = thermal_mmio_get_temperature,
};
static int thermal_mmio_probe(struct platform_device *pdev)
{
struct resource *resource;
struct thermal_mmio *sensor;
int (*sensor_init_func)(struct platform_device *pdev,
struct thermal_mmio *sensor);
struct thermal_zone_device *thermal_zone;
int ret;
int temperature;
sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (IS_ERR(resource)) {
dev_err(&pdev->dev,
"fail to get platform memory resource (%ld)\n",
PTR_ERR(resource));
return PTR_ERR(resource);
}
sensor->mmio_base = devm_ioremap_resource(&pdev->dev, resource);
if (IS_ERR(sensor->mmio_base)) {
dev_err(&pdev->dev, "failed to ioremap memory (%ld)\n",
PTR_ERR(sensor->mmio_base));
return PTR_ERR(sensor->mmio_base);
}
sensor_init_func = device_get_match_data(&pdev->dev);
if (sensor_init_func) {
ret = sensor_init_func(pdev, sensor);
if (ret) {
dev_err(&pdev->dev,
"failed to initialize sensor (%d)\n",
ret);
return ret;
}
}
thermal_zone = devm_thermal_zone_of_sensor_register(&pdev->dev,
0,
sensor,
&thermal_mmio_ops);
if (IS_ERR(thermal_zone)) {
dev_err(&pdev->dev,
"failed to register sensor (%ld)\n",
PTR_ERR(thermal_zone));
return PTR_ERR(thermal_zone);
}
thermal_mmio_get_temperature(sensor, &temperature);
dev_info(&pdev->dev,
"thermal mmio sensor %s registered, current temperature: %d\n",
pdev->name, temperature);
return 0;
}
static int al_thermal_init(struct platform_device *pdev,
struct thermal_mmio *sensor)
{
sensor->read_mmio = thermal_mmio_readb;
sensor->mask = 0xff;
sensor->factor = 1000;
return 0;
}
static const struct of_device_id thermal_mmio_id_table[] = {
{ .compatible = "amazon,al-thermal", .data = al_thermal_init},
{}
};
MODULE_DEVICE_TABLE(of, thermal_mmio_id_table);
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),
},
};
module_platform_driver(thermal_mmio_driver);
MODULE_AUTHOR("Talel Shenhar <talel@amazon.com>");
MODULE_DESCRIPTION("Thermal MMIO Driver");
MODULE_LICENSE("GPL v2");

View File

@ -12,9 +12,9 @@
#define TEGRA124_SOCTHERM_SENSOR_PLLX 3
#define TEGRA124_SOCTHERM_SENSOR_NUM 4
#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 0
#define TEGRA_SOCTHERM_THROT_LEVEL_MED 1
#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 2
#define TEGRA_SOCTHERM_THROT_LEVEL_NONE -1
#define TEGRA_SOCTHERM_THROT_LEVEL_NONE 0
#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 1
#define TEGRA_SOCTHERM_THROT_LEVEL_MED 2
#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 3
#endif

View File

@ -447,6 +447,11 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np, char *, void *,
const struct thermal_cooling_device_ops *);
struct thermal_cooling_device *
devm_thermal_of_cooling_device_register(struct device *dev,
struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *ops);
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
@ -503,6 +508,14 @@ static inline struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
char *type, void *devdata, const struct thermal_cooling_device_ops *ops)
{ return ERR_PTR(-ENODEV); }
static inline struct thermal_cooling_device *
devm_thermal_of_cooling_device_register(struct device *dev,
struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
{
return ERR_PTR(-ENODEV);
}
static inline void thermal_cooling_device_unregister(
struct thermal_cooling_device *cdev)
{ }