Merge branch 'thermal/next' into next

* thermal/next: (12 commits)
  MLK-23010 thermal: imx_sc_thermal: Correct message format to avoid stack corruption
  thermal: imx_sc_thermal: Add system-wide device cooling to all thermal zones
  thermal: qoriq: add thermal monitor unit version 2 support
  thermal: imx: Add device cooling support
  thermal: imx8mm: Add device cooling support
  ...
This commit is contained in:
Dong Aisheng 2019-12-02 18:05:29 +08:00
commit e6fbc0b2a6
9 changed files with 960 additions and 36 deletions

View File

@ -0,0 +1,15 @@
* Thermal Monitoring Unit (TMU) on Freescale i.MX8MM SoC
Required properties:
- compatible : Must be "fsl,imx8mm-tmu".
- reg : Address range of TMU registers.
- clocks : TMU's clock source.
- #thermal-sensor-cells : Should be 0. See ./thermal.txt for a description.
Example:
tmu: tmu@30260000 {
compatible = "fsl,imx8mm-tmu";
reg = <0x30260000 0x10000>;
clocks = <&clk IMX8MM_CLK_TMU_ROOT>;
#thermal-sensor-cells = <0>;
};

View File

@ -233,6 +233,34 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
config IMX_SC_THERMAL
tristate "Temperature sensor driver for NXP i.MX SoCs with System Controller"
depends on (ARCH_MXC && IMX_SCU) || COMPILE_TEST
depends on OF
help
Support for Temperature Monitor (TEMPMON) found on NXP i.MX SoCs with
system controller inside, Linux kernel has to communicate with system
controller via MU (message unit) IPC to get temperature from thermal
sensor. It supports one critical trip point and one
passive trip point for each thermal sensor.
config DEVICE_THERMAL
tristate "generic device cooling support"
help
Support for device cooling.
It supports notification of crossing passive trip for devices,
devices need to do their own actions to cool down the SOC.
config IMX8MM_THERMAL
tristate "Temperature sensor driver for Freescale i.MX8MM SoC"
depends on ARCH_MXC
depends on OF
help
Support for Thermal Monitoring Unit (TMU) found on Freescale i.MX8MM SoC.
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 MAX77620_THERMAL
tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
depends on MFD_MAX77620

View File

@ -41,6 +41,9 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
obj-$(CONFIG_DEVICE_THERMAL) += device_cooling.o
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/thermal.h>
#include <linux/err.h>
#include <linux/slab.h>
struct devfreq_cooling_device {
int id;
struct thermal_cooling_device *cool_dev;
unsigned int devfreq_state;
};
static DEFINE_IDR(devfreq_idr);
static DEFINE_MUTEX(devfreq_cooling_lock);
#define MAX_STATE 1
static BLOCKING_NOTIFIER_HEAD(devfreq_cooling_chain_head);
int register_devfreq_cooling_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(
&devfreq_cooling_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_devfreq_cooling_notifier);
int unregister_devfreq_cooling_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(
&devfreq_cooling_chain_head, nb);
}
EXPORT_SYMBOL_GPL(unregister_devfreq_cooling_notifier);
static int devfreq_cooling_notifier_call_chain(unsigned long val)
{
return (blocking_notifier_call_chain(
&devfreq_cooling_chain_head, val, NULL)
== NOTIFY_BAD) ? -EINVAL : 0;
}
static int devfreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct devfreq_cooling_device *devfreq_device = cdev->devdata;
int ret;
ret = devfreq_cooling_notifier_call_chain(state);
if (ret)
return -EINVAL;
devfreq_device->devfreq_state = state;
return 0;
}
static int devfreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = MAX_STATE;
return 0;
}
static int devfreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct devfreq_cooling_device *devfreq_device = cdev->devdata;
*state = devfreq_device->devfreq_state;
return 0;
}
static struct thermal_cooling_device_ops const devfreq_cooling_ops = {
.get_max_state = devfreq_get_max_state,
.get_cur_state = devfreq_get_cur_state,
.set_cur_state = devfreq_set_cur_state,
};
static int get_idr(struct idr *idr, int *id)
{
int ret;
mutex_lock(&devfreq_cooling_lock);
ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
mutex_unlock(&devfreq_cooling_lock);
if (unlikely(ret < 0))
return ret;
*id = ret;
return 0;
}
static void release_idr(struct idr *idr, int id)
{
mutex_lock(&devfreq_cooling_lock);
idr_remove(idr, id);
mutex_unlock(&devfreq_cooling_lock);
}
struct thermal_cooling_device *devfreq_cooling_register(void)
{
struct thermal_cooling_device *cool_dev;
struct devfreq_cooling_device *devfreq_dev = NULL;
char dev_name[THERMAL_NAME_LENGTH];
int ret = 0;
devfreq_dev = kzalloc(sizeof(struct devfreq_cooling_device),
GFP_KERNEL);
if (!devfreq_dev)
return ERR_PTR(-ENOMEM);
ret = get_idr(&devfreq_idr, &devfreq_dev->id);
if (ret) {
kfree(devfreq_dev);
return ERR_PTR(-EINVAL);
}
snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d",
devfreq_dev->id);
cool_dev = thermal_cooling_device_register(dev_name, devfreq_dev,
&devfreq_cooling_ops);
if (!cool_dev) {
release_idr(&devfreq_idr, devfreq_dev->id);
kfree(devfreq_dev);
return ERR_PTR(-EINVAL);
}
devfreq_dev->cool_dev = cool_dev;
devfreq_dev->devfreq_state = 0;
return cool_dev;
}
EXPORT_SYMBOL_GPL(devfreq_cooling_register);
void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
struct devfreq_cooling_device *devfreq_dev = cdev->devdata;
thermal_cooling_device_unregister(devfreq_dev->cool_dev);
release_idr(&devfreq_idr, devfreq_dev->id);
kfree(devfreq_dev);
}
EXPORT_SYMBOL_GPL(devfreq_cooling_unregister);

