mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
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:
commit
e6fbc0b2a6
15
Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
Normal file
15
Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
Normal 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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
152
drivers/thermal/device_cooling.c
Normal file
152
drivers/thermal/device_cooling.c
Normal 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);
|
208
drivers/thermal/imx8mm_thermal.c
Normal file
208
drivers/thermal/imx8mm_thermal.c
Normal 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");
|
280
drivers/thermal/imx_sc_thermal.c
Normal file
280
drivers/thermal/imx_sc_thermal.c
Normal 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");
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
45
include/linux/device_cooling.h
Normal file
45
include/linux/device_cooling.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user