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 <guanyi@xiaomi.com>
This commit is contained in:
guanyi 2025-04-02 14:19:24 +08:00 committed by Alan C. Assis
parent 875e86bd35
commit e29a7bcb07
12 changed files with 2006 additions and 0 deletions

View file

@ -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"

View file

@ -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

View file

@ -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()

36
drivers/devfreq/Kconfig Normal file
View file

@ -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

30
drivers/devfreq/Make.defs Normal file
View file

@ -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

856
drivers/devfreq/devfreq.c Normal file
View file

@ -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;
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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;
}

324
include/nuttx/devfreq.h Normal file
View file

@ -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 */

View file

@ -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

346
include/nuttx/plist.h Normal file
View file

@ -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 */