View File

@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 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 TRITSR 0x20 /* TMU immediate temp */
#define TER_EN BIT(31)
#define TRITSR_VAL_MASK 0xff
#define TEMP_LOW_LIMIT 10
#define IMX_TEMP_PASSIVE_COOL_DELTA 10000
struct imx8mm_tmu {
struct thermal_zone_device *tzd;
void __iomem *base;
struct clk *clk;
struct thermal_cooling_device *cdev;
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 */
usleep_range(1000, 2000);
val = readl_relaxed(tmu->base + TRITSR) & TRITSR_VAL_MASK;
if (val < TEMP_LOW_LIMIT)
return -EAGAIN;
*temp = val * 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 int imx8mm_tmu_probe(struct platform_device *pdev)
{
const struct thermal_trip *trips;
struct imx8mm_tmu *tmu;
u32 val;
int ret;
tmu = devm_kzalloc(&pdev->dev, sizeof(struct imx8mm_tmu), GFP_KERNEL);
if (!tmu)
return -ENOMEM;
tmu->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tmu->base))
return PTR_ERR(tmu->base);
tmu->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tmu->clk)) {
ret = PTR_ERR(tmu->clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to get tmu clock: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(tmu->clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
return ret;
}
tmu->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
tmu, &tmu_tz_ops);
if (IS_ERR(tmu->tzd)) {
dev_err(&pdev->dev,
"failed to register thermal zone sensor: %d\n", ret);
return PTR_ERR(tmu->tzd);
}
platform_set_drvdata(pdev, tmu);
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);
return ret;
}
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);
return ret;
}
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 monitor */
val = readl_relaxed(tmu->base + TER);
val |= TER_EN;
writel_relaxed(val, tmu->base + TER);
return 0;
}
static int imx8mm_tmu_remove(struct platform_device *pdev)
{
struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
u32 val;
/* disable TMU */
val = readl_relaxed(tmu->base + TER);
val &= ~TER_EN;
writel_relaxed(val, tmu->base + TER);
clk_disable_unprepare(tmu->clk);
platform_set_drvdata(pdev, NULL);
return 0;
}
static const struct of_device_id imx8mm_tmu_table[] = {
{ .compatible = "fsl,imx8mm-tmu", },
{ },
};
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("Anson Huang <Anson.Huang@nxp.com>");
MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP.
*/
#include <linux/device_cooling.h>
#include <linux/err.h>
#include <linux/firmware/imx/sci.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "thermal_core.h"
#define IMX_SC_MISC_FUNC_GET_TEMP 13
#define IMX_SC_C_TEMP 0
#define IMX_SC_TEMP_PASSIVE_COOL_DELTA 10000
static struct imx_sc_ipc *thermal_ipc_handle;
struct imx_sc_sensor {
struct thermal_zone_device *tzd;
u32 resource_id;
struct thermal_cooling_device *cdev;
int temp_passive;
int temp_critical;
};
struct imx_sc_thermal_data {
struct imx_sc_sensor *sensor;
};
/* 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,
};
struct req_get_temp {
u16 resource_id;
u8 type;
} __packed __aligned(4);
struct resp_get_temp {
u16 celsius;
u8 tenths;
} __packed __aligned(4);
struct imx_sc_msg_misc_get_temp {
struct imx_sc_rpc_msg hdr;
union {
struct req_get_temp req;
struct resp_get_temp resp;
} data;
} __packed __aligned(4);
static int imx_sc_thermal_get_temp(void *data, int *temp)
{
struct imx_sc_msg_misc_get_temp msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
struct imx_sc_sensor *sensor = data;
int ret;
msg.data.req.resource_id = sensor->resource_id;
msg.data.req.type = IMX_SC_C_TEMP;
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = IMX_SC_RPC_SVC_MISC;
hdr->func = IMX_SC_MISC_FUNC_GET_TEMP;
hdr->size = 2;
ret = imx_scu_call_rpc(thermal_ipc_handle, &msg, true);
if (ret) {
/*
* if the SS power domain is down, read temp will fail, so
* we can print error once and return 0 directly.
*/
pr_err_once("read temp sensor %d failed, could be SS NOT powered up,\
return 0 for this thermal zone, ret %d\n",
sensor->resource_id, ret);
*temp = 0;
return 0;
}
*temp = msg.data.resp.celsius * 1000 + msg.data.resp.tenths * 100;
return 0;
}
static int imx_sc_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
{
int trip_temp;
struct imx_sc_sensor *sensor = p;
if (!sensor->tzd)
return 0;
trip_temp = (trip == IMX_TRIP_PASSIVE) ? sensor->temp_passive :
sensor->temp_critical;
if (sensor->tzd->temperature >=
(trip_temp - IMX_SC_TEMP_PASSIVE_COOL_DELTA))
*trend = THERMAL_TREND_RAISE_FULL;
else
*trend = THERMAL_TREND_DROP_FULL;
return 0;
}
static int imx_sc_thermal_set_trip_temp(void *p, int trip, int temp)
{
struct imx_sc_sensor *sensor = p;
if (trip == IMX_TRIP_CRITICAL)
sensor->temp_critical = temp;
if (trip == IMX_TRIP_PASSIVE)
sensor->temp_passive = temp;
return 0;
}
static const struct thermal_zone_of_device_ops imx_sc_thermal_ops = {
.get_temp = imx_sc_thermal_get_temp,
.get_trend = imx_sc_thermal_get_trend,
.set_trip_temp = imx_sc_thermal_set_trip_temp,
};
static int imx_sc_thermal_register_sensor(struct platform_device *pdev,
struct imx_sc_sensor *sensor)
{
struct thermal_zone_device *tzd;
tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
sensor->resource_id,
sensor,
&imx_sc_thermal_ops);
if (IS_ERR(tzd)) {
dev_err(&pdev->dev, "failed to register sensor: %d\n",
sensor->resource_id);
return PTR_ERR(tzd);
}
sensor->tzd = tzd;
return 0;
}
static int imx_sc_thermal_get_sensor_id(struct device_node *sensor_np, u32 *id)
{
struct of_phandle_args sensor_specs;
int ret;
ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
"#thermal-sensor-cells",
0, &sensor_specs);
if (ret)
return ret;
if (sensor_specs.args_count >= 1) {
*id = sensor_specs.args[0];
WARN(sensor_specs.args_count > 1,
"%pOFn: too many cells in sensor specifier %d\n",
sensor_specs.np, sensor_specs.args_count);
} else {
return -EINVAL;
}
return 0;
}
static int imx_sc_thermal_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *sensor_np = NULL;
struct imx_sc_thermal_data *data;
struct imx_sc_sensor *sensors;
const struct thermal_trip *trip;
u32 sensor_num;
int ret, i;
ret = imx_scu_get_handle(&thermal_ipc_handle);
if (ret)
return ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
ret = of_property_read_u32(np, "tsens-num", &sensor_num);
if (ret || !sensor_num) {
dev_err(&pdev->dev, "failed to get valid temp sensor number!\n");
return -EINVAL;
}
sensors = devm_kzalloc(&pdev->dev, sizeof(*data->sensor) * sensor_num,
GFP_KERNEL);
if (!sensors)
return -ENOMEM;
data->sensor = sensors;
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np)
return -ENODEV;
for (i = 0; i < sensor_num; i++) {
struct imx_sc_sensor *sensor = &data->sensor[i];
sensor_np = of_get_next_child(np, sensor_np);
ret = imx_sc_thermal_get_sensor_id(sensor_np, &sensor->resource_id);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to get valid sensor resource id: %d\n",
ret);
break;
}
ret = imx_sc_thermal_register_sensor(pdev, sensor);
if (ret) {
dev_err(&pdev->dev, "failed to register thermal sensor: %d\n",
ret);
break;
}
trip = of_thermal_get_trip_points(sensor->tzd);
sensor->temp_passive = trip[0].temperature;
sensor->temp_critical = trip[1].temperature;
sensor->cdev = devfreq_cooling_register();
if (IS_ERR(sensor->cdev)) {
dev_err(&pdev->dev,
"failed to register devfreq cooling device: %d\n",
ret);
return ret;
}
ret = thermal_zone_bind_cooling_device(sensor->tzd,
IMX_TRIP_PASSIVE,
sensor->cdev,
THERMAL_NO_LIMIT,
THERMAL_NO_LIMIT,
THERMAL_WEIGHT_DEFAULT);
if (ret) {
dev_err(&sensor->tzd->device,
"binding zone %s with cdev %s failed:%d\n",
sensor->tzd->type, sensor->cdev->type, ret);
devfreq_cooling_unregister(sensor->cdev);
return ret;
}
}
of_node_put(np);
of_node_put(sensor_np);
return ret;
}
static const struct of_device_id imx_sc_thermal_table[] = {
{ .compatible = "fsl,imx8qxp-sc-thermal", },
{ .compatible = "fsl,imx8qm-sc-thermal", },
{}
};
MODULE_DEVICE_TABLE(of, imx_sc_thermal_table);
static struct platform_driver imx_sc_thermal_driver = {
.probe = imx_sc_thermal_probe,
.driver = {
.name = "imx-sc-thermal",
.of_match_table = imx_sc_thermal_table,
},
};
module_platform_driver(imx_sc_thermal_driver);
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
MODULE_DESCRIPTION("Thermal driver for NXP i.MX SoCs with system controller");
MODULE_LICENSE("GPL v2");

