This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit d1b87bd021d8e3259e906ff8449e71564b71686c
Author: wangjianyu3 <[email protected]>
AuthorDate: Fri Oct 13 23:10:56 2023 +0800

    Add thermal framework
    
    Signed-off-by: wangjianyu3 <[email protected]>
---
 Kconfig                        |  31 ++
 drivers/Kconfig                |   1 +
 drivers/Makefile               |   1 +
 drivers/drivers_initialize.c   |   5 +
 drivers/thermal/Kconfig        |  20 +
 drivers/thermal/Make.defs      |  30 ++
 drivers/thermal/thermal_core.c | 843 +++++++++++++++++++++++++++++++++++++++++
 drivers/thermal/thermal_core.h |  76 ++++
 include/debug.h                |  18 +
 include/nuttx/thermal.h        | 205 ++++++++++
 10 files changed, 1230 insertions(+)

diff --git a/Kconfig b/Kconfig
index 305ab99027..340838fc9a 100644
--- a/Kconfig
+++ b/Kconfig
@@ -1936,6 +1936,37 @@ config DEBUG_SPI_INFO
 
 endif # DEBUG_SPI
 
+config DEBUG_THERMAL
+       bool "Thermal Debug Features"
+       default n
+       ---help---
+               Enable thermal debug features.
+
+if DEBUG_THERMAL
+
+config DEBUG_THERMAL_ERROR
+       bool "Thermal Error Output"
+       default n
+       depends on DEBUG_ERROR
+       ---help---
+               Enable thermal error output to SYSLOG.
+
+config DEBUG_THERMAL_WARN
+       bool "Thermal Warnings Output"
+       default n
+       depends on DEBUG_WARN
+       ---help---
+               Enable thermal warning output to SYSLOG.
+
+config DEBUG_THERMAL_INFO
+       bool "Thermal Informational Output"
+       default n
+       depends on DEBUG_INFO
+       ---help---
+               Enable thermal informational output to SYSLOG.
+
+endif # DEBUG_THERMAL
+
 config DEBUG_TIMER
        bool "Timer Debug Features"
        default n
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d5e9f18608..751b07052a 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -42,6 +42,7 @@ source "drivers/rpmsg/Kconfig"
 source "drivers/rptun/Kconfig"
 source "drivers/sensors/Kconfig"
 source "drivers/serial/Kconfig"
+source "drivers/thermal/Kconfig"
 source "drivers/usbdev/Kconfig"
 source "drivers/usbhost/Kconfig"
 source "drivers/usbmisc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 6dbc5563f0..87992a1d62 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -63,6 +63,7 @@ include sensors/Make.defs
 include serial/Make.defs
 include spi/Make.defs
 include syslog/Make.defs
+include thermal/Make.defs
 include timers/Make.defs
 include usbdev/Make.defs
 include usbhost/Make.defs
diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c
index 0be8a0519e..8431d0c6c5 100644
--- a/drivers/drivers_initialize.c
+++ b/drivers/drivers_initialize.c
@@ -46,6 +46,7 @@
 #include <nuttx/serial/uart_ram.h>
 #include <nuttx/syslog/syslog.h>
 #include <nuttx/syslog/syslog_console.h>
+#include <nuttx/thermal.h>
 #include <nuttx/trace.h>
 #include <nuttx/usrsock/usrsock_rpmsg.h>
 #include <nuttx/virtio/virtio.h>
@@ -271,5 +272,9 @@ void drivers_initialize(void)
   optee_register();
 #endif
 
