| /* |
| * 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_callback_t user_cb; |
| |
| 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, &value); |
| |
| return value; |
| } |
| |
| static int aon_timer_qmsi_set_alarm(struct device *dev, |
| counter_callback_t callback, |
| u32_t count, void *user_data) |
| { |
| qm_aonpt_config_t qmsi_cfg; |
| int result = 0; |
| |
| /* Check if timer has been started */ |
| if (QM_AONC[QM_AONC_0]->aonpt_cfg == 0) { |
| return -ENOTSUP; |
| } |
| |
| user_cb = callback; |
| |
| qmsi_cfg.callback = aonpt_int_callback; |
| qmsi_cfg.int_en = true; |
| qmsi_cfg.count = count; |
| 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_alarm = aon_timer_qmsi_set_alarm, |
| .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) |
| { |
| if (ctrl_command == DEVICE_PM_SET_POWER_STATE) { |
| if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) { |
| return aonpt_suspend_device(dev); |
| } else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) { |
| return aonpt_resume_device_from_suspend(dev); |
| } |
| } else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) { |
| *((u32_t *)context) = aonpt_qmsi_get_power_state(dev); |
| return 0; |
| } |
| |
| return 0; |
| } |
| #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; |
| } |
| |
| |
| DEVICE_DEFINE(aon_timer, CONFIG_AON_TIMER_QMSI_DEV_NAME, aon_timer_init, |
| aonpt_qmsi_device_ctrl, AONPT_CONTEXT, NULL, 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); |
| } |
| } |