thermal: Add thermal driver for i.MX8M

The driver is ported form Linux Kernel and support driver model.
Users need to provide the tmu node and sensors nodes in DTB.

Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
This commit is contained in:
Peng Fan 2020-05-03 22:19:46 +08:00 committed by Stefano Babic
parent 6039d669f4
commit 2cd7f47983
3 changed files with 335 additions and 0 deletions

View File

@ -26,6 +26,15 @@ config IMX_SCU_THERMAL
boot is hold to the cool device to throttle CPUs when the passive
trip is crossed
config IMX_TMU
bool "Thermal Management Unit driver for NXP i.MX8M"
depends on ARCH_IMX8M
help
Support for Temperature sensors on NXP i.MX8M.
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

@ -7,3 +7,4 @@ 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
obj-$(CONFIG_IMX_TMU) += imx_tmu.o

325
drivers/thermal/imx_tmu.c Normal file
View File

@ -0,0 +1,325 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017~2020 NXP
*
*/
#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/device.h>
#include <errno.h>
#include <fuse.h>
#include <malloc.h>
#include <thermal.h>
DECLARE_GLOBAL_DATA_PTR;
#define SITES_MAX 16
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
#define TMR_ALPF 0x0c000000
#define TMTMIR_DEFAULT 0x00000002
#define TIER_DISABLE 0x0
/*
* i.MX TMU Registers
*/
struct imx_tmu_site_regs {
u32 tritsr; /* Immediate Temperature Site Register */
u32 tratsr; /* Average Temperature Site Register */
u8 res0[0x8];
};
struct imx_tmu_regs {
u32 tmr; /* Mode Register */
u32 tsr; /* Status Register */
u32 tmtmir; /* Temperature measurement interval Register */
u8 res0[0x14];
u32 tier; /* Interrupt Enable Register */
u32 tidr; /* Interrupt Detect Register */
u32 tiscr; /* Interrupt Site Capture Register */
u32 ticscr; /* Interrupt Critical Site Capture Register */
u8 res1[0x10];
u32 tmhtcrh; /* High Temperature Capture Register */
u32 tmhtcrl; /* Low Temperature Capture Register */
u8 res2[0x8];
u32 tmhtitr; /* High Temperature Immediate Threshold */
u32 tmhtatr; /* High Temperature Average Threshold */
u32 tmhtactr; /* High Temperature Average Crit Threshold */
u8 res3[0x24];
u32 ttcfgr; /* Temperature Configuration Register */
u32 tscfgr; /* Sensor Configuration Register */
u8 res4[0x78];
struct imx_tmu_site_regs site[SITES_MAX];
u8 res5[0x9f8];
u32 ipbrr0; /* IP Block Revision Register 0 */
u32 ipbrr1; /* IP Block Revision Register 1 */
u8 res6[0x310];
u32 ttr0cr; /* Temperature Range 0 Control Register */
u32 ttr1cr; /* Temperature Range 1 Control Register */
u32 ttr2cr; /* Temperature Range 2 Control Register */
u32 ttr3cr; /* Temperature Range 3 Control Register */
};
struct imx_tmu_plat {
int critical;
int alert;
int polling_delay;
int id;
bool zone_node;
struct imx_tmu_regs *regs;
};
static int read_temperature(struct udevice *dev, int *temp)
{
struct imx_tmu_plat *pdata = dev_get_platdata(dev);
u32 val;
do {
val = readl(&pdata->regs->site[pdata->id].tritsr);
} while (!(val & 0x80000000));
*temp = (val & 0xff) * 1000;
return 0;
}
int imx_tmu_get_temp(struct udevice *dev, int *temp)
{
struct imx_tmu_plat *pdata = dev_get_platdata(dev);
int cpu_tmp = 0;
int ret;
ret = read_temperature(dev, &cpu_tmp);
if (ret)
return ret;
while (cpu_tmp >= pdata->alert) {
printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)", cpu_tmp, pdata->alert, pdata->critical);
puts(" waiting...\n");
mdelay(pdata->polling_delay);
ret = read_temperature(dev, &cpu_tmp);
if (ret)
return ret;
}
*temp = cpu_tmp / 1000;
return 0;
}
static const struct dm_thermal_ops imx_tmu_ops = {
.get_temp = imx_tmu_get_temp,
};
static int imx_tmu_calibration(struct udevice *dev)
{
int i, val, len, ret;
u32 range[4];
const fdt32_t *calibration;
struct imx_tmu_plat *pdata = dev_get_platdata(dev);
debug("%s\n", __func__);
ret = dev_read_u32_array(dev, "fsl,tmu-range", range, 4);
if (ret) {
printf("TMU: missing calibration range, ret = %d.\n", ret);
return ret;
}
/* Init temperature range registers */
writel(range[0], &pdata->regs->ttr0cr);
writel(range[1], &pdata->regs->ttr1cr);
writel(range[2], &pdata->regs->ttr2cr);
writel(range[3], &pdata->regs->ttr3cr);
calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
if (!calibration || len % 8) {
printf("TMU: invalid calibration data.\n");
return -ENODEV;
}
for (i = 0; i < len; i += 8, calibration += 2) {
val = fdt32_to_cpu(*calibration);
writel(val, &pdata->regs->ttcfgr);
val = fdt32_to_cpu(*(calibration + 1));
writel(val, &pdata->regs->tscfgr);
}
return 0;
}
static void imx_tmu_init(struct imx_tmu_plat *pdata)
{
debug("%s\n", __func__);
/* Disable monitoring */
writel(TMR_DISABLE, &pdata->regs->tmr);
/* Disable interrupt, using polling instead */
writel(TIER_DISABLE, &pdata->regs->tier);
/* Set update_interval */
writel(TMTMIR_DEFAULT, &pdata->regs->tmtmir);
}
static int imx_tmu_enable_msite(struct udevice *dev)
{
struct imx_tmu_plat *pdata = dev_get_platdata(dev);
u32 reg;
debug("%s\n", __func__);
if (!pdata->regs)
return -EIO;
/* Clear the ME before setting MSITE and ALPF*/
reg = readl(&pdata->regs->tmr);
reg &= ~TMR_ME;
writel(reg, &pdata->regs->tmr);
reg |= 1 << (15 - pdata->id);
reg |= TMR_ALPF;
writel(reg, &pdata->regs->tmr);
/* Enable ME */
reg |= TMR_ME;
writel(reg, &pdata->regs->tmr);
return 0;
}
static int imx_tmu_bind(struct udevice *dev)
{
struct imx_tmu_plat *pdata = dev_get_platdata(dev);
int ret;
ofnode node, offset;
const char *name;
const void *prop;
debug("%s dev name %s\n", __func__, dev->name);
prop = dev_read_prop(dev, "compatible", NULL);
if (!prop)
return 0;
pdata->zone_node = 1;
node = ofnode_path("/thermal-zones");
ofnode_for_each_subnode(offset, node) {
/* Bind the subnode to this driver */
name = ofnode_get_name(offset);
ret = device_bind_with_driver_data(dev, dev->driver, name,
dev->driver_data, offset,
NULL);
if (ret)
printf("Error binding driver '%s': %d\n",
dev->driver->name, ret);
}
return 0;
}
static int imx_tmu_parse_fdt(struct udevice *dev)
{
struct imx_tmu_plat *pdata = dev_get_platdata(dev), *p_parent_data;
struct ofnode_phandle_args args;
ofnode trips_np;
int ret;
debug("%s dev name %s\n", __func__, dev->name);
if (pdata->zone_node) {
pdata->regs = (struct imx_tmu_regs *)dev_read_addr_ptr(dev);
if (!pdata->regs)
return -EINVAL;
return 0;
}
p_parent_data = dev_get_platdata(dev->parent);
if (p_parent_data->zone_node)
pdata->regs = p_parent_data->regs;
ret = dev_read_phandle_with_args(dev, "thermal-sensors",
"#thermal-sensor-cells",
0, 0, &args);
if (ret)
return ret;
if (!ofnode_equal(args.node, dev_ofnode(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 = dev_read_u32_default(dev, "polling-delay", 1000);
trips_np = ofnode_path("/thermal-zones/cpu-thermal/trips");
ofnode_for_each_subnode(trips_np, trips_np) {
const char *type;
type = ofnode_get_property(trips_np, "type", NULL);
if (!type)
continue;
if (!strcmp(type, "critical"))
pdata->critical = ofnode_read_u32_default(trips_np, "temperature", 85);
else if (strcmp(type, "passive") == 0)
pdata->alert = ofnode_read_u32_default(trips_np, "temperature", 80);
else
continue;
}
debug("id %d polling_delay %d, critical %d, alert %d\n",
pdata->id, pdata->polling_delay, pdata->critical, pdata->alert);
return 0;
}
static int imx_tmu_probe(struct udevice *dev)
{
struct imx_tmu_plat *pdata = dev_get_platdata(dev);
int ret;
ret = imx_tmu_parse_fdt(dev);
if (ret) {
printf("Error in parsing TMU FDT %d\n", ret);
return ret;
}
if (pdata->zone_node) {
imx_tmu_init(pdata);
imx_tmu_calibration(dev);
} else {
imx_tmu_enable_msite(dev);
}
return 0;
}
static const struct udevice_id imx_tmu_ids[] = {
{ .compatible = "fsl,imx8mq-tmu", },
{ }
};
U_BOOT_DRIVER(imx_tmu) = {
.name = "imx_tmu",
.id = UCLASS_THERMAL,
.ops = &imx_tmu_ops,
.of_match = imx_tmu_ids,
.bind = imx_tmu_bind,
.probe = imx_tmu_probe,
.platdata_auto_alloc_size = sizeof(struct imx_tmu_plat),
.flags = DM_FLAG_PRE_RELOC,
};