View File

@ -8,6 +8,7 @@
#include <linux/cpu_cooling.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/device_cooling.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -92,6 +93,8 @@ enum imx_thermal_trip {
#define TEMPMON_IMX6SX 2
#define TEMPMON_IMX7D 3
#define IMX_TEMP_PASSIVE_COOL_DELTA 10000
struct thermal_soc_data {
u32 version;
@ -203,7 +206,7 @@ static struct thermal_soc_data thermal_imx7d_data = {
struct imx_thermal_data {
struct cpufreq_policy *policy;
struct thermal_zone_device *tz;
struct thermal_cooling_device *cdev;
struct thermal_cooling_device *cdev[2];
enum thermal_device_mode mode;
struct regmap *tempmon;
u32 c1, c2; /* See formula in imx_init_calib() */
@ -420,17 +423,40 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
{
struct imx_thermal_data *data = tz->devdata;
/* do not allow changing critical threshold */
if (trip == IMX_TRIP_CRITICAL)
return -EPERM;
/* do not allow passive to be set higher than critical */
if (temp < 0 || temp > data->temp_critical)
return -EINVAL;
data->temp_passive = temp;
if (trip == IMX_TRIP_CRITICAL) {
data->temp_critical = temp;
if (data->socdata->version == TEMPMON_IMX6SX)
imx_set_panic_temp(data, temp);
}
imx_set_alarm_temp(data, temp);
if (trip == IMX_TRIP_PASSIVE) {
if (temp > (data->temp_max - (1000 * 10)))
return -EINVAL;
data->temp_passive = temp;
imx_set_alarm_temp(data, temp);
}
return 0;
}
static int imx_get_trend(struct thermal_zone_device *tz,
int trip, enum thermal_trend *trend)
{
int ret;
int trip_temp;
ret = imx_get_trip_temp(tz, trip, &trip_temp);
if (ret < 0)
return ret;
if (tz->temperature >= (trip_temp - IMX_TEMP_PASSIVE_COOL_DELTA))
*trend = THERMAL_TREND_RAISE_FULL;
else
*trend = THERMAL_TREND_DROP_FULL;
return 0;
}
@ -480,6 +506,7 @@ static struct thermal_zone_device_ops imx_tz_ops = {
.get_trip_temp = imx_get_trip_temp,
.get_crit_temp = imx_get_crit_temp,
.set_trip_temp = imx_set_trip_temp,
.get_trend = imx_get_trend,
};
static int imx_init_calib(struct platform_device *pdev, u32 ocotp_ana1)
@ -667,21 +694,33 @@ static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data)
np = of_get_cpu_node(data->policy->cpu, NULL);
if (!np || !of_find_property(np, "#cooling-cells", NULL)) {
data->cdev = cpufreq_cooling_register(data->policy);
if (IS_ERR(data->cdev)) {
ret = PTR_ERR(data->cdev);
data->cdev[0] = cpufreq_cooling_register(data->policy);
if (IS_ERR(data->cdev[0])) {
ret = PTR_ERR(data->cdev[0]);
cpufreq_cpu_put(data->policy);
return ret;
}
}
data->cdev[1] = devfreq_cooling_register();
if (IS_ERR(data->cdev[1])) {
ret = PTR_ERR(data->cdev[1]);
if (ret != -EPROBE_DEFER) {
pr_err("failed to register cpufreq cooling device: %d\n",
ret);
cpufreq_cooling_unregister(data->cdev[0]);
}
return ret;
}
return 0;
}
static void imx_thermal_unregister_legacy_cooling(struct imx_thermal_data *data)
{
cpufreq_cooling_unregister(data->cdev);
cpufreq_cooling_unregister(data->cdev[0]);
cpufreq_cpu_put(data->policy);
devfreq_cooling_unregister(data->cdev[1]);
}
#else
@ -805,7 +844,8 @@ static int imx_thermal_probe(struct platform_device *pdev)
data->tz = thermal_zone_device_register("imx_thermal_zone",
IMX_TRIP_NUM,
BIT(IMX_TRIP_PASSIVE), data,
BIT(IMX_TRIP_PASSIVE) | BIT(IMX_TRIP_CRITICAL),
data,
&imx_tz_ops, NULL,
IMX_PASSIVE_DELAY,
IMX_POLLING_DELAY);
@ -872,8 +912,9 @@ static int imx_thermal_remove(struct platform_device *pdev)
clk_disable_unprepare(data->thermal_clk);
thermal_zone_device_unregister(data->tz);
cpufreq_cooling_unregister(data->cdev);
cpufreq_cooling_unregister(data->cdev[0]);
cpufreq_cpu_put(data->policy);
devfreq_cooling_unregister(data->cdev[1]);
return 0;
}

View File

@ -3,6 +3,7 @@
// Copyright 2016 Freescale Semiconductor, Inc.
#include <linux/clk.h>
#include <linux/device_cooling.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@ -14,6 +15,16 @@
#include "thermal_core.h"
#define SITES_MAX 16
#define TMU_TEMP_PASSIVE_COOL_DELTA 10000
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
#define TMR_ALPF 0x0c000000
#define TMR_ALPF_V2 0x03000000
#define TMTMIR_DEFAULT 0x0000000f
#define TIER_DISABLE 0x0
#define TEUMR0_V2 0x51009c00
#define TMU_VER1 0x1
#define TMU_VER2 0x2
/*
* QorIQ TMU Registers
@ -24,17 +35,12 @@ struct qoriq_tmu_site_regs {
u8 res0[0x8];
};
struct qoriq_tmu_regs {
struct qoriq_tmu_regs_v1 {
u32 tmr; /* Mode Register */
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
#define TMR_ALPF 0x0c000000
u32 tsr; /* Status Register */
u32 tmtmir; /* Temperature measurement interval Register */
#define TMTMIR_DEFAULT 0x0000000f
u8 res0[0x14];
u32 tier; /* Interrupt Enable Register */
#define TIER_DISABLE 0x0
u32 tidr; /* Interrupt Detect Register */
u32 tiscr; /* Interrupt Site Capture Register */
u32 ticscr; /* Interrupt Critical Site Capture Register */
@ -54,12 +60,52 @@ struct qoriq_tmu_regs {
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 */
u32 ttrcr[4]; /* Temperature Range Control Register */
};
struct qoriq_tmu_regs_v2 {
u32 tmr; /* Mode Register */
u32 tsr; /* Status Register */
u32 tmsr; /* monitor site register */
u32 tmtmir; /* Temperature measurement interval Register */
u8 res0[0x10];
u32 tier; /* Interrupt Enable Register */
u32 tidr; /* Interrupt Detect Register */
u8 res1[0x8];
u32 tiiscr; /* interrupt immediate site capture register */
u32 tiascr; /* interrupt average site capture register */
u32 ticscr; /* Interrupt Critical Site Capture Register */
u32 res2;
u32 tmhtcr; /* monitor high temperature capture register */
u32 tmltcr; /* monitor low temperature capture register */
u32 tmrtrcr; /* monitor rising temperature rate capture register */
u32 tmftrcr; /* monitor falling temperature rate capture register */
u32 tmhtitr; /* High Temperature Immediate Threshold */
u32 tmhtatr; /* High Temperature Average Threshold */
u32 tmhtactr; /* High Temperature Average Crit Threshold */
u32 res3;
u32 tmltitr; /* monitor low temperature immediate threshold */
u32 tmltatr; /* monitor low temperature average threshold register */
u32 tmltactr; /* monitor low temperature average critical threshold */
u32 res4;
u32 tmrtrctr; /* monitor rising temperature rate critical threshold */
u32 tmftrctr; /* monitor falling temperature rate critical threshold*/
u8 res5[0x8];
u32 ttcfgr; /* Temperature Configuration Register */
u32 tscfgr; /* Sensor Configuration Register */
u8 res6[0x78];
struct qoriq_tmu_site_regs site[SITES_MAX];
u8 res7[0x9f8];
u32 ipbrr0; /* IP Block Revision Register 0 */
u32 ipbrr1; /* IP Block Revision Register 1 */
u8 res8[0x300];
u32 teumr0;
u32 teumr1;
u32 teumr2;
u32 res9;
u32 ttrcr[4]; /* Temperature Range Control Register */
};
struct qoriq_tmu_data;
/*
@ -69,15 +115,26 @@ struct qoriq_sensor {
struct thermal_zone_device *tzd;
struct qoriq_tmu_data *qdata;
int id;
int temp_passive;
int temp_critical;
struct thermal_cooling_device *cdev;
};
struct qoriq_tmu_data {
struct qoriq_tmu_regs __iomem *regs;
int ver;
struct qoriq_tmu_regs_v1 __iomem *regs;
struct qoriq_tmu_regs_v2 __iomem *regs_v2;
struct clk *clk;
bool little_endian;
struct qoriq_sensor *sensor[SITES_MAX];
};
enum tmu_trip {
TMU_TRIP_PASSIVE,
TMU_TRIP_CRITICAL,
TMU_TRIP_NUM,
};
static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
{
if (p->little_endian)
@ -106,14 +163,51 @@ static int tmu_get_temp(void *p, int *temp)
return 0;
}
static int tmu_get_trend(void *p, int trip, enum thermal_trend *trend)
{
struct qoriq_sensor *qsensor = p;
int trip_temp;
if (!qsensor->tzd)
return 0;
trip_temp = (trip == TMU_TRIP_PASSIVE) ? qsensor->temp_passive :
qsensor->temp_critical;
if (qsensor->tzd->temperature >=
(trip_temp - TMU_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 qoriq_sensor *qsensor = p;
if (trip == TMU_TRIP_CRITICAL)
qsensor->temp_critical = temp;
if (trip == TMU_TRIP_PASSIVE)
qsensor->temp_passive = temp;
return 0;
}
static const 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 int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
{
struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev);
int id, sites = 0;
const struct thermal_trip *trip;
int id, sites = 0, ret;
for (id = 0; id < SITES_MAX; id++) {
qdata->sensor[id] = devm_kzalloc(&pdev->dev,
@ -132,12 +226,53 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
return PTR_ERR(qdata->sensor[id]->tzd);
}
sites |= 0x1 << (15 - id);
/* first thermal zone takes care of system-wide device cooling */
if (id == 0) {
qdata->sensor[id]->cdev = devfreq_cooling_register();
if (IS_ERR(qdata->sensor[id]->cdev)) {
ret = PTR_ERR(qdata->sensor[id]->cdev);
pr_err("failed to register devfreq cooling device: %d\n",
ret);
return ret;
}
ret = thermal_zone_bind_cooling_device(qdata->sensor[id]->tzd,
TMU_TRIP_PASSIVE,
qdata->sensor[id]->cdev,
THERMAL_NO_LIMIT,
THERMAL_NO_LIMIT,
THERMAL_WEIGHT_DEFAULT);
if (ret) {
pr_err("binding zone %s with cdev %s failed:%d\n",
qdata->sensor[id]->tzd->type,
qdata->sensor[id]->cdev->type,
ret);
devfreq_cooling_unregister(qdata->sensor[id]->cdev);
return ret;
}
trip = of_thermal_get_trip_points(qdata->sensor[id]->tzd);
qdata->sensor[id]->temp_passive = trip[0].temperature;
qdata->sensor[id]->temp_critical = trip[1].temperature;
}
if (qdata->ver == TMU_VER1)
sites |= 0x1 << (15 - id);
else
sites |= 0x1 << id;
}
/* Enable monitoring */
if (sites != 0)
tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr);
if (sites != 0) {
if (qdata->ver == TMU_VER1) {
tmu_write(qdata, sites | TMR_ME | TMR_ALPF,
&qdata->regs->tmr);
} else {
tmu_write(qdata, sites, &qdata->regs_v2->tmsr);
tmu_write(qdata, TMR_ME | TMR_ALPF_V2,
&qdata->regs_v2->tmr);
}
}
return 0;
}
@ -150,16 +285,21 @@ static int qoriq_tmu_calibration(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
dev_err(&pdev->dev, "missing calibration range.\n");
return -ENODEV;
len = of_property_count_u32_elems(np, "fsl,tmu-range");
if (len < 0 || len > 4) {
dev_err(&pdev->dev, "invalid range data.\n");
return len;
}
val = of_property_read_u32_array(np, "fsl,tmu-range", range, len);
if (val != 0) {
dev_err(&pdev->dev, "failed to read range data.\n");
return val;
}
/* Init temperature range registers */
tmu_write(data, range[0], &data->regs->ttr0cr);
tmu_write(data, range[1], &data->regs->ttr1cr);
tmu_write(data, range[2], &data->regs->ttr2cr);
tmu_write(data, range[3], &data->regs->ttr3cr);
for (i = 0; i < len; i++)
tmu_write(data, range[i], &data->regs->ttrcr[i]);
calibration = of_get_property(np, "fsl,tmu-calibration", &len);
if (calibration == NULL || len % 8) {
@ -183,7 +323,12 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
tmu_write(data, TIER_DISABLE, &data->regs->tier);
/* Set update_interval */
tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
if (data->ver == TMU_VER1) {
tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
} else {
tmu_write(data, TMTMIR_DEFAULT, &data->regs_v2->tmtmir);
tmu_write(data, TEUMR0_V2, &data->regs_v2->teumr0);
}
/* Disable monitoring */
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
@ -192,6 +337,7 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
static int qoriq_tmu_probe(struct platform_device *pdev)
{
int ret;
u32 ver;
struct qoriq_tmu_data *data;
struct device_node *np = pdev->dev.of_node;
@ -220,6 +366,12 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
return ret;
}
/* version register offset at: 0xbf8 on both v1 and v2 */
ver = tmu_read(data, &data->regs->ipbrr0);
data->ver = (ver >> 8) & 0xff;
if (data->ver == TMU_VER2)
data->regs_v2 = (void __iomem *)data->regs;
qoriq_tmu_init_device(data); /* TMU initialization */
ret = qoriq_tmu_calibration(pdev); /* TMU calibration */

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __DEVICE_THERMAL_H__
#define __DEVICE_THERMAL_H__
#include <linux/thermal.h>
#ifdef CONFIG_DEVICE_THERMAL
int register_devfreq_cooling_notifier(struct notifier_block *nb);
int unregister_devfreq_cooling_notifier(struct notifier_block *nb);
struct thermal_cooling_device *devfreq_cooling_register(void);
void devfreq_cooling_unregister(struct thermal_cooling_device *cdev);
#else
static inline
int register_devfreq_cooling_notifier(struct notifier_block *nb)
{
return 0;
}
static inline
int unregister_devfreq_cooling_notifier(struct notifier_block *nb)
{
return 0;
}
static inline
struct thermal_cooling_device *devfreq_cooling_register(void)
{
return NULL;
}
static inline
void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
return;
}
#endif
#endif /* __DEVICE_THERMAL_H__ */