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

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

commit e29a7bcb077be8584a82b4ab6e6846eadfd9ffbd
Author: guanyi <[email protected]>
AuthorDate: Wed Apr 2 14:19:24 2025 +0800

    driver/devfreq: DVFS framework for devices
    
    This commit introduces a devfreq framework to manage device frequency
    scaling. The framework includes the following features:
    1.devfreq governor
      - provide governor ops, including init, start, stop, exit
      - default governor, performance & powersave
      - customized governor, device can provide governor when register
    2.runtime register and unregister
      - device can runtime register & unregister, search by name
    3.suspend and resume
      - suspend and resume frequency scaling
    4.notify
      - register & unregister notifier callback, notify frequency changes
    5.qos support
      - simplified QoS, manage multiple freq range request
      - including init, add/remove/update request, get value
    
    Signed-off-by: guanyi <[email protected]>
---
 drivers/Kconfig                       |   1 +
 drivers/Makefile                      |   1 +
 drivers/devfreq/CMakeLists.txt        |  25 +
 drivers/devfreq/Kconfig               |  36 ++
 drivers/devfreq/Make.defs             |  30 ++
 drivers/devfreq/devfreq.c             | 856 ++++++++++++++++++++++++++++++++++
 drivers/devfreq/devfreq_performance.c |  63 +++
 drivers/devfreq/devfreq_powersave.c   |  63 +++
 drivers/devfreq/devfreq_qos.c         | 195 ++++++++
 include/nuttx/devfreq.h               | 324 +++++++++++++
 include/nuttx/devfreq/devfreq_qos.h   |  66 +++
 include/nuttx/plist.h                 | 346 ++++++++++++++
 12 files changed, 2006 insertions(+)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 20e77b4a971..2dd9ff9cdcb 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -11,6 +11,7 @@ source "drivers/crypto/Kconfig"
 source "drivers/loop/Kconfig"
 source "drivers/can/Kconfig"
 source "drivers/clk/Kconfig"
+source "drivers/devfreq/Kconfig"
 source "drivers/i2c/Kconfig"
 source "drivers/i3c/Kconfig"
 source "drivers/spi/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index dd39ff4d7f1..fe6b440e539 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -34,6 +34,7 @@ include bch/Make.defs
 include can/Make.defs
 include clk/Make.defs
 include crypto/Make.defs
+include devfreq/Make.defs
 include devicetree/Make.defs
 include dma/Make.defs
 include math/Make.defs
