mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-26 06:06:07 +00:00
The existing device_set_power_state() API works only in synchronous mode and this is not desirable for devices(ex: Gyro) which take longer time (few 100 mSec) to suspend/resume. To support async mode, a new callback argument is added to the API. The device drivers can asynchronously suspend/resume and call the callback function upon completion of the async request. This commit adds the missing callback parameter to all the drivers to make it compliant with the new API. Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
266 lines
5.5 KiB
C
266 lines
5.5 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <device.h>
|
|
#include <init.h>
|
|
#include <drivers/ioapic.h>
|
|
#include <counter.h>
|
|
#include <power.h>
|
|
#include <soc.h>
|
|
#include <misc/util.h>
|
|
|
|
#include "qm_aon_counters.h"
|
|
#include "qm_isr.h"
|
|
|
|
|
|
static void aonpt_int_callback(void *user_data);
|
|
|
|
static counter_top_callback_t user_cb;
|
|
|
|
struct aonpt_config {
|
|
struct counter_config_info info;
|
|
};
|
|
|
|
struct aon_data {
|
|
#ifdef CONFIG_AON_API_REENTRANCY
|
|
struct k_sem sem;
|
|
#endif
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
u32_t device_power_state;
|
|
#endif
|
|
};
|
|
|
|
#define AONPT_HAS_CONTEXT_DATA \
|
|
(CONFIG_AON_API_REENTRANCY || CONFIG_DEVICE_POWER_MANAGEMENT)
|
|
|
|
#if AONPT_HAS_CONTEXT_DATA
|
|
static struct aon_data aonpt_context;
|
|
#define AONPT_CONTEXT (&aonpt_context)
|
|
#else
|
|
#define AONPT_CONTEXT (NULL)
|
|
#endif
|
|
|
|
#ifdef CONFIG_AON_API_REENTRANCY
|
|
#define RP_GET(dev) (&((struct aon_data *)(dev->driver_data))->sem)
|
|
#else
|
|
#define RP_GET(dev) (NULL)
|
|
#endif
|
|
|
|
static int aon_timer_qmsi_start(struct device *dev)
|
|
{
|
|
qm_aonpt_config_t qmsi_cfg;
|
|
int result = 0;
|
|
|
|
user_cb = NULL;
|
|
|
|
qmsi_cfg.callback = NULL;
|
|
qmsi_cfg.int_en = false;
|
|
/* AONPT is a countdown timer. So, set the initial value to
|
|
* the maximum value.
|
|
*/
|
|
qmsi_cfg.count = 0xffffffff;
|
|
qmsi_cfg.callback_data = NULL;
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_take(RP_GET(dev), K_FOREVER);
|
|
}
|
|
|
|
if (qm_aonpt_set_config(QM_AONC_0, &qmsi_cfg)) {
|
|
result = -EIO;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_give(RP_GET(dev));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int aon_timer_qmsi_stop(struct device *dev)
|
|
{
|
|
qm_aonpt_config_t qmsi_cfg;
|
|
|
|
qmsi_cfg.callback = NULL;
|
|
qmsi_cfg.int_en = false;
|
|
qmsi_cfg.count = 0;
|
|
qmsi_cfg.callback_data = NULL;
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_take(RP_GET(dev), K_FOREVER);
|
|
}
|
|
|
|
qm_aonpt_set_config(QM_AONC_0, &qmsi_cfg);
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_give(RP_GET(dev));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u32_t aon_timer_qmsi_read(struct device *dev)
|
|
{
|
|
u32_t value;
|
|
|
|
qm_aonpt_get_value(QM_AONC_0, (uint32_t *)&value);
|
|
|
|
return value;
|
|
}
|
|
|
|
static int aon_timer_qmsi_set_top(struct device *dev,
|
|
u32_t ticks,
|
|
counter_top_callback_t callback,
|
|
void *user_data)
|
|
{
|
|
qm_aonpt_config_t qmsi_cfg;
|
|
int result = 0;
|
|
|
|
user_cb = callback;
|
|
|
|
qmsi_cfg.callback = aonpt_int_callback;
|
|
qmsi_cfg.int_en = true;
|
|
qmsi_cfg.count = ticks;
|
|
qmsi_cfg.callback_data = user_data;
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_take(RP_GET(dev), K_FOREVER);
|
|
}
|
|
|
|
if (qm_aonpt_set_config(QM_AONC_0, &qmsi_cfg)) {
|
|
user_cb = NULL;
|
|
result = -EIO;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_give(RP_GET(dev));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static u32_t aon_timer_qmsi_get_pending_int(struct device *dev)
|
|
{
|
|
return QM_AONC[QM_AONC_0]->aonpt_stat;
|
|
}
|
|
|
|
static const struct counter_driver_api aon_timer_qmsi_api = {
|
|
.start = aon_timer_qmsi_start,
|
|
.stop = aon_timer_qmsi_stop,
|
|
.read = aon_timer_qmsi_read,
|
|
.set_top_value = aon_timer_qmsi_set_top,
|
|
.get_pending_int = aon_timer_qmsi_get_pending_int,
|
|
};
|
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
static qm_aonc_context_t aonc_ctx;
|
|
|
|
static void aonpt_qmsi_set_power_state(struct device *dev, u32_t power_state)
|
|
{
|
|
struct aon_data *context = dev->driver_data;
|
|
|
|
context->device_power_state = power_state;
|
|
}
|
|
|
|
static u32_t aonpt_qmsi_get_power_state(struct device *dev)
|
|
{
|
|
struct aon_data *context = dev->driver_data;
|
|
|
|
return context->device_power_state;
|
|
}
|
|
|
|
static int aonpt_suspend_device(struct device *dev)
|
|
{
|
|
qm_aonpt_save_context(QM_AONC_0, &aonc_ctx);
|
|
|
|
aonpt_qmsi_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aonpt_resume_device_from_suspend(struct device *dev)
|
|
{
|
|
qm_aonpt_restore_context(QM_AONC_0, &aonc_ctx);
|
|
|
|
aonpt_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Implements the driver control management functionality
|
|
* the *context may include IN data or/and OUT data
|
|
*/
|
|
static int aonpt_qmsi_device_ctrl(struct device *dev, u32_t ctrl_command,
|
|
void *context, device_pm_cb cb, void *arg)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
|
|
if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
|
|
ret = aonpt_suspend_device(dev);
|
|
} else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
|
|
ret = aonpt_resume_device_from_suspend(dev);
|
|
}
|
|
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
|
|
*((u32_t *)context) = aonpt_qmsi_get_power_state(dev);
|
|
}
|
|
|
|
if (cb) {
|
|
cb(dev, ret, context, arg);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define aonpt_qmsi_set_power_state(...)
|
|
#endif
|
|
|
|
static int aon_timer_init(struct device *dev)
|
|
{
|
|
dev->driver_api = &aon_timer_qmsi_api;
|
|
|
|
user_cb = NULL;
|
|
|
|
IRQ_CONNECT(IRQ_GET_NUMBER(QM_IRQ_AONPT_0_INT),
|
|
CONFIG_AON_TIMER_IRQ_PRI, qm_aonpt_0_isr, NULL,
|
|
IOAPIC_EDGE | IOAPIC_HIGH);
|
|
|
|
irq_enable(IRQ_GET_NUMBER(QM_IRQ_AONPT_0_INT));
|
|
|
|
QM_IR_UNMASK_INTERRUPTS(QM_INTERRUPT_ROUTER->aonpt_0_int_mask);
|
|
|
|
if (IS_ENABLED(CONFIG_AON_API_REENTRANCY)) {
|
|
k_sem_init(RP_GET(dev), 1, UINT_MAX);
|
|
}
|
|
|
|
aonpt_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct aonpt_config aonpt_conf_info = {
|
|
.info = {
|
|
.max_top_value = UINT32_MAX,
|
|
.freq = 32768,
|
|
.count_up = false,
|
|
.channels = 0,
|
|
}
|
|
};
|
|
|
|
DEVICE_DEFINE(aon_timer, CONFIG_COUNTER_0_NAME, aon_timer_init,
|
|
aonpt_qmsi_device_ctrl, AONPT_CONTEXT, &aonpt_conf_info,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
&aon_timer_qmsi_api);
|
|
|
|
static void aonpt_int_callback(void *user_data)
|
|
{
|
|
if (user_cb) {
|
|
(*user_cb)(DEVICE_GET(aon_timer), user_data);
|
|
}
|
|
}
|