Commit 4c5d4995 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull thermal management updates from Zhang Rui:
 "First of all, the most important change is the thermal cpu cooling
  fixes.  The major fix here is to have proper sequencing between
  cpufreq layer and thermal cpu cooling registration.  A take away of
  this fix is an improvement in the thermal drivers code.  Thermal
  drivers that require cpu cooling do not need to check for cpufreq
  layer.  The requirement now is to propagate the error code, if any,
  while registering cpu cooling device.  Thanks to Viresh for
  implementing the required CPUfreq changes.

  Second, a new driver is introduced for int340x processor thermal
  device.  Given that int340x thermal is disabled by default, and this
  processor thermal device is only available on limited platforms, plus
  the driver does nothing but exposes some thermal limitation
  information for user space to use, thus I think it is safe to include
  it in this pull request after missing 3.19-rc2.

  Specifics:

   - Thermal cpu cooling fixes and cleanups.

   - introduce INT340X processor thermal reporting device driver.

   - several small fixes and cleanups for int340x thermal drivers"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (43 commits)
  Thermal/int340x/int3403: Free acpi notification handler
  Thermal/int340x/processor_thermal: Fix memory leak
  Thermal/int340x/int3403: Fix memory leak
  thermal: int340x: Introduce processor reporting device
  thermal: int340x_thermal: drop owner assignment from platform_drivers
  thermal: drop owner assignment from platform_drivers
  thermal: cpu_cooling: document node in struct cpufreq_cooling_device
  thermal/powerclamp: add ids for future xeon cpus
  Thermal/int340x: Handle properly the case when _trt or _art acpi entry is missing
  thermal: cpu_cooling: return ERR_PTR() for !CPU_THERMAL or !THERMAL_OF
  thermal: cpu_cooling: small memory leak on error
  thermal: ti-soc-thermal: Do not print error message in the EPROBE_DEFER case
  thermal: db8500: Do not print error message in the EPROBE_DEFER case
  thermal: imx: Do not print error message in the EPROBE_DEFER case
  thermal: Fix cdev registration with THERMAL_NO_LIMIT on 64bit
  drivers: thermal: Remove ARCH_HAS_BANDGAP dependency for samsung
  thermal:core:fix: Check return code of the ->get_max_state() callback
  thermal: cpu_cooling: update copyright tags
  thermal: cpu_cooling: Use cpufreq_dev->freq_table for finding level/freq
  thermal: cpu_cooling: Store frequencies in descending order
  ...