diff --git a/drivers/devfreq/CMakeLists.txt b/drivers/devfreq/CMakeLists.txt
new file mode 100644
index 00000000000..91ba9ef4760
--- /dev/null
+++ b/drivers/devfreq/CMakeLists.txt
@@ -0,0 +1,25 @@
+# 
##############################################################################
+# drivers/devfreq/CMakeLists.txt
+#
+# 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.
+#
+# 
##############################################################################
+
+if(CONFIG_DEVFREQ)
+  set(SRCS devfreq.c devfreq_performance.c devfreq_powersave.c devfreq_qos.c)
+
+  target_sources(drivers PRIVATE ${SRCS})
+endif()
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
new file mode 100644
index 00000000000..7bb0a18ab51
--- /dev/null
+++ b/drivers/devfreq/Kconfig
@@ -0,0 +1,36 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config DEVFREQ
+       bool "devfreq"
+       default n
+       ---help---
+               devfreq framework, dynamic voltage frequency scaling(DVFS)
+               for device
+
+if DEVFREQ
+
+choice
+       prompt "DEVFREQ_DEFAULT_GOVERNOR"
+       default DEVFREQ_DEFAULT_GOV_PERFORMANCE
+
+config DEVFREQ_DEFAULT_GOV_PERFORMANCE
+       bool "devfreq_performance"
+       ---help---
+               devfreq performance governor, always choose the highest 
frequency
+
+config DEVFREQ_DEFAULT_GOV_POWERSAVE
+       bool "devfreq_powersave"
+       ---help---
+               devfreq powersave governor, always choose the lowest frequency
+
+config DEVFREQ_DEFAULT_GOV_PASSIVE
+       bool "devfreq_passive"
+       ---help---
+               devfreq passive governor, a device-defined governor
+
+endchoice
+
+endif
diff --git a/drivers/devfreq/Make.defs b/drivers/devfreq/Make.defs
new file mode 100644
index 00000000000..e50951e5b6b
--- /dev/null
+++ b/drivers/devfreq/Make.defs
@@ -0,0 +1,30 @@
+############################################################################
+# drivers/devfreq/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 devfreq sources
+
+ifeq ($(CONFIG_DEVFREQ),y)
+
+CSRCS += devfreq.c devfreq_performance.c devfreq_powersave.c devfreq_qos.c
+
+DEPPATH += --dep-path devfreq
+VPATH += devfreq
+
+endif
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
new file mode 100644
index 00000000000..2bb3a599290
--- /dev/null
+++ b/drivers/devfreq/devfreq.c
@@ -0,0 +1,856 @@
+/****************************************************************************
+ * drivers/devfreq/devfreq.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 <debug.h>
+
+#include <nuttx/devfreq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/sched.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct list_node g_devfreq_list = LIST_INITIAL_VALUE(g_devfreq_list);
+static mutex_t g_devfreq_list_lock = NXMUTEX_INITIALIZER;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int devfreq_init_governor(FAR struct devfreq_s *devfreq);
+static void devfreq_exit_governor(FAR struct devfreq_s *devfreq);
+static int devfreq_start_governor(FAR struct devfreq_s *devfreq);
+static void devfreq_stop_governor(FAR struct devfreq_s *devfreq);
+static void devfreq_limit_governor(FAR struct devfreq_s *devfreq);
+static ssize_t devfreq_table_find_freq(FAR struct devfreq_s *devfreq,
+                                       uint32_t target_freq,
+                                       int relation);
+static int devfreq_table_validate(FAR struct devfreq_s *devfreq);
+static void devfreq_refresh_limit(FAR struct devfreq_s *devfreq);
+static int devfreq_driver_target(FAR struct devfreq_s *devfreq,
+                                 uint32_t target_freq,
+                                 int relation);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devfreq_init_governor
+ *
+ * Description:
+ *   Initialize governor
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int devfreq_init_governor(FAR struct devfreq_s *devfreq)
+{
+  if (!devfreq->governor)
+    {
+      return -EINVAL;
+    }
+
+  if (devfreq->governor->init)
+    {
+      return devfreq->governor->init(devfreq);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_exit_governor
+ *
+ * Description:
+ *   Exit governor
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void devfreq_exit_governor(FAR struct devfreq_s *devfreq)
+{
+  if (!devfreq->governor)
+    {
+      return;
+    }
+
+  if (devfreq->governor->exit)
+    {
+      devfreq->governor->exit(devfreq);
+    }
+}
+
+/****************************************************************************
+ * Name: devfreq_start_governor
+ *
+ * Description:
+ *   Start governor
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int devfreq_start_governor(FAR struct devfreq_s *devfreq)
+{
+  if (devfreq->suspended)
+    {
+      return 0;
+    }
+
+  if (!devfreq->governor)
+    {
+      return -EINVAL;
+    }
+
+  if (devfreq->governor->start)
+    {
+      int ret = devfreq->governor->start(devfreq);
+      if (ret < 0)
+        {
+          return ret;
+        }
+    }
+
+  devfreq_limit_governor(devfreq);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_stop_governor
+ *
+ * Description:
+ *   Stop governor
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void devfreq_stop_governor(FAR struct devfreq_s *devfreq)
+{
+  if (devfreq->suspended || !devfreq->governor)
+    {
+      return;
+    }
+
+  if (devfreq->governor->stop)
+    {
+      devfreq->governor->stop(devfreq);
+    }
+}
+
+/****************************************************************************
+ * Name: devfreq_limit_governor
+ *
+ * Description:
+ *   Limit governor
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void devfreq_limit_governor(FAR struct devfreq_s *devfreq)
+{
+  if (devfreq->suspended || !devfreq->governor)
+    {
+      return;
+    }
+
+  if (devfreq->governor->limit)
+    {
+      uint32_t freq = devfreq->governor->limit(devfreq);
+      devfreq_driver_target(devfreq, freq, DEVFREQ_RELATION_L);
+    }
+}
+
+/****************************************************************************
+ * Name: devfreq_table_find_freq
+ *
+ * Description:
+ *   Find frequency in table
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *   target_freq - target frequency
+ *   relation - relation
+ *
+ * Returned Value:
+ *   target index on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+static ssize_t devfreq_table_find_freq(FAR struct devfreq_s *devfreq,
+                                       uint32_t target_freq,
+                                       int relation)
+{
+  ssize_t best = -ENOENT;
+  size_t i;
+
+  if (relation == DEVFREQ_RELATION_L)
+    {
+      for (i = 0; devfreq->freq_table[i] != DEVFREQ_ENTRY_END; i++)
+        {
+          if (devfreq->freq_table[i] == DEVFREQ_ENTRY_INVALID)
+            {
+              continue;
+            }
+
+          if (devfreq->freq_table[i] >= target_freq)
+            {
+              best = i;
+              break;
+            }
+        }
+    }
+  else if (relation == DEVFREQ_RELATION_H)
+    {
+      for (i = 0; devfreq->freq_table[i] != DEVFREQ_ENTRY_END; i++)
+        {
+          if (devfreq->freq_table[i] == DEVFREQ_ENTRY_INVALID)
+            {
+              continue;
+            }
+
+          if (devfreq->freq_table[i] > target_freq)
+            {
+              break;
+            }
+
+          best = i;
+        }
+    }
+
+  return best;
+}
+
+/****************************************************************************
+ * Name: devfreq_table_validate
+ *
+ * Description:
+ *   Validate table
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int devfreq_table_validate(FAR struct devfreq_s *devfreq)
+{
+  FAR const uint32_t *table = devfreq->freq_table;
+  uint32_t prv_freq         = 0;
+  uint32_t min_freq         = UINT32_MAX;
+  uint32_t max_freq         = 0;
+  size_t i;
+
+  if (!table)
+    {
+      return -EINVAL;
+    }
+
+  for (i = 0; table[i] != DEVFREQ_ENTRY_END; i++)
+    {
+      if (table[i] == DEVFREQ_ENTRY_INVALID)
+        {
+          continue;
+        }
+
+      if (i && table[i] <= prv_freq)
+        {
+          return -EINVAL;
+        }
+
+      if (table[i] < min_freq)
+        {
+          min_freq = table[i];
+        }
+
+      if (table[i] > max_freq)
+        {
+          max_freq = table[i];
+        }
+
+      prv_freq = table[i];
+    }
+
+  devfreq->min = min_freq;
+  devfreq->max = max_freq;
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_refresh_limit
+ *
+ * Description:
+ *   Refresh limit
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void devfreq_refresh_limit(FAR struct devfreq_s *devfreq)
+{
+  uint32_t min;
+  uint32_t max;
+  ssize_t idx;
+
+  min = qos_get_value(&devfreq->constraints, QOS_REQ_MIN);
+  max = qos_get_value(&devfreq->constraints, QOS_REQ_MAX);
+
+  if (min > max)
+    {
+      min = max;
+    }
+
+  idx = devfreq_table_find_freq(devfreq, min, DEVFREQ_RELATION_L);
+  if (idx >= 0)
+    {
+      devfreq->min = devfreq->freq_table[idx];
+    }
+
+  idx = devfreq_table_find_freq(devfreq, max, DEVFREQ_RELATION_H);
+  if (idx >= 0)
+    {
+      devfreq->max = devfreq->freq_table[idx];
+    }
+
+  devfreq_limit_governor(devfreq);
+}
+
+/****************************************************************************
+ * Name: devfreq_driver_target
+ *
+ * Description:
+ *   Set target frequency
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *   target_freq - target frequency
+ *   relation - relation to target frequency
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int devfreq_driver_target(FAR struct devfreq_s *devfreq,
+                                 uint32_t target_freq,
+                                 int relation)
+{
+  struct devfreq_notifier_s freq;
+  ssize_t idx;
+  int ret;
+
+  if (!devfreq)
+    {
+      return -EINVAL;
+    }
+
+  idx = devfreq_table_find_freq(devfreq, target_freq, relation);
+  if (idx < 0)
+    {
+      return -ENOENT;
+    }
+
+  target_freq = devfreq->freq_table[idx];
+  if (target_freq == devfreq->cur)
+    {
+      return 0;
+    }
+
+  freq.old = devfreq->cur;
+  freq.new = target_freq;
+
+  blocking_notifier_call_chain(&devfreq->notifier_list,
+                               DEVFREQ_PRECHANGE, &freq);
+  ret = devfreq->driver->target_index(devfreq, idx);
+  blocking_notifier_call_chain(&devfreq->notifier_list,
+                               DEVFREQ_POSTCHANGE, &freq);
+  if (ret < 0)
+    {
+      freq.old = target_freq;
+      freq.new = devfreq->cur;
+      blocking_notifier_call_chain(&devfreq->notifier_list,
+                                   DEVFREQ_PRECHANGE, &freq);
+      blocking_notifier_call_chain(&devfreq->notifier_list,
+                                   DEVFREQ_POSTCHANGE, &freq);
+      return ret;
+    }
+
+  devfreq->cur = target_freq;
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devfreq_register
+ *
+ * Description:
+ *   Register devfreq device
+ *
+ * Input Parameters:
+ *   name - device name
+ *   governor - governor
+ *   driver - driver
+ *   priv - private data
+ *
+ * Returned Value:
+ *   devfreq device on success; NULL on failure
+ *
+ ****************************************************************************/
+
+FAR struct devfreq_s *devfreq_register(
+                          const char *name,
+                          FAR struct devfreq_governor_s *governor,
+                          FAR struct devfreq_driver_s *driver,
+                          FAR void *priv)
+{
+  FAR struct devfreq_s *devfreq = devfreq_find_by_name(name);
+
+  if (devfreq || !driver)
+    {
+      return NULL;
+    }
+
+  devfreq = (FAR struct devfreq_s *)kmm_zalloc(sizeof(struct devfreq_s));
+  if (!devfreq)
+    {
+      return NULL;
+    }
+
+  qos_constraints_init(&devfreq->constraints);
+  BLOCKING_INIT_NOTIFIER_HEAD(&devfreq->notifier_list);
+  nxmutex_init(&devfreq->lock);
+
+  devfreq->driver     = driver;
+  devfreq->priv       = priv;
+  devfreq->suspended  = false;
+  devfreq->freq_table = driver->get_table(devfreq);
+  if (!devfreq->freq_table)
+    {
+      goto out;
+    }
+
+  if (devfreq_table_validate(devfreq) < 0)
+    {
+      goto out;
+    }
+
+  if (!governor)
+    {
+      devfreq->governor = devfreq_default_governor();
+    }
+  else
+    {
+      devfreq->governor = governor;
+    }
+
+  if (devfreq_init_governor(devfreq) < 0)
+    {
+      goto out;
+    }
+
+  devfreq_start_governor(devfreq);
+
+  nxmutex_lock(&g_devfreq_list_lock);
+  list_add_tail(&g_devfreq_list, &devfreq->node);
+  nxmutex_unlock(&g_devfreq_list_lock);
+
+  return devfreq;
+
+out:
+  nxmutex_destroy(&devfreq->lock);
+  nxmutex_destroy(&devfreq->notifier_list.mutex);
+  kmm_free(devfreq);
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: devfreq_unregister
+ *
+ * Description:
+ *   Unregister devfreq device
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_unregister(FAR struct devfreq_s *devfreq)
+{
+  if (!devfreq)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&g_devfreq_list_lock);
+  list_delete(&devfreq->node);
+  nxmutex_unlock(&g_devfreq_list_lock);
+
+  devfreq_stop_governor(devfreq);
+  devfreq_exit_governor(devfreq);
+
+  nxmutex_destroy(&devfreq->lock);
+  nxmutex_destroy(&devfreq->notifier_list.mutex);
+  kmm_free(devfreq);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_suspend
+ *
+ * Description:
+ *   Suspend devfreq device
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_suspend(FAR struct devfreq_s *devfreq)
+{
+  nxmutex_lock(&devfreq->lock);
+
+  devfreq_stop_governor(devfreq);
+
+  if (devfreq->driver->suspend)
+    {
+      int ret = devfreq->driver->suspend(devfreq);
+      if (ret < 0)
+        {
+          nxmutex_unlock(&devfreq->lock);
+          return ret;
+        }
+    }
+
+  devfreq->suspended = true;
+  nxmutex_unlock(&devfreq->lock);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_resume
+ *
+ * Description:
+ *   Resume devfreq device
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_resume(struct devfreq_s *devfreq)
+{
+  nxmutex_lock(&devfreq->lock);
+
+  if (devfreq->driver->resume)
+    {
+      int ret = devfreq->driver->resume(devfreq);
+      if (ret < 0)
+        {
+          nxmutex_unlock(&devfreq->lock);
+          return ret;
+        }
+    }
+
+  devfreq->suspended = false;
+  devfreq_start_governor(devfreq);
+
+  nxmutex_unlock(&devfreq->lock);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_register_notifier
+ *
+ * Description:
+ *   Register notifier
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *   nb - notifier block
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_register_notifier(FAR struct devfreq_s *devfreq,
+                              FAR struct notifier_block *nb)
+{
+  if (!devfreq || !nb)
+    {
+      return -EINVAL;
+    }
+
+  blocking_notifier_chain_register(&devfreq->notifier_list, nb);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_unregister_notifier
+ *
+ * Description:
+ *   Unregister notifier
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *   nb - notifier block
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_unregister_notifier(FAR struct devfreq_s *devfreq,
+                                FAR struct notifier_block *nb)
+{
+  if (!devfreq || !nb)
+    {
+      return -EINVAL;
+    }
+
+  blocking_notifier_chain_unregister(&devfreq->notifier_list, nb);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: devfreq_get_frequency
+ *
+ * Description:
+ *   Get current frequency
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *
+ * Returned Value:
+ *   Current frequency
+ *
+ ****************************************************************************/
+
+FAR uint32_t devfreq_get_frequency(FAR struct devfreq_s *devfreq)
+{
+  return devfreq->driver->get_frequency(devfreq);
+}
+
+/****************************************************************************
+ * Name: devfreq_qos_add_request
+ *
+ * Description:
+ *   Add a new request
+ *
+ * Input Parameters:
+ *   devfreq - devfreq device
+ *   min - minimum frequency
+ *   max - maximum frequency
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+FAR struct qos_request_s *devfreq_qos_add_request(
+                            FAR struct devfreq_s *devfreq,
+                            uint32_t min, uint32_t max)
+{
+  FAR struct qos_request_s *req;
+
+  if (!devfreq)
+    {
+      return NULL;
+    }
+
+  nxmutex_lock(&devfreq->lock);
+
+  req = qos_add_request(&devfreq->constraints, min, max);
+  devfreq_refresh_limit(devfreq);
+
+  nxmutex_unlock(&devfreq->lock);
+  return req;
+}
+
+/****************************************************************************
+ * Name: devfreq_qos_update_request
+ *
+ * Description:
+ *   Update a request
+ *
+ * Input Parameters:
+ *   qos - devfreq qos
+ *   min - minimum frequency
+ *   max - maximum frequency
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_qos_update_request(FAR struct devfreq_s *devfreq,
+                               FAR struct qos_request_s *req,
+                               uint32_t min, uint32_t max)
+{
+  int ret;
+
+  if (!devfreq || !req)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&devfreq->lock);
+
+  ret = qos_update_request(&devfreq->constraints, req, min, max);
+  if (ret < 0)
+    {
+      nxmutex_unlock(&devfreq->lock);
+      return ret;
+    }
+
+  devfreq_refresh_limit(devfreq);
+
+  nxmutex_unlock(&devfreq->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: devfreq_qos_remove_request
+ *
+ * Description:
+ *   Remove a request
+ *
+ * Input Parameters:
+ *   qos - devfreq qos
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int devfreq_qos_remove_request(FAR struct devfreq_s *devfreq,
+                               FAR struct qos_request_s *req)
+{
+  int ret;
+
+  if (!devfreq || !req)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&devfreq->lock);
+
+  ret = qos_remove_request(&devfreq->constraints, req);
+  if (ret < 0)
+    {
+      nxmutex_unlock(&devfreq->lock);
+      return ret;
+    }
+
+  devfreq_refresh_limit(devfreq);
+
+  nxmutex_unlock(&devfreq->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: devfreq_find_by_name
+ *
+ * Description:
+ *   find a devfreq entry from global list by name
+ *
+ * Input Parameters:
+ *   name - devfreq name
+ *
+ * Returned Value:
+ *   devfreq handle
+ *
+ ****************************************************************************/
+
+FAR struct devfreq_s *devfreq_find_by_name(FAR const char *name)
+{
+  FAR struct devfreq_s *devfreq;
+
+  if (!name)
+    {
+      return NULL;
+    }
+
+  nxmutex_lock(&g_devfreq_list_lock);
+
+  list_for_every_entry(&g_devfreq_list, devfreq, struct devfreq_s, node)
+    {
+      if (!strcmp(devfreq->name, name))
+        {
+          nxmutex_unlock(&g_devfreq_list_lock);
+          return devfreq;
+        }
+    }
+
+  nxmutex_unlock(&g_devfreq_list_lock);
+  return NULL;
+}
diff --git a/drivers/devfreq/devfreq_performance.c 
b/drivers/devfreq/devfreq_performance.c
new file mode 100644
index 00000000000..5e582607f9e
--- /dev/null
+++ b/drivers/devfreq/devfreq_performance.c
@@ -0,0 +1,63 @@
+/****************************************************************************
+ * drivers/devfreq/devfreq_performance.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/devfreq.h>
+
+#ifdef CONFIG_DEVFREQ_DEFAULT_GOV_PERFORMANCE
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static uint32_t devfreq_performance_limit(FAR struct devfreq_s *devfreq);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct devfreq_governor_s g_devfreq_gov_performance =
+{
+  .name   = "performance",
+  .limit = devfreq_performance_limit,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint32_t devfreq_performance_limit(FAR struct devfreq_s *devfreq)
+{
+  return devfreq->max;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR struct devfreq_governor_s *devfreq_default_governor(void)
+{
+  return &g_devfreq_gov_performance;
+}
+
+#endif /* CONFIG_DEVFREQ_DEFAULT_GOV_PERFORMANCE */
diff --git a/drivers/devfreq/devfreq_powersave.c 
b/drivers/devfreq/devfreq_powersave.c
new file mode 100644
index 00000000000..bc4e991dd12
--- /dev/null
+++ b/drivers/devfreq/devfreq_powersave.c
@@ -0,0 +1,63 @@
+/****************************************************************************
+ * drivers/devfreq/devfreq_powersave.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/devfreq.h>
+
+#ifdef CONFIG_DEVFREQ_DEFAULT_GOV_POWERSAVE
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static uint32_t devfreq_powersave_limit(FAR struct devfreq_s *devfreq);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct devfreq_governor_s g_devfreq_gov_powersave =
+{
+  .name   = "powersave",
+  .limit = devfreq_powersave_limit,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static uint32_t devfreq_powersave_limit(FAR struct devfreq_s *devfreq)
+{
+  return devfreq->min;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR struct devfreq_governor_s *devfreq_default_governor(void)
+{
+  return &g_devfreq_gov_powersave;
+}
+
+#endif /* CONFIG_DEVFREQ_DEFAULT_GOV_POWERSAVE */
diff --git a/drivers/devfreq/devfreq_qos.c b/drivers/devfreq/devfreq_qos.c
new file mode 100644
index 00000000000..251b25f0880
--- /dev/null
+++ b/drivers/devfreq/devfreq_qos.c
@@ -0,0 +1,195 @@
+/****************************************************************************
+ * drivers/devfreq/devfreq_qos.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/devfreq/devfreq_qos.h>
+#include <nuttx/kmalloc.h>
+#include <errno.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qos_constraints_init
+ *
+ * Description:
+ *   Initialize a qos_constraints_s struct.
+ *
+ * Input Parameters:
+ *   qos - The qos constraints struct to initialize.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void qos_constraints_init(FAR struct qos_constraints_s *constraints)
+{
+  plist_head_init(&constraints->min_requests);
+  plist_head_init(&constraints->max_requests);
+}
+
+/****************************************************************************
+ * Name: qos_add_request
+ *
+ * Description:
+ *   Add a qos request to qos constraints.
+ *
+ * Input Parameters:
+ *   qos - The qos constraints to add the request to.
+ *   min - The minimum priority/value of the request.
+ *   max - The maximum priority/value of the request.
+ *
+ * Returned Value:
+ *   A pointer to the new qos_request_s.
+ *
+ ****************************************************************************/
+
+FAR struct qos_request_s *qos_add_request(
+              FAR struct qos_constraints_s *constraints,
+              uint32_t min, uint32_t max)
+{
+  FAR struct qos_request_s *req;
+
+  if (!constraints)
+    {
+      return NULL;
+    }
+
+  req = kmm_zalloc(sizeof(struct qos_request_s));
+  if (!req)
+    {
+      return NULL;
+    }
+
+  plist_node_init(&req->min_req, min);
+  plist_node_init(&req->max_req, max);
+
+  plist_add(&req->min_req, &constraints->min_requests);
+  plist_add(&req->max_req, &constraints->max_requests);
+
+  return req;
+}
+
+/****************************************************************************
+ * Name: qos_remove_request
+ *
+ * Description:
+ *   Remove qos request from qos constraints.
+ *
+ * Input Parameters:
+ *   qos - The qos_constraints_s to remove the request from.
+ *   req - The qos_request_s to remove.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int qos_remove_request(FAR struct qos_constraints_s *constraints,
+                       FAR struct qos_request_s *req)
+{
+  if (!req || !constraints)
+    {
+      return -EINVAL;
+    }
+
+  plist_del(&req->min_req, &constraints->min_requests);
+  plist_del(&req->max_req, &constraints->max_requests);
+
+  kmm_free(req);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: qos_update_request
+ *
+ * Description:
+ *   Update qos request.
+ *
+ * Input Parameters:
+ *   qos - The qos constraints.
+ *   req - The qos request to update.
+ *   min - The new minimum priority/value of the request.
+ *   max - The new maximum priority/value of the request.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+int qos_update_request(FAR struct qos_constraints_s *constraints,
+                       FAR struct qos_request_s *req,
+                       uint32_t min, uint32_t max)
+{
+  if (!req || !constraints)
+    {
+      return -EINVAL;
+    }
+
+  req->min_req.prio = min;
+  req->max_req.prio = max;
+
+  plist_del(&req->min_req, &constraints->min_requests);
+  plist_del(&req->max_req, &constraints->max_requests);
+  plist_add(&req->min_req, &constraints->min_requests);
+  plist_add(&req->max_req, &constraints->max_requests);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: qos_get_value
+ *
+ * Description:
+ *   Get min or max value of qos constraints.
+ *
+ * Input Parameters:
+ *   qos - The qos constraints.
+ *   type - The type of request to get the value of.
+ *
+ * Returned Value:
+ *   The value of the qos request.
+ *
+ ****************************************************************************/
+
+uint32_t qos_get_value(FAR struct qos_constraints_s *constraints,
+                       enum qos_req_type_e type)
+{
+  switch (type)
+    {
+      case QOS_REQ_MIN:
+        {
+          return plist_first(&constraints->min_requests)->prio;
+        }
+
+      case QOS_REQ_MAX:
+        {
+          return plist_last(&constraints->max_requests)->prio;
+        }
+    }
+
+  return 0;
+}
diff --git a/include/nuttx/devfreq.h b/include/nuttx/devfreq.h
new file mode 100644
index 00000000000..367986e5b50
--- /dev/null
+++ b/include/nuttx/devfreq.h
@@ -0,0 +1,324 @@
+/****************************************************************************
+ * include/nuttx/devfreq.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_DEVFREQ_H
+#define __INCLUDE_NUTTX_DEVFREQ_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/devfreq/devfreq_qos.h>
+#include <nuttx/list.h>
+#include <nuttx/notifier.h>
+#include <sys/types.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEVFREQ_PRECHANGE     0
+#define DEVFREQ_POSTCHANGE    1
+
+/* Special Values of .frequency field */
+
+#define DEVFREQ_ENTRY_INVALID ~0u
+#define DEVFREQ_ENTRY_END     ~1u
+
+#define DEVFREQ_RELATION_L    0   /* lowest frequency at or above target */
+#define DEVFREQ_RELATION_H    1   /* highest frequency below or at target */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct devfreq_s
+{
+  char name[NAME_MAX];
+  struct list_node node;
+
+  FAR struct devfreq_governor_s *governor;
+  FAR struct devfreq_driver_s *driver;
+
+  FAR const uint32_t *freq_table;
+
+  struct qos_constraints_s constraints;
+
+  struct blocking_notifier_head notifier_list;
+
+  uint32_t min;                     /* in kHz */
+  uint32_t max;                     /* in kHz */
+  uint32_t cur;                     /* in kHz */
+
+  bool suspended;
+
+  mutex_t lock;
+
+  FAR void *priv;
+};
+
+struct devfreq_governor_s
+{
+  char name[NAME_MAX];
+  CODE int (*init)(FAR struct devfreq_s *devfreq);
+  CODE int (*exit)(FAR struct devfreq_s *devfreq);
+  CODE int (*start)(FAR struct devfreq_s *devfreq);
+  CODE void (*stop)(FAR struct devfreq_s *devfreq);
+  CODE uint32_t (*limit)(FAR struct devfreq_s *devfreq);
+};
+
+struct devfreq_driver_s
+{
+  CODE FAR const uint32_t *
+           (*get_table)(FAR struct devfreq_s *devfreq);
+  CODE int (*target_index)(FAR struct devfreq_s *devfreq,
+                           size_t index);
+  CODE uint32_t (*get_frequency)(FAR struct devfreq_s *devfreq);
+  CODE int (*suspend)(FAR struct devfreq_s *devfreq);
+  CODE int (*resume)(FAR struct devfreq_s *devfreq);
+};
+
+struct devfreq_notifier_s
+{
+  uint32_t old;
+  uint32_t new;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: devfreq_register
+ *
+ * Description:
+ *   Register devfreq device
+ *
+ * Input Parameters:
+ *   name - device name
+ *   governor - governor
+ *   driver - driver
+ *   priv - private data
+ *
+ * Returned Value:
+ *   devfreq device on success; NULL on failure
+ *
+ ****************************************************************************/
+
+FAR struct devfreq_s *devfreq_register(
+                          FAR const char *name,
+                          FAR struct devfreq_governor_s *governor,
+                          FAR struct devfreq_driver_s *driver,
+                          FAR void *priv);
+
+/****************************************************************************
+ * Name: devfreq_unregister
+ *
+ * Description:
+ *   unregister devfreq
+ *
+ * Input Parameters:
+ *   devfreq - devfreq_s handle
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_unregister(FAR struct devfreq_s *devfreq);
+
+/****************************************************************************
+ * Name: devfreq_suspend
+ *
+ * Description:
+ *   suspend devfreq governors
+ *
+ * Input Parameters:
+ *   devfreq - devfreq_s handle
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_suspend(FAR struct devfreq_s *devfreq);
+
+/****************************************************************************
+ * Name: devfreq_resume
+ *
+ * Description:
+ *   resume devfreq governors
+ *
+ * Input Parameters:
+ *   devfreq - devfreq_s handle
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_resume(FAR struct devfreq_s *devfreq);
+
+/****************************************************************************
+ * Name: devfreq_set_governor
+ *
+ * Description:
+ *   set devfreq governor
+ *
+ * Input Parameters:
+ *   devfreq - devfreq_s handle
+ *   governor - governor handle
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_register_notifier(FAR struct devfreq_s *devfreq,
+                              FAR struct notifier_block *nb);
+
+/****************************************************************************
+ * Name: devfreq_unregister_notifier
+ *
+ * Description:
+ *   unregister devfreq notifier
+ *
+ * Input Parameters:
+ *   devfreq - devfreq_s handle
+ *   nb - notifier block
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_unregister_notifier(FAR struct devfreq_s *devfreq,
+                                FAR struct notifier_block *nb);
+
+/****************************************************************************
+ * Name: devfreq_get
+ *
+ * Description:
+ *   get the current device frequency (in kHz)
+ *
+ * Input Parameters:
+ *   devfreq - devfreq_s handle
+ *
+ * Returned Value:
+ *   a non-negative value
+ *
+ ****************************************************************************/
+
+uint32_t devfreq_get_frequency(FAR struct devfreq_s *devfreq);
+
+/****************************************************************************
+ * Name: devfreq_qos_add_request
+ *
+ * Description:
+ *   Insert new frequency QoS request
+ *
+ * Input Parameters:
+ *   policy - devfreq_policy handle
+ *   min    - min freq
+ *   max    - max freq
+ *
+ * Returned Value:
+ *   qos handle for update and remove, or NULL if fail
+ *
+ ****************************************************************************/
+
+FAR struct qos_request_s *devfreq_qos_add_request(
+                            FAR struct devfreq_s *devfreq,
+                            uint32_t min, uint32_t max);
+
+/****************************************************************************
+ * Name: devfreq_qos_update_request
+ *
+ * Description:
+ *   Update frequency QoS request from its list.
+ *
+ * Input Parameters:
+ *   qos - Request to remove.
+ *   min - min freq
+ *   max - max freq
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_qos_update_request(FAR struct devfreq_s *devfreq,
+                               FAR struct qos_request_s *qos,
+                               uint32_t min, uint32_t max);
+
+/****************************************************************************
+ * Name: devfreq_qos_remove_request
+ *
+ * Description:
+ *   Remove frequency QoS request from its list.
+ *
+ * Input Parameters:
+ *   qos - Request to remove.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int devfreq_qos_remove_request(FAR struct devfreq_s *devfreq,
+                               FAR struct qos_request_s *req);
+
+/****************************************************************************
+ * Name: devfreq_find_by_name
+ *
+ * Description:
+ *   find a devfreq entry from global list by name
+ *
+ * Input Parameters:
+ *   name - devfreq name
+ *
+ * Returned Value:
+ *   devfreq handle
+ *
+ ****************************************************************************/
+
+FAR struct devfreq_s *devfreq_find_by_name(FAR const char *name);
+
+#ifdef CONFIG_DEVFREQ_DEFAULT_GOV_PASSIVE
+#define devfreq_default_governor() NULL
+#else
+FAR struct devfreq_governor_s *devfreq_default_governor(void);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_DEVFREQ_H */
diff --git a/include/nuttx/devfreq/devfreq_qos.h 
b/include/nuttx/devfreq/devfreq_qos.h
new file mode 100644
index 00000000000..47837b4c6e0
--- /dev/null
+++ b/include/nuttx/devfreq/devfreq_qos.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+ * include/nuttx/devfreq/devfreq_qos.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_DEVFREQ_DEVFREQ_QOS_H
+#define __INCLUDE_NUTTX_DEVFREQ_DEVFREQ_OQS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/plist.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum qos_req_type_e
+{
+  QOS_REQ_MIN,
+  QOS_REQ_MAX
+};
+
+struct qos_request_s
+{
+  struct plist_node min_req;
+  struct plist_node max_req;
+};
+
+struct qos_constraints_s
+{
+  struct plist_head min_requests;
+  struct plist_head max_requests;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+void qos_constraints_init(FAR struct qos_constraints_s *constraints);
+FAR struct qos_request_s *qos_add_request(FAR struct qos_constraints_s *qos,
+                                          uint32_t min, uint32_t max);
+int qos_remove_request(FAR struct qos_constraints_s *constraints,
+                       FAR struct qos_request_s *req);
+int qos_update_request(FAR struct qos_constraints_s *constraints,
+                       FAR struct qos_request_s *req,
+                       uint32_t min, uint32_t max);
+uint32_t qos_get_value(FAR struct qos_constraints_s *constraints,
+                       enum qos_req_type_e type);
+#endif
diff --git a/include/nuttx/plist.h b/include/nuttx/plist.h
new file mode 100644
index 00000000000..79177159e00
--- /dev/null
+++ b/include/nuttx/plist.h
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * include/nuttx/plist.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_PLIST_H
+#define __INCLUDE_NUTTX_PLIST_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <assert.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/list.h>
+
+/****************************************************************************
+ * Public Type Definitions
+ ****************************************************************************/
+
+struct plist_head
+{
+  struct list_node node_list;
+};
+
+struct plist_node
+{
+  uint32_t prio;
+  struct list_node prio_list;
+  struct list_node node_list;
+};
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* PLIST_HEAD_INIT - static struct plist_head initializer
+ * head: struct plist_head variable name
+ */
+
+#define PLIST_HEAD_INIT(head)                     \
+  {                                               \
+    .node_list = LIST_HEAD_INIT((head).node_list) \
+  }
+
+/* PLIST_HEAD - declare and init plist_head
+ * head: name for struct plist_head variable
+ */
+
+#define PLIST_HEAD(head) \
+  struct plist_head head = PLIST_HEAD_INIT(head)
+
+/* PLIST_NODE_INIT - static struct plist_node initializer
+ * node: struct plist_node variable name
+ * prio: initial node priority
+ */
+
+#define PLIST_NODE_INIT(node, val)                 \
+  {                                                \
+    .prio      = (val),                            \
+    .prio_list = LIST_HEAD_INIT((node).prio_list), \
+    .node_list = LIST_HEAD_INIT((node).node_list), \
+  }
+
+/* plist_head_init - dynamic struct plist_head initializer
+ * head: struct plist_head pointer
+ */
+
+#define plist_head_init(head) \
+  list_initialize(&(head)->node_list)
+
+/* plist_node_init - Dynamic struct plist_node initializer
+ * node: struct plist_node pointer
+ * prio: initial node priority
+ */
+
+#define plist_node_init(node, val)         \
+  do                                       \
+    {                                      \
+      (node)->prio = (val);                \
+      list_initialize(&(node)->prio_list); \
+      list_initialize(&(node)->node_list); \
+    }                                      \
+  while(0)
+
+/* plist_for_each - iterate over the plist
+ * pos:  the type * to use as a loop counter
+ * head: the head for your list
+ */
+
+#define plist_for_each(pos, head) \
+  list_for_every_entry(&(head)->node_list, pos, typeof(*pos), node_list)
+
+/* plist_for_each_continue - continue iteration over the plist
+ * pos:  the type * to use as a loop cursor
+ * head: the head for your list
+ *
+ * Continue to iterate over plist, continuing after the current position.
+ */
+
+#define plist_for_each_continue(pos, head) \
+  list_for_every_entry_continue(pos, &(head)->node_list, typeof(*pos), 
node_list)
+
+/* plist_for_each_safe - iterate safely over a plist of given type
+ * pos:  the type * to use as a loop counter
+ * n:    another type * to use as temporary storage
+ * head: the head for your list
+ *
+ * Iterate over a plist of given type, safe against removal of list entry.
+ */
+
+#define plist_for_each_safe(pos, n, head) \
+  list_for_every_entry_safe(&(head)->node_list, pos, n, typeof(*pos), 
node_list)
+
+/* plist_for_each_entry - iterate over list of given type
+ * pos:  the type * to use as a loop counter
+ * head: the head for your list
+ * mem:  the name of the list_node within the struct
+ */
+
+#define plist_for_each_entry(pos, head, mem) \
+  list_for_every_entry(&(head)->node_list, pos, typeof(*pos), mem.node_list)
+
+/* plist_for_each_entry_continue - continue iteration over list of given type
+ * pos:  the type * to use as a loop cursor
+ * head: the head for your list
+ * m:    the name of the list_node within the struct
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define plist_for_each_entry_continue(pos, head, m) \
+  list_for_every_entry_continue(pos, &(head)->node_list, typeof(*pos), 
m.node_list)
+
+/* plist_for_each_entry_safe - iterate safely over list of given type
+ * pos:  the type * to use as a loop counter
+ * n:    another type * to use as temporary storage
+ * head: the head for your list
+ * m:    the name of the list_node within the struct
+ *
+ * Iterate over list of given type, safe against removal of list entry.
+ */
+#define plist_for_each_entry_safe(pos, n, head, m) \
+  list_for_every_entry_safe(&(head)->node_list, pos, n, typeof(*pos), 
m.node_list)
+
+/* All functions below assume the plist_head is not empty. */
+
+/* plist_first_entry - get the struct for the first entry
+ * head:   the struct plist_head pointer
+ * type:   the type of the struct this is embedded in
+ * member: the name of the list_node within the struct
+ */
+
+#define plist_first_entry(head, type, member) \
+  container_of(plist_first(head), type, member)
+
+/* plist_last_entry - get the struct for the last entry
+ * head:   the struct plist_head pointer
+ * type:   the type of the struct this is embedded in
+ * member: the name of the list_node within the struct
+ */
+
+#define plist_last_entry(head, type, member) \
+  container_of(plist_last(head), type, member)
+
+/* plist_next - get the next entry in list
+ * pos: the type * to cursor
+ */
+
+#define plist_next(pos) list_next_entry(pos, typeof(*(pos)), node_list)
+
+/* plist_prev - get the prev entry in list
+ * pos: the type * to cursor
+ */
+
+#define plist_prev(pos) list_prev_entry(pos, typeof(*(pos)), node_list)
+
+/* plist_head_empty - return !0 if a plist_head is empty
+ * head: struct plist_head pointer
+ */
+
+#define plist_head_empty(head) list_is_empty(&(head)->node_list)
+
+/* plist_node_empty - return !0 if plist_node is not on a list
+ * node: struct plist_node pointer
+ */
+
+#define plist_node_empty(node) list_is_empty(&(node)->node_list)
+
+/* plist_first - return the first node (and thus, highest priority)
+ * head: the struct plist_head pointer
+ *
+ * Assumes the plist is _not_ empty.
+ */
+
+#define plist_first(head) \
+  list_entry((head)->node_list.next, struct plist_node, node_list)
+
+/* plist_last - return the last node (and thus, lowest priority)
+ * head: the struct plist_head pointer
+ *
+ * Assumes the plist is _not_ empty.
+ */
+
+#define plist_last(head) \
+    list_entry((head)->node_list.prev, struct plist_node, node_list)
+
+/* plist_add - add node to head
+ * node: struct plist_node pointer
+ * head: struct plist_head pointer
+ */
+
+#define plist_add(node, head)                                      \
+  do                                                               \
+    {                                                              \
+      FAR struct plist_head *head_ = (head);                       \
+      FAR struct plist_node *node_ = (node);                       \
+      FAR struct list_node *node_next_ = &head_->node_list;        \
+                                                                   \
+      DEBUGASSERT(plist_node_empty(node_));                        \
+      DEBUGASSERT(list_is_empty(&node_->prio_list));               \
+                                                                   \
+      if (!plist_head_empty(head_))                                \
+        {                                                          \
+          FAR struct plist_node *first_ = plist_first(head_);      \
+          FAR struct plist_node *iter_ = first_;                   \
+          FAR struct plist_node *prev_ = NULL;                     \
+                                                                   \
+          do                                                       \
+            {                                                      \
+              if (node_->prio < iter_->prio)                       \
+                {                                                  \
+                  node_next_ = &iter_->node_list;                  \
+                  break;                                           \
+                }                                                  \
+                                                                   \
+                prev_ = iter_;                                     \
+                iter_ = list_entry(iter_->prio_list.next,          \
+                                   struct plist_node, prio_list);  \
+            }                                                      \
+          while (iter_ != first_);                                 \
+                                                                   \
+          if (!prev_ || prev_->prio != node_->prio)                \
+            {                                                      \
+              list_add_tail(&iter_->prio_list, &node_->prio_list); \
+            }                                                      \
+        }                                                          \
+                                                                   \
+      list_add_tail(node_next_, &node_->node_list);                \
+    }                                                              \
+  while (0)
+
+/* plist_del - Remove a node from plist.
+ * node: struct plist_node pointer - entry to be removed
+ * head: struct plist_head pointer - list head
+ */
+
+#define plist_del(node, head)                                          \
+  do                                                                   \
+    {                                                                  \
+      FAR struct plist_head *head_ = (head);                           \
+      FAR struct plist_node *node_ = (node);                           \
+                                                                       \
+      if (!list_is_empty(&node_->prio_list))                           \
+        {                                                              \
+          if (node_->node_list.next != &head_->node_list)              \
+            {                                                          \
+              FAR struct plist_node *next_ =                           \
+                list_entry(node_->node_list.next,                      \
+                           struct plist_node, node_list);              \
+                                                                       \
+              /* Add the next plist_node into prio_list */             \
+                                                                       \
+              if (list_is_empty(&next_->prio_list))                    \
+                {                                                      \
+                  list_add_head(&node_->prio_list, &next_->prio_list); \
+                }                                                      \
+            }                                                          \
+                                                                       \
+          list_delete_init(&node_->prio_list);                         \
+        }                                                              \
+                                                                       \
+      list_delete_init(&node_->node_list);                             \
+    }                                                                  \
+  while (0)
+
+/* plist_requeue - Requeue node at end of same-prio entries.
+ *
+ * This is essentially an optimized plist_del() followed by
+ * plist_add().  It moves an entry already in the plist to
+ * after any other same-priority entries.
+ * node: struct plist_node pointer - entry to be moved
+ * head: struct plist_head pointer - list head
+ */
+
+#define plist_requeue(node, head)                                   \
+  do                                                                \
+    {                                                               \
+      FAR struct plist_head *head_ = (head);                        \
+      FAR struct plist_node *node_ = (node);                        \
+                                                                    \
+      DEBUGASSERT(!plist_head_empty(head_));                        \
+      DEBUGASSERT(!plist_node_empty(node_));                        \
+                                                                    \
+      if (node_ != plist_last(head_))                               \
+        {                                                           \
+          FAR struct plist_node *iter_ = plist_next(node_);         \
+                                                                    \
+          if (node_->prio == iter_->prio)                           \
+            {                                                       \
+              FAR struct list_node *node_next_ = &head_->node_list; \
+                                                                    \
+              plist_del(node_, head_);                              \
+                                                                    \
+              plist_for_each_continue(iter_, head_)                 \
+                {                                                   \
+                  if (node_->prio != iter_->prio)                   \
+                    {                                               \
+                      node_next_ = &iter_->node_list;               \
+                      break;                                        \
+                    }                                               \
+                }                                                   \
+                                                                    \
+              list_add_tail(node_next_, &node_->node_list);         \
+            }                                                       \
+        }                                                           \
+    }                                                               \
+  while (0)
+
+#endif /* __INCLUDE_NUTTX_PLIST_H */

Reply via email to