diff --git a/drivers/power/pm/Kconfig b/drivers/power/pm/Kconfig index c0b0c954cc2..58c65f7a711 100644 --- a/drivers/power/pm/Kconfig +++ b/drivers/power/pm/Kconfig @@ -29,6 +29,16 @@ config PM_PROCFS ---help--- Enable procfs for pm. +config PM_RUNTIME + bool "PM runtime support" + default n + ---help--- + Enable PM runtime that can suspend/resume device by driver + when system is running. If the device is not used, you can use + PM rutime interface to suspend the device. When the device is + needed again, the driver can call the framework to wake up + the device. + config PM_GOVERNOR_GREEDY bool "Greedy governor" ---help--- diff --git a/drivers/power/pm/Make.defs b/drivers/power/pm/Make.defs index 853046d78a7..645bb7a0bbd 100644 --- a/drivers/power/pm/Make.defs +++ b/drivers/power/pm/Make.defs @@ -31,6 +31,12 @@ CSRCS += pm_procfs.c endif +ifeq ($(CONFIG_PM_RUNTIME),y) + +CSRCS += pm_runtime.c + +endif + # Governor implementations ifeq ($(CONFIG_PM_GOVERNOR_ACTIVITY),y) diff --git a/drivers/power/pm/pm_runtime.c b/drivers/power/pm/pm_runtime.c new file mode 100644 index 00000000000..223e9b40f04 --- /dev/null +++ b/drivers/power/pm/pm_runtime.c @@ -0,0 +1,326 @@ +/**************************************************************************** + * drivers/power/pm/pm_runtime.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 + +#include "pm.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rpm_changestate(FAR struct pm_runtime_s *rpm, rpm_state_e state); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int rpm_suspend(FAR struct pm_runtime_s *rpm) +{ + int ret = 0; + + if (rpm->ops && rpm->ops->runtime_suspend) + { + ret = rpm->ops->runtime_suspend(rpm); + } + + return ret; +} + +static int rpm_resume(FAR struct pm_runtime_s *rpm) +{ + int ret = 0; + + if (rpm->ops && rpm->ops->runtime_resume) + { + ret = rpm->ops->runtime_resume(rpm); + } + + return ret; +} + +static void rpm_autosuspend_cb(FAR void *arg) +{ + FAR struct pm_runtime_s *rpm = arg; + irqstate_t flags; + + flags = pm_lock(&rpm->lock); + + if (rpm->state != RPM_SUSPENDING || !work_available(&rpm->suspend_work)) + { + goto out; + } + + if (rpm_changestate(rpm, RPM_SUSPENDED) < 0) + { + pwrerr("%p runtime suspend failed\n", rpm); + rpm->state = RPM_ACTIVE; + } + else + { + rpm->state = RPM_SUSPENDED; + } + +out: + pm_unlock(&rpm->lock, flags); +} + +static int rpm_changestate(FAR struct pm_runtime_s *rpm, rpm_state_e state) +{ + int ret = 0; + + switch (rpm->state) + { + case RPM_ACTIVE: + if (state == RPM_SUSPENDED) + { + ret = rpm_suspend(rpm); + } + else if (state == RPM_SUSPENDING) + { + ret = work_queue(HPWORK, &rpm->suspend_work, + rpm_autosuspend_cb, rpm, + MSEC2TICK(rpm->suspend_delay)); + } + break; + + case RPM_SUSPENDED: + if (state == RPM_ACTIVE) + { + ret = rpm_resume(rpm); + } + break; + + case RPM_SUSPENDING: + if (state == RPM_ACTIVE) + { + work_cancel(HPWORK, &rpm->suspend_work); + } + else if (state == RPM_SUSPENDED) + { + ret = rpm_suspend(rpm); + } + break; + + default: + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pm_runtime_init + * + * Description: + * init struct pm_runtime_s members + * + * Input Parameters: + * rpm - the struct pm_runtime_s addr + * state - the init state of the rpm + * ops - the struct pm_runtime_ops_s addr + * + * Returned Value: + * None + ****************************************************************************/ + +void pm_runtime_init(FAR struct pm_runtime_s *rpm, rpm_state_e state, + FAR struct pm_runtime_ops_s *ops) +{ + DEBUGASSERT(rpm != NULL && ops != NULL); + DEBUGASSERT(state == RPM_ACTIVE || state == RPM_SUSPENDED); + nxrmutex_init(&rpm->lock); + rpm->use_count = 0; + rpm->suspend_delay = 0; + rpm->state = state; + rpm->ops = ops; +} + +/**************************************************************************** + * Name: pm_runtime_get + * + * Description: + * add the rpm use_count, if the first time resume + * + * Input Parameters: + * rpm - the struct pm_runtime_s addr + * + * Returned Value: + * Zero on success or a negated errno value on failure + ****************************************************************************/ + +int pm_runtime_get(FAR struct pm_runtime_s *rpm) +{ + irqstate_t flags; + int ret = 0; + + DEBUGASSERT(rpm != NULL); + flags = pm_lock(&rpm->lock); + + if (rpm->use_count++ > 0) + { + DEBUGASSERT(rpm->state == RPM_ACTIVE); + goto out; + } + + ret = rpm_changestate(rpm, RPM_ACTIVE); + if (ret < 0) + { + rpm->use_count--; + goto out; + } + + rpm->state = RPM_ACTIVE; +out: + pm_unlock(&rpm->lock, flags); + return ret; +} + +/**************************************************************************** + * Name: pm_runtime_put + * + * Description: + * drop the rpm use_count, if refcnt is zero suspend + * + * Input Parameters: + * rpm - the struct pm_runtime_s addr + * + * Returned Value: + * Zero on success or a negated errno value on failure + ****************************************************************************/ + +int pm_runtime_put(FAR struct pm_runtime_s *rpm) +{ + irqstate_t flags; + int ret = 0; + + DEBUGASSERT(rpm != NULL); + flags = pm_lock(&rpm->lock); + if (rpm->use_count == 0) + { + ret = -EPERM; + goto out; + } + + DEBUGASSERT(rpm->state == RPM_ACTIVE); + if (--rpm->use_count > 0) + { + goto out; + } + + ret = rpm_changestate(rpm, RPM_SUSPENDED); + if (ret < 0) + { + rpm->use_count++; + goto out; + } + + rpm->state = RPM_SUSPENDED; +out: + pm_unlock(&rpm->lock, flags); + return ret; +} + +/**************************************************************************** + * Name: pm_runtime_put_autosuspend + * + * Description: + * drop the rpm use_count, if refcnt is zero suspend or suspend + * (depends suspend_delay) after a delay duration + * + * Input Parameters: + * rpm - the struct pm_runtime_s addr + * + * Returned Value: + * Zero on success or a negated errno value on failure + ****************************************************************************/ + +int pm_runtime_put_autosuspend(FAR struct pm_runtime_s *rpm) +{ + irqstate_t flags; + int ret = 0; + + DEBUGASSERT(rpm != NULL); + flags = pm_lock(&rpm->lock); + if (rpm->use_count == 0) + { + ret = -EPERM; + goto out; + } + + DEBUGASSERT(rpm->state == RPM_ACTIVE); + if (--rpm->use_count > 0) + { + goto out; + } + + ret = rpm_changestate(rpm, RPM_SUSPENDING); + if (ret < 0) + { + rpm->use_count++; + goto out; + } + + rpm->state = RPM_SUSPENDING; +out: + pm_unlock(&rpm->lock, flags); + return ret; +} + +/**************************************************************************** + * Name: pm_runtime_set_autosuspend_delay + * + * Description: + * set rpm autosuspend_delay + * + * Input Parameters: + * rpm - the struct pm_runtime_s addr + * delay - the delay in milliseconds + * + * Returned Value: + * None + ****************************************************************************/ + +void pm_runtime_set_autosuspend_delay(FAR struct pm_runtime_s *rpm, + unsigned int delay) +{ + irqstate_t flags; + + DEBUGASSERT(rpm != NULL); + flags = pm_lock(&rpm->lock); + rpm->suspend_delay = delay; + pm_unlock(&rpm->lock, flags); +} diff --git a/include/nuttx/power/pm_runtime.h b/include/nuttx/power/pm_runtime.h new file mode 100644 index 00000000000..cbbc9c38cd5 --- /dev/null +++ b/include/nuttx/power/pm_runtime.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * include/nuttx/power/pm_runtime.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_POWER_PM_RUNTIME_H +#define __INCLUDE_NUTTX_POWER_PM_RUNTIME_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#ifdef CONFIG_PM_RUNTIME + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + RPM_ACTIVE = 0, + RPM_SUSPENDED, + RPM_SUSPENDING, +} rpm_state_e; + +struct pm_runtime_ops_s; +struct pm_runtime_s +{ + rmutex_t lock; + unsigned int use_count; + rpm_state_e state; + unsigned int suspend_delay; + struct work_s suspend_work; + FAR const struct pm_runtime_ops_s *ops; +}; + +struct pm_runtime_ops_s +{ + CODE int (*runtime_suspend)(FAR struct pm_runtime_s *rpm); + CODE int (*runtime_resume)(FAR struct pm_runtime_s *rpm); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void pm_runtime_init(FAR struct pm_runtime_s *rpm, rpm_state_e state, + FAR struct pm_runtime_ops_s *rops); +int pm_runtime_get(FAR struct pm_runtime_s *rpm); +int pm_runtime_put(FAR struct pm_runtime_s *rpm); +int pm_runtime_put_autosuspend(FAR struct pm_runtime_s *rpm); +void pm_runtime_set_autosuspend_delay(FAR struct pm_runtime_s *rpm, + unsigned int delay); +#endif /* CONFIG_PM_RUNTIME */ +#endif /* __INCLUDE_NUTTX_POWER_PM_RUNTIME_H */