1
0
Fork 0

MLK-18428-01 driver: thermal: add tmu driver on imx8mm

add thermal driver on i.MX8MM

Signed-off-by: Bai Ping <ping.bai@nxp.com>
Reviewed-by: Anson Huang <Anson.Huang@nxp.com>
pull/10/head
Bai Ping 2018-06-11 16:45:50 +08:00 committed by Jason Liu
parent eea321eaaf
commit 99e929e755
3 changed files with 248 additions and 0 deletions

View File

@ -224,6 +224,16 @@ config IMX8M_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
config IMX8MM_THERMAL
tristate "i.MX8MM Thermal Monitor Unit"
depends on THERMAL_OF
depends on HAS_IOMEM
help
Support for Thermal Monitor Unit (TMU) found on i.MX8MM platforms.
It supports one critical trip point and one passive trip point. The
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
config IMX_SC_THERMAL
tristate "thermal sensor driver for NXP i.MX8 SoCs"
depends on THERMAL_OF && ARCH_MXC_ARM64

View File

@ -42,6 +42,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX8M_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o

View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2018 NXP.
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device_cooling.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include "thermal_core.h"
#define TER 0x0 /* TMU enable */
#define TSR 0x4 /* TMU status */
#define TIER 0x8 /* TMU interrupt enable */
#define TIDR 0xc /* TMU interrupt detect */
#define TMHTITR 0x10 /* TMU high immediate threshold */
#define TMHTATR 0x14 /* TMU high average threshold */
#define TMHTCATR 0x18 /* TMU high average critical threshold */
#define TSCR 0x1c /* TMU sensor val(raw, no calibration) */
#define TRITSR 0x20 /* TMU immediate temp */
#define TRATSR 0x24 /* TMU average temp */
#define TER_EN BIT(31)
#define TRITSR_VALID BIT(31)
#define TEMP_VAL_MASK 0xff
#define IMX_TEMP_PASSIVE_COOL_DELTA 10000
struct imx8mm_tmu {
struct thermal_zone_device *tzd;
struct thermal_cooling_device *cdev;
struct clk *clk;
void __iomem *tmu_base;
bool enabled;
int temp_passive;
int temp_critical;
};
/* The driver support 1 passive trip point and 1 critical trip point */
enum imx_thermal_trip {
IMX_TRIP_PASSIVE,
IMX_TRIP_CRITICAL,
IMX_TRIP_NUM,
};
static int tmu_get_temp(void *data, int *temp)
{
struct imx8mm_tmu *tmu = data;
u32 val;
/* the temp sensor need about 1ms to finish the measurement */
msleep(1);
/* read the calibrated temp value */
val = readl_relaxed(tmu->tmu_base + TRITSR);
if (!(val & TRITSR_VALID))
return -EAGAIN;
*temp = (val & TEMP_VAL_MASK) * 1000;
return 0;
}
static int tmu_get_trend(void *p, int trip, enum thermal_trend *trend)
{
int trip_temp;
struct imx8mm_tmu *tmu = p;
if (!tmu->tzd)
return 0;
trip_temp = (trip == IMX_TRIP_PASSIVE) ? tmu->temp_passive : tmu->temp_critical;
if (tmu->tzd->temperature >= (trip_temp - IMX_TEMP_PASSIVE_COOL_DELTA))
*trend = THERMAL_TREND_RAISE_FULL;
else
*trend = THERMAL_TREND_DROP_FULL;
return 0;
}
static int tmu_set_trip_temp(void *p, int trip, int temp)
{
struct imx8mm_tmu *tmu = p;
if (trip == IMX_TRIP_CRITICAL)
tmu->temp_critical = temp;
if (trip == IMX_TRIP_PASSIVE)
tmu->temp_passive = temp;
return 0;
}
static struct thermal_zone_of_device_ops tmu_tz_ops = {
.get_temp = tmu_get_temp,
.get_trend = tmu_get_trend,
.set_trip_temp = tmu_set_trip_temp,
};
static const struct of_device_id imx8mm_tmu_table[] = {
{ .compatible = "fsl,imx8mm-tmu", },
{ },
};
static int imx8mm_tmu_probe(struct platform_device *pdev)
{
int ret;
u32 val;
struct imx8mm_tmu *tmu;
const struct thermal_trip *trips;
struct device_node *np = pdev->dev.of_node;
if (!np) {
dev_err(&pdev->dev, "device node NOT found\n");
return -ENODEV;
}
tmu = devm_kzalloc(&pdev->dev, sizeof(*tmu), GFP_KERNEL);
if (!tmu)
return -ENOMEM;
platform_set_drvdata(pdev, tmu);
tmu->tmu_base = of_iomap(np, 0);
if (!tmu->tmu_base) {
dev_err(&pdev->dev, "Failed to map the memory\n");
return -ENODEV;
}
tmu->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tmu->clk)) {
ret = PTR_ERR(tmu->clk);
dev_err(&pdev->dev, "Failed to get the tmu clk\n");
goto err;
}
/* register the thermal zone sensor */
tmu->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, tmu, &tmu_tz_ops);
if (IS_ERR(tmu->tzd)) {
ret = PTR_ERR(tmu->tzd);
dev_err(&pdev->dev, "Failed to register thermal zone sensor %d\n", ret);
goto err;
}
tmu->cdev = devfreq_cooling_register();
if (IS_ERR(tmu->cdev)) {
ret = PTR_ERR(tmu->cdev);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to register devfreq cooling device %d\n", ret);
goto err;
}
ret = thermal_zone_bind_cooling_device(tmu->tzd,
IMX_TRIP_PASSIVE,
tmu->cdev,
THERMAL_NO_LIMIT,
THERMAL_NO_LIMIT,
THERMAL_WEIGHT_DEFAULT);
if (ret) {
dev_err(&pdev->dev,
"binding zone %s with cdev %s failed:%d\n",
tmu->tzd->type, tmu->cdev->type, ret);
devfreq_cooling_unregister(tmu->cdev);
goto err;
}
trips = of_thermal_get_trip_points(tmu->tzd);
/* get the thermal trip temp */
tmu->temp_passive = trips[0].temperature;
tmu->temp_critical = trips[1].temperature;
/* enable the tmu clock */
ret = clk_prepare_enable(tmu->clk);
if (ret) {
dev_warn(&pdev->dev, "tmu clock enable failed:%d\n", ret);
thermal_zone_unbind_cooling_device(tmu->tzd, IMX_TRIP_PASSIVE, tmu->cdev);
devfreq_cooling_unregister(tmu->cdev);
goto err;
}
/* enable the monitor */
val = readl_relaxed(tmu->tmu_base + TER);
val |= TER_EN;
writel_relaxed(val, tmu->tmu_base + TER);
return 0;
err:
iounmap(tmu->tmu_base);
return ret;
}
static int imx8mm_tmu_remove(struct platform_device *pdev)
{
u32 val;
struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
thermal_zone_unbind_cooling_device(tmu->tzd, IMX_TRIP_PASSIVE, tmu->cdev);
devfreq_cooling_unregister(tmu->cdev);
/* disable TMU */
val = readl_relaxed(tmu->tmu_base + TER);
val &= ~TER_EN;
writel_relaxed(val, tmu->tmu_base + TER);
/* disable TMU clk */
clk_disable_unprepare(tmu->clk);
iounmap(tmu->tmu_base);
return 0;
}
static struct platform_driver imx8mm_tmu = {
.driver = {
.name = "i.mx8mm_thermal",
.of_match_table = imx8mm_tmu_table,
},
.probe = imx8mm_tmu_probe,
.remove = imx8mm_tmu_remove,
};
module_platform_driver(imx8mm_tmu);
MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
MODULE_LICENSE("GPL v2");