From e29a7bcb077be8584a82b4ab6e6846eadfd9ffbd Mon Sep 17 00:00:00 2001 From: guanyi Date: Wed, 2 Apr 2025 14:19:24 +0800 Subject: [PATCH] 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 --- 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(+) create mode 100644 drivers/devfreq/CMakeLists.txt create mode 100644 drivers/devfreq/Kconfig create mode 100644 drivers/devfreq/Make.defs create mode 100644 drivers/devfreq/devfreq.c create mode 100644 drivers/devfreq/devfreq_performance.c create mode 100644 drivers/devfreq/devfreq_powersave.c create mode 100644 drivers/devfreq/devfreq_qos.c create mode 100644 include/nuttx/devfreq.h create mode 100644 include/nuttx/devfreq/devfreq_qos.h create mode 100644 include/nuttx/plist.h 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 + +#include +#include +#include + +/**************************************************************************** + * 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 + +#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 + +#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 +#include +#include + +/**************************************************************************** + * 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 +#include +#include +#include + +/**************************************************************************** + * 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 + +/**************************************************************************** + * 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 + +#include +#include + +/**************************************************************************** + * 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 */