+#ifdef CONFIG_THERMAL
+  thermal_init();
+#endif
+
   drivers_trace_end();
 }
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
new file mode 100644
index 0000000000..568c762edd
--- /dev/null
+++ b/drivers/thermal/Kconfig
@@ -0,0 +1,20 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+menuconfig THERMAL
+       bool "Thermal framework"
+       default n
+       ---help---
+               Enable thermal framework.
+
+if THERMAL
+
+config THERMAL_DEFAULT_GOVERNOR
+       string "Thermal default governor name"
+       default "step_wise"
+       ---help---
+               Default governor name.
+
+endif # THERMAL
diff --git a/drivers/thermal/Make.defs b/drivers/thermal/Make.defs
new file mode 100644
index 0000000000..20c8c4f054
--- /dev/null
+++ b/drivers/thermal/Make.defs
@@ -0,0 +1,30 @@
+############################################################################
+# drivers/thermal/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+# Include thermal sources
+
+ifeq ($(CONFIG_THERMAL),y)
+
+CSRCS += thermal_core.c
+
+DEPPATH += --dep-path thermal
+VPATH += thermal
+
+endif
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
new file mode 100644
index 0000000000..a7de5fc86c
--- /dev/null
+++ b/drivers/thermal/thermal_core.c
@@ -0,0 +1,843 @@
+/****************************************************************************
+ * drivers/thermal/thermal_core.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/kmalloc.h>
+
+#include <debug.h>
+#include <stdio.h>
+#include <sys/boardctl.h>
+
+#include "thermal_core.h"
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int  zone_bind_cooling  (FAR struct thermal_zone_device_s *zdev,
+                                int trip,
+                                FAR struct thermal_cooling_device_s *cdev,
+                                unsigned int upper, unsigned int lower,
+                                unsigned int weight);
+static void zone_unbind_cooling(FAR struct thermal_zone_device_s *zdev,
+                                int trip,
+                                FAR struct thermal_cooling_device_s *cdev);
+
+static FAR struct thermal_governor_s *
+find_governor_by_name          (FAR const char *name);
+static int  zone_set_governor  (FAR struct thermal_zone_device_s *zdev,
+                                FAR struct thermal_governor_s *gov);
+
+static void device_bind        (FAR struct thermal_zone_device_s *zdev,
+                                FAR struct thermal_cooling_device_s *cdev);
+static void device_unbind      (FAR struct thermal_zone_device_s *zdev,
+                                FAR struct thermal_cooling_device_s *cdev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct list_node
+g_cooling_dev_list = LIST_INITIAL_VALUE(g_cooling_dev_list);
+
+static struct list_node
+g_governor_list = LIST_INITIAL_VALUE(g_governor_list);
+
+static struct list_node
+g_zone_dev_list = LIST_INITIAL_VALUE(g_zone_dev_list);
+
+static mutex_t g_thermal_lock = NXMUTEX_INITIALIZER;
+
+static FAR struct thermal_governor_s *g_def_governor = NULL;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int zone_set_governor(FAR struct thermal_zone_device_s *zdev,
+                             FAR struct thermal_governor_s *gov)
+{
+  int ret = OK;
+
+  /* The caller must use `g_thermal_lock` to protect zones and governors */
+
+  if (zdev->governor && zdev->governor->unbind_from_tz)
+    {
+      zdev->governor->unbind_from_tz(zdev);
+    }
+
+  if (gov && gov->bind_to_tz)
+    {
+      ret = gov->bind_to_tz(zdev);
+      if (ret < 0)
+        {
+          if (zdev->governor->bind_to_tz(zdev) < 0)
+            {
+              zdev->governor = NULL;
+            }
+
+          return ret;
+        }
+    }
+
+  zdev->governor = gov;
+  return ret;
+}
+
+static int zone_bind_cooling(FAR struct thermal_zone_device_s *zdev,
+                             int trip,
+                             FAR struct thermal_cooling_device_s *cdev,
+                             unsigned int upper, unsigned int lower,
+                             unsigned int weight)
+{
+  FAR struct thermal_instance_s *ins;
+  FAR struct thermal_instance_s *pos;
+  unsigned int max_state;
+  int ret;
+
+  ret = cdev->ops->get_max_state(cdev, &max_state);
+  if (ret < 0)
+    {
+      therr("Get max state failed!\n");
+      return ret;
+    }
+
+  list_for_every_entry(&zdev->instance_list, pos,
+                       struct thermal_instance_s, zdev_node)
+    {
+      if (zdev == pos->zdev && cdev == pos->cdev && trip == pos->trip)
+        {
+          thwarn("Instance %s %s %d already exist!",
+                 zdev->name, cdev->name, trip);
+          return -EEXIST;
+        }
+    }
+
+  ins = kmm_malloc(sizeof(struct thermal_instance_s));
+  if (!ins)
+    {
+      return -ENOMEM;
+    }
+
+  ins->zdev = zdev;
+  ins->cdev = cdev;
+  ins->trip = trip;
+  ins->target = THERMAL_NO_TARGET;
+
+  if (lower == THERMAL_NO_LIMIT)
+    {
+      ins->lower = 0;
+    }
+  else
+    {
+      ins->lower = lower;
+    }
+
+  if (upper == THERMAL_NO_LIMIT)
+    {
+      ins->upper = max_state;
+    }
+  else
+    {
+      ins->upper = upper;
+    }
+
+  ins->weight = weight;
+
+  thinfo("Adding instance zdev:%-4s cdev:%-4s h:%2u l:%2u t:%d\n",
+         zdev->name, cdev->name, ins->upper, ins->lower, ins->trip);
+
+  list_add_tail(&zdev->instance_list, &ins->zdev_node);
+  list_add_tail(&cdev->instance_list, &ins->cdev_node);
+  return OK;
+}
+
+static void zone_unbind_cooling(FAR struct thermal_zone_device_s *zdev,
+                                int trip,
+                                FAR struct thermal_cooling_device_s *cdev)
+{
+  FAR struct thermal_instance_s *ins;
+
+  list_for_every_entry(&zdev->instance_list, ins,
+                       struct thermal_instance_s, zdev_node)
+    {
+      if (zdev == ins->zdev && cdev == ins->cdev && trip == ins->trip)
+        {
+          list_delete(&ins->zdev_node);
+          list_delete(&ins->cdev_node);
+
+          kmm_free(ins);
+          break;
+        }
+    }
+}
+
+static void device_bind(FAR struct thermal_zone_device_s *zdev,
+                        FAR struct thermal_cooling_device_s *cdev)
+{
+  FAR const struct thermal_zone_map_s *map;
+  FAR const struct thermal_zone_trip_s *trip;
+  int ret;
+  int i;
+  int j;
+
+  for (i = 0; i < zdev->params->num_maps; i++)
+    {
+      map = &zdev->params->maps[i];
+
+      if (strcmp(map->cdev_name, cdev->name))
+        {
+          continue;
+        }
+
+      for (j = 0; j < zdev->params->num_trips; j++)
+        {
+          trip = &zdev->params->trips[j];
+          if (strcmp(map->trip_name, trip->name))
+            {
+              continue;
+            }
+
+          ret = zone_bind_cooling(zdev, j, cdev, map->high, map->low,
+                                  map->weight);
+          if (ret < 0)
+            {
+              therr("Failed to bind %s and %s, trip %d\n",
+                    zdev->name, cdev->name, j);
+            }
+        }
+    }
+}
+
+static void device_unbind(FAR struct thermal_zone_device_s *zdev,
+                          FAR struct thermal_cooling_device_s *cdev)
+{
+  int i;
+
+  for (i = 0; i < zdev->params->num_trips; i++)
+    {
+      zone_unbind_cooling(zdev, i, cdev);
+    }
+}
+
+static FAR struct thermal_governor_s *
+find_governor_by_name(FAR const char *name)
+{
+  FAR struct thermal_governor_s *gov;
+
+  if (!name)
+    {
+      return NULL;
+    }
+
+  /* The caller must use `g_thermal_lock` to protect governors */
+
+  list_for_every_entry(&g_governor_list, gov, struct thermal_governor_s,
+                       node)
+    {
+      if (!strcmp(gov->name, name))
+        {
+          return gov;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int thermal_zone_enable(FAR struct thermal_zone_device_s *zdev,
+                        bool enabled)
+{
+  nxmutex_lock(&g_thermal_lock);
+
+  if (enabled == zdev->enabled)
+    {
+      nxmutex_unlock(&g_thermal_lock);
+      return OK;
+    }
+
+  zdev->enabled = enabled;
+  work_cancel_sync(LPWORK, &zdev->monitor);
+
+  nxmutex_unlock(&g_thermal_lock);
+
+  thermal_zone_device_update(zdev);
+  return OK;
+}
+
+int thermal_zone_get_trend(FAR struct thermal_zone_device_s *zdev)
+{
+  enum thermal_trend_e trend;
+
+  if (zdev->ops->get_trend)
+    {
+      if (!zdev->ops->get_trend(zdev, &trend))
+        {
+          return trend;
+        }
+    }
+
+  if (zdev->last_temperature == THERMAL_INVALID_TEMP ||
+      zdev->temperature      == THERMAL_INVALID_TEMP)
+    {
+      trend = THERMAL_TREND_STABLE;
+    }
+  else if (zdev->last_temperature > zdev->temperature)
+    {
+      trend = THERMAL_TREND_DROPPING;
+    }
+  else if (zdev->last_temperature < zdev->temperature)
+    {
+      trend = THERMAL_TREND_RAISING;
+    }
+  else
+    {
+      trend = THERMAL_TREND_STABLE;
+    }
+
+  return trend;
+}
+
+int thermal_zone_get_trip_temp(FAR struct thermal_zone_device_s *zdev,
+                               int trip,
+                               FAR int *temp)
+{
+  if (!temp || trip >= zdev->params->num_trips)
+    {
+      return -EINVAL;
+    }
+
+  *temp = zdev->params->trips[trip].temp;
+  return OK;
+}
+
+int thermal_zone_get_trip_type(FAR struct thermal_zone_device_s *zdev,
+                               int trip,
+                               FAR enum thermal_trip_type_e *type)
+{
+  if (!type || trip >= zdev->params->num_trips)
+    {
+      return -EINVAL;
+    }
+
+  *type = zdev->params->trips[trip].type;
+  return OK;
+}
+
+int thermal_zone_get_trip_hyst(FAR struct thermal_zone_device_s *zdev,
+                               int trip,
+                               FAR int *hyst)
+{
+  if (!hyst || trip >= zdev->params->num_trips)
+    {
+      return -EINVAL;
+    }
+
+  *hyst = zdev->params->trips[trip].hyst;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: thermal_register_governor
+ *
+ * Description:
+ *   Register governor
+ *
+ * Input Parameters:
+ *   gov - the struct thermal_governor_s addr
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  Otherwise a negated errno value is
+ *   returned to indicate the nature of the failure.
+ ****************************************************************************/
+
+int thermal_register_governor(FAR struct thermal_governor_s *gov)
+{
+  FAR struct thermal_governor_s *pos;
+
+  if (!gov || !gov->throttle)
+    {
+      therr("Invalid governor!\n");
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&g_thermal_lock);
+
+  list_for_every_entry(&g_governor_list, pos,
+                       struct thermal_governor_s, node)
+    {
+      if (!strcmp(gov->name, pos->name))
+        {
+          thwarn("Governor (%s) already exists!", gov->name);
+          nxmutex_unlock(&g_thermal_lock);
+          return -EEXIST;
+        }
+    }
+
+  list_add_tail(&g_governor_list, &gov->node);
+
+  thinfo("Register governor %s\n", gov->name);
+
+  /* Default governor */
+
+  if (!strcmp(gov->name, CONFIG_THERMAL_DEFAULT_GOVERNOR))
+    {
+      g_def_governor = gov;
+      thinfo("Default governor %s registered!\n", g_def_governor->name);
+    }
+
+  nxmutex_unlock(&g_thermal_lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: thermal_unregister_governor
+ *
+ * Description:
+ *   Unregister governor
+ *
+ * Input Parameters:
+ *   gov - the struct thermal_governor_s addr
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void thermal_unregister_governor(FAR struct thermal_governor_s *gov)
+{
+  FAR struct thermal_zone_device_s *zdev;
+
+  if (!gov)
+    {
+      return;
+    }
+
+  nxmutex_lock(&g_thermal_lock);
+
+  list_for_every_entry(&g_zone_dev_list, zdev,
+                       struct thermal_zone_device_s, node)
+    {
+      if (zdev->governor == gov)
+        {
+          zone_set_governor(zdev, NULL);
+        }
+    }
+
+  list_delete(&gov->node);
+  nxmutex_unlock(&g_thermal_lock);
+}
+
+/****************************************************************************
+ * Name: thermal_cooling_device_register
+ *
+ * Description:
+ *   Register thermal cooling device.
+ *
+ * Input Parameters:
+ *   name    - Name of cooling device
+ *   devdata - Device driver data
+ *   ops     - Operations
+ *
+ * Returned Value:
+ *   Addr of created cooling device entry.
+ ****************************************************************************/
+
+FAR struct thermal_cooling_device_s *
+thermal_cooling_device_register(FAR const char *name, void *devdata,
+                          FAR const struct thermal_cooling_device_ops_s *ops)
+{
+  FAR struct thermal_zone_device_s *zdev;
+  FAR struct thermal_cooling_device_s *cdev;
+
+  nxmutex_lock(&g_thermal_lock);
+
+  list_for_every_entry(&g_cooling_dev_list, cdev,
+                       struct thermal_cooling_device_s, node)
+    {
+      if (!strcmp(cdev->name, name))
+        {
+          thwarn("Cooling device (%s) already exists!", name);
+          nxmutex_unlock(&g_thermal_lock);
+          return NULL;
+        }
+    }
+
+  cdev = kmm_zalloc(sizeof(*cdev));
+  if (!cdev)
+    {
+      therr("Cannot allocate memory for cooling device registering!\n");
+      nxmutex_unlock(&g_thermal_lock);
+      return NULL;
+    }
+
+  strlcpy(cdev->name, name, THERMAL_NAME_LEN);
+  cdev->ops     = ops;
+  cdev->devdata = devdata;
+
+  list_initialize(&cdev->instance_list);
+  list_add_tail(&g_cooling_dev_list, &cdev->node);
+
+  list_for_every_entry(&g_zone_dev_list, zdev,
+                       struct thermal_zone_device_s, node)
+    {
+      device_bind(zdev, cdev);
+    }
+
+  thinfo("Registered cooling device %s\n", cdev->name);
+  nxmutex_unlock(&g_thermal_lock);
+
+  return cdev;
+}
+
+/****************************************************************************
+ * Name: thermal_cooling_device_unregister
+ *
+ * Description:
+ *   Unregister thermal cooling device.
+ *
+ * Input Parameters:
+ *   cdev - Cooling Device
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void
+thermal_cooling_device_unregister(FAR struct thermal_cooling_device_s *cdev)
+{
+  FAR struct thermal_zone_device_s *zdev;
+
+  /* Unbind */
+
+  nxmutex_lock(&g_thermal_lock);
+
+  list_delete(&cdev->node);
+
+  list_for_every_entry(&g_zone_dev_list, zdev,
+                       struct thermal_zone_device_s, node)
+    {
+      device_unbind(zdev, cdev);
+    }
+
+  kmm_free(cdev);
+  nxmutex_unlock(&g_thermal_lock);
+}
+
+/****************************************************************************
+ * Name: thermal_cooling_device_update
+ *
+ * Description:
+ *   Update thermal cooling device.
+ *
+ * Input Parameters:
+ *   cdev - Cooling Device.
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void thermal_cooling_device_update(FAR struct thermal_cooling_device_s *cdev)
+{
+  FAR struct thermal_instance_s *instance;
+  unsigned int target  = THERMAL_NO_TARGET;
+  unsigned int current = THERMAL_NO_TARGET;
+  int ret;
+
+  ret = cdev->ops->get_state(cdev, &current);
+  if (ret < 0)
+    {
+      thwarn("Thermal get cooling state failed!\n");
+      return;
+    }
+
+  list_for_every_entry(&cdev->instance_list, instance,
+                       struct thermal_instance_s, cdev_node)
+    {
+      if ((instance->target != THERMAL_NO_TARGET) &&
+          (instance->target > target ||
+           target == THERMAL_NO_TARGET))
+        {
+          target = instance->target;
+        }
+    }
+
+  if (target != THERMAL_NO_TARGET && target != current)
+    {
+      ret = cdev->ops->set_state(cdev, target);
+      if (ret < 0)
+        {
+          thwarn("Thermal set cooling state of %s failed!\n", cdev->name);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: thermal_zone_device_register
+ *
+ * Description:
+ *   Register thermal zone device.
+ *
+ * Input Parameters:
+ *   name    - Name of zone.
+ *   devdata - Device driver data.
+ *   ops     - Operations of zone deivce.
+ *   params  - Parameter of zone device.
+ *
+ * Returned Value:
+ *   Addr of created zone device entry.
+ ****************************************************************************/
+
+struct thermal_zone_device_s *
+thermal_zone_device_register(FAR const char *name,
+                             FAR void *devdata,
+                             FAR const struct thermal_zone_device_ops_s *ops,
+                             FAR const struct thermal_zone_params_s *params)
+{
+  FAR struct thermal_cooling_device_s *cdev;
+  FAR struct thermal_governor_s *gov;
+  FAR struct thermal_zone_device_s *pos;
+  FAR struct thermal_zone_device_s *zdev;
+
+  if (!ops || !ops->get_temp)
+    {
+      therr("Invalid zone operations!\n");
+      return NULL;
+    }
+
+  nxmutex_lock(&g_thermal_lock);
+
+  list_for_every_entry(&g_zone_dev_list, pos,
+                       struct thermal_zone_device_s, node)
+    {
+      if (!strcmp(name, pos->name))
+        {
+          thwarn("Zone device (%s) already exists!", name);
+          nxmutex_unlock(&g_thermal_lock);
+          return NULL;
+        }
+    }
+
+  zdev = kmm_zalloc(sizeof(struct thermal_zone_device_s));
+  if (!zdev)
+    {
+      nxmutex_unlock(&g_thermal_lock);
+      return NULL;
+    }
+
+  zdev->ops     = ops;
+  zdev->devdata = devdata;
+  zdev->enabled = true;
+
+  strlcpy(zdev->name, name, THERMAL_NAME_LEN);
+
+  zdev->params      = params;
+  zdev->temperature = THERMAL_INVALID_TEMP;
+
+  list_initialize(&zdev->instance_list);
+
+  /* Set governor */
+
+  gov = find_governor_by_name(zdev->params->gov_name);
+  zone_set_governor(zdev, gov ? gov : g_def_governor);
+
+  thinfo("Set governor of zone %s to %s.\n", zdev->name,
+         zdev->governor ? zdev->governor->name : "");
+
+  list_add_tail(&g_zone_dev_list, &zdev->node);
+
+  list_for_every_entry(&g_cooling_dev_list, cdev,
+                       struct thermal_cooling_device_s, node)
+    {
+      device_bind(zdev, cdev);
+    }
+
+  nxmutex_unlock(&g_thermal_lock);
+
+  thinfo("Registered zone device %s\n", zdev->name);
+  thermal_zone_device_update(zdev);
+  return zdev;
+}
+
+/****************************************************************************
+ * Name: thermal_zone_device_unregister
+ *
+ * Description:
+ *   Unregister thermal zone device.
+ *
+ * Input Parameters:
+ *   zdev - Zone Device
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void thermal_zone_device_unregister(FAR struct thermal_zone_device_s *zdev)
+{
+  FAR struct thermal_cooling_device_s *cdev;
+
+  /* Disable Zone */
+
+  thermal_zone_enable(zdev, false);
+
+  /* Unbind */
+
+  nxmutex_lock(&g_thermal_lock);
+
+  list_for_every_entry(&g_cooling_dev_list, cdev,
+                       struct thermal_cooling_device_s, node)
+    {
+      device_unbind(zdev, cdev);
+    }
+
+  list_delete(&zdev->node);
+  zone_set_governor(zdev, NULL);
+  kmm_free(zdev);
+  nxmutex_unlock(&g_thermal_lock);
+}
+
+/****************************************************************************
+ * Name: thermal_zone_device_update
+ *
+ * Description:
+ *   Update thermal zone device.
+ *   Get temperature from sensor and throttle if necessary.
+ *
+ * Input Parameters:
+ *   zdev - Zone Device
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void thermal_zone_device_update(FAR struct thermal_zone_device_s *zdev)
+{
+  int trip_high = INT_MAX;
+  int trip_low  = INT_MIN;
+  int trip;
+  int temp;
+  int ret;
+
+  nxmutex_lock(&g_thermal_lock);
+
+  /* Update termerature */
+
+  if (!zdev->enabled)
+    {
+      goto unlock;
+    }
+
+  ret = zdev->ops->get_temp(zdev, &temp);
+  if (ret < 0)
+    {
+      therr("Failed to get temperature from zone %s \n", zdev->name);
+      goto unlock;
+    }
+
+  zdev->last_temperature = zdev->temperature;
+  zdev->temperature = temp;
+
+  for (trip = 0; trip < zdev->params->num_trips; trip++)
+    {
+      enum thermal_trip_type_e type;
+      int temp_low;
+      int hyst;
+
+      thermal_zone_get_trip_temp(zdev, trip, &temp);
+      thermal_zone_get_trip_hyst(zdev, trip, &hyst);
+      thermal_zone_get_trip_type(zdev, trip, &type);
+
+      if (zdev->temperature < temp && trip_high > temp)
+        {
+          trip_high = temp;
+        }
+
+      temp_low = temp - hyst;
+
+      if (zdev->temperature > temp_low && trip_low < temp_low)
+        {
+          trip_low  = temp_low;
+        }
+
+      /* Critical */
+
+      if (type == THERMAL_CRITICAL && zdev->temperature >= temp)
+        {
+          therr("Thermal critical (%d), resetting...\n", zdev->temperature);
+#ifdef CONFIG_BOARDCTL_RESET
+          boardctl(BOARDIOC_RESET, BOARDIOC_SOFTRESETCAUSE_THERMAL);
+#endif
+        }
+      else if (zdev->governor)
+        {
+          zdev->governor->throttle(zdev, trip);
+        }
+      else if(g_def_governor)
+        {
+          g_def_governor->throttle(zdev, trip);
+        }
+      else
+        {
+          therr("No valid governor!\n");
+        }
+    }
+
+  if (zdev->ops->set_trips)
+    {
+      ret = zdev->ops->set_trips(zdev, trip_low, trip_high);
+      if (ret < 0)
+        {
+          thwarn("Set trip points (l:%d, h:%d) for %s failed\n",
+                 trip_low, trip_high, zdev->name);
+        }
+    }
+
+  work_queue(LPWORK, &zdev->monitor, (worker_t)thermal_zone_device_update,
+             zdev, zdev->params->polling_delay);
+
+unlock:
+  nxmutex_unlock(&g_thermal_lock);
+}
+
+/****************************************************************************
+ * Name: thermal_init
+ *
+ * Description:
+ *   Init thermal framework
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  Otherwise a negated errno value is
+ *   returned to indicate the nature of the failure.
+ ****************************************************************************/
+
+int thermal_init(void)
+{
+  int ret = OK;
+
+  return ret;
+}
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
new file mode 100644
index 0000000000..3ea97df9db
--- /dev/null
+++ b/drivers/thermal/thermal_core.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * drivers/thermal/thermal_core.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_THERMAL_THERMAL_CORE_H
+#define __DRIVERS_THERMAL_THERMAL_CORE_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+ #include <nuttx/thermal.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct thermal_instance_s
+{
+  FAR struct thermal_cooling_device_s *cdev;
+  FAR struct thermal_zone_device_s    *zdev;
+
+  int  trip;
+
+  /* Cooling State */
+
+  unsigned int target;   /* Expected Cooling State */
+  unsigned int upper;    /* The Maximum cooling state for this trip point */
+  unsigned int lower;    /* Minimum cooling state */
+  unsigned int weight;
+
+  struct list_node cdev_node;
+  struct list_node zdev_node;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/* Cooling Device */
+
+void
+thermal_cooling_device_update (FAR struct thermal_cooling_device_s *cdev);
+
+/* Zone Device */
+
+int thermal_zone_enable       (FAR struct thermal_zone_device_s *zdev,
+                               bool enabled);
+int thermal_zone_get_trend    (FAR struct thermal_zone_device_s *zdev);
+int thermal_zone_get_trip_temp(FAR struct thermal_zone_device_s *zdev,
+                               int trip,
+                               FAR int *temp);
+int thermal_zone_get_trip_type(FAR struct thermal_zone_device_s *zdev,
+                               int trip,
+                               FAR enum thermal_trip_type_e *type);
+int thermal_zone_get_trip_hyst(FAR struct thermal_zone_device_s *zdev,
+                               int trip,
+                               FAR int *hyst);
+
+#endif /* __DRIVERS_THERMAL_THERMAL_CORE_H */
diff --git a/include/debug.h b/include/debug.h
index 418ecc6fb6..bfe42a199e 100644
--- a/include/debug.h
+++ b/include/debug.h
@@ -758,6 +758,24 @@
 #  define spiinfo     _none
 #endif
 
+#ifdef CONFIG_DEBUG_THERMAL_ERROR
+#  define therr         _err
+#else
+#  define therr        _none
+#endif
+
+#ifdef CONFIG_DEBUG_THERMAL_WARN
+#  define thwarn       _warn
+#else
+#  define thwarn       _none
+#endif
+
+#ifdef CONFIG_DEBUG_THERMAL_INFO
+#  define thinfo       _info
+#else
+#  define thinfo       _none
+#endif
+
 #ifdef CONFIG_DEBUG_TIMER_ERROR
 #  define tmrerr       _err
 #else
diff --git a/include/nuttx/thermal.h b/include/nuttx/thermal.h
new file mode 100644
index 0000000000..53a7c668a2
--- /dev/null
+++ b/include/nuttx/thermal.h
@@ -0,0 +1,205 @@
+/****************************************************************************
+ * include/nuttx/thermal.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_THERMAL_H
+#define __INCLUDE_NUTTX_THERMAL_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/list.h>
+#include <nuttx/mutex.h>
+#include <nuttx/wqueue.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Generic */
+
+#define THERMAL_NAME_LEN 32
+
+/* Trips */
+
+#define THERMAL_NO_LIMIT 0
+
+/* Temperature */
+
+#define THERMAL_INVALID_TEMP INT_MIN
+
+/* Cooling State */
+
+#define THERMAL_TARGET_MIN   0
+#define THERMAL_TARGET_MAX   (UINT_MAX - 1)
+#define THERMAL_NO_TARGET    UINT_MAX
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct thermal_cooling_device_ops_s;
+struct thermal_zone_device_ops_s;
+struct thermal_zone_device_s;
+struct thermal_zone_params_s;
+
+enum thermal_trend_e
+{
+  THERMAL_TREND_RAISING,
+  THERMAL_TREND_DROPPING,
+  THERMAL_TREND_STABLE,
+};
+
+enum thermal_trip_type_e
+{
+  THERMAL_COLD,
+  THERMAL_NORMAL,
+  THERMAL_HOT,
+  THERMAL_CRITICAL,
+};
+
+struct thermal_governor_s
+{
+  struct list_node node;
+
+  FAR const char *name;
+
+  CODE int  (*bind_to_tz)    (FAR struct thermal_zone_device_s *zdev);
+  CODE int  (*throttle)      (FAR struct thermal_zone_device_s *zdev,
+                              int trip);
+  CODE void (*unbind_from_tz)(FAR struct thermal_zone_device_s *zdev);
+};
+
+struct thermal_cooling_device_s
+{
+  struct list_node node;
+
+  char name[THERMAL_NAME_LEN];
+  FAR void *devdata;
+  FAR const struct thermal_cooling_device_ops_s *ops;
+
+  struct list_node instance_list;
+};
+
+struct thermal_zone_device_s
+{
+  struct list_node node;
+
+  char name[THERMAL_NAME_LEN];
+  bool enabled;
+
+  FAR void *devdata;
+
+  int last_temperature;
+  int temperature;
+
+  FAR const struct thermal_zone_device_ops_s *ops;
+  FAR struct thermal_governor_s *governor;
+
+  FAR const struct thermal_zone_params_s *params;
+  struct work_s monitor;
+
+  struct list_node instance_list;
+};
+
+struct thermal_cooling_device_ops_s
+{
+  CODE int (*set_state)    (FAR struct thermal_cooling_device_s *cdev,
+                            unsigned int state);
+  CODE int (*get_state)    (FAR struct thermal_cooling_device_s *cdev,
+                            FAR unsigned int *state);
+  CODE int (*get_max_state)(FAR struct thermal_cooling_device_s *cdev,
+                            FAR unsigned int *state);
+};
+
+struct thermal_zone_device_ops_s
+{
+  CODE int  (*get_temp)    (FAR struct thermal_zone_device_s *zdev,
+                            FAR int *temp);
+  CODE int  (*get_trend)   (FAR struct thermal_zone_device_s *zdev,
+                            FAR enum thermal_trend_e *trend);
+  CODE int  (*set_trips)   (FAR struct thermal_zone_device_s *zdev,
+                            int low, int high);
+};
+
+struct thermal_zone_map_s
+{
+  FAR const char *trip_name;
+
+  FAR const char *cdev_name;
+  unsigned int low;
+  unsigned int high;
+
+  unsigned int weight;
+};
+
+struct thermal_zone_trip_s
+{
+  FAR const char *name;
+  unsigned int temp;
+  unsigned int hyst;
+  enum thermal_trip_type_e type;
+};
+
+struct thermal_zone_params_s
+{
+  FAR const char *gov_name;
+  int polling_delay;
+  FAR const struct thermal_zone_trip_s *trips;
+  int num_trips;
+  FAR const struct thermal_zone_map_s *maps;
+  int num_maps;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/* Governor */
+
+int  thermal_register_governor  (FAR struct thermal_governor_s *gov);
+void thermal_unregister_governor(FAR struct thermal_governor_s *gov);
+
+/* Cooling Device */
+
+FAR struct thermal_cooling_device_s *
+thermal_cooling_device_register(FAR const char *name, void *devdata,
+                         FAR const struct thermal_cooling_device_ops_s *ops);
+void
+thermal_cooling_device_unregister(
+                               FAR struct thermal_cooling_device_s *cdev);
+
+/* Zone Device */
+
+FAR struct thermal_zone_device_s *
+thermal_zone_device_register(FAR const char *name, FAR void *devdata,
+                             FAR const struct thermal_zone_device_ops_s *ops,
+                             FAR const struct thermal_zone_params_s *params);
+void
+thermal_zone_device_unregister(
+                             FAR struct thermal_zone_device_s *zdev);
+void
+thermal_zone_device_update  (FAR struct thermal_zone_device_s *zdev);
+
+/* Thermal Framework initialization */
+
+int thermal_init(void);
+
+#endif /* __INCLUDE_NUTTX_THERMAL_H */


Reply via email to