Commit bfb76444 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull thermal management updates from Zhang Rui:

 - Introduce generic ADC thermal driver, based on OF thermal (Laxman
   Dewangan)

 - Introduce new thermal driver for Tango chips (Marc Gonzalez)

 - Rockchip driver support for RK3399, RK3366, and some fixes (Caesar
   Wang, Elaine Zhang and Shawn Lin)

 - Add CPU power cooling model to Mediatek thermal driver (Dawei Chien)

 - Wider usage of dev_thermal_zone_of_sensor_register (Eduardo Valentin)

 - TI thermal driver gained a new maintainer (Keerthy).

 - Enabled powerclamp driver by checking CPU feature and package cstate
   counter instead of CPU whitelist (Jacob Pan)

 - Various fixes on thermal governor, OF thermal, Tegra, and RCAR

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (50 commits)
  thermal: tango: initialize TEMPSI_CFG
  thermal: rockchip: use the usleep_range instead of udelay
  thermal: rockchip: add the notes for better reading
  thermal: rockchip: Support RK3366 SoCs in the thermal driver
  thermal: rockchip: handle the power sequence for tsadc controller
  thermal: rockchip: update the tsadc table for rk3399
  thermal: rockchip: fixes the code_to_temp for tsadc driver
  thermal: rockchip: disable thermal->clk in err case
  thermal: tegra: add Tegra132 specific SOC_THERM driver
  thermal: fix ptr_ret.cocci warnings
  thermal: mediatek: Add cpu dynamic power cooling model.
  thermal: generic-adc: Add ADC based thermal sensor driver
  thermal: generic-adc: Add DT binding for ADC based thermal sensor
  thermal: tegra: fix static checker warning
  thermal: tegra: mark PM functions __maybe_unused
  thermal: add temperature sensor support for tango SoC
  thermal: hisilicon: fix IRQ imbalance enabling
  thermal: hisilicon: support to use any sensor
  thermal: rcar: Remove binding docs for r8a7794
  thermal: tegra: add PM support
  ...
