thermal: add i.MX8 thermal driver

Add i.MX8 thermal driver to support get temperature from SCU.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
This commit is contained in:
Peng Fan 2019-05-05 13:23:54 +00:00 committed by Stefano Babic
parent 7752a0fef7
commit 5ef5b6d46f
3 changed files with 213 additions and 0 deletions

View File

@ -17,6 +17,15 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
config IMX_SCU_THERMAL
bool "Temperature sensor driver for NXP i.MX8"
depends on ARCH_IMX8
help
Support for Temperature sensors on NXP i.MX8.
It supports one critical trip point and one passive trip point. The
boot is hold to the cool device to throttle CPUs when the passive
trip is crossed
config TI_DRA7_THERMAL
bool "Temperature sensor driver for TI dra7xx SOCs"
help

View File

@ -5,4 +5,5 @@
obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o

View File

@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2019 NXP
*/
#include <config.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <thermal.h>
#include <dm/device-internal.h>
#include <dm/device.h>
#include <asm/arch/sci/sci.h>
DECLARE_GLOBAL_DATA_PTR;
struct imx_sc_thermal_plat {
int critical;
int alert;
int polling_delay;
int id;
bool zone_node;
};
static int read_temperature(struct udevice *dev, int *temp)
{
s16 celsius;
s8 tenths;
int ret;
sc_rsrc_t *sensor_rsrc = (sc_rsrc_t *)dev_get_driver_data(dev);
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
if (!temp)
return -EINVAL;
ret = sc_misc_get_temp(-1, sensor_rsrc[pdata->id], SC_C_TEMP,
&celsius, &tenths);
if (ret) {
printf("Error: get temperature failed! (error = %d)\n", ret);
return ret;
}
*temp = celsius * 1000 + tenths * 100;
return 0;
}
int imx_sc_thermal_get_temp(struct udevice *dev, int *temp)
{
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
int cpu_temp = 0;
int ret;
ret = read_temperature(dev, &cpu_temp);
if (ret)
return ret;
while (cpu_temp >= pdata->alert) {
printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)",
cpu_temp, pdata->alert, pdata->critical);
puts(" waiting...\n");
mdelay(pdata->polling_delay);
ret = read_temperature(dev, &cpu_temp);
if (ret)
return ret;
}
*temp = cpu_temp / 1000;
return 0;
}
static const struct dm_thermal_ops imx_sc_thermal_ops = {
.get_temp = imx_sc_thermal_get_temp,
};
static int imx_sc_thermal_probe(struct udevice *dev)
{
debug("%s dev name %s\n", __func__, dev->name);
return 0;
}
static int imx_sc_thermal_bind(struct udevice *dev)
{
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
int reg, ret;
int offset;
const char *name;
const void *prop;
debug("%s dev name %s\n", __func__, dev->name);
prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "compatible",
NULL);
if (!prop)
return 0;
pdata->zone_node = 1;
reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "tsens-num", 0);
if (reg == 0) {
printf("%s: no temp sensor number provided!\n", __func__);
return -EINVAL;
}
offset = fdt_subnode_offset(gd->fdt_blob, 0, "thermal-zones");
fdt_for_each_subnode(offset, gd->fdt_blob, offset) {
/* Bind the subnode to this driver */
name = fdt_get_name(gd->fdt_blob, offset, NULL);
ret = device_bind_with_driver_data(dev, dev->driver, name,
dev->driver_data,
offset_to_ofnode(offset),
NULL);
if (ret)
printf("Error binding driver '%s': %d\n",
dev->driver->name, ret);
}
return 0;
}
static int imx_sc_thermal_ofdata_to_platdata(struct udevice *dev)
{
struct imx_sc_thermal_plat *pdata = dev_get_platdata(dev);
struct fdtdec_phandle_args args;
const char *type;
int ret;
int trips_np;
debug("%s dev name %s\n", __func__, dev->name);
if (pdata->zone_node)
return 0;
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
"thermal-sensors",
"#thermal-sensor-cells",
0, 0, &args);
if (ret)
return ret;
if (args.node != dev_of_offset(dev->parent))
return -EFAULT;
if (args.args_count >= 1)
pdata->id = args.args[0];
else
pdata->id = 0;
debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
pdata->polling_delay = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"polling-delay", 1000);
trips_np = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(dev),
"trips");
fdt_for_each_subnode(trips_np, gd->fdt_blob, trips_np) {
type = fdt_getprop(gd->fdt_blob, trips_np, "type", NULL);
if (type) {
if (strcmp(type, "critical") == 0) {
pdata->critical = fdtdec_get_int(gd->fdt_blob,
trips_np,
"temperature",
85);
} else if (strcmp(type, "passive") == 0) {
pdata->alert = fdtdec_get_int(gd->fdt_blob,
trips_np,
"temperature",
80);
}
}
}
debug("id %d polling_delay %d, critical %d, alert %d\n", pdata->id,
pdata->polling_delay, pdata->critical, pdata->alert);
return 0;
}
static const sc_rsrc_t imx8qxp_sensor_rsrc[] = {
SC_R_SYSTEM, SC_R_DRC_0, SC_R_PMIC_0,
SC_R_PMIC_1, SC_R_PMIC_2,
};
static const struct udevice_id imx_sc_thermal_ids[] = {
{ .compatible = "nxp,imx8qxp-sc-tsens", .data =
(ulong)&imx8qxp_sensor_rsrc, },
{ }
};
U_BOOT_DRIVER(imx_sc_thermal) = {
.name = "imx_sc_thermal",
.id = UCLASS_THERMAL,
.ops = &imx_sc_thermal_ops,
.of_match = imx_sc_thermal_ids,
.bind = imx_sc_thermal_bind,
.probe = imx_sc_thermal_probe,
.ofdata_to_platdata = imx_sc_thermal_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct imx_sc_thermal_plat),
.flags = DM_FLAG_PRE_RELOC,
};