tegrakernel/kernel/nvidia/drivers/thermal/tegra/tegra_tj_thermal.c

185 lines
4.4 KiB
C

/*
* Copyright (c) 2016 - 2017, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Navneet Kumar <navneetk@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/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
struct tj_tzs {
struct thermal_zone_device *tzd;
int offset;
};
struct tj {
struct device *dev;
struct thermal_zone_device *self;
int num_tzs;
struct tj_tzs *tzs;
};
static int tegra_tj_thermal_get_temp(void *data, int *out_temp)
{
int i;
struct tj *ptj = (struct tj *)data;
*out_temp = THERMAL_TEMP_INVALID;
for (i = 0; i < ptj->num_tzs; i++) {
int temp, ret;
if (ptj->tzs[i].tzd) {
ret = thermal_zone_get_temp(ptj->tzs[i].tzd, &temp);
if (ret < 0) {
dev_err(ptj->dev, "get_temp failed (%d) [%d]\n",
i, ret);
return ret;
}
temp += ptj->tzs[i].offset;
*out_temp = *out_temp > temp ? *out_temp : temp;
}
}
return 0;
}
static struct thermal_zone_of_device_ops of_thermal_ops = {
.get_temp = tegra_tj_thermal_get_temp,
};
static const struct of_device_id tegra_tj_thermal_of_match[] = {
{
.compatible = "nvidia,tegra-tj-thermal",
},
{ },
};
MODULE_DEVICE_TABLE(of, tegra_tj_thermal_of_match);
static int match(struct thermal_zone_device *tzd, void *data)
{
return !strncmp(tzd->type, (char *)data, 32);
}
static int tegra_tj_thermal_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int i, ret = 0, j = 0;
struct thermal_zone_device *tzd;
int temp;
struct tj *ptj = devm_kzalloc(dev, sizeof(*ptj), GFP_KERNEL);
if (IS_ERR_OR_NULL(ptj))
return -ENOMEM;
platform_set_drvdata(pdev, ptj);
ptj->dev = dev;
ret = of_count_phandle_with_args(dev->of_node, "data", NULL);
if (ret < 0) {
dev_err(dev, "failed to parse sensors data\n");
return ret;
}
if (ret % 2) {
dev_err(dev, "invalid sensors data\n");
return -ENODEV;
}
ptj->num_tzs = ret / 2;
ptj->tzs = devm_kzalloc(dev, ptj->num_tzs * sizeof(struct tj_tzs), GFP_KERNEL);
if (!ptj->tzs)
return -ENOMEM;
for (i = 0; i < ptj->num_tzs; i++) {
struct of_phandle_args args;
struct thermal_zone_device *tzd;
ret = of_parse_phandle_with_fixed_args(dev->of_node, "data", 1,
i, &args);
if (ret) {
dev_err(dev, "parse handle failed for %d [%d]\n", i,
ret);
continue;
}
if (args.args_count != 1)
continue;
tzd = thermal_zone_device_find((void *)args.np->name, match);
if (IS_ERR_OR_NULL(tzd)) {
dev_err(dev, "failed to find thermal zone for %s\n",
args.np->name);
ptj->tzs[i].tzd = NULL;
continue;
}
dev_info(dev, "found thermal zone %s + offset %d\n", tzd->type,
args.args[0]);
/*
* To check if thermal sensor of thermal zone driver is already
* initialized, need to check with error code -EINVAL.
* get_temp call returns -EINVAL when thermal sensor driver is
* not initialied.
*/
if (thermal_zone_get_temp(tzd, &temp) == -EINVAL) {
dev_info(dev, "%s driver not initialized.. Deferring the probe\n",
tzd->type);
return -EPROBE_DEFER;
}
ptj->tzs[i].tzd = tzd;
ptj->tzs[i].offset = args.args[0];
j++;
}
if (!j) {
dev_err(dev, "could not add any thermal zone\n");
return -ENODEV;
}
tzd = thermal_zone_of_sensor_register(dev, 0, ptj, &of_thermal_ops);
if (IS_ERR_OR_NULL(tzd)) {
ret = PTR_ERR(tzd);
dev_err(dev, "failed to register sensor %d\n", ret);
} else {
ptj->self = tzd;
}
return ret;
}
static int tegra_tj_thermal_remove(struct platform_device *pdev)
{
struct tj *ptj = platform_get_drvdata(pdev);
thermal_zone_of_sensor_unregister(&pdev->dev, ptj->self);
return 0;
}
static struct platform_driver tegra_tj_thermal_driver = {
.probe = tegra_tj_thermal_probe,
.remove = tegra_tj_thermal_remove,
.driver = {
.name = "tegra-tj-thermal",
.of_match_table = tegra_tj_thermal_of_match,
},
};
module_platform_driver(tegra_tj_thermal_driver);
MODULE_AUTHOR("Navneet Kumar <navneetk@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra Tj sensor driver");
MODULE_LICENSE("GPL v2");