parents 45f87de5 7429b1e0
This diff is collapsed.
......@@ -18,7 +18,6 @@
*/
#include <linux/cpu_cooling.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
......@@ -28,18 +27,17 @@
static int db8500_cpufreq_cooling_probe(struct platform_device *pdev)
{
struct thermal_cooling_device *cdev;
struct cpumask mask_val;
/* make sure cpufreq driver has been initialized */
if (!cpufreq_frequency_get_table(0))
return -EPROBE_DEFER;
cpumask_set_cpu(0, &mask_val);
cdev = cpufreq_cooling_register(&mask_val);
cdev = cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(cdev)) {
dev_err(&pdev->dev, "Failed to register cooling device\n");
return PTR_ERR(cdev);
int ret = PTR_ERR(cdev);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to register cooling device %d\n",
ret);
return ret;
}
platform_set_drvdata(pdev, cdev);
......
......@@ -9,7 +9,6 @@
#include <linux/clk.h>
#include <linux/cpu_cooling.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
......@@ -454,15 +453,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
const struct of_device_id *of_id =
of_match_device(of_imx_thermal_match, &pdev->dev);
struct imx_thermal_data *data;
struct cpumask clip_cpus;
struct regmap *map;
int measure_freq;
int ret;
if (!cpufreq_get_current_driver()) {
dev_dbg(&pdev->dev, "no cpufreq driver!");
return -EPROBE_DEFER;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
......@@ -516,12 +510,13 @@ static int imx_thermal_probe(struct platform_device *pdev)
regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
cpumask_set_cpu(0, &clip_cpus);
data->cdev = cpufreq_cooling_register(&clip_cpus);
data->cdev = cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(data->cdev)) {
ret = PTR_ERR(data->cdev);
dev_err(&pdev->dev,
"failed to register cpufreq cooling device: %d\n", ret);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to register cpufreq cooling device: %d\n",
ret);
return ret;
}
......
obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
......@@ -82,7 +82,7 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
if (!acpi_has_method(handle, "_TRT"))
return 0;
return -ENODEV;
status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
if (ACPI_FAILURE(status))
......@@ -167,7 +167,7 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
if (!acpi_has_method(handle, "_ART"))
return 0;
return -ENODEV;
status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
if (ACPI_FAILURE(status))
......@@ -321,8 +321,8 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
unsigned long length = 0;
int count = 0;
char __user *arg = (void __user *)__arg;
struct trt *trts;
struct art *arts;
struct trt *trts = NULL;
struct art *arts = NULL;
switch (cmd) {
case ACPI_THERMAL_GET_TRT_COUNT:
......
......@@ -335,7 +335,6 @@ static struct platform_driver int3400_thermal_driver = {
.remove = int3400_thermal_remove,
.driver = {
.name = "int3400 thermal",
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(int3400_thermal_match),
},
};
......
......@@ -231,7 +231,6 @@ static struct platform_driver int3402_thermal_driver = {
.remove = int3402_thermal_remove,
.driver = {
.name = "int3402 thermal",
.owner = THIS_MODULE,
.acpi_match_table = int3402_thermal_match,
},
};
......
......@@ -301,6 +301,8 @@ static int int3403_sensor_remove(struct int3403_priv *priv)
{
struct int3403_sensor *obj = priv->priv;
acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify);
thermal_zone_device_unregister(obj->tzone);
return 0;
}
......@@ -369,6 +371,7 @@ static int int3403_cdev_add(struct int3403_priv *priv)
p = buf.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
printk(KERN_WARNING "Invalid PPSS data\n");
kfree(buf.pointer);
return -EFAULT;
}
......@@ -381,6 +384,7 @@ static int int3403_cdev_add(struct int3403_priv *priv)
priv->priv = obj;
kfree(buf.pointer);
/* TODO: add ACPI notification support */
return result;
......
/*
* processor_thermal_device.c
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
/* Broadwell-U/HSB thermal reporting device */
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03
/* Braswell thermal reporting device */
#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC
struct power_config {
u32 index;
u32 min_uw;
u32 max_uw;
u32 tmin_us;
u32 tmax_us;
u32 step_uw;
};
struct proc_thermal_device {
struct device *dev;
struct acpi_device *adev;
struct power_config power_limits[2];
};
enum proc_thermal_emum_mode_type {
PROC_THERMAL_NONE,
PROC_THERMAL_PCI,
PROC_THERMAL_PLATFORM_DEV
};
/*
* We can have only one type of enumeration, PCI or Platform,
* not both. So we don't need instance specific data.
*/
static enum proc_thermal_emum_mode_type proc_thermal_emum_mode =
PROC_THERMAL_NONE;
#define POWER_LIMIT_SHOW(index, suffix) \
static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct pci_dev *pci_dev; \
struct platform_device *pdev; \
struct proc_thermal_device *proc_dev; \
\
if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { \
pdev = to_platform_device(dev); \
proc_dev = platform_get_drvdata(pdev); \
} else { \
pci_dev = to_pci_dev(dev); \
proc_dev = pci_get_drvdata(pci_dev); \
} \
return sprintf(buf, "%lu\n",\
(unsigned long)proc_dev->power_limits[index].suffix * 1000); \
}
POWER_LIMIT_SHOW(0, min_uw)
POWER_LIMIT_SHOW(0, max_uw)
POWER_LIMIT_SHOW(0, step_uw)
POWER_LIMIT_SHOW(0, tmin_us)
POWER_LIMIT_SHOW(0, tmax_us)
POWER_LIMIT_SHOW(1, min_uw)
POWER_LIMIT_SHOW(1, max_uw)
POWER_LIMIT_SHOW(1, step_uw)
POWER_LIMIT_SHOW(1, tmin_us)
POWER_LIMIT_SHOW(1, tmax_us)
static DEVICE_ATTR_RO(power_limit_0_min_uw);
static DEVICE_ATTR_RO(power_limit_0_max_uw);
static DEVICE_ATTR_RO(power_limit_0_step_uw);
static DEVICE_ATTR_RO(power_limit_0_tmin_us);
static DEVICE_ATTR_RO(power_limit_0_tmax_us);
static DEVICE_ATTR_RO(power_limit_1_min_uw);
static DEVICE_ATTR_RO(power_limit_1_max_uw);
static DEVICE_ATTR_RO(power_limit_1_step_uw);
static DEVICE_ATTR_RO(power_limit_1_tmin_us);
static DEVICE_ATTR_RO(power_limit_1_tmax_us);
static struct attribute *power_limit_attrs[] = {
&dev_attr_power_limit_0_min_uw.attr,
&dev_attr_power_limit_1_min_uw.attr,
&dev_attr_power_limit_0_max_uw.attr,
&dev_attr_power_limit_1_max_uw.attr,
&dev_attr_power_limit_0_step_uw.attr,
&dev_attr_power_limit_1_step_uw.attr,
&dev_attr_power_limit_0_tmin_us.attr,
&dev_attr_power_limit_1_tmin_us.attr,
&dev_attr_power_limit_0_tmax_us.attr,
&dev_attr_power_limit_1_tmax_us.attr,
NULL
};
static struct attribute_group power_limit_attribute_group = {
.attrs = power_limit_attrs,
.name = "power_limits"
};
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;
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc;
union acpi_object *p;
int i;
int ret;
adev = ACPI_COMPANION(dev);
status = acpi_evaluate_object(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");
ret = -EFAULT;
goto free_buffer;
}
if (!p->package.count) {
dev_err(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 ||
elements->package.count != 6) {
ret = -EFAULT;
goto free_buffer;
}
ppcc = elements->package.elements;
proc_priv->power_limits[i].index = ppcc[0].integer.value;
proc_priv->power_limits[i].min_uw = ppcc[1].integer.value;
proc_priv->power_limits[i].max_uw = ppcc[2].integer.value;
proc_priv->power_limits[i].tmin_us = ppcc[3].integer.value;
proc_priv->power_limits[i].tmax_us = ppcc[4].integer.value;
proc_priv->power_limits[i].step_uw = ppcc[5].integer.value;
}
*priv = proc_priv;
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
free_buffer:
kfree(buf.pointer);
return ret;
}
void proc_thermal_remove(struct proc_thermal_device *proc_priv)
{
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
}
static int int3401_add(struct platform_device *pdev)
{
struct proc_thermal_device *proc_priv;
int ret;
if (proc_thermal_emum_mode == PROC_THERMAL_PCI) {
dev_err(&pdev->dev, "error: enumerated as PCI dev\n");
return -ENODEV;
}
ret = proc_thermal_add(&pdev->dev, &proc_priv);
if (ret)
return ret;
platform_set_drvdata(pdev, proc_priv);
proc_thermal_emum_mode = PROC_THERMAL_PLATFORM_DEV;
return 0;
}
static int int3401_remove(struct platform_device *pdev)
{
proc_thermal_remove(platform_get_drvdata(pdev));
return 0;
}
static int proc_thermal_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *unused)
{
struct proc_thermal_device *proc_priv;
int ret;
if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) {
dev_err(&pdev->dev, "error: enumerated as platform dev\n");
return -ENODEV;
}
ret = pci_enable_device(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "error: could not enable device\n");
return ret;
}
ret = proc_thermal_add(&pdev->dev, &proc_priv);
if (ret) {
pci_disable_device(pdev);
return ret;
}
pci_set_drvdata(pdev, proc_priv);
proc_thermal_emum_mode = PROC_THERMAL_PCI;
return 0;
}
static void proc_thermal_pci_remove(struct pci_dev *pdev)
{
proc_thermal_remove(pci_get_drvdata(pdev));
pci_disable_device(pdev);
}
static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
static struct pci_driver proc_thermal_pci_driver = {
.name = "proc_thermal",
.probe = proc_thermal_pci_probe,
.remove = proc_thermal_pci_remove,
.id_table = proc_thermal_pci_ids,
};
static const struct acpi_device_id int3401_device_ids[] = {
{"INT3401", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3401_device_ids);
static struct platform_driver int3401_driver = {
.probe = int3401_add,
.remove = int3401_remove,
.driver = {
.name = "int3401 thermal",
.acpi_match_table = int3401_device_ids,
},
};
static int __init proc_thermal_init(void)
{
int ret;
ret = platform_driver_register(&int3401_driver);
if (ret)
return ret;
ret = pci_register_driver(&proc_thermal_pci_driver);
return ret;
}
static void __exit proc_thermal_exit(void)
{
platform_driver_unregister(&int3401_driver);
pci_unregister_driver(&proc_thermal_pci_driver);
}
module_init(proc_thermal_init);
module_exit(proc_thermal_exit);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
MODULE_LICENSE("GPL v2");
......@@ -688,6 +688,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = {
{ X86_VENDOR_INTEL, 6, 0x45},
{ X86_VENDOR_INTEL, 6, 0x46},
{ X86_VENDOR_INTEL, 6, 0x4c},
{ X86_VENDOR_INTEL, 6, 0x56},
{}
};
MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
......
......@@ -677,7 +677,6 @@ static SIMPLE_DEV_PM_OPS(rockchip_thermal_pm_ops,
static struct platform_driver rockchip_thermal_driver = {
.driver = {
.name = "rockchip-thermal",
.owner = THIS_MODULE,
.pm = &rockchip_thermal_pm_ops,
.of_match_table = of_rockchip_thermal_match,
},
......
config EXYNOS_THERMAL
tristate "Exynos thermal management unit driver"
depends on ARCH_HAS_BANDGAP && OF
depends on OF
help
If you say yes here you get support for the TMU (Thermal Management
Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises
......
......@@ -347,7 +347,6 @@ void exynos_report_trigger(struct thermal_sensor_conf *conf)
int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
{
int ret;
struct cpumask mask_val;
struct exynos_thermal_zone *th_zone;
if (!sensor_conf || !sensor_conf->read_temperature) {
......@@ -367,13 +366,14 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
* sensor
*/
if (sensor_conf->cooling_data.freq_clip_count > 0) {
cpumask_set_cpu(0, &mask_val);
th_zone->cool_dev[th_zone->cool_dev_size] =
cpufreq_cooling_register(&mask_val);
cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
dev_err(sensor_conf->dev,
"Failed to register cpufreq cooling device\n");
ret = -EINVAL;
ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]);
if (ret != -EPROBE_DEFER)
dev_err(sensor_conf->dev,
"Failed to register cpufreq cooling device: %d\n",
ret);
goto err_unregister;
}
th_zone->cool_dev_size++;
......
......@@ -927,7 +927,10 @@ static int exynos_tmu_probe(struct platform_device *pdev)
/* Register the sensor with thermal management interface */
ret = exynos_register_thermal(sensor_conf);
if (ret) {
dev_err(&pdev->dev, "Failed to register thermal interface\n");
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to register thermal interface: %d\n",
ret);
goto err_clk;
}
data->reg_conf = sensor_conf;
......
......@@ -930,7 +930,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
struct thermal_zone_device *pos1;
struct thermal_cooling_device *pos2;
unsigned long max_state;
int result;
int result, ret;
if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
return -EINVAL;
......@@ -947,7 +947,9 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (tz != pos1 || cdev != pos2)
return -EINVAL;
cdev->ops->get_max_state(cdev, &max_state);
ret = cdev->ops->get_max_state(cdev, &max_state);
if (ret)
return ret;
/* lower default 0, upper default max_state */
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
......
......@@ -28,7 +28,6 @@
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/thermal.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cpu_cooling.h>
#include <linux/of.h>
......@@ -407,17 +406,17 @@ int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
if (!data)
return -EINVAL;
if (!cpufreq_get_current_driver()) {
dev_dbg(bgp->dev, "no cpufreq driver yet\n");
return -EPROBE_DEFER;
}
/* Register cooling device */
data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(data->cool_dev)) {
dev_err(bgp->dev,
"Failed to register cpufreq cooling device\n");
return PTR_ERR(data->cool_dev);
int ret = PTR_ERR(data->cool_dev);
if (ret != -EPROBE_DEFER)
dev_err(bgp->dev,
"Failed to register cpu cooling device %d\n",
ret);
return ret;
}
ti_bandgap_set_sensor_data(bgp, id, data);
......
......@@ -11,7 +11,7 @@
#define _DT_BINDINGS_THERMAL_THERMAL_H
/* On cooling devices upper and lower limits */
#define THERMAL_NO_LIMIT (-1UL)
#define THERMAL_NO_LIMIT (~0)
#endif
......@@ -50,7 +50,7 @@ static inline struct thermal_cooling_device *
of_cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus)
{
return NULL;
return ERR_PTR(-ENOSYS);
}
#endif
......@@ -65,13 +65,13 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq);
static inline struct thermal_cooling_device *
cpufreq_cooling_register(const struct cpumask *clip_cpus)
{
return NULL;
return ERR_PTR(-ENOSYS);
}
static inline struct thermal_cooling_device *
of_cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus)
{
return NULL;
return ERR_PTR(-ENOSYS);
}
static inline
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
......
......@@ -38,7 +38,7 @@
#define THERMAL_CSTATE_INVALID -1UL
/* No upper/lower limit requirement */
#define THERMAL_NO_LIMIT THERMAL_CSTATE_INVALID
#define THERMAL_NO_LIMIT ((u32)~0)
/* Unit conversion macros */
#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment