mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-30 10:39:04 +00:00
254 lines
7.0 KiB
Diff
254 lines
7.0 KiB
Diff
|
From 97abcfc5219149ff7d4883b295c80257f0315b5e Mon Sep 17 00:00:00 2001
|
||
|
From: Anson Huang <Anson.Huang@nxp.com>
|
||
|
Date: Wed, 7 Aug 2019 08:40:59 +0800
|
||
|
Subject: [PATCH] thermal: Add generic device cooling support
|
||
|
|
||
|
To compatible with previous implementation, add generic device
|
||
|
cooling support, each thermal zone will register a cooling
|
||
|
device, and when temperature exceed passive trip, the device
|
||
|
cooling driver will send out a system wide notification, each
|
||
|
device supporting cooling will need to register device cooling
|
||
|
and takes action when passive trip is exceeded;
|
||
|
|
||
|
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
|
||
|
[rebase]
|
||
|
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||
|
---
|
||
|
drivers/thermal/Kconfig | 7 ++
|
||
|
drivers/thermal/Makefile | 1 +
|
||
|
drivers/thermal/device_cooling.c | 152 +++++++++++++++++++++++++++++++++++++++
|
||
|
include/linux/device_cooling.h | 45 ++++++++++++
|
||
|
4 files changed, 205 insertions(+)
|
||
|
create mode 100644 drivers/thermal/device_cooling.c
|
||
|
create mode 100644 include/linux/device_cooling.h
|
||
|
|
||
|
--- a/drivers/thermal/Kconfig
|
||
|
+++ b/drivers/thermal/Kconfig
|
||
|
@@ -233,6 +233,13 @@ config IMX_THERMAL
|
||
|
cpufreq is used as the cooling device to throttle CPUs when the
|
||
|
passive trip is crossed.
|
||
|
|
||
|
+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 MAX77620_THERMAL
|
||
|
tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
|
||
|
depends on MFD_MAX77620
|
||
|
--- a/drivers/thermal/Makefile
|
||
|
+++ b/drivers/thermal/Makefile
|
||
|
@@ -41,6 +41,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_t
|
||
|
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
|
||
|
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
|
||
|
obj-$(CONFIG_IMX_THERMAL) += imx_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
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/thermal/device_cooling.c
|
||
|
@@ -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);
|
||
|
--- /dev/null
|
||
|
+++ b/include/linux/device_cooling.h
|
||
|
@@ -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__ */
|