parents 159d08f4 88ac9906
......@@ -26,6 +26,10 @@ Required properties :
of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a
list of valid values when referring to thermal sensors.
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.
Example :
......@@ -51,5 +55,13 @@ Example: referring to thermal sensors :
thermal-sensors =
<&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
trips {
cpu_shutdown_trip: shutdown-trip {
temperature = <102500>;
hysteresis = <1000>;
type = "critical";
};
};
};
};
......@@ -11,7 +11,6 @@ Required properties:
- "renesas,thermal-r8a7791" (R-Car M2-W)
- "renesas,thermal-r8a7792" (R-Car V2H)
- "renesas,thermal-r8a7793" (R-Car M2-N)
- "renesas,thermal-r8a7794" (R-Car E2)
- reg : Address range of the thermal registers.
The 1st reg will be recognized as common register
if it has "interrupts".
......
* Tango Thermal
The SMP8758 SoC includes 3 instances of this temperature sensor
(in the CPU, video decoder, and PCIe controller).
Required properties:
- #thermal-sensor-cells: Should be 0 (see thermal.txt)
- compatible: "sigma,smp8758-thermal"
- reg: Address range of the thermal registers
Example:
cpu_temp: thermal@920100 {
#thermal-sensor-cells = <0>;
compatible = "sigma,smp8758-thermal";
reg = <0x920100 12>;
};
General Purpose Analog To Digital Converter (ADC) based thermal sensor.
On some of platforms, thermal sensor like thermistors are connected to
one of ADC channel and sensor resistance is read via voltage across the
sensor resistor. The voltage read across the sensor is mapped to
temperature using voltage-temperature lookup table.
Required properties:
===================
- compatible: Must be "generic-adc-thermal".
- 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.
Example :
#include <dt-bindings/thermal/thermal.h>
i2c@7000c400 {
ads1015: ads1015@4a {
reg = <0x4a>;
compatible = "ads1015";
sampling-frequency = <3300>;
#io-channel-cells = <1>;
};
};
tboard_thermistor: thermal-sensor {
compatible = "generic-adc-thermal";
#thermal-sensor-cells = <0>;
io-channels = <&ads1015 1>;
io-channel-names = "sensor-channel";
temperature-lookup-table = < (-40000) 2578
(-39000) 2577
(-38000) 2576
(-37000) 2575
(-36000) 2574
(-35000) 2573
(-34000) 2572
(-33000) 2571
(-32000) 2569
(-31000) 2568
(-30000) 2567
::::::::::
118000 254
119000 247
120000 240
121000 233
122000 226
123000 220
124000 214
125000 208>;
};
dummy_cool_dev: dummy-cool-dev {
compatible = "dummy-cooling-dev";
#cooling-cells = <2>; /* min followed by max */
};
thermal-zones {
Tboard {
polling-delay = <15000>; /* milliseconds */
polling-delay-passive = <0>; /* milliseconds */
thermal-sensors = <&tboard_thermistor>;
trips {
therm_est_trip: therm_est_trip {
temperature = <40000>;
type = "active";
hysteresis = <1000>;
};
};
cooling-maps {
map0 {
trip = <&therm_est_trip>;
cooling-device = <&dummy_cool_dev THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
contribution = <100>;
};
};
};
};
......@@ -69,8 +69,8 @@ temperature) and throttle appropriate devices.
1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
This interface function removes the thermal zone device.
It deletes the corresponding entry form /sys/class/thermal folder and
unbind all the thermal cooling devices it uses.
It deletes the corresponding entry from /sys/class/thermal folder and
unbinds all the thermal cooling devices it uses.
1.1.3 struct thermal_zone_device *thermal_zone_of_sensor_register(
struct device *dev, int sensor_id, void *data,
......@@ -146,32 +146,32 @@ temperature) and throttle appropriate devices.
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 register at the same time.
to all the thermal zone devices registered at the same time.
name: the cooling device name.
devdata: device private data.
ops: thermal cooling devices call-backs.
.get_max_state: get the Maximum throttle state of the cooling device.
.get_cur_state: get the Current throttle state of the cooling device.
.get_cur_state: get the Currently requested throttle state of the cooling device.
.set_cur_state: set the Current throttle state of the cooling device.
1.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
This interface function remove the thermal cooling device.
It deletes the corresponding entry form /sys/class/thermal folder and
unbind itself from all the thermal zone devices using it.
This interface function removes the thermal cooling device.
It deletes the corresponding entry from /sys/class/thermal folder and
unbinds itself from all the thermal zone devices using it.
1.3 interface for binding a thermal zone device with a thermal cooling device
1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
int trip, struct thermal_cooling_device *cdev,
unsigned long upper, unsigned long lower, unsigned int weight);
This interface function bind a thermal cooling device to the certain trip
This interface function binds a thermal cooling device to a particular trip
point of a thermal zone device.
This function is usually called in the thermal zone device .bind callback.
tz: the thermal zone device
cdev: thermal cooling device
trip: indicates which trip point the cooling devices is associated with
in this thermal zone.
trip: indicates which trip point in this thermal zone the cooling device
is associated with.
upper:the Maximum cooling state for this trip point.
THERMAL_NO_LIMIT means no upper limit,
and the cooling device can be in max_state.
......@@ -184,13 +184,13 @@ temperature) and throttle appropriate devices.
1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
int trip, struct thermal_cooling_device *cdev);
This interface function unbind a thermal cooling device from the certain
This interface function unbinds a thermal cooling device from a particular
trip point of a thermal zone device. This function is usually called in
the thermal zone device .unbind callback.
tz: the thermal zone device
cdev: thermal cooling device
trip: indicates which trip point the cooling devices is associated with
in this thermal zone.
trip: indicates which trip point in this thermal zone the cooling device
is associated with.
1.4 Thermal Zone Parameters
1.4.1 struct thermal_bind_params
......@@ -210,13 +210,13 @@ temperature) and throttle appropriate devices.
this thermal zone and cdev, for a particular trip point.
If nth bit is set, then the cdev and thermal zone are bound
for trip point n.
.limits: This is an array of cooling state limits. Must have exactly
2 * thermal_zone.number_of_trip_points. It is an array consisting
of tuples <lower-state upper-state> of state limits. Each trip
will be associated with one state limit tuple when binding.
A NULL pointer means <THERMAL_NO_LIMITS THERMAL_NO_LIMITS>
on all trips. These limits are used when binding a cdev to a
trip point.
.binding_limits: This is an array of cooling state limits. Must have
exactly 2 * thermal_zone.number_of_trip_points. It is an
array consisting of tuples <lower-state upper-state> of
state limits. Each trip will be associated with one state
limit tuple when binding. A NULL pointer means
<THERMAL_NO_LIMITS THERMAL_NO_LIMITS> on all trips.
These limits are used when binding a cdev to a trip point.
.match: This call back returns success(0) if the 'tz and cdev' need to
be bound, as per platform data.
1.4.2 struct thermal_zone_params
......@@ -351,8 +351,8 @@ cdev[0-*]
RO, Optional
cdev[0-*]_trip_point
The trip point with which cdev[0-*] is associated in this thermal
zone; -1 means the cooling device is not associated with any trip
The trip point in this thermal zone which cdev[0-*] is associated
with; -1 means the cooling device is not associated with any trip
point.
RO, Optional
......
......@@ -11296,6 +11296,7 @@ F: drivers/platform/x86/thinkpad_acpi.c
TI BANDGAP AND THERMAL DRIVER
M: Eduardo Valentin <edubezval@gmail.com>
M: Keerthy <j-keerthy@ti.com>
L: linux-pm@vger.kernel.org
L: linux-omap@vger.kernel.org
S: Maintained
......
......@@ -307,17 +307,24 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
return 0;
}
#define DYNAMIC_POWER "dynamic-power-coefficient"
static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
{
struct mtk_cpu_dvfs_info *info = policy->driver_data;
struct device_node *np = of_node_get(info->cpu_dev->of_node);
u32 capacitance = 0;
if (WARN_ON(!np))
return;
if (of_find_property(np, "#cooling-cells", NULL)) {
info->cdev = of_cpufreq_cooling_register(np,
policy->related_cpus);
of_property_read_u32(np, DYNAMIC_POWER, &capacitance);
info->cdev = of_cpufreq_power_cooling_register(np,
policy->related_cpus,
capacitance,
NULL);
if (IS_ERR(info->cdev)) {
dev_err(info->cpu_dev,
......
......@@ -77,7 +77,6 @@ static const u8 LM75_REG_TEMP[3] = {
struct lm75_data {
struct i2c_client *client;
struct device *hwmon_dev;
struct thermal_zone_device *tz;
struct mutex update_lock;
u8 orig_conf;
u8 resolution; /* In bits, between 9 and 12 */
......@@ -306,11 +305,9 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (IS_ERR(data->hwmon_dev))
return PTR_ERR(data->hwmon_dev);
data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, 0,
data->hwmon_dev,
&lm75_of_thermal_ops);
if (IS_ERR(data->tz))
data->tz = NULL;
devm_thermal_zone_of_sensor_register(data->hwmon_dev, 0,
data->hwmon_dev,
&lm75_of_thermal_ops);
dev_info(dev, "%s: sensor '%s'\n",
dev_name(data->hwmon_dev), client->name);
......@@ -322,7 +319,6 @@ static int lm75_remove(struct i2c_client *client)
{
struct lm75_data *data = i2c_get_clientdata(client);
thermal_zone_of_sensor_unregister(data->hwmon_dev, data->tz);
hwmon_device_unregister(data->hwmon_dev);
lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
return 0;
......
......@@ -259,7 +259,6 @@ struct ntc_data {
struct device *dev;
int n_comp;
char name[PLATFORM_NAME_SIZE];
struct thermal_zone_device *tz;
};
#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
......@@ -579,6 +578,7 @@ static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
static int ntc_thermistor_probe(struct platform_device *pdev)
{
struct thermal_zone_device *tz;
const struct of_device_id *of_id =
of_match_device(of_match_ptr(ntc_match), &pdev->dev);
const struct platform_device_id *pdev_id;
......@@ -677,12 +677,10 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
pdev_id->name);
data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev,
&ntc_of_thermal_ops);
if (IS_ERR(data->tz)) {
tz = devm_thermal_zone_of_sensor_register(data->dev, 0, data->dev,
&ntc_of_thermal_ops);
if (IS_ERR(tz))
dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
data->tz = NULL;
}
return 0;
err_after_sysfs:
......@@ -700,8 +698,6 @@ static int ntc_thermistor_remove(struct platform_device *pdev)
sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
ntc_iio_channel_release(pdata);
thermal_zone_of_sensor_unregister(data->dev, data->tz);
return 0;
}
......
......@@ -31,10 +31,8 @@ struct sensor_data {
};
struct scpi_thermal_zone {
struct list_head list;
int sensor_id;
struct scpi_sensors *scpi_sensors;
struct thermal_zone_device *tzd;
};
struct scpi_sensors {
......@@ -92,20 +90,6 @@ scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%s\n", sensor->info.name);
}
static void
unregister_thermal_zones(struct platform_device *pdev,
struct scpi_sensors *scpi_sensors)
{
struct list_head *pos;
list_for_each(pos, &scpi_sensors->thermal_zones) {
struct scpi_thermal_zone *zone;
zone = list_entry(pos, struct scpi_thermal_zone, list);
thermal_zone_of_sensor_unregister(&pdev->dev, zone->tzd);
}
}
static struct thermal_zone_of_device_ops scpi_sensor_ops = {
.get_temp = scpi_read_temp,
};
......@@ -118,7 +102,7 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
struct scpi_ops *scpi_ops;
struct device *hwdev, *dev = &pdev->dev;
struct scpi_sensors *scpi_sensors;
int ret, idx;
int idx, ret;
scpi_ops = get_scpi_ops();
if (!scpi_ops)
......@@ -232,47 +216,34 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
for (i = 0; i < nr_sensors; i++) {
struct sensor_data *sensor = &scpi_sensors->data[i];
struct thermal_zone_device *z;
struct scpi_thermal_zone *zone;
if (sensor->info.class != TEMPERATURE)
continue;
zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
if (!zone) {
ret = -ENOMEM;
goto unregister_tzd;
}
if (!zone)
return -ENOMEM;
zone->sensor_id = i;
zone->scpi_sensors = scpi_sensors;
zone->tzd = thermal_zone_of_sensor_register(dev,
sensor->info.sensor_id, zone, &scpi_sensor_ops);
z = devm_thermal_zone_of_sensor_register(dev,
sensor->info.sensor_id,
zone,
&scpi_sensor_ops);
/*
* The call to thermal_zone_of_sensor_register returns
* an error for sensors that are not associated with
* any thermal zones or if the thermal subsystem is
* not configured.
*/
if (IS_ERR(zone->tzd)) {
if (IS_ERR(z)) {
devm_kfree(dev, zone);
continue;
}
list_add(&zone->list, &scpi_sensors->thermal_zones);
}
return 0;
unregister_tzd:
unregister_thermal_zones(pdev, scpi_sensors);
return ret;
}
static int scpi_hwmon_remove(struct platform_device *pdev)
{
struct scpi_sensors *scpi_sensors = platform_get_drvdata(pdev);
unregister_thermal_zones(pdev, scpi_sensors);
return 0;
}
......@@ -288,7 +259,6 @@ static struct platform_driver scpi_hwmon_platdrv = {
.of_match_table = scpi_of_match,
},
.probe = scpi_hwmon_probe,
.remove = scpi_hwmon_remove,
};
module_platform_driver(scpi_hwmon_platdrv);
......
......@@ -53,7 +53,6 @@
struct tmp102 {
struct i2c_client *client;
struct device *hwmon_dev;
struct thermal_zone_device *tz;
struct mutex lock;
u16 config_orig;
unsigned long last_update;
......@@ -232,10 +231,8 @@ static int tmp102_probe(struct i2c_client *client,
goto fail_restore_config;
}
tmp102->hwmon_dev = hwmon_dev;
tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
&tmp102_of_thermal_ops);
if (IS_ERR(tmp102->tz))
tmp102->tz = NULL;
devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
&tmp102_of_thermal_ops);
dev_info(dev, "initialized\n");
......@@ -251,7 +248,6 @@ static int tmp102_remove(struct i2c_client *client)
{
struct tmp102 *tmp102 = i2c_get_clientdata(client);
thermal_zone_of_sensor_unregister(tmp102->hwmon_dev, tmp102->tz);
hwmon_device_unregister(tmp102->hwmon_dev);
/* Stop monitoring if device was stopped originally */
......
......@@ -115,7 +115,6 @@
struct sun4i_ts_data {
struct device *dev;
struct input_dev *input;
struct thermal_zone_device *tz;
void __iomem *base;
unsigned int irq;
bool ignore_fifo_data;
......@@ -366,10 +365,7 @@ static int sun4i_ts_probe(struct platform_device *pdev)
if (IS_ERR(hwmon))
return PTR_ERR(hwmon);
ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts,
&sun4i_ts_tz_ops);
if (IS_ERR(ts->tz))
ts->tz = NULL;
devm_thermal_zone_of_sensor_register(ts->dev, 0, ts, &sun4i_ts_tz_ops);
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
......@@ -377,7 +373,6 @@ static int sun4i_ts_probe(struct platform_device *pdev)
error = input_register_device(ts->input);
if (error) {
writel(0, ts->base + TP_INT_FIFOC);
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
return error;
}
}
......@@ -394,8 +389,6 @@ static int sun4i_ts_remove(struct platform_device *pdev)
if (ts->input)
input_unregister_device(ts->input);
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
/* Deactivate all IRQs */
writel(0, ts->base + TP_INT_FIFOC);
......
......@@ -260,16 +260,6 @@ config ARMADA_THERMAL
Enable this option if you want to have support for thermal management
controller present in Armada 370 and Armada XP SoC.
config TEGRA_SOCTHERM
tristate "Tegra SOCTHERM thermal management"
depends on ARCH_TEGRA
help
Enable this option for integrated thermal management support on NVIDIA
Tegra124 systems-on-chip. The driver supports four thermal zones
(CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
zones to manage temperatures. This option is also required for the
emergency thermal reset (thermtrip) feature to function.
config DB8500_CPUFREQ_COOLING
tristate "DB8500 cpufreq cooling"
depends on ARCH_U8500 || COMPILE_TEST
......@@ -377,6 +367,17 @@ depends on ARCH_STI && OF
source "drivers/thermal/st/Kconfig"
endmenu
config TANGO_THERMAL
tristate "Tango thermal management"
depends on ARCH_TANGO || COMPILE_TEST
help
Enable the Tango thermal driver, which supports the primitive
temperature sensor embedded in Tango chips since the SMP8758.
This sensor only generates a 1-bit signal to indicate whether
the die temperature exceeds a programmable threshold.
source "drivers/thermal/tegra/Kconfig"
config QCOM_SPMI_TEMP_ALARM
tristate "Qualcomm SPMI PMIC Temperature Alarm"
depends on OF && SPMI && IIO
......@@ -388,4 +389,14 @@ config QCOM_SPMI_TEMP_ALARM
real time die temperature if an ADC is present or an estimate of the
temperature based upon the over temperature stage value.
config GENERIC_ADC_THERMAL
tristate "Generic ADC based thermal sensor"
depends on IIO
help
This enabled a thermal sysfs driver for the temperature sensor
which is connected to the General Purpose ADC. The ADC channel
is read via IIO framework and the channel information is provided
to this driver. This driver reports the temperature by reading ADC
channel and converts it to temperature based on lookup table.
endif
......@@ -35,6 +35,7 @@ obj-y += samsung/
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
......@@ -46,6 +47,7 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
obj-$(CONFIG_ST_THERMAL) += st/
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
......@@ -29,7 +29,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
struct thermal_instance *instance;
tz->ops->get_trip_temp(tz, trip, &trip_temp);
tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
if (!tz->ops->get_trip_hyst) {
pr_warn_once("Undefined get_trip_hyst for thermal zone %s - "
"running with default hysteresis zero\n", tz->type);
trip_hyst = 0;
} else
tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
trip, trip_temp, tz->temperature,
......
......@@ -160,7 +160,7 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
struct hisi_thermal_sensor *sensor = _sensor;
struct hisi_thermal_data *data = sensor->thermal;
int sensor_id = 0, i;
int sensor_id = -1, i;
long max_temp = 0;
*temp = hisi_thermal_get_sensor_temp(data, sensor);
......@@ -168,12 +168,19 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
sensor->sensor_temp = *temp;
for (i = 0; i < HISI_MAX_SENSORS; i++) {
if (!data->sensors[i].tzd)
continue;
if (data->sensors[i].sensor_temp >= max_temp) {
max_temp = data->sensors[i].sensor_temp;
sensor_id = i;
}
}
/* If no sensor has been enabled, then skip to enable irq */
if (sensor_id == -1)
return 0;
mutex_lock(&data->thermal_lock);
data->irq_bind_sensor = sensor_id;
mutex_unlock(&data->thermal_lock);
......@@ -226,8 +233,12 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
sensor->thres_temp / 1000);
mutex_unlock(&data->thermal_lock);
for (i = 0; i < HISI_MAX_SENSORS; i++)
for (i = 0; i < HISI_MAX_SENSORS; i++) {
if (!data->sensors[i].tzd)
continue;
thermal_zone_device_update(data->sensors[i].tzd);
}
return IRQ_HANDLED;
}
......@@ -243,10 +254,11 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
sensor->id = index;
sensor->thermal = data;
sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, sensor->id,
sensor, &hisi_of_thermal_ops);
sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
sensor->id, sensor, &hisi_of_thermal_ops);
if (IS_ERR(sensor->tzd)) {
ret = PTR_ERR(sensor->tzd);
sensor->tzd = NULL;
dev_err(&pdev->dev, "failed to register sensor id %d: %d\n",
sensor->id, ret);
return ret;
......@@ -331,28 +343,21 @@ static int hisi_thermal_probe(struct platform_device *pdev)
return ret;
}
hisi_thermal_enable_bind_irq_sensor(data);
irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED,
&data->irq_enabled);
for (i = 0; i < HISI_MAX_SENSORS; ++i) {
ret = hisi_thermal_register_sensor(pdev, data,
&data->sensors[i], i);
if (ret) {
if (ret)
dev_err(&pdev->dev,
"failed to register thermal sensor: %d\n", ret);
goto err_get_sensor_data;
}
else
hisi_thermal_toggle_sensor(&data->sensors[i], true);
}
hisi_thermal_enable_bind_irq_sensor(data);
data->irq_enabled = true;
for (i = 0; i < HISI_MAX_SENSORS; i++)
hisi_thermal_toggle_sensor(&data->sensors[i], true);
return 0;
err_get_sensor_data:
clk_disable_unprepare(data->clk);
return ret;
}
static int hisi_thermal_remove(struct platform_device *pdev)
......@@ -363,8 +368,10 @@ static int hisi_thermal_remove(struct platform_device *pdev)
for (i = 0; i < HISI_MAX_SENSORS; i++) {
struct hisi_thermal_sensor *sensor = &data->sensors[i];
if (!sensor->tzd)
continue;
hisi_thermal_toggle_sensor(sensor, false);
thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd);
}
hisi_thermal_disable_sensor(data);
......
......@@ -198,49 +198,33 @@ static struct thermal_zone_device_ops proc_thermal_local_ops = {
.get_temp = proc_thermal_get_zone_temp,
};
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
{
struct proc_thermal_device *proc_priv;
struct acpi_device *adev;
int i;
acpi_status status;
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc;
union acpi_object *p;
unsigned long long tmp;
struct thermal_zone_device_ops *ops = NULL;
int i;
int ret;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
int ret = 0;
status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf);
status = acpi_evaluate_object(proc_priv->adev->handle, "PPCC",
NULL, &buf);
if (ACPI_FAILURE(status))
return -ENODEV;
p = buf.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
dev_err(dev, "Invalid PPCC data\n");
dev_err(proc_priv->dev, "Invalid PPCC data\n");
ret = -EFAULT;
goto free_buffer;
}
if (!p->package.count) {
dev_err(dev, "Invalid PPCC package size\n");
dev_err(proc_priv->dev, "Invalid PPCC package size\n");
ret = -EFAULT;
goto free_buffer;
}
proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
if (!proc_priv) {
ret = -ENOMEM;
goto free_buffer;
}
proc_priv->dev = dev;
proc_priv->adev = adev;
for (i = 0; i < min((int)p->package.count - 1, 2); ++i) {
elements = &(p->package.elements[i+1]);
if (elements->type != ACPI_TYPE_PACKAGE ||
......@@ -257,12 +241,62 @@ static int proc_thermal_add(struct device *dev,
proc_priv->power_limits[i].step_uw = ppcc[5].integer.value;
}
free_buffer:
kfree(buf.pointer);
return ret;
}
#define PROC_POWER_CAPABILITY_CHANGED 0x83
static void proc_thermal_notify(acpi_handle handle, u32 event, void *data)
{
struct proc_thermal_device *proc_priv = data;
if (!proc_priv)
return;
switch (event) {
case PROC_POWER_CAPABILITY_CHANGED:
proc_thermal_read_ppcc(proc_priv);
int340x_thermal_zone_device_update(proc_priv->int340x_zone);
break;
default:
dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event);
break;
}
}
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
{
struct proc_thermal_device *proc_priv;
struct acpi_device *adev;
acpi_status status;
unsigned long long tmp;
struct thermal_zone_device_ops *ops = NULL;
int ret;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
if (!proc_priv)
return -ENOMEM;
proc_priv->dev = dev;
proc_priv->adev = adev;
*priv = proc_priv;
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
ret = proc_thermal_read_ppcc(proc_priv);
if (!ret) {
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
}
if (ret)
goto free_buffer;
return ret;
status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status)) {
......@@ -274,20 +308,32 @@ static int proc_thermal_add(struct device *dev,
proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
if (IS_ERR(proc_priv->int340x_zone)) {
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
ret = PTR_ERR(proc_priv->int340x_zone);
goto remove_group;
} else
ret = 0;
free_buffer:
kfree(buf.pointer);
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
proc_thermal_notify,
(void *)proc_priv);
if (ret)
goto remove_zone;
return 0;
remove_zone:
int340x_thermal_zone_remove(proc_priv->int340x_zone);
remove_group:
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
return ret;
}
static void proc_thermal_remove(struct proc_thermal_device *proc_priv)
{
acpi_remove_notify_handler(proc_priv->adev->handle,
ACPI_DEVICE_NOTIFY, proc_thermal_notify);
int340x_thermal_zone_remove(proc_priv->int340x_zone);
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
......
......@@ -510,12 +510,6 @@ static int start_power_clamp(void)
unsigned long cpu;
struct task_struct *thread;
/* check if pkg cstate counter is completely 0, abort in this case */
if (!has_pkg_state_counter()) {
pr_err("pkg cstate counter not functional, abort\n");
return -EINVAL;
}
set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
/* prevent cpu hotplug */
get_online_cpus();
......@@ -672,35 +666,11 @@ static struct thermal_cooling_device_ops powerclamp_cooling_ops = {
.set_cur_state = powerclamp_set_cur_state,
};
/* runs on Nehalem and later */
static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = {
{ X86_VENDOR_INTEL, 6, 0x1a},
{ X86_VENDOR_INTEL, 6, 0x1c},
{ X86_VENDOR_INTEL, 6, 0x1e},
{ X86_VENDOR_INTEL, 6, 0x1f},
{ X86_VENDOR_INTEL, 6, 0x25},
{ X86_VENDOR_INTEL, 6, 0x26},
{ X86_VENDOR_INTEL, 6, 0x2a},
{ X86_VENDOR_INTEL, 6, 0x2c},
{ X86_VENDOR_INTEL, 6, 0x2d},
{ X86_VENDOR_INTEL, 6, 0x2e},
{ X86_VENDOR_INTEL, 6, 0x2f},
{ X86_VENDOR_INTEL, 6, 0x37},
{ X86_VENDOR_INTEL, 6, 0x3a},
{ X86_VENDOR_INTEL, 6, 0x3c},
{ X86_VENDOR_INTEL, 6, 0x3d},
{ X86_VENDOR_INTEL, 6, 0x3e},
{ X86_VENDOR_INTEL, 6, 0x3f},
{ X86_VENDOR_INTEL, 6, 0x45},
{ X86_VENDOR_INTEL, 6, 0x46},
{ X86_VENDOR_INTEL, 6, 0x47},
{ X86_VENDOR_INTEL, 6, 0x4c},
{ X86_VENDOR_INTEL, 6, 0x4d},
{ X86_VENDOR_INTEL, 6, 0x4e},
{ X86_VENDOR_INTEL, 6, 0x4f},
{ X86_VENDOR_INTEL, 6, 0x56},
{ X86_VENDOR_INTEL, 6, 0x57},
{ X86_VENDOR_INTEL, 6, 0x5e},
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_MWAIT },
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ARAT },
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_NONSTOP_TSC },
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_CONSTANT_TSC},
{}
};
MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
......@@ -712,11 +682,12 @@ static int __init powerclamp_probe(void)
boot_cpu_data.x86, boot_cpu_data.x86_model);
return -ENODEV;
}
if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC) ||
!boot_cpu_has(X86_FEATURE_CONSTANT_TSC) ||
!boot_cpu_has(X86_FEATURE_MWAIT) ||
!boot_cpu_has(X86_FEATURE_ARAT))
/* The goal for idle time alignment is to achieve package cstate. */
if (!has_pkg_state_counter()) {
pr_info("No package C-state available");
return -ENODEV;
}
/* find the deepest mwait value */
find_target_mwait();
......
......@@ -144,7 +144,6 @@ struct mtk_thermal {
s32 o_slope;
s32 vts[MT8173_NUM_SENSORS];
struct thermal_zone_device *tzd;
};
struct mtk_thermal_bank_cfg {
......@@ -572,16 +571,11 @@ static int mtk_thermal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mt);
mt->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
&mtk_thermal_ops);
if (IS_ERR(mt->tzd))
goto err_register;
devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
&mtk_thermal_ops);
return 0;
err_register:
clk_disable_unprepare(mt->clk_peri_therm);
err_disable_clk_auxadc:
clk_disable_unprepare(mt->clk_auxadc);
......@@ -592,8 +586,6 @@ static int mtk_thermal_remove(struct platform_device *pdev)
{
struct mtk_thermal *mt = platform_get_drvdata(pdev);
thermal_zone_of_sensor_unregister(&pdev->dev, mt->tzd);
clk_disable_unprepare(mt->clk_peri_therm);
clk_disable_unprepare(mt->clk_auxadc);
......
......@@ -331,6 +331,14 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
if (trip >= data->ntrips || trip < 0)
return -EDOM;
if (data->ops->set_trip_temp) {
int ret;
ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
if (ret)
return ret;
}
/* thermal framework should take care of data->mask & (1 << trip) */
data->trips[trip].temperature = temp;
......@@ -906,7 +914,7 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
return tz;
free_tbps:
for (i = 0; i < tz->num_tbps; i++)
for (i = i - 1; i >= 0; i--)
of_node_put(tz->tbps[i].cooling_device);
kfree(tz->tbps);
free_trips:
......
......@@ -260,7 +260,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
if (ret < 0)
goto fail;
chip->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
&qpnp_tm_sensor_ops);
if (IS_ERR(chip->tz_dev)) {
dev_err(&pdev->dev, "failed to register sensor\n");
......@@ -281,7 +281,6 @@ static int qpnp_tm_remove(struct platform_device *pdev)
{
struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);
thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev);
if (!IS_ERR(chip->adc))
iio_channel_release(chip->adc);
......
......@@ -492,7 +492,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
goto error_unregister;
if (of_data == USE_OF_THERMAL)
priv->zone = thermal_zone_of_sensor_register(
priv->zone = devm_thermal_zone_of_sensor_register(
dev, i, priv,
&rcar_thermal_zone_of_ops);
else
......
This diff is collapsed.
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>
/*
* According to a data sheet draft, "this temperature sensor uses a bandgap
* type of circuit to compare a voltage which has a negative temperature
* coefficient with a voltage that is proportional to absolute temperature.
* A resistor bank allows 41 different temperature thresholds to be selected
* and the logic output will then indicate whether the actual die temperature
* lies above or below the selected threshold."
*/
#define TEMPSI_CMD 0
#define TEMPSI_RES 4
#define TEMPSI_CFG 8
#define CMD_OFF 0
#define CMD_ON 1
#define CMD_READ 2
#define IDX_MIN 15
#define IDX_MAX 40
struct tango_thermal_priv {
void __iomem *base;
int thresh_idx;
};
static bool temp_above_thresh(void __iomem *base, int thresh_idx)
{
writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
usleep_range(10, 20);
writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
return readl(base + TEMPSI_RES);
}
static int tango_get_temp(void *arg, int *res)
{
struct tango_thermal_priv *priv = arg;
int idx = priv->thresh_idx;
if (temp_above_thresh(priv->base, idx)) {
/* Search upward by incrementing thresh_idx */
while (idx < IDX_MAX && temp_above_thresh(priv->base, ++idx))
cpu_relax();
idx = idx - 1; /* always return lower bound */
} else {
/* Search downward by decrementing thresh_idx */
while (idx > IDX_MIN && !temp_above_thresh(priv->base, --idx))
cpu_relax();
}
*res = (idx * 9 / 2 - 38) * 1000; /* millidegrees Celsius */
priv->thresh_idx = idx;
return 0;
}
static const struct thermal_zone_of_device_ops ops = {
.get_temp = tango_get_temp,
};
static int tango_thermal_probe(struct platform_device *pdev)
{
struct resource *res;
struct tango_thermal_priv *priv;
struct thermal_zone_device *tzdev;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->thresh_idx = IDX_MIN;
writel(0, priv->base + TEMPSI_CFG);
writel(CMD_ON, priv->base + TEMPSI_CMD);
tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops);
return PTR_ERR_OR_ZERO(tzdev);
}
static const struct of_device_id tango_sensor_ids[] = {
{
.compatible = "sigma,smp8758-thermal",
},
{ /* sentinel */ }
};
static struct platform_driver tango_thermal_driver = {
.probe = tango_thermal_probe,
.driver = {
.name = "tango-thermal",
.of_match_table = tango_sensor_ids,
},
};
module_platform_driver(tango_thermal_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sigma Designs");
MODULE_DESCRIPTION("Tango temperature sensor");
menu "NVIDIA Tegra thermal drivers"
depends on ARCH_TEGRA
config TEGRA_SOCTHERM
tristate "Tegra SOCTHERM thermal management"
help
Enable this option for integrated thermal management support on NVIDIA
Tegra systems-on-chip. The driver supports four thermal zones
(CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
zones to manage temperatures. This option is also required for the
emergency thermal reset (thermtrip) feature to function.
endmenu
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra-soctherm.o
tegra-soctherm-y := soctherm.o soctherm-fuse.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-soctherm.o
/*
* Copyright (c) 2014-2016, 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
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <soc/tegra/fuse.h>
#include "soctherm.h"
#define NOMINAL_CALIB_FT 105
#define NOMINAL_CALIB_CP 25
#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
#define FUSE_TSENSOR_COMMON 0x180
/*
* Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
* 3 2 1 0
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | BASE_FT | BASE_CP | SHFT_FT | SHIFT_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Tegra12x, etc:
* In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
* and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
* were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
*
* FUSE_TSENSOR_COMMON:
* 3 2 1 0
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |-----------| SHFT_FT | BASE_FT | BASE_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* FUSE_SPARE_REALIGNMENT_REG:
* 3 2 1 0
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |---------------------------------------------------| SHIFT_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#define CALIB_COEFFICIENT 1000000LL
/**
* div64_s64_precise() - wrapper for div64_s64()
* @a: the dividend
* @b: the divisor
*
* Implements division with fairly accurate rounding instead of truncation by
* shifting the dividend to the left by 16 so that the quotient has a
* much higher precision.
*
* Return: the quotient of a / b.
*/
static s64 div64_s64_precise(s64 a, s32 b)
{
s64 r, al;
/* Scale up for increased precision division */
al = a << 16;
r = div64_s64(al * 2 + 1, 2 * b);
return r >> 16;
}
int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
struct tsensor_shared_calib *shared)
{
u32 val;
s32 shifted_cp, shifted_ft;
int err;
err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
if (err)
return err;
shared->base_cp = (val & tfuse->fuse_base_cp_mask) >>
tfuse->fuse_base_cp_shift;
shared->base_ft = (val & tfuse->fuse_base_ft_mask) >>
tfuse->fuse_base_ft_shift;
shifted_ft = (val & tfuse->fuse_shift_ft_mask) >>
tfuse->fuse_shift_ft_shift;
shifted_ft = sign_extend32(shifted_ft, 4);
if (tfuse->fuse_spare_realignment) {
err = tegra_fuse_readl(tfuse->fuse_spare_realignment, &val);
if (err)
return err;
}
shifted_cp = sign_extend32(val, 5);
shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
return 0;
}
int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
const struct tsensor_shared_calib *shared,
u32 *calibration)
{
const struct tegra_tsensor_group *sensor_group;
u32 val, calib;
s32 actual_tsensor_ft, actual_tsensor_cp;
s32 delta_sens, delta_temp;
s32 mult, div;
s16 therma, thermb;
s64 temp;
int err;
sensor_group = sensor->group;
err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
if (err)
return err;
actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) >>
FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
delta_sens = actual_tsensor_ft - actual_tsensor_cp;
delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
mult = sensor_group->pdiv * sensor->config->tsample_ate;
div = sensor->config->tsample * sensor_group->pdiv_ate;
temp = (s64)delta_temp * (1LL << 13) * mult;
therma = div64_s64_precise(temp, (s64)delta_sens * div);
temp = ((s64)actual_tsensor_ft * shared->actual_temp_cp) -
((s64)actual_tsensor_cp * shared->actual_temp_ft);
thermb = div64_s64_precise(temp, delta_sens);
temp = (s64)therma * sensor->fuse_corr_alpha;
therma = div64_s64_precise(temp, CALIB_COEFFICIENT);
temp = (s64)thermb * sensor->fuse_corr_alpha + sensor->fuse_corr_beta;
thermb = div64_s64_precise(temp, CALIB_COEFFICIENT);
calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
*calibration = calib;
return 0;
}
MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
/*
* Copyright (c) 2014-2016, 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
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#ifndef __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
#define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
#define SENSOR_CONFIG2 8
#define SENSOR_CONFIG2_THERMA_MASK (0xffff << 16)
#define SENSOR_CONFIG2_THERMA_SHIFT 16
#define SENSOR_CONFIG2_THERMB_MASK 0xffff
#define SENSOR_CONFIG2_THERMB_SHIFT 0
#define THERMCTL_THERMTRIP_CTL 0x80
/* BITs are defined in device file */
#define SENSOR_PDIV 0x1c0
#define SENSOR_PDIV_CPU_MASK (0xf << 12)
#define SENSOR_PDIV_GPU_MASK (0xf << 8)
#define SENSOR_PDIV_MEM_MASK (0xf << 4)
#define SENSOR_PDIV_PLLX_MASK (0xf << 0)
#define SENSOR_HOTSPOT_OFF 0x1c4
#define SENSOR_HOTSPOT_CPU_MASK (0xff << 16)
#define SENSOR_HOTSPOT_GPU_MASK (0xff << 8)
#define SENSOR_HOTSPOT_MEM_MASK (0xff << 0)
#define SENSOR_TEMP1 0x1c8
#define SENSOR_TEMP1_CPU_TEMP_MASK (0xffff << 16)
#define SENSOR_TEMP1_GPU_TEMP_MASK 0xffff
#define SENSOR_TEMP2 0x1cc
#define SENSOR_TEMP2_MEM_TEMP_MASK (0xffff << 16)
#define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff
/**
* struct tegra_tsensor_group - SOC_THERM sensor group data
* @name: short name of the temperature sensor group
* @id: numeric ID of the temperature sensor group
* @sensor_temp_offset: offset of the SENSOR_TEMP* register
* @sensor_temp_mask: bit mask for this sensor group in SENSOR_TEMP* register
* @pdiv: the sensor count post-divider to use during runtime
* @pdiv_ate: the sensor count post-divider used during automated test
* @pdiv_mask: register bitfield mask for the PDIV field for this sensor
* @pllx_hotspot_diff: hotspot offset from the PLLX sensor, must be 0 for
PLLX sensor group
* @pllx_hotspot_mask: register bitfield mask for the HOTSPOT field
*/
struct tegra_tsensor_group {
const char *name;
u8 id;
u16 sensor_temp_offset;
u32 sensor_temp_mask;
u32 pdiv, pdiv_ate, pdiv_mask;
u32 pllx_hotspot_diff, pllx_hotspot_mask;
u32 thermtrip_enable_mask;
u32 thermtrip_any_en_mask;
u32 thermtrip_threshold_mask;
};
struct tegra_tsensor_configuration {
u32 tall, tiddq_en, ten_count, pdiv, pdiv_ate, tsample, tsample_ate;
};
struct tegra_tsensor {
const char *name;
const u32 base;
const struct tegra_tsensor_configuration *config;
const u32 calib_fuse_offset;
/*
* Correction values used to modify values read from
* calibration fuses
*/
const s32 fuse_corr_alpha, fuse_corr_beta;
const struct tegra_tsensor_group *group;
};
struct tegra_soctherm_fuse {
u32 fuse_base_cp_mask, fuse_base_cp_shift;
u32 fuse_base_ft_mask, fuse_base_ft_shift;
u32 fuse_shift_ft_mask, fuse_shift_ft_shift;
u32 fuse_spare_realignment;
};
struct tsensor_shared_calib {
u32 base_cp, base_ft;
u32 actual_temp_cp, actual_temp_ft;
};
struct tegra_soctherm_soc {
const struct tegra_tsensor *tsensors;
const unsigned int num_tsensors;
const struct tegra_tsensor_group **ttgs;
const unsigned int num_ttgs;
const struct tegra_soctherm_fuse *tfuse;
const int thresh_grain;
};
int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
struct tsensor_shared_calib *shared);
int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
const struct tsensor_shared_calib *shared,
u32 *calib);
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_soctherm_soc tegra124_soctherm;
#endif
#ifdef CONFIG_ARCH_TEGRA_132_SOC
extern const struct tegra_soctherm_soc tegra132_soctherm;
#endif
#ifdef CONFIG_ARCH_TEGRA_210_SOC
extern const struct tegra_soctherm_soc tegra210_soctherm;
#endif
#endif
/*
* Copyright (c) 2014-2016, 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
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include "soctherm.h"
#define TEGRA124_THERMTRIP_ANY_EN_MASK (0x1 << 28)
#define TEGRA124_THERMTRIP_MEM_EN_MASK (0x1 << 27)
#define TEGRA124_THERMTRIP_GPU_EN_MASK (0x1 << 26)
#define TEGRA124_THERMTRIP_CPU_EN_MASK (0x1 << 25)
#define TEGRA124_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
#define TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
#define TEGRA124_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
#define TEGRA124_THERMTRIP_TSENSE_THRESH_MASK 0xff
#define TEGRA124_THRESH_GRAIN 1000
static const struct tegra_tsensor_configuration tegra124_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 120,
.tsample_ate = 480,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = {
.id = TEGRA124_SOCTHERM_SENSOR_CPU,
.name = "cpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
.pllx_hotspot_diff = 10,
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
.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,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
.id = TEGRA124_SOCTHERM_SENSOR_GPU,
.name = "gpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
.pllx_hotspot_diff = 5,
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
.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,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
.id = TEGRA124_SOCTHERM_SENSOR_PLLX,
.name = "pll",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
.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,
};
static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
.id = TEGRA124_SOCTHERM_SENSOR_MEM,
.name = "mem",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
.pllx_hotspot_diff = 0,
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
.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,
};
static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = {
&tegra124_tsensor_group_cpu,
&tegra124_tsensor_group_gpu,
&tegra124_tsensor_group_pll,
&tegra124_tsensor_group_mem,
};
static const struct tegra_tsensor tegra124_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1135400,
.fuse_corr_beta = -6266900,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1122220,
.fuse_corr_beta = -5700700,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1127000,
.fuse_corr_beta = -6768200,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1110900,
.fuse_corr_beta = -6232000,
.group = &tegra124_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1122300,
.fuse_corr_beta = -5936400,
.group = &tegra124_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1145700,
.fuse_corr_beta = -7124600,
.group = &tegra124_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1120100,
.fuse_corr_beta = -6000500,
.group = &tegra124_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra124_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1106500,
.fuse_corr_beta = -6729300,
.group = &tegra124_tsensor_group_pll,
},
};
/*
* Mask/shift bits in FUSE_TSENSOR_COMMON and
* FUSE_TSENSOR_COMMON, which are described in
* tegra_soctherm_fuse.c
*/
static const struct tegra_soctherm_fuse tegra124_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
.fuse_spare_realignment = 0x1fc,
};
const struct tegra_soctherm_soc tegra124_soctherm = {
.tsensors = tegra124_tsensors,
.num_tsensors = ARRAY_SIZE(tegra124_tsensors),
.ttgs = tegra124_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups),
.tfuse = &tegra124_soctherm_fuse,
.thresh_grain = TEGRA124_THRESH_GRAIN,
};
/*
* Copyright (c) 2014-2016, 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
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include "soctherm.h"
#define TEGRA132_THERMTRIP_ANY_EN_MASK (0x1 << 28)
#define TEGRA132_THERMTRIP_MEM_EN_MASK (0x1 << 27)
#define TEGRA132_THERMTRIP_GPU_EN_MASK (0x1 << 26)
#define TEGRA132_THERMTRIP_CPU_EN_MASK (0x1 << 25)
#define TEGRA132_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
#define TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
#define TEGRA132_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
#define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK 0xff
#define TEGRA132_THRESH_GRAIN 1000
static const struct tegra_tsensor_configuration tegra132_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 120,
.tsample_ate = 480,
};
static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = {
.id = TEGRA124_SOCTHERM_SENSOR_CPU,
.name = "cpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
.pllx_hotspot_diff = 10,
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
.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,
};
static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
.id = TEGRA124_SOCTHERM_SENSOR_GPU,
.name = "gpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
.pllx_hotspot_diff = 5,
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
.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,
};
static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
.id = TEGRA124_SOCTHERM_SENSOR_PLLX,
.name = "pll",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
.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,
};
static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
.id = TEGRA124_SOCTHERM_SENSOR_MEM,
.name = "mem",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
.pllx_hotspot_diff = 0,
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
.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,
};
static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = {
&tegra132_tsensor_group_cpu,
&tegra132_tsensor_group_gpu,
&tegra132_tsensor_group_pll,
&tegra132_tsensor_group_mem,
};
static struct tegra_tsensor tegra132_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1126600,
.fuse_corr_beta = -9433500,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1110800,
.fuse_corr_beta = -7383000,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1113800,
.fuse_corr_beta = -6215200,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1129600,
.fuse_corr_beta = -8196100,
.group = &tegra132_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1132900,
.fuse_corr_beta = -6755300,
.group = &tegra132_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1142300,
.fuse_corr_beta = -7374200,
.group = &tegra132_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1125100,
.fuse_corr_beta = -6350400,
.group = &tegra132_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra132_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1118100,
.fuse_corr_beta = -8208800,
.group = &tegra132_tsensor_group_pll,
},
};
/*
* Mask/shift bits in FUSE_TSENSOR_COMMON and
* FUSE_TSENSOR_COMMON, which are described in
* tegra_soctherm_fuse.c
*/
static const struct tegra_soctherm_fuse tegra132_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
.fuse_spare_realignment = 0x1fc,
};
const struct tegra_soctherm_soc tegra132_soctherm = {
.tsensors = tegra132_tsensors,
.num_tsensors = ARRAY_SIZE(tegra132_tsensors),
.ttgs = tegra132_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups),
.tfuse = &tegra132_soctherm_fuse,
.thresh_grain = TEGRA132_THRESH_GRAIN,
};
/*
* Copyright (c) 2014-2016, 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
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <soc/tegra/fuse.h>
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include "soctherm.h"
#define TEGRA210_THERMTRIP_ANY_EN_MASK (0x1 << 31)
#define TEGRA210_THERMTRIP_MEM_EN_MASK (0x1 << 30)
#define TEGRA210_THERMTRIP_GPU_EN_MASK (0x1 << 29)
#define TEGRA210_THERMTRIP_CPU_EN_MASK (0x1 << 28)
#define TEGRA210_THERMTRIP_TSENSE_EN_MASK (0x1 << 27)
#define TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK (0x1ff << 18)
#define TEGRA210_THERMTRIP_CPU_THRESH_MASK (0x1ff << 9)
#define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK 0x1ff
#define TEGRA210_THRESH_GRAIN 500
static const struct tegra_tsensor_configuration tegra210_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 120,
.tsample_ate = 480,
};
static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = {
.id = TEGRA124_SOCTHERM_SENSOR_CPU,
.name = "cpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
.pllx_hotspot_diff = 10,
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
.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,
};
static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
.id = TEGRA124_SOCTHERM_SENSOR_GPU,
.name = "gpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
.pllx_hotspot_diff = 5,
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
.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,
};
static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
.id = TEGRA124_SOCTHERM_SENSOR_PLLX,
.name = "pll",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
.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,
};
static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
.id = TEGRA124_SOCTHERM_SENSOR_MEM,
.name = "mem",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
.pdiv = 8,
.pdiv_ate = 8,
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
.pllx_hotspot_diff = 0,
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
.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,
};
static const struct tegra_tsensor_group *tegra210_tsensor_groups[] = {
&tegra210_tsensor_group_cpu,
&tegra210_tsensor_group_gpu,
&tegra210_tsensor_group_pll,
&tegra210_tsensor_group_mem,
};
static const struct tegra_tsensor tegra210_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1085000,
.fuse_corr_beta = 3244200,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1126200,
.fuse_corr_beta = -67500,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1098400,
.fuse_corr_beta = 2251100,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1108000,
.fuse_corr_beta = 602700,
.group = &tegra210_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1069200,
.fuse_corr_beta = 3549900,
.group = &tegra210_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1173700,
.fuse_corr_beta = -6263600,
.group = &tegra210_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1074300,
.fuse_corr_beta = 2734900,
.group = &tegra210_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra210_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1039700,
.fuse_corr_beta = 6829100,
.group = &tegra210_tsensor_group_pll,
},
};
/*
* Mask/shift bits in FUSE_TSENSOR_COMMON and
* FUSE_TSENSOR_COMMON, which are described in
* tegra_soctherm_fuse.c
*/
static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff << 11,
.fuse_base_cp_shift = 11,
.fuse_base_ft_mask = 0x7ff << 21,
.fuse_base_ft_shift = 21,
.fuse_shift_ft_mask = 0x1f << 6,
.fuse_shift_ft_shift = 6,
.fuse_spare_realignment = 0,
};
const struct tegra_soctherm_soc tegra210_soctherm = {
.tsensors = tegra210_tsensors,
.num_tsensors = ARRAY_SIZE(tegra210_tsensors),
.ttgs = tegra210_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra210_tsensor_groups),
.tfuse = &tegra210_soctherm_fuse,
.thresh_grain = TEGRA210_THRESH_GRAIN,
};
This diff is collapsed.
/*
* Generic ADC thermal driver
*
* Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/iio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/thermal.h>
struct gadc_thermal_info {
struct device *dev;
struct thermal_zone_device *tz_dev;
struct iio_channel *channel;
s32 *lookup_table;
int nlookup_table;
};
static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val)
{
int temp, adc_hi, adc_lo;
int i;
for (i = 0; i < gti->nlookup_table; i++) {
if (val >= gti->lookup_table[2 * i + 1])
break;
}
if (i == 0) {
temp = gti->lookup_table[0];
} else if (i >= (gti->nlookup_table - 1)) {
temp = gti->lookup_table[2 * (gti->nlookup_table - 1)];
} else {
adc_hi = gti->lookup_table[2 * i - 1];
adc_lo = gti->lookup_table[2 * i + 1];
temp = gti->lookup_table[2 * i];
temp -= ((val - adc_lo) * 1000) / (adc_hi - adc_lo);
}
return temp;
}
static int gadc_thermal_get_temp(void *data, int *temp)
{
struct gadc_thermal_info *gti = data;
int val;
int ret;
ret = iio_read_channel_processed(gti->channel, &val);
if (ret < 0) {
dev_err(gti->dev, "IIO channel read failed %d\n", ret);
return ret;
}
*temp = gadc_thermal_adc_to_temp(gti, val);
return 0;
}
static const struct thermal_zone_of_device_ops gadc_thermal_ops = {
.get_temp = gadc_thermal_get_temp,
};
static int gadc_thermal_read_linear_lookup_table(struct device *dev,
struct gadc_thermal_info *gti)
{
struct device_node *np = dev->of_node;
int ntable;
int ret;
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 % 2) {
dev_err(dev, "Pair of temperature vs ADC read value missing\n");
return -EINVAL;
}
gti->lookup_table = devm_kzalloc(dev, sizeof(*gti->lookup_table) *
ntable, GFP_KERNEL);
if (!gti->lookup_table)
return -ENOMEM;
ret = of_property_read_u32_array(np, "temperature-lookup-table",
(u32 *)gti->lookup_table, ntable);
if (ret < 0) {
dev_err(dev, "Failed to read temperature lookup table: %d\n",
ret);
return ret;
}
gti->nlookup_table = ntable / 2;
return 0;
}
static int gadc_thermal_probe(struct platform_device *pdev)
{
struct gadc_thermal_info *gti;
int ret;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "Only DT based supported\n");
return -ENODEV;
}
gti = devm_kzalloc(&pdev->dev, sizeof(*gti), GFP_KERNEL);
if (!gti)
return -ENOMEM;
ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti);
if (ret < 0)
return ret;
gti->dev = &pdev->dev;
platform_set_drvdata(pdev, gti);
gti->channel = iio_channel_get(&pdev->dev, "sensor-channel");
if (IS_ERR(gti->channel)) {
ret = PTR_ERR(gti->channel);
dev_err(&pdev->dev, "IIO channel not found: %d\n", ret);
return ret;
}
gti->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0,
gti, &gadc_thermal_ops);
if (IS_ERR(gti->tz_dev)) {
ret = PTR_ERR(gti->tz_dev);
dev_err(&pdev->dev, "Thermal zone sensor register failed: %d\n",
ret);
goto sensor_fail;
}
return 0;
sensor_fail:
iio_channel_release(gti->channel);
return ret;
}
static int gadc_thermal_remove(struct platform_device *pdev)
{
struct gadc_thermal_info *gti = platform_get_drvdata(pdev);
thermal_zone_of_sensor_unregister(&pdev->dev, gti->tz_dev);
iio_channel_release(gti->channel);
return 0;
}
static const struct of_device_id of_adc_thermal_match[] = {
{ .compatible = "generic-adc-thermal", },
{},
};
MODULE_DEVICE_TABLE(of, of_adc_thermal_match);
static struct platform_driver gadc_thermal_driver = {
.driver = {
.name = "generic-adc-thermal",
.of_match_table = of_adc_thermal_match,
},
.probe = gadc_thermal_probe,
.remove = gadc_thermal_remove,
};
module_platform_driver(gadc_thermal_driver);
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT");
MODULE_LICENSE("GPL v2");
......@@ -337,7 +337,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
return -EINVAL;
/* in case this is specified by DT */
data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id,
data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id,
data, &ti_of_thermal_ops);
if (IS_ERR(data->ti_thermal)) {
/* Create thermal zone */
......@@ -368,9 +368,6 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
if (data && data->ti_thermal) {
if (data->our_zone)
thermal_zone_device_unregister(data->ti_thermal);
else
thermal_zone_of_sensor_unregister(bgp->dev,
data->ti_thermal);
}
return 0;
......
......@@ -555,7 +555,7 @@ static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb,
{
unsigned int cpu = (unsigned long) hcpu;
switch (action) {
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
case CPU_DOWN_FAILED:
get_core_online(cpu);
......
......@@ -9,5 +9,6 @@
#define TEGRA124_SOCTHERM_SENSOR_MEM 1
#define TEGRA124_SOCTHERM_SENSOR_GPU 2
#define TEGRA124_SOCTHERM_SENSOR_PLLX 3
#define TEGRA124_SOCTHERM_SENSOR_NUM 4
#endif
......@@ -340,6 +340,7 @@ struct thermal_zone_of_device_ops {
int (*get_temp)(void *, int *);
int (*get_trend)(void *, long *);
int (*set_emul_temp)(void *, int);
int (*set_trip_temp)(void *, int, int);
};
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment