forked from Minki/linux
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 ...
This commit is contained in:
commit
bfb764440d
@ -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".
|
||||
|
17
Documentation/devicetree/bindings/thermal/tango-thermal.txt
Normal file
17
Documentation/devicetree/bindings/thermal/tango-thermal.txt
Normal file
@ -0,0 +1,17 @@
|
||||
* 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>;
|
||||
};
|
@ -0,0 +1,89 @@
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
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 @@ finish:
|
||||
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
|
||||
|
@ -1,7 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
|
||||
* Copyright (c) 2014-2016, Fuzhou Rockchip Electronics Co., Ltd
|
||||
* Caesar Wang <wxt@rock-chips.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@ -23,8 +21,10 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/**
|
||||
@ -73,7 +73,7 @@ enum adc_sort_mode {
|
||||
#define SOC_MAX_SENSORS 2
|
||||
|
||||
/**
|
||||
* struct chip_tsadc_table: hold information about chip-specific differences
|
||||
* struct chip_tsadc_table - hold information about chip-specific differences
|
||||
* @id: conversion table
|
||||
* @length: size of conversion table
|
||||
* @data_mask: mask to apply on data inputs
|
||||
@ -86,6 +86,20 @@ struct chip_tsadc_table {
|
||||
enum adc_sort_mode mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_tsadc_chip - hold the private data of tsadc chip
|
||||
* @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel
|
||||
* @chn_num: the channel number of tsadc chip
|
||||
* @tshut_temp: the hardware-controlled shutdown temperature value
|
||||
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
|
||||
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
|
||||
* @initialize: SoC special initialize tsadc controller method
|
||||
* @irq_ack: clear the interrupt
|
||||
* @get_temp: get the temperature
|
||||
* @set_tshut_temp: set the hardware-controlled shutdown temperature
|
||||
* @set_tshut_mode: set the hardware-controlled shutdown mode
|
||||
* @table: the chip-specific conversion table
|
||||
*/
|
||||
struct rockchip_tsadc_chip {
|
||||
/* The sensor id of chip correspond to the ADC channel */
|
||||
int chn_id[SOC_MAX_SENSORS];
|
||||
@ -97,7 +111,8 @@ struct rockchip_tsadc_chip {
|
||||
enum tshut_polarity tshut_polarity;
|
||||
|
||||
/* Chip-wide methods */
|
||||
void (*initialize)(void __iomem *reg, enum tshut_polarity p);
|
||||
void (*initialize)(struct regmap *grf,
|
||||
void __iomem *reg, enum tshut_polarity p);
|
||||
void (*irq_ack)(void __iomem *reg);
|
||||
void (*control)(void __iomem *reg, bool on);
|
||||
|
||||
@ -112,12 +127,32 @@ struct rockchip_tsadc_chip {
|
||||
struct chip_tsadc_table table;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_thermal_sensor - hold the information of thermal sensor
|
||||
* @thermal: pointer to the platform/configuration data
|
||||
* @tzd: pointer to a thermal zone
|
||||
* @id: identifier of the thermal sensor
|
||||
*/
|
||||
struct rockchip_thermal_sensor {
|
||||
struct rockchip_thermal_data *thermal;
|
||||
struct thermal_zone_device *tzd;
|
||||
int id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_thermal_data - hold the private data of thermal driver
|
||||
* @chip: pointer to the platform/configuration data
|
||||
* @pdev: platform device of thermal
|
||||
* @reset: the reset controller of tsadc
|
||||
* @sensors[SOC_MAX_SENSORS]: the thermal sensor
|
||||
* @clk: the controller clock is divided by the exteral 24MHz
|
||||
* @pclk: the advanced peripherals bus clock
|
||||
* @grf: the general register file will be used to do static set by software
|
||||
* @regs: the base address of tsadc controller
|
||||
* @tshut_temp: the hardware-controlled shutdown temperature value
|
||||
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
|
||||
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
|
||||
*/
|
||||
struct rockchip_thermal_data {
|
||||
const struct rockchip_tsadc_chip *chip;
|
||||
struct platform_device *pdev;
|
||||
@ -128,6 +163,7 @@ struct rockchip_thermal_data {
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
|
||||
struct regmap *grf;
|
||||
void __iomem *regs;
|
||||
|
||||
int tshut_temp;
|
||||
@ -142,6 +178,7 @@ struct rockchip_thermal_data {
|
||||
* TSADCV3_* are used for newer SoCs than RK3288. (e.g: RK3228, RK3399)
|
||||
*
|
||||
*/
|
||||
#define TSADCV2_USER_CON 0x00
|
||||
#define TSADCV2_AUTO_CON 0x04
|
||||
#define TSADCV2_INT_EN 0x08
|
||||
#define TSADCV2_INT_PD 0x0c
|
||||
@ -155,12 +192,7 @@ struct rockchip_thermal_data {
|
||||
#define TSADCV2_AUTO_EN BIT(0)
|
||||
#define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn))
|
||||
#define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8)
|
||||
/**
|
||||
* TSADCV1_AUTO_Q_SEL_EN:
|
||||
* whether select (1024 - tsadc_q) as output
|
||||
* 1'b0:use tsadc_q as output(temperature-code is rising sequence)
|
||||
* 1'b1:use(1024 - tsadc_q) as output (temperature-code is falling sequence)
|
||||
*/
|
||||
|
||||
#define TSADCV3_AUTO_Q_SEL_EN BIT(1)
|
||||
|
||||
#define TSADCV2_INT_SRC_EN(chn) BIT(chn)
|
||||
@ -177,19 +209,32 @@ struct rockchip_thermal_data {
|
||||
#define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4
|
||||
#define TSADCV2_AUTO_PERIOD_TIME 250 /* msec */
|
||||
#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */
|
||||
#define TSADCV2_USER_INTER_PD_SOC 0x340 /* 13 clocks */
|
||||
|
||||
#define GRF_SARADC_TESTBIT 0x0e644
|
||||
#define GRF_TSADC_TESTBIT_L 0x0e648
|
||||
#define GRF_TSADC_TESTBIT_H 0x0e64c
|
||||
|
||||
#define GRF_TSADC_TSEN_PD_ON (0x30003 << 0)
|
||||
#define GRF_TSADC_TSEN_PD_OFF (0x30000 << 0)
|
||||
#define GRF_SARADC_TESTBIT_ON (0x10001 << 2)
|
||||
#define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2)
|
||||
|
||||
/**
|
||||
* struct tsadc_table - code to temperature conversion table
|
||||
* @code: the value of adc channel
|
||||
* @temp: the temperature
|
||||
* Note:
|
||||
* code to temperature mapping of the temperature sensor is a piece wise linear
|
||||
* curve.Any temperature, code faling between to 2 give temperatures can be
|
||||
* linearly interpolated.
|
||||
* Code to Temperature mapping should be updated based on manufacturer results.
|
||||
*/
|
||||
struct tsadc_table {
|
||||
u32 code;
|
||||
int temp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* Code to Temperature mapping of the Temperature sensor is a piece wise linear
|
||||
* curve.Any temperature, code faling between to 2 give temperatures can be
|
||||
* linearly interpolated.
|
||||
* Code to Temperature mapping should be updated based on sillcon results.
|
||||
*/
|
||||
static const struct tsadc_table rk3228_code_table[] = {
|
||||
{0, -40000},
|
||||
{588, -40000},
|
||||
@ -308,40 +353,40 @@ static const struct tsadc_table rk3368_code_table[] = {
|
||||
|
||||
static const struct tsadc_table rk3399_code_table[] = {
|
||||
{0, -40000},
|
||||
{593, -40000},
|
||||
{598, -35000},
|
||||
{603, -30000},
|
||||
{609, -25000},
|
||||
{614, -20000},
|
||||
{619, -15000},
|
||||
{625, -10000},
|
||||
{630, -5000},
|
||||
{635, 0},
|
||||
{641, 5000},
|
||||
{646, 10000},
|
||||
{651, 15000},
|
||||
{657, 20000},
|
||||
{662, 25000},
|
||||
{667, 30000},
|
||||
{673, 35000},
|
||||
{678, 40000},
|
||||
{684, 45000},
|
||||
{689, 50000},
|
||||
{694, 55000},
|
||||
{700, 60000},
|
||||
{705, 65000},
|
||||
{711, 70000},
|
||||
{716, 75000},
|
||||
{722, 80000},
|
||||
{727, 85000},
|
||||
{733, 90000},
|
||||
{738, 95000},
|
||||
{743, 100000},
|
||||
{749, 105000},
|
||||
{754, 110000},
|
||||
{760, 115000},
|
||||
{765, 120000},
|
||||
{771, 125000},
|
||||
{402, -40000},
|
||||
{410, -35000},
|
||||
{419, -30000},
|
||||
{427, -25000},
|
||||
{436, -20000},
|
||||
{444, -15000},
|
||||
{453, -10000},
|
||||
{461, -5000},
|
||||
{470, 0},
|
||||
{478, 5000},
|
||||
{487, 10000},
|
||||
{496, 15000},
|
||||
{504, 20000},
|
||||
{513, 25000},
|
||||
{521, 30000},
|
||||
{530, 35000},
|
||||
{538, 40000},
|
||||
{547, 45000},
|
||||
{555, 50000},
|
||||
{564, 55000},
|
||||
{573, 60000},
|
||||
{581, 65000},
|
||||
{590, 70000},
|
||||
{599, 75000},
|
||||
{607, 80000},
|
||||
{616, 85000},
|
||||
{624, 90000},
|
||||
{633, 95000},
|
||||
{642, 100000},
|
||||
{650, 105000},
|
||||
{659, 110000},
|
||||
{668, 115000},
|
||||
{677, 120000},
|
||||
{685, 125000},
|
||||
{TSADCV3_DATA_MASK, 125000},
|
||||
};
|
||||
|
||||
@ -405,8 +450,8 @@ static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
|
||||
return -EAGAIN; /* Incorrect reading */
|
||||
|
||||
while (low <= high) {
|
||||
if (code >= table.id[mid - 1].code &&
|
||||
code < table.id[mid].code)
|
||||
if (code <= table.id[mid].code &&
|
||||
code > table.id[mid - 1].code)
|
||||
break;
|
||||
else if (code > table.id[mid].code)
|
||||
low = mid + 1;
|
||||
@ -449,7 +494,7 @@ static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
|
||||
* If the temperature is higher than COMP_INT or COMP_SHUT for
|
||||
* "debounce" times, TSADC controller will generate interrupt or TSHUT.
|
||||
*/
|
||||
static void rk_tsadcv2_initialize(void __iomem *regs,
|
||||
static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs,
|
||||
enum tshut_polarity tshut_polarity)
|
||||
{
|
||||
if (tshut_polarity == TSHUT_HIGH_ACTIVE)
|
||||
@ -466,6 +511,62 @@ static void rk_tsadcv2_initialize(void __iomem *regs,
|
||||
regs + TSADCV2_AUTO_PERIOD_HT);
|
||||
writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
|
||||
regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
|
||||
|
||||
if (IS_ERR(grf)) {
|
||||
pr_warn("%s: Missing rockchip,grf property\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rk_tsadcv3_initialize - initialize TASDC Controller.
|
||||
*
|
||||
* (1) The tsadc control power sequence.
|
||||
*
|
||||
* (2) Set TSADC_V2_AUTO_PERIOD:
|
||||
* Configure the interleave between every two accessing of
|
||||
* TSADC in normal operation.
|
||||
*
|
||||
* (2) Set TSADCV2_AUTO_PERIOD_HT:
|
||||
* Configure the interleave between every two accessing of
|
||||
* TSADC after the temperature is higher than COM_SHUT or COM_INT.
|
||||
*
|
||||
* (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE:
|
||||
* If the temperature is higher than COMP_INT or COMP_SHUT for
|
||||
* "debounce" times, TSADC controller will generate interrupt or TSHUT.
|
||||
*/
|
||||
static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
|
||||
enum tshut_polarity tshut_polarity)
|
||||
{
|
||||
/* The tsadc control power sequence */
|
||||
if (IS_ERR(grf)) {
|
||||
/* Set interleave value to workround ic time sync issue */
|
||||
writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs +
|
||||
TSADCV2_USER_CON);
|
||||
} else {
|
||||
regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON);
|
||||
mdelay(10);
|
||||
regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF);
|
||||
usleep_range(15, 100); /* The spec note says at least 15 us */
|
||||
regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
|
||||
regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
|
||||
usleep_range(90, 200); /* The spec note says at least 90 us */
|
||||
}
|
||||
|
||||
if (tshut_polarity == TSHUT_HIGH_ACTIVE)
|
||||
writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
|
||||
regs + TSADCV2_AUTO_CON);
|
||||
else
|
||||
writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
|
||||
regs + TSADCV2_AUTO_CON);
|
||||
|
||||
writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
|
||||
writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
|
||||
regs + TSADCV2_HIGHT_INT_DEBOUNCE);
|
||||
writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
|
||||
regs + TSADCV2_AUTO_PERIOD_HT);
|
||||
writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
|
||||
regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
|
||||
}
|
||||
|
||||
static void rk_tsadcv2_irq_ack(void __iomem *regs)
|
||||
@ -498,10 +599,11 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable)
|
||||
}
|
||||
|
||||
/**
|
||||
* @rk_tsadcv3_control:
|
||||
* TSADC controller works at auto mode, and some SoCs need set the tsadc_q_sel
|
||||
* bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output adc value if
|
||||
* setting this bit to enable.
|
||||
* rk_tsadcv3_control - the tsadc controller is enabled or disabled.
|
||||
*
|
||||
* NOTE: TSADC controller works at auto mode, and some SoCs need set the
|
||||
* tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output
|
||||
* adc value if setting this bit to enable.
|
||||
*/
|
||||
static void rk_tsadcv3_control(void __iomem *regs, bool enable)
|
||||
{
|
||||
@ -603,6 +705,30 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rockchip_tsadc_chip rk3366_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, /* two channels for tsadc */
|
||||
|
||||
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
|
||||
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
|
||||
.tshut_temp = 95000,
|
||||
|
||||
.initialize = rk_tsadcv3_initialize,
|
||||
.irq_ack = rk_tsadcv3_irq_ack,
|
||||
.control = rk_tsadcv3_control,
|
||||
.get_temp = rk_tsadcv2_get_temp,
|
||||
.set_tshut_temp = rk_tsadcv2_tshut_temp,
|
||||
.set_tshut_mode = rk_tsadcv2_tshut_mode,
|
||||
|
||||
.table = {
|
||||
.id = rk3228_code_table,
|
||||
.length = ARRAY_SIZE(rk3228_code_table),
|
||||
.data_mask = TSADCV3_DATA_MASK,
|
||||
.mode = ADC_INCREMENT,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
|
||||
.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
|
||||
.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
|
||||
@ -636,7 +762,7 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
|
||||
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
|
||||
.tshut_temp = 95000,
|
||||
|
||||
.initialize = rk_tsadcv2_initialize,
|
||||
.initialize = rk_tsadcv3_initialize,
|
||||
.irq_ack = rk_tsadcv3_irq_ack,
|
||||
.control = rk_tsadcv3_control,
|
||||
.get_temp = rk_tsadcv2_get_temp,
|
||||
@ -660,6 +786,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
|
||||
.compatible = "rockchip,rk3288-tsadc",
|
||||
.data = (void *)&rk3288_tsadc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3366-tsadc",
|
||||
.data = (void *)&rk3366_tsadc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3368-tsadc",
|
||||
.data = (void *)&rk3368_tsadc_data,
|
||||
@ -768,6 +898,11 @@ static int rockchip_configure_from_dt(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The tsadc wont to handle the error in here since some SoCs didn't
|
||||
* need this property.
|
||||
*/
|
||||
thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -786,8 +921,8 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
|
||||
|
||||
sensor->thermal = thermal;
|
||||
sensor->id = id;
|
||||
sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, id, sensor,
|
||||
&rockchip_of_thermal_ops);
|
||||
sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id,
|
||||
sensor, &rockchip_of_thermal_ops);
|
||||
if (IS_ERR(sensor->tzd)) {
|
||||
error = PTR_ERR(sensor->tzd);
|
||||
dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
|
||||
@ -815,7 +950,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int i, j;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
match = of_match_node(of_rockchip_thermal_match, np);
|
||||
@ -888,7 +1023,8 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
|
||||
thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
|
||||
thermal->chip->initialize(thermal->grf, thermal->regs,
|
||||
thermal->tshut_polarity);
|
||||
|
||||
for (i = 0; i < thermal->chip->chn_num; i++) {
|
||||
error = rockchip_thermal_register_sensor(pdev, thermal,
|
||||
@ -898,9 +1034,6 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register sensor[%d] : error = %d\n",
|
||||
i, error);
|
||||
for (j = 0; j < i; j++)
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
thermal->sensors[j].tzd);
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
}
|
||||
@ -912,7 +1045,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request tsadc irq: %d\n", error);
|
||||
goto err_unregister_sensor;
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
|
||||
thermal->chip->control(thermal->regs, true);
|
||||
@ -924,11 +1057,6 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_sensor:
|
||||
while (i--)
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
thermal->sensors[i].tzd);
|
||||
|
||||
err_disable_pclk:
|
||||
clk_disable_unprepare(thermal->pclk);
|
||||
err_disable_clk:
|
||||
@ -946,7 +1074,6 @@ static int rockchip_thermal_remove(struct platform_device *pdev)
|
||||
struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
|
||||
|
||||
rockchip_thermal_toggle_sensor(sensor, false);
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd);
|
||||
}
|
||||
|
||||
thermal->chip->control(thermal->regs, false);
|
||||
@ -988,12 +1115,15 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
|
||||
return error;
|
||||
|
||||
error = clk_enable(thermal->pclk);
|
||||
if (error)
|
||||
if (error) {
|
||||
clk_disable(thermal->clk);
|
||||
return error;
|
||||
}
|
||||
|
||||
rockchip_thermal_reset_controller(thermal->reset);
|
||||
|
||||
thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
|
||||
thermal->chip->initialize(thermal->grf, thermal->regs,
|
||||
thermal->tshut_polarity);
|
||||
|
||||
for (i = 0; i < thermal->chip->chn_num; i++) {
|
||||
int id = thermal->sensors[i].id;
|
||||
|
109
drivers/thermal/tango_thermal.c
Normal file
109
drivers/thermal/tango_thermal.c
Normal file
@ -0,0 +1,109 @@
|
||||
#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");
|
13
drivers/thermal/tegra/Kconfig
Normal file
13
drivers/thermal/tegra/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
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
|
6
drivers/thermal/tegra/Makefile
Normal file
6
drivers/thermal/tegra/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
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
|
169
drivers/thermal/tegra/soctherm-fuse.c
Normal file
169
drivers/thermal/tegra/soctherm-fuse.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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");
|
685
drivers/thermal/tegra/soctherm.c
Normal file
685
drivers/thermal/tegra/soctherm.c
Normal file
@ -0,0 +1,685 @@
|
||||
/*
|
||||
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Mikko Perttunen <mperttunen@nvidia.com>
|
||||
*
|
||||
* 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/debugfs.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include <dt-bindings/thermal/tegra124-soctherm.h>
|
||||
|
||||
#include "soctherm.h"
|
||||
|
||||
#define SENSOR_CONFIG0 0
|
||||
#define SENSOR_CONFIG0_STOP BIT(0)
|
||||
#define SENSOR_CONFIG0_CPTR_OVER BIT(2)
|
||||
#define SENSOR_CONFIG0_OVER BIT(3)
|
||||
#define SENSOR_CONFIG0_TCALC_OVER BIT(4)
|
||||
#define SENSOR_CONFIG0_TALL_MASK (0xfffff << 8)
|
||||
#define SENSOR_CONFIG0_TALL_SHIFT 8
|
||||
|
||||
#define SENSOR_CONFIG1 4
|
||||
#define SENSOR_CONFIG1_TSAMPLE_MASK 0x3ff
|
||||
#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0
|
||||
#define SENSOR_CONFIG1_TIDDQ_EN_MASK (0x3f << 15)
|
||||
#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15
|
||||
#define SENSOR_CONFIG1_TEN_COUNT_MASK (0x3f << 24)
|
||||
#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24
|
||||
#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31)
|
||||
|
||||
/*
|
||||
* SENSOR_CONFIG2 is defined in soctherm.h
|
||||
* because, it will be used by tegra_soctherm_fuse.c
|
||||
*/
|
||||
|
||||
#define SENSOR_STATUS0 0xc
|
||||
#define SENSOR_STATUS0_VALID_MASK BIT(31)
|
||||
#define SENSOR_STATUS0_CAPTURE_MASK 0xffff
|
||||
|
||||
#define SENSOR_STATUS1 0x10
|
||||
#define SENSOR_STATUS1_TEMP_VALID_MASK BIT(31)
|
||||
#define SENSOR_STATUS1_TEMP_MASK 0xffff
|
||||
|
||||
#define READBACK_VALUE_MASK 0xff00
|
||||
#define READBACK_VALUE_SHIFT 8
|
||||
#define READBACK_ADD_HALF BIT(7)
|
||||
#define READBACK_NEGATE BIT(0)
|
||||
|
||||
/* get val from register(r) mask bits(m) */
|
||||
#define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1))
|
||||
/* set val(v) to mask bits(m) of register(r) */
|
||||
#define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \
|
||||
(((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1)))
|
||||
|
||||
static const int min_low_temp = -127000;
|
||||
static const int max_high_temp = 127000;
|
||||
|
||||
struct tegra_thermctl_zone {
|
||||
void __iomem *reg;
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *tz;
|
||||
const struct tegra_tsensor_group *sg;
|
||||
};
|
||||
|
||||
struct tegra_soctherm {
|
||||
struct reset_control *reset;
|
||||
struct clk *clock_tsensor;
|
||||
struct clk *clock_soctherm;
|
||||
void __iomem *regs;
|
||||
struct thermal_zone_device **thermctl_tzs;
|
||||
|
||||
u32 *calib;
|
||||
struct tegra_soctherm_soc *soc;
|
||||
|
||||
struct dentry *debugfs_dir;
|
||||
};
|
||||
|
||||
static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i)
|
||||
{
|
||||
const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i];
|
||||
void __iomem *base = tegra->regs + sensor->base;
|
||||
unsigned int val;
|
||||
|
||||
val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
|
||||
writel(val, base + SENSOR_CONFIG0);
|
||||
|
||||
val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
|
||||
val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
|
||||
val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
|
||||
val |= SENSOR_CONFIG1_TEMP_ENABLE;
|
||||
writel(val, base + SENSOR_CONFIG1);
|
||||
|
||||
writel(tegra->calib[i], base + SENSOR_CONFIG2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate from soctherm readback format to millicelsius.
|
||||
* The soctherm readback format in bits is as follows:
|
||||
* TTTTTTTT H______N
|
||||
* where T's contain the temperature in Celsius,
|
||||
* H denotes an addition of 0.5 Celsius and N denotes negation
|
||||
* of the final value.
|
||||
*/
|
||||
static int translate_temp(u16 val)
|
||||
{
|
||||
int t;
|
||||
|
||||
t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
|
||||
if (val & READBACK_ADD_HALF)
|
||||
t += 500;
|
||||
if (val & READBACK_NEGATE)
|
||||
t *= -1;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int tegra_thermctl_get_temp(void *data, int *out_temp)
|
||||
{
|
||||
struct tegra_thermctl_zone *zone = data;
|
||||
u32 val;
|
||||
|
||||
val = readl(zone->reg);
|
||||
val = REG_GET_MASK(val, zone->sg->sensor_temp_mask);
|
||||
*out_temp = translate_temp(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
thermtrip_program(struct device *dev, const struct tegra_tsensor_group *sg,
|
||||
int trip_temp);
|
||||
|
||||
static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
|
||||
{
|
||||
struct tegra_thermctl_zone *zone = data;
|
||||
struct thermal_zone_device *tz = zone->tz;
|
||||
const struct tegra_tsensor_group *sg = zone->sg;
|
||||
struct device *dev = zone->dev;
|
||||
enum thermal_trip_type type;
|
||||
int ret;
|
||||
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tz->ops->get_trip_type(tz, trip, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (type != THERMAL_TRIP_CRITICAL)
|
||||
return 0;
|
||||
|
||||
return thermtrip_program(dev, sg, temp);
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
|
||||
.get_temp = tegra_thermctl_get_temp,
|
||||
.set_trip_temp = tegra_thermctl_set_trip_temp,
|
||||
};
|
||||
|
||||
/**
|
||||
* enforce_temp_range() - check and enforce temperature range [min, max]
|
||||
* @trip_temp: the trip temperature to check
|
||||
*
|
||||
* Checks and enforces the permitted temperature range that SOC_THERM
|
||||
* HW can support This is
|
||||
* done while taking care of precision.
|
||||
*
|
||||
* Return: The precision adjusted capped temperature in millicelsius.
|
||||
*/
|
||||
static int enforce_temp_range(struct device *dev, int trip_temp)
|
||||
{
|
||||
int temp;
|
||||
|
||||
temp = clamp_val(trip_temp, min_low_temp, max_high_temp);
|
||||
if (temp != trip_temp)
|
||||
dev_info(dev, "soctherm: trip temperature %d forced to %d\n",
|
||||
trip_temp, temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermtrip_program() - Configures the hardware to shut down the
|
||||
* system if a given sensor group reaches a given temperature
|
||||
* @dev: ptr to the struct device for the SOC_THERM IP block
|
||||
* @sg: pointer to the sensor group to set the thermtrip temperature for
|
||||
* @trip_temp: the temperature in millicelsius to trigger the thermal trip at
|
||||
*
|
||||
* Sets the thermal trip threshold of the given sensor group to be the
|
||||
* @trip_temp. If this threshold is crossed, the hardware will shut
|
||||
* down.
|
||||
*
|
||||
* Note that, although @trip_temp is specified in millicelsius, the
|
||||
* hardware is programmed in degrees Celsius.
|
||||
*
|
||||
* Return: 0 upon success, or %-EINVAL upon failure.
|
||||
*/
|
||||
static int thermtrip_program(struct device *dev,
|
||||
const struct tegra_tsensor_group *sg,
|
||||
int trip_temp)
|
||||
{
|
||||
struct tegra_soctherm *ts = dev_get_drvdata(dev);
|
||||
int temp;
|
||||
u32 r;
|
||||
|
||||
if (!sg || !sg->thermtrip_threshold_mask)
|
||||
return -EINVAL;
|
||||
|
||||
temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain;
|
||||
|
||||
r = readl(ts->regs + THERMCTL_THERMTRIP_CTL);
|
||||
r = REG_SET_MASK(r, sg->thermtrip_threshold_mask, temp);
|
||||
r = REG_SET_MASK(r, sg->thermtrip_enable_mask, 1);
|
||||
r = REG_SET_MASK(r, sg->thermtrip_any_en_mask, 0);
|
||||
writel(r, ts->regs + THERMCTL_THERMTRIP_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_soctherm_set_hwtrips() - set HW trip point from DT data
|
||||
* @dev: struct device * of the SOC_THERM instance
|
||||
*
|
||||
* Configure the SOC_THERM HW trip points, setting "THERMTRIP"
|
||||
* trip points , using "critical" type trip_temp from thermal
|
||||
* zone.
|
||||
* After they have been configured, THERMTRIP will take action
|
||||
* when the configured SoC thermal sensor group reaches a
|
||||
* certain temperature.
|
||||
*
|
||||
* Return: 0 upon success, or a negative error code on failure.
|
||||
* "Success" does not mean that trips was enabled; it could also
|
||||
* mean that no node was found in DT.
|
||||
* THERMTRIP has been enabled successfully when a message similar to
|
||||
* this one appears on the serial console:
|
||||
* "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC"
|
||||
*/
|
||||
static int tegra_soctherm_set_hwtrips(struct device *dev,
|
||||
const struct tegra_tsensor_group *sg,
|
||||
struct thermal_zone_device *tz)
|
||||
{
|
||||
int temperature;
|
||||
int ret;
|
||||
|
||||
ret = tz->ops->get_crit_temp(tz, &temperature);
|
||||
if (ret) {
|
||||
dev_warn(dev, "thermtrip: %s: missing critical temperature\n",
|
||||
sg->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = thermtrip_program(dev, sg, temperature);
|
||||
if (ret) {
|
||||
dev_err(dev, "thermtrip: %s: error during enable\n",
|
||||
sg->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev,
|
||||
"thermtrip: will shut down when %s reaches %d mC\n",
|
||||
sg->name, temperature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int regs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct platform_device *pdev = s->private;
|
||||
struct tegra_soctherm *ts = platform_get_drvdata(pdev);
|
||||
const struct tegra_tsensor *tsensors = ts->soc->tsensors;
|
||||
const struct tegra_tsensor_group **ttgs = ts->soc->ttgs;
|
||||
u32 r, state;
|
||||
int i;
|
||||
|
||||
seq_puts(s, "-----TSENSE (convert HW)-----\n");
|
||||
|
||||
for (i = 0; i < ts->soc->num_tsensors; i++) {
|
||||
r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG1);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG1_TEMP_ENABLE);
|
||||
|
||||
seq_printf(s, "%s: ", tsensors[i].name);
|
||||
seq_printf(s, "En(%d) ", state);
|
||||
|
||||
if (!state) {
|
||||
seq_puts(s, "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG1_TIDDQ_EN_MASK);
|
||||
seq_printf(s, "tiddq(%d) ", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG1_TEN_COUNT_MASK);
|
||||
seq_printf(s, "ten_count(%d) ", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG1_TSAMPLE_MASK);
|
||||
seq_printf(s, "tsample(%d) ", state + 1);
|
||||
|
||||
r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS1);
|
||||
state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_VALID_MASK);
|
||||
seq_printf(s, "Temp(%d/", state);
|
||||
state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_MASK);
|
||||
seq_printf(s, "%d) ", translate_temp(state));
|
||||
|
||||
r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS0);
|
||||
state = REG_GET_MASK(r, SENSOR_STATUS0_VALID_MASK);
|
||||
seq_printf(s, "Capture(%d/", state);
|
||||
state = REG_GET_MASK(r, SENSOR_STATUS0_CAPTURE_MASK);
|
||||
seq_printf(s, "%d) ", state);
|
||||
|
||||
r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG0);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG0_STOP);
|
||||
seq_printf(s, "Stop(%d) ", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG0_TALL_MASK);
|
||||
seq_printf(s, "Tall(%d) ", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG0_TCALC_OVER);
|
||||
seq_printf(s, "Over(%d/", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG0_OVER);
|
||||
seq_printf(s, "%d/", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG0_CPTR_OVER);
|
||||
seq_printf(s, "%d) ", state);
|
||||
|
||||
r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG2);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMA_MASK);
|
||||
seq_printf(s, "Therm_A/B(%d/", state);
|
||||
state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMB_MASK);
|
||||
seq_printf(s, "%d)\n", (s16)state);
|
||||
}
|
||||
|
||||
r = readl(ts->regs + SENSOR_PDIV);
|
||||
seq_printf(s, "PDIV: 0x%x\n", r);
|
||||
|
||||
r = readl(ts->regs + SENSOR_HOTSPOT_OFF);
|
||||
seq_printf(s, "HOTSPOT: 0x%x\n", r);
|
||||
|
||||
seq_puts(s, "\n");
|
||||
seq_puts(s, "-----SOC_THERM-----\n");
|
||||
|
||||
r = readl(ts->regs + SENSOR_TEMP1);
|
||||
state = REG_GET_MASK(r, SENSOR_TEMP1_CPU_TEMP_MASK);
|
||||
seq_printf(s, "Temperatures: CPU(%d) ", translate_temp(state));
|
||||
state = REG_GET_MASK(r, SENSOR_TEMP1_GPU_TEMP_MASK);
|
||||
seq_printf(s, " GPU(%d) ", translate_temp(state));
|
||||
r = readl(ts->regs + SENSOR_TEMP2);
|
||||
state = REG_GET_MASK(r, SENSOR_TEMP2_PLLX_TEMP_MASK);
|
||||
seq_printf(s, " PLLX(%d) ", translate_temp(state));
|
||||
state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK);
|
||||
seq_printf(s, " MEM(%d)\n", translate_temp(state));
|
||||
|
||||
r = readl(ts->regs + THERMCTL_THERMTRIP_CTL);
|
||||
state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask);
|
||||
seq_printf(s, "Thermtrip Any En(%d)\n", state);
|
||||
for (i = 0; i < ts->soc->num_ttgs; i++) {
|
||||
state = REG_GET_MASK(r, ttgs[i]->thermtrip_enable_mask);
|
||||
seq_printf(s, " %s En(%d) ", ttgs[i]->name, state);
|
||||
state = REG_GET_MASK(r, ttgs[i]->thermtrip_threshold_mask);
|
||||
state *= ts->soc->thresh_grain;
|
||||
seq_printf(s, "Thresh(%d)\n", state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, regs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations regs_fops = {
|
||||
.open = regs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void soctherm_debug_init(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
|
||||
struct dentry *root, *file;
|
||||
|
||||
root = debugfs_create_dir("soctherm", NULL);
|
||||
if (!root) {
|
||||
dev_err(&pdev->dev, "failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tegra->debugfs_dir = root;
|
||||
|
||||
file = debugfs_create_file("reg_contents", 0644, root,
|
||||
pdev, ®s_fops);
|
||||
if (!file) {
|
||||
dev_err(&pdev->dev, "failed to create debugfs file\n");
|
||||
debugfs_remove_recursive(tegra->debugfs_dir);
|
||||
tegra->debugfs_dir = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void soctherm_debug_init(struct platform_device *pdev) {}
|
||||
#endif
|
||||
|
||||
static int soctherm_clk_enable(struct platform_device *pdev, bool enable)
|
||||
{
|
||||
struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
|
||||
int err;
|
||||
|
||||
if (!tegra->clock_soctherm || !tegra->clock_tsensor)
|
||||
return -EINVAL;
|
||||
|
||||
reset_control_assert(tegra->reset);
|
||||
|
||||
if (enable) {
|
||||
err = clk_prepare_enable(tegra->clock_soctherm);
|
||||
if (err) {
|
||||
reset_control_deassert(tegra->reset);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(tegra->clock_tsensor);
|
||||
if (err) {
|
||||
clk_disable_unprepare(tegra->clock_soctherm);
|
||||
reset_control_deassert(tegra->reset);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
clk_disable_unprepare(tegra->clock_tsensor);
|
||||
clk_disable_unprepare(tegra->clock_soctherm);
|
||||
}
|
||||
|
||||
reset_control_deassert(tegra->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void soctherm_init(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
|
||||
const struct tegra_tsensor_group **ttgs = tegra->soc->ttgs;
|
||||
int i;
|
||||
u32 pdiv, hotspot;
|
||||
|
||||
/* Initialize raw sensors */
|
||||
for (i = 0; i < tegra->soc->num_tsensors; ++i)
|
||||
enable_tsensor(tegra, i);
|
||||
|
||||
/* program pdiv and hotspot offsets per THERM */
|
||||
pdiv = readl(tegra->regs + SENSOR_PDIV);
|
||||
hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF);
|
||||
for (i = 0; i < tegra->soc->num_ttgs; ++i) {
|
||||
pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask,
|
||||
ttgs[i]->pdiv);
|
||||
/* hotspot offset from PLLX, doesn't need to configure PLLX */
|
||||
if (ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX)
|
||||
continue;
|
||||
hotspot = REG_SET_MASK(hotspot,
|
||||
ttgs[i]->pllx_hotspot_mask,
|
||||
ttgs[i]->pllx_hotspot_diff);
|
||||
}
|
||||
writel(pdiv, tegra->regs + SENSOR_PDIV);
|
||||
writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF);
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_soctherm_of_match[] = {
|
||||
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
||||
{
|
||||
.compatible = "nvidia,tegra124-soctherm",
|
||||
.data = &tegra124_soctherm,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_132_SOC
|
||||
{
|
||||
.compatible = "nvidia,tegra132-soctherm",
|
||||
.data = &tegra132_soctherm,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_210_SOC
|
||||
{
|
||||
.compatible = "nvidia,tegra210-soctherm",
|
||||
.data = &tegra210_soctherm,
|
||||
},
|
||||
#endif
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match);
|
||||
|
||||
static int tegra_soctherm_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct tegra_soctherm *tegra;
|
||||
struct thermal_zone_device *z;
|
||||
struct tsensor_shared_calib shared_calib;
|
||||
struct resource *res;
|
||||
struct tegra_soctherm_soc *soc;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
soc = (struct tegra_soctherm_soc *)match->data;
|
||||
if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
|
||||
if (!tegra)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, tegra);
|
||||
|
||||
tegra->soc = soc;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tegra->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(tegra->regs))
|
||||
return PTR_ERR(tegra->regs);
|
||||
|
||||
tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
|
||||
if (IS_ERR(tegra->reset)) {
|
||||
dev_err(&pdev->dev, "can't get soctherm reset\n");
|
||||
return PTR_ERR(tegra->reset);
|
||||
}
|
||||
|
||||
tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor");
|
||||
if (IS_ERR(tegra->clock_tsensor)) {
|
||||
dev_err(&pdev->dev, "can't get tsensor clock\n");
|
||||
return PTR_ERR(tegra->clock_tsensor);
|
||||
}
|
||||
|
||||
tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm");
|
||||
if (IS_ERR(tegra->clock_soctherm)) {
|
||||
dev_err(&pdev->dev, "can't get soctherm clock\n");
|
||||
return PTR_ERR(tegra->clock_soctherm);
|
||||
}
|
||||
|
||||
tegra->calib = devm_kzalloc(&pdev->dev,
|
||||
sizeof(u32) * soc->num_tsensors,
|
||||
GFP_KERNEL);
|
||||
if (!tegra->calib)
|
||||
return -ENOMEM;
|
||||
|
||||
/* calculate shared calibration data */
|
||||
err = tegra_calc_shared_calib(soc->tfuse, &shared_calib);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* calculate tsensor calibaration data */
|
||||
for (i = 0; i < soc->num_tsensors; ++i) {
|
||||
err = tegra_calc_tsensor_calib(&soc->tsensors[i],
|
||||
&shared_calib,
|
||||
&tegra->calib[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
tegra->thermctl_tzs = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*z) * soc->num_ttgs,
|
||||
GFP_KERNEL);
|
||||
if (!tegra->thermctl_tzs)
|
||||
return -ENOMEM;
|
||||
|
||||
err = soctherm_clk_enable(pdev, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
soctherm_init(pdev);
|
||||
|
||||
for (i = 0; i < soc->num_ttgs; ++i) {
|
||||
struct tegra_thermctl_zone *zone =
|
||||
devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
|
||||
if (!zone) {
|
||||
err = -ENOMEM;
|
||||
goto disable_clocks;
|
||||
}
|
||||
|
||||
zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset;
|
||||
zone->dev = &pdev->dev;
|
||||
zone->sg = soc->ttgs[i];
|
||||
|
||||
z = devm_thermal_zone_of_sensor_register(&pdev->dev,
|
||||
soc->ttgs[i]->id, zone,
|
||||
&tegra_of_thermal_ops);
|
||||
if (IS_ERR(z)) {
|
||||
err = PTR_ERR(z);
|
||||
dev_err(&pdev->dev, "failed to register sensor: %d\n",
|
||||
err);
|
||||
goto disable_clocks;
|
||||
}
|
||||
|
||||
zone->tz = z;
|
||||
tegra->thermctl_tzs[soc->ttgs[i]->id] = z;
|
||||
|
||||
/* Configure hw trip points */
|
||||
tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z);
|
||||
}
|
||||
|
||||
soctherm_debug_init(pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clocks:
|
||||
soctherm_clk_enable(pdev, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_soctherm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
|
||||
|
||||
debugfs_remove_recursive(tegra->debugfs_dir);
|
||||
|
||||
soctherm_clk_enable(pdev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused soctherm_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
soctherm_clk_enable(pdev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused soctherm_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
|
||||
struct tegra_soctherm_soc *soc = tegra->soc;
|
||||
int err, i;
|
||||
|
||||
err = soctherm_clk_enable(pdev, true);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Resume failed: enable clocks failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
soctherm_init(pdev);
|
||||
|
||||
for (i = 0; i < soc->num_ttgs; ++i) {
|
||||
struct thermal_zone_device *tz;
|
||||
|
||||
tz = tegra->thermctl_tzs[soc->ttgs[i]->id];
|
||||
tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tegra_soctherm_pm, soctherm_suspend, soctherm_resume);
|
||||
|
||||
static struct platform_driver tegra_soctherm_driver = {
|
||||
.probe = tegra_soctherm_probe,
|
||||
.remove = tegra_soctherm_remove,
|
||||
.driver = {
|
||||
.name = "tegra_soctherm",
|
||||
.pm = &tegra_soctherm_pm,
|
||||
.of_match_table = tegra_soctherm_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_soctherm_driver);
|
||||
|
||||
MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver");
|
||||
MODULE_LICENSE("GPL v2");
|
127
drivers/thermal/tegra/soctherm.h
Normal file
127
drivers/thermal/tegra/soctherm.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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
|
||||
|
196
drivers/thermal/tegra/tegra124-soctherm.c
Normal file
196
drivers/thermal/tegra/tegra124-soctherm.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
196
drivers/thermal/tegra/tegra132-soctherm.c
Normal file
196
drivers/thermal/tegra/tegra132-soctherm.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
197
drivers/thermal/tegra/tegra210-soctherm.c
Normal file
197
drivers/thermal/tegra/tegra210-soctherm.c
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
@ -1,476 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Mikko Perttunen <mperttunen@nvidia.com>
|
||||
*
|
||||
* 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/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#define SENSOR_CONFIG0 0
|
||||
#define SENSOR_CONFIG0_STOP BIT(0)
|
||||
#define SENSOR_CONFIG0_TALL_SHIFT 8
|
||||
#define SENSOR_CONFIG0_TCALC_OVER BIT(4)
|
||||
#define SENSOR_CONFIG0_OVER BIT(3)
|
||||
#define SENSOR_CONFIG0_CPTR_OVER BIT(2)
|
||||
|
||||
#define SENSOR_CONFIG1 4
|
||||
#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0
|
||||
#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15
|
||||
#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24
|
||||
#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31)
|
||||
|
||||
#define SENSOR_CONFIG2 8
|
||||
#define SENSOR_CONFIG2_THERMA_SHIFT 16
|
||||
#define SENSOR_CONFIG2_THERMB_SHIFT 0
|
||||
|
||||
#define SENSOR_PDIV 0x1c0
|
||||
#define SENSOR_PDIV_T124 0x8888
|
||||
#define SENSOR_HOTSPOT_OFF 0x1c4
|
||||
#define SENSOR_HOTSPOT_OFF_T124 0x00060600
|
||||
#define SENSOR_TEMP1 0x1c8
|
||||
#define SENSOR_TEMP2 0x1cc
|
||||
|
||||
#define SENSOR_TEMP_MASK 0xffff
|
||||
#define READBACK_VALUE_MASK 0xff00
|
||||
#define READBACK_VALUE_SHIFT 8
|
||||
#define READBACK_ADD_HALF BIT(7)
|
||||
#define READBACK_NEGATE BIT(0)
|
||||
|
||||
#define FUSE_TSENSOR8_CALIB 0x180
|
||||
#define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc
|
||||
|
||||
#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_TSENSOR8_CALIB_CP_TS_BASE_MASK 0x3ff
|
||||
#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK (0x7ff << 10)
|
||||
#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT 10
|
||||
|
||||
#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f
|
||||
#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21)
|
||||
#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21
|
||||
|
||||
#define NOMINAL_CALIB_FT_T124 105
|
||||
#define NOMINAL_CALIB_CP_T124 25
|
||||
|
||||
struct tegra_tsensor_configuration {
|
||||
u32 tall, tsample, tiddq_en, ten_count, pdiv, tsample_ate, pdiv_ate;
|
||||
};
|
||||
|
||||
struct tegra_tsensor {
|
||||
const struct tegra_tsensor_configuration *config;
|
||||
u32 base, calib_fuse_offset;
|
||||
/* Correction values used to modify values read from calibration fuses */
|
||||
s32 fuse_corr_alpha, fuse_corr_beta;
|
||||
};
|
||||
|
||||
struct tegra_thermctl_zone {
|
||||
void __iomem *reg;
|
||||
unsigned int shift;
|
||||
};
|
||||
|
||||
static const struct tegra_tsensor_configuration t124_tsensor_config = {
|
||||
.tall = 16300,
|
||||
.tsample = 120,
|
||||
.tiddq_en = 1,
|
||||
.ten_count = 1,
|
||||
.pdiv = 8,
|
||||
.tsample_ate = 480,
|
||||
.pdiv_ate = 8
|
||||
};
|
||||
|
||||
static const struct tegra_tsensor t124_tsensors[] = {
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0xc0,
|
||||
.calib_fuse_offset = 0x098,
|
||||
.fuse_corr_alpha = 1135400,
|
||||
.fuse_corr_beta = -6266900,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0xe0,
|
||||
.calib_fuse_offset = 0x084,
|
||||
.fuse_corr_alpha = 1122220,
|
||||
.fuse_corr_beta = -5700700,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0x100,
|
||||
.calib_fuse_offset = 0x088,
|
||||
.fuse_corr_alpha = 1127000,
|
||||
.fuse_corr_beta = -6768200,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0x120,
|
||||
.calib_fuse_offset = 0x12c,
|
||||
.fuse_corr_alpha = 1110900,
|
||||
.fuse_corr_beta = -6232000,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0x140,
|
||||
.calib_fuse_offset = 0x158,
|
||||
.fuse_corr_alpha = 1122300,
|
||||
.fuse_corr_beta = -5936400,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0x160,
|
||||
.calib_fuse_offset = 0x15c,
|
||||
.fuse_corr_alpha = 1145700,
|
||||
.fuse_corr_beta = -7124600,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0x180,
|
||||
.calib_fuse_offset = 0x154,
|
||||
.fuse_corr_alpha = 1120100,
|
||||
.fuse_corr_beta = -6000500,
|
||||
},
|
||||
{
|
||||
.config = &t124_tsensor_config,
|
||||
.base = 0x1a0,
|
||||
.calib_fuse_offset = 0x160,
|
||||
.fuse_corr_alpha = 1106500,
|
||||
.fuse_corr_beta = -6729300,
|
||||
},
|
||||
};
|
||||
|
||||
struct tegra_soctherm {
|
||||
struct reset_control *reset;
|
||||
struct clk *clock_tsensor;
|
||||
struct clk *clock_soctherm;
|
||||
void __iomem *regs;
|
||||
|
||||
struct thermal_zone_device *thermctl_tzs[4];
|
||||
};
|
||||
|
||||
struct tsensor_shared_calibration {
|
||||
u32 base_cp, base_ft;
|
||||
u32 actual_temp_cp, actual_temp_ft;
|
||||
};
|
||||
|
||||
static int calculate_shared_calibration(struct tsensor_shared_calibration *r)
|
||||
{
|
||||
u32 val, shifted_cp, shifted_ft;
|
||||
int err;
|
||||
|
||||
err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val);
|
||||
if (err)
|
||||
return err;
|
||||
r->base_cp = val & FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK;
|
||||
r->base_ft = (val & FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK)
|
||||
>> FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT;
|
||||
val = ((val & FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK)
|
||||
>> FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT);
|
||||
shifted_ft = sign_extend32(val, 4);
|
||||
|
||||
err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val);
|
||||
if (err)
|
||||
return err;
|
||||
shifted_cp = sign_extend32(val, 5);
|
||||
|
||||
r->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + shifted_cp;
|
||||
r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s64 div64_s64_precise(s64 a, s64 b)
|
||||
{
|
||||
s64 r, al;
|
||||
|
||||
/* Scale up for increased precision division */
|
||||
al = a << 16;
|
||||
|
||||
r = div64_s64(al * 2 + 1, 2 * b);
|
||||
return r >> 16;
|
||||
}
|
||||
|
||||
static int
|
||||
calculate_tsensor_calibration(const struct tegra_tsensor *sensor,
|
||||
const struct tsensor_shared_calibration *shared,
|
||||
u32 *calib)
|
||||
{
|
||||
u32 val;
|
||||
s32 actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp,
|
||||
mult, div;
|
||||
s16 therma, thermb;
|
||||
s64 tmp;
|
||||
int err;
|
||||
|
||||
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->config->pdiv * sensor->config->tsample_ate;
|
||||
div = sensor->config->tsample * sensor->config->pdiv_ate;
|
||||
|
||||
therma = div64_s64_precise((s64) delta_temp * (1LL << 13) * mult,
|
||||
(s64) delta_sens * div);
|
||||
|
||||
tmp = (s64)actual_tsensor_ft * shared->actual_temp_cp -
|
||||
(s64)actual_tsensor_cp * shared->actual_temp_ft;
|
||||
thermb = div64_s64_precise(tmp, (s64)delta_sens);
|
||||
|
||||
therma = div64_s64_precise((s64)therma * sensor->fuse_corr_alpha,
|
||||
(s64)1000000LL);
|
||||
thermb = div64_s64_precise((s64)thermb * sensor->fuse_corr_alpha +
|
||||
sensor->fuse_corr_beta, (s64)1000000LL);
|
||||
|
||||
*calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
|
||||
((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_tsensor(struct tegra_soctherm *tegra,
|
||||
const struct tegra_tsensor *sensor,
|
||||
const struct tsensor_shared_calibration *shared)
|
||||
{
|
||||
void __iomem *base = tegra->regs + sensor->base;
|
||||
unsigned int val;
|
||||
u32 calib;
|
||||
int err;
|
||||
|
||||
err = calculate_tsensor_calibration(sensor, shared, &calib);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
|
||||
writel(val, base + SENSOR_CONFIG0);
|
||||
|
||||
val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
|
||||
val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
|
||||
val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
|
||||
val |= SENSOR_CONFIG1_TEMP_ENABLE;
|
||||
writel(val, base + SENSOR_CONFIG1);
|
||||
|
||||
writel(calib, base + SENSOR_CONFIG2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate from soctherm readback format to millicelsius.
|
||||
* The soctherm readback format in bits is as follows:
|
||||
* TTTTTTTT H______N
|
||||
* where T's contain the temperature in Celsius,
|
||||
* H denotes an addition of 0.5 Celsius and N denotes negation
|
||||
* of the final value.
|
||||
*/
|
||||
static int translate_temp(u16 val)
|
||||
{
|
||||
long t;
|
||||
|
||||
t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
|
||||
if (val & READBACK_ADD_HALF)
|
||||
t += 500;
|
||||
if (val & READBACK_NEGATE)
|
||||
t *= -1;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int tegra_thermctl_get_temp(void *data, int *out_temp)
|
||||
{
|
||||
struct tegra_thermctl_zone *zone = data;
|
||||
u32 val;
|
||||
|
||||
val = (readl(zone->reg) >> zone->shift) & SENSOR_TEMP_MASK;
|
||||
*out_temp = translate_temp(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
|
||||
.get_temp = tegra_thermctl_get_temp,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_soctherm_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-soctherm" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match);
|
||||
|
||||
struct thermctl_zone_desc {
|
||||
unsigned int offset;
|
||||
unsigned int shift;
|
||||
};
|
||||
|
||||
static const struct thermctl_zone_desc t124_thermctl_temp_zones[] = {
|
||||
{ SENSOR_TEMP1, 16 },
|
||||
{ SENSOR_TEMP2, 16 },
|
||||
{ SENSOR_TEMP1, 0 },
|
||||
{ SENSOR_TEMP2, 0 }
|
||||
};
|
||||
|
||||
static int tegra_soctherm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_soctherm *tegra;
|
||||
struct thermal_zone_device *tz;
|
||||
struct tsensor_shared_calibration shared_calib;
|
||||
struct resource *res;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
const struct tegra_tsensor *tsensors = t124_tsensors;
|
||||
|
||||
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
|
||||
if (!tegra)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tegra->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(tegra->regs))
|
||||
return PTR_ERR(tegra->regs);
|
||||
|
||||
tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
|
||||
if (IS_ERR(tegra->reset)) {
|
||||
dev_err(&pdev->dev, "can't get soctherm reset\n");
|
||||
return PTR_ERR(tegra->reset);
|
||||
}
|
||||
|
||||
tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor");
|
||||
if (IS_ERR(tegra->clock_tsensor)) {
|
||||
dev_err(&pdev->dev, "can't get tsensor clock\n");
|
||||
return PTR_ERR(tegra->clock_tsensor);
|
||||
}
|
||||
|
||||
tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm");
|
||||
if (IS_ERR(tegra->clock_soctherm)) {
|
||||
dev_err(&pdev->dev, "can't get soctherm clock\n");
|
||||
return PTR_ERR(tegra->clock_soctherm);
|
||||
}
|
||||
|
||||
reset_control_assert(tegra->reset);
|
||||
|
||||
err = clk_prepare_enable(tegra->clock_soctherm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(tegra->clock_tsensor);
|
||||
if (err) {
|
||||
clk_disable_unprepare(tegra->clock_soctherm);
|
||||
return err;
|
||||
}
|
||||
|
||||
reset_control_deassert(tegra->reset);
|
||||
|
||||
/* Initialize raw sensors */
|
||||
|
||||
err = calculate_shared_calibration(&shared_calib);
|
||||
if (err)
|
||||
goto disable_clocks;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(t124_tsensors); ++i) {
|
||||
err = enable_tsensor(tegra, tsensors + i, &shared_calib);
|
||||
if (err)
|
||||
goto disable_clocks;
|
||||
}
|
||||
|
||||
writel(SENSOR_PDIV_T124, tegra->regs + SENSOR_PDIV);
|
||||
writel(SENSOR_HOTSPOT_OFF_T124, tegra->regs + SENSOR_HOTSPOT_OFF);
|
||||
|
||||
/* Initialize thermctl sensors */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
|
||||
struct tegra_thermctl_zone *zone =
|
||||
devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
|
||||
if (!zone) {
|
||||
err = -ENOMEM;
|
||||
goto unregister_tzs;
|
||||
}
|
||||
|
||||
zone->reg = tegra->regs + t124_thermctl_temp_zones[i].offset;
|
||||
zone->shift = t124_thermctl_temp_zones[i].shift;
|
||||
|
||||
tz = thermal_zone_of_sensor_register(&pdev->dev, i, zone,
|
||||
&tegra_of_thermal_ops);
|
||||
if (IS_ERR(tz)) {
|
||||
err = PTR_ERR(tz);
|
||||
dev_err(&pdev->dev, "failed to register sensor: %d\n",
|
||||
err);
|
||||
goto unregister_tzs;
|
||||
}
|
||||
|
||||
tegra->thermctl_tzs[i] = tz;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_tzs:
|
||||
while (i--)
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
tegra->thermctl_tzs[i]);
|
||||
|
||||
disable_clocks:
|
||||
clk_disable_unprepare(tegra->clock_tsensor);
|
||||
clk_disable_unprepare(tegra->clock_soctherm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_soctherm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
tegra->thermctl_tzs[i]);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(tegra->clock_tsensor);
|
||||
clk_disable_unprepare(tegra->clock_soctherm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra_soctherm_driver = {
|
||||
.probe = tegra_soctherm_probe,
|
||||
.remove = tegra_soctherm_remove,
|
||||
.driver = {
|
||||
.name = "tegra-soctherm",
|
||||
.of_match_table = tegra_soctherm_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_soctherm_driver);
|
||||
|
||||
MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver");
|
||||
MODULE_LICENSE("GPL v2");
|
182
drivers/thermal/thermal-generic-adc.c
Normal file
182
drivers/thermal/thermal-generic-adc.c
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user