@@ -284,6 +284,13 @@ config K3_THERMAL
This includes temperature reading functionality.
+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
@@ -46,6 +46,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_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
new file mode 100644
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 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.
+ *
+ * Copyright 2023 NXP
+ */
+
+#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 const struct thermal_cooling_device_ops 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);
new file mode 100644
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * 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.
+ *
+ * Copyright 2023 NXP
+ */
+
+#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 ERR_PTR(-EINVAL);
+}
+
+static inline
+void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
+{
+}
+#endif
+#endif /* __DEVICE_THERMAL_H__ */