Commit f883de99 authored by Ye Li's avatar Ye Li Committed by Jason Liu

MLK-16238-1 thermal: Add TMU driver for i.MX8M

Porting the TMU driver from kernel and supporting DM in the driver.
Users need to provide the TMU node and sensors nodes in DTB.
Signed-off-by: default avatarYe Li <ye.li@nxp.com>
Acked-by: default avatarPeng Fan <peng.fan@nxp.com>
parent cc4e6b37
......@@ -26,4 +26,13 @@ config IMX_SC_THERMAL
boot is hold to the cool device to throttle CPUs when the passive trip
is crossed
config NXP_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
endif # if DM_THERMAL
......@@ -8,3 +8,4 @@
obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
obj-$(CONFIG_NXP_TMU) += nxp_tmu.o
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <fuse.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <thermal.h>
#include <dm/device-internal.h>
#include <dm/device.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
/*
* NXP TMU Registers
*/
struct nxp_tmu_site_regs {
u32 tritsr; /* Immediate Temperature Site Register */
u32 tratsr; /* Average Temperature Site Register */
u8 res0[0x8];
};
struct nxp_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 nxp_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 nxp_tmu_plat {
int critical;
int alert;
int polling_delay;
int id;
bool zone_node;
struct nxp_tmu_regs *regs;
};
static int read_temperature(struct udevice *dev, int *temp)
{
struct nxp_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 nxp_tmu_get_temp(struct udevice *dev, int *temp)
{
struct nxp_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 nxp_tmu_ops = {
.get_temp = nxp_tmu_get_temp,
};
static int nxp_tmu_calibration(struct udevice *dev)
{
int i, val, len, ret;
u32 range[4];
const fdt32_t *calibration;
struct nxp_tmu_plat *pdata = dev_get_platdata(dev);
debug("%s\n", __func__);
ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(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 = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
"fsl,tmu-calibration", &len);
if (calibration == NULL || 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 nxp_tmu_init(struct nxp_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 nxp_tmu_enable_msite(struct udevice *dev)
{
struct nxp_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 nxp_tmu_probe(struct udevice *dev)
{
struct nxp_tmu_plat *pdata = dev_get_platdata(dev);
debug("%s dev name %s\n", __func__, dev->name);
if (pdata->zone_node) {
nxp_tmu_init(pdata);
nxp_tmu_calibration(dev);
} else {
nxp_tmu_enable_msite(dev);
}
return 0;
}
static int nxp_tmu_bind(struct udevice *dev)
{
int ret;
int offset;
const char *name;
const void *prop;
struct nxp_tmu_plat *pdata = dev_get_platdata(dev);
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;
else
pdata->zone_node = 1;
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, NULL);
if (ret)
printf("Error binding driver '%s': %d\n", dev->driver->name,
ret);
}
return 0;
}
static int nxp_tmu_ofdata_to_platdata(struct udevice *dev)
{
int ret;
int trips_np;
struct nxp_tmu_plat *pdata = dev_get_platdata(dev);
struct fdtdec_phandle_args args;
debug("%s dev name %s\n", __func__, dev->name);
if (pdata->zone_node) {
pdata->regs = (struct nxp_tmu_regs *)fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), "reg");
if ((fdt_addr_t)pdata->regs == FDT_ADDR_T_NONE)
return -EINVAL;
return 0;
} else {
struct nxp_tmu_plat *p_parent_data = dev_get_platdata(dev->parent);
if (p_parent_data->zone_node)
pdata->regs = p_parent_data->regs;
}
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) {
const char *type;
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 struct udevice_id nxp_tmu_ids[] = {
{ .compatible = "fsl,imx8mq-tmu", },
{ }
};
U_BOOT_DRIVER(nxp_tmu) = {
.name = "nxp_tmu",
.id = UCLASS_THERMAL,
.ops = &nxp_tmu_ops,
.of_match = nxp_tmu_ids,
.bind = nxp_tmu_bind,
.probe = nxp_tmu_probe,
.ofdata_to_platdata = nxp_tmu_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct nxp_tmu_plat),
.flags = DM_FLAG_PRE_RELOC,
};
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