/**************************************************************************** * drivers/modem/altair/altmdm_pm.c * * Copyright 2018 Sony Semiconductor Solutions Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of Sony Semiconductor Solutions Corporation nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include "altmdm_sys.h" #include "altmdm_pm.h" #include "altmdm_pm_state.h" #if defined(CONFIG_MODEM_ALTMDM) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ # define EVENT_BIT(b) (1<<(b)) # define EVENT_NONE (0) # define EVENT_D2H_DOWN EVENT_BIT(0) # define EVENT_D2H_UP EVENT_BIT(1) # define EVENT_MODEM_SLEPT_NOTIF EVENT_BIT(2) # define EVENT_MODEM_WAKEUP_NOTIF EVENT_BIT(3) # define EVENT_MODEM_GOING_TO_SLEEP_NOTIF EVENT_BIT(4) # define EVENT_MODEM_WAKEUP_REQ EVENT_BIT(5) # define EVENT_MODEM_SLEEP_REQ EVENT_BIT(6) # define EVENT_EXIT EVENT_BIT(7) # define EVENT_MODEM_RESET_NOTIF EVENT_BIT(8) # define EVENT_MODEM_POWERON_REQ EVENT_BIT(9) # define EVENT_MODEM_POWEROFF_REQ EVENT_BIT(10) # define EVENT_PM_TASK_WAIT (EVENT_D2H_DOWN | EVENT_D2H_UP | \ EVENT_MODEM_WAKEUP_REQ | \ EVENT_MODEM_RESET_NOTIF | \ EVENT_MODEM_POWERON_REQ | \ EVENT_MODEM_POWEROFF_REQ | \ EVENT_MODEM_SLEEP_REQ | EVENT_EXIT) # define EVENT_STATE_CHG_WAIT (EVENT_MODEM_SLEPT_NOTIF | \ EVENT_MODEM_GOING_TO_SLEEP_NOTIF | \ EVENT_MODEM_WAKEUP_NOTIF) # define EVENT_MODEM_SLEEP_REQ_DONE_OK EVENT_BIT(0) # define EVENT_MODEM_SLEEP_REQ_DONE_NG EVENT_BIT(1) # define EVENT_MODEM_SLEEP_REQ_DONE (EVENT_MODEM_SLEEP_REQ_DONE_OK | \ EVENT_MODEM_SLEEP_REQ_DONE_NG) # define EVENT_MODEM_WAKEUP_REQ_DONE_OK EVENT_BIT(0) # define EVENT_MODEM_WAKEUP_REQ_DONE_NG EVENT_BIT(1) # define EVENT_MODEM_WAKEUP_REQ_DONE (EVENT_MODEM_WAKEUP_REQ_DONE_OK | \ EVENT_MODEM_WAKEUP_REQ_DONE_NG) # define EVENT_MODEM_POWER_ON_DONE EVENT_BIT(0) # define EVENT_MODEM_POWER_OFF_DONE EVENT_BIT(1) /* Timeout is counted in units of millisecond. */ # define D2H_UP_EVENT_TIMEOUT (5*1000) # define ST_TRANS_EVENT_TIMEOUT (ALTMDM_SYS_FLAG_TMOFEVR) # define PM_TASK_PRI (90) # define PM_TASK_NAME "altmdm_pm_task" # define PM_TASK_STKSIZE (1024) # define GPIO_LOW (false) # define GPIO_HIGH (true) /**************************************************************************** * Private Data ****************************************************************************/ static struct altmdm_dev_s *g_privdata = NULL; static struct altmdm_sys_flag_s g_statetrans_flag; static struct altmdm_sys_flag_s g_pmtask_flag; static struct altmdm_sys_flag_s g_wakeup_done_flag; static struct altmdm_sys_flag_s g_sleep_done_flag; static struct altmdm_sys_flag_s g_power_done_flag; static bool g_is_initdone = false; static bool g_is_waitnotif = false; static bool g_is_notrun; static int g_taskid; static altmdm_pm_cbfunc_t g_pm_callback = NULL; static altmdm_pm_cbfunc_t g_pm_errcallback = NULL; static sq_queue_t g_wakelock; static uint32_t g_boot_stat = MODEM_PM_ERR_RESET_BOOTSTAT_NONE; /**************************************************************************** * Name: init_h2d_gpio * * Description: * Initialize Host to Device GPIO line. * ****************************************************************************/ static int init_h2d_gpio(FAR struct altmdm_dev_s *priv) { return 0; } /**************************************************************************** * Name: uninit_h2d_gpio * * Description: * Uninitialize Host to Device GPIO line. * ****************************************************************************/ static int uninit_h2d_gpio(FAR struct altmdm_dev_s *priv) { return 0; } /**************************************************************************** * Name: init_d2h_gpio * * Description: * Initialize Device to Host GPIO line. * ****************************************************************************/ static int init_d2h_gpio(FAR struct altmdm_dev_s *priv) { return 0; } /**************************************************************************** * Name: uninit_d2h_gpio * * Description: * Uninitialize Device to Host GPIO line. * ****************************************************************************/ static int uninit_d2h_gpio(FAR struct altmdm_dev_s *priv) { return 0; } # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 /**************************************************************************** * Name: set_mreq_gpio * * Description: * Control Master-request GPIO line. * ****************************************************************************/ static int set_mreq_gpio(FAR struct altmdm_dev_s *priv, bool value) { return 0; } # endif /**************************************************************************** * Name: set_h2d_gpio * * Description: * Control Host to Device GPIO line. * ****************************************************************************/ static int set_h2d_gpio(FAR struct altmdm_dev_s *priv, bool value) { return 0; } /**************************************************************************** * Name: get_d2h_gpio * * Description: * Get current value of Device to Host GPIO line. * ****************************************************************************/ static int get_d2h_gpio(FAR struct altmdm_dev_s *priv, FAR bool * value) { return 0; } # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 /**************************************************************************** * Name: set_mreq_down * * Description: * Deassert Master-request GPIO line. * ****************************************************************************/ static int set_mreq_down(FAR struct altmdm_dev_s *priv) { return set_mreq_gpio(priv, GPIO_LOW); } /**************************************************************************** * Name: set_mreq_up * * Description: * Assert Master-request GPIO line. * ****************************************************************************/ static int set_mreq_up(FAR struct altmdm_dev_s *priv) { return set_mreq_gpio(priv, GPIO_HIGH); } # endif /**************************************************************************** * Name: set_h2d_down * * Description: * Deassert Host to Device GPIO line. * ****************************************************************************/ static int set_h2d_down(FAR struct altmdm_dev_s *priv) { return set_h2d_gpio(priv, GPIO_LOW); } /**************************************************************************** * Name: set_h2d_up * * Description: * Assert Host to Device GPIO line. * ****************************************************************************/ static int set_h2d_up(FAR struct altmdm_dev_s *priv) { return set_h2d_gpio(priv, GPIO_HIGH); } /**************************************************************************** * Name: send_d2h_down_notif * * Description: * Notify that Device to Host GPIO line has been deassertted. * ****************************************************************************/ static int send_d2h_down_notif(FAR struct altmdm_dev_s *priv) { int ret = 0; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_D2H_DOWN); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_d2h_up_notif * * Description: * Notify that Device to Host GPIO line has been assertted. * ****************************************************************************/ static int send_d2h_up_notif(FAR struct altmdm_dev_s *priv) { int ret = 0; uint32_t pm_state; pm_state = altmdm_pm_getinternalstate(); if ((pm_state == MODEM_PM_INTERNAL_STATE_SLEEP) || (pm_state == MODEM_PM_INTERNAL_STATE_GOING_TO_WAKE)) { ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_D2H_UP); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } } return ret; } /**************************************************************************** * Name: send_modem_slept_notif * * Description: * Notify that the modem slept. * ****************************************************************************/ static int send_modem_slept_notif(FAR struct altmdm_dev_s *priv) { int ret = 0; if (g_is_waitnotif) { /* Send only when waiting for notification. */ ret = altmdm_sys_setflag(&g_statetrans_flag, EVENT_MODEM_SLEPT_NOTIF); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } } return ret; } /**************************************************************************** * Name: send_modem_wakeup_notif * * Description: * Notify that the modem has woken up. * ****************************************************************************/ static int send_modem_wakeup_notif(FAR struct altmdm_dev_s *priv) { int ret = 0; if (g_is_waitnotif) { /* Send only when waiting for notification. */ ret = altmdm_sys_setflag(&g_statetrans_flag, EVENT_MODEM_WAKEUP_NOTIF); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } } # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 else { altmdm_spi_setreceiverready(priv); } # endif return ret; } /**************************************************************************** * Name: send_modem_reset_notif * * Description: * Notify that the modem is reset. * ****************************************************************************/ static int send_modem_reset_notif(FAR struct altmdm_dev_s *priv) { int ret = 0; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_MODEM_RESET_NOTIF); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_wakeup_request * * Description: * Send request to wake up the modem. * ****************************************************************************/ static int send_modem_wakeup_request(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_MODEM_WAKEUP_REQ); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_sleep_request * * Description: * Send request to sleep the modem. * ****************************************************************************/ static int send_modem_sleep_request(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_MODEM_SLEEP_REQ); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_exit_request * * Description: * Send request to terminate the power management task. * ****************************************************************************/ static int send_exit_request(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_EXIT); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_poweron_request * * Description: * Send request to power on the modem. * ****************************************************************************/ static int send_modem_poweron_request(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_MODEM_POWERON_REQ); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_poweroff_request * * Description: * Send request to power off the modem. * ****************************************************************************/ static int send_modem_poweroff_request(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_pmtask_flag, EVENT_MODEM_POWEROFF_REQ); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_wakeup_done * * Description: * Notify that the wake up of the modem is complete. * ****************************************************************************/ static int send_modem_wakeup_done(FAR struct altmdm_dev_s *priv, int result) { int ret; uint32_t ptn; if (result == 0) { ptn = EVENT_MODEM_WAKEUP_REQ_DONE_OK; } else { ptn = EVENT_MODEM_WAKEUP_REQ_DONE_NG; } ret = altmdm_sys_setflag(&g_wakeup_done_flag, ptn); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_sleep_done * * Description: * Notify that the modem slept. * ****************************************************************************/ static int send_modem_sleep_done(FAR struct altmdm_dev_s *priv, int result) { int ret; uint32_t ptn; if (result == 0) { ptn = EVENT_MODEM_SLEEP_REQ_DONE_OK; } else { ptn = EVENT_MODEM_SLEEP_REQ_DONE_NG; } ret = altmdm_sys_setflag(&g_sleep_done_flag, ptn); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_poweron_done * * Description: * Notify that the modem power on. * ****************************************************************************/ static int send_modem_poweron_done(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_power_done_flag, EVENT_MODEM_POWER_ON_DONE); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: send_modem_poweroff_done * * Description: * Notify that the modem power off. * ****************************************************************************/ static int send_modem_poweroff_done(FAR struct altmdm_dev_s *priv) { int ret; ret = altmdm_sys_setflag(&g_power_done_flag, EVENT_MODEM_POWER_OFF_DONE); if (ret != 0) { m_err("ERR:%04d Set flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: wait_for_d2h_up_complete * * Description: * Wait until the Device to Host GPIO line is assert. * ****************************************************************************/ static int wait_for_d2h_up_complete(FAR struct altmdm_dev_s *priv) { int ret; uint32_t ptn; ret = altmdm_sys_waitflag(&g_pmtask_flag, EVENT_D2H_UP, ALTMDM_SYS_FLAG_WMODEOR, &ptn, D2H_UP_EVENT_TIMEOUT); return ret; } /**************************************************************************** * Name: wait_for_modem_state_change_notif * * Description: * Wait until the state of the modem changes. * ****************************************************************************/ static int wait_for_modem_state_change_notif(FAR struct altmdm_dev_s *priv, FAR uint32_t * state) { int ret; uint32_t ptn; g_is_waitnotif = true; ret = altmdm_sys_waitflag(&g_statetrans_flag, EVENT_STATE_CHG_WAIT, ALTMDM_SYS_FLAG_WMODEOR, &ptn, ST_TRANS_EVENT_TIMEOUT); if (ret != 0) { m_err("ERR:%04d Wait flag:%d.\n", __LINE__, ret); } else { if (ptn & EVENT_MODEM_SLEPT_NOTIF) { *state = MODEM_PM_INTERNAL_STATE_SLEEP; } else if (ptn & EVENT_MODEM_WAKEUP_NOTIF) { *state = MODEM_PM_INTERNAL_STATE_WAKE; } else if (ptn & EVENT_MODEM_GOING_TO_SLEEP_NOTIF) { *state = MODEM_PM_INTERNAL_STATE_GOING_TO_SLEEP; } else { *state = MODEM_PM_INTERNAL_STATE_MAX; } } g_is_waitnotif = false; return ret; } /**************************************************************************** * Name: wait_for_wakeup_requeset_done * * Description: * Wait until the wakeup request is completed. * ****************************************************************************/ static int wait_for_wakeup_requeset_done(FAR struct altmdm_dev_s *priv) { int ret; uint32_t ptn; ret = altmdm_sys_waitflag(&g_wakeup_done_flag, EVENT_MODEM_WAKEUP_REQ_DONE, ALTMDM_SYS_FLAG_WMODEOR, &ptn, ALTMDM_SYS_FLAG_TMOFEVR); if (ret != 0) { m_err("ERR:%04d Wait flag:%d.\n", __LINE__, ret); } else { if (ptn & EVENT_MODEM_WAKEUP_REQ_DONE_NG) { ret = -1; } } return ret; } /**************************************************************************** * Name: wait_for_sleep_requeset_done * * Description: * Wait until the sleep request is completed. * ****************************************************************************/ static int wait_for_sleep_requeset_done(FAR struct altmdm_dev_s *priv) { int ret; uint32_t ptn; ret = altmdm_sys_waitflag(&g_sleep_done_flag, EVENT_MODEM_SLEEP_REQ_DONE, ALTMDM_SYS_FLAG_WMODEOR, &ptn, ALTMDM_SYS_FLAG_TMOFEVR); if (ret != 0) { m_err("ERR:%04d Wait flag:%d.\n", __LINE__, ret); } else { if (ptn & EVENT_MODEM_SLEEP_REQ_DONE_NG) { ret = -1; } } return ret; } /**************************************************************************** * Name: wait_for_poweron_done * * Description: * Wait until the power on is completed. * ****************************************************************************/ static int wait_for_poweron_done(FAR struct altmdm_dev_s *priv) { int ret; uint32_t ptn; ret = altmdm_sys_waitflag(&g_power_done_flag, EVENT_MODEM_POWER_ON_DONE, ALTMDM_SYS_FLAG_WMODEOR, &ptn, ALTMDM_SYS_FLAG_TMOFEVR); if (ret != 0) { m_err("ERR:%04d Wait flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: wait_for_poweroff_done * * Description: * Wait until the power off is completed. * ****************************************************************************/ static int wait_for_poweroff_done(FAR struct altmdm_dev_s *priv) { int ret; uint32_t ptn; ret = altmdm_sys_waitflag(&g_power_done_flag, EVENT_MODEM_POWER_OFF_DONE, ALTMDM_SYS_FLAG_WMODEOR, &ptn, ALTMDM_SYS_FLAG_TMOFEVR); if (ret != 0) { m_err("ERR:%04d Wait flag:%d.\n", __LINE__, ret); } return ret; } /**************************************************************************** * Name: d2h_gpio_isr * * Description: * Interrupt handler for Device to Host GPIO line. * ****************************************************************************/ static void d2h_gpio_isr(FAR struct altmdm_dev_s *priv) { int ret; bool val = GPIO_LOW; ret = get_d2h_gpio(priv, &val); if (ret == 0) { switch (val) { case GPIO_LOW: ret = send_d2h_down_notif(priv); if (ret != 0) { m_err("ERR:%04d send d2h down notification:%d.\n", __LINE__, ret); } break; case GPIO_HIGH: ret = send_d2h_up_notif(priv); if (ret != 0) { m_err("ERR:%04d send device up notification:%d.\n", __LINE__, ret); } break; default: m_err("ERR:%04d Invalid D2H GPIO value:%d.\n", __LINE__, val); break; } } } /**************************************************************************** * Name: exe_callback * * Description: * Execute callback of modem power manager. * ****************************************************************************/ static int exe_callback(uint32_t type, uint32_t cb_event) { altmdm_pm_cbfunc_t lcallback = NULL; if (type == MODEM_PM_CB_TYPE_ERROR) { lcallback = g_pm_errcallback; } else { lcallback = g_pm_callback; } if (lcallback == NULL) { return -EPERM; } lcallback(cb_event); return 0; } /**************************************************************************** * Name: wakeup_modem_itself * * Description: * The modem wakes up by itself, it transitions to the wakeup state. * ****************************************************************************/ static int wakeup_modem_itself(FAR struct altmdm_dev_s *priv, bool wakeupreq) { int ret; altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_GOING_TO_WAKE); ret = set_h2d_up(priv); if (ret == 0) { /* Transitions to the wakeup state. */ altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_WAKE); /* When the EVENT_MODEM_WAKEUP_REQ is simultaneous, * there is no need to notify. */ if (!wakeupreq) { /* Perform processing at the time of state transition. */ send_modem_wakeup_notif(priv); } exe_callback(MODEM_PM_CB_TYPE_NORMAL, MODEM_PM_CB_WAKE); } return ret; } /**************************************************************************** * Name: sleep_modem_itself * * Description: * The modem slept on itself, it transitions to the sleep state. * ****************************************************************************/ static int sleep_modem_itself(FAR struct altmdm_dev_s *priv) { set_h2d_down(priv); /* Transitions to the sleep state. */ altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_SLEEP); /* Perform processing at the time of state transition. */ send_modem_slept_notif(priv); exe_callback(MODEM_PM_CB_TYPE_NORMAL, MODEM_PM_CB_SLEEP); return 0; } /**************************************************************************** * Name: modem_sleep_procedure * * Description: * Make the modem transition to sleep. * ****************************************************************************/ static int modem_sleep_procedure(FAR struct altmdm_dev_s *priv) { int ret; ret = set_h2d_down(priv); if (ret == 0) { /* Transitions to the sleep state. */ altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_SLEEP); /* Perform processing at the time of state transition. */ send_modem_slept_notif(priv); exe_callback(MODEM_PM_CB_TYPE_NORMAL, MODEM_PM_CB_SLEEP); } return ret; } /**************************************************************************** * Name: modem_wakeup_procedure * * Description: * Make the modem transition to wakeup. * ****************************************************************************/ static int modem_wakeup_procedure(FAR struct altmdm_dev_s *priv) { int ret = 0; altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_GOING_TO_WAKE); ret = set_h2d_up(priv); if (ret == 0) { # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 set_mreq_up(priv); # endif ret = wait_for_d2h_up_complete(priv); if (ret != 0) { m_err("ERR:%04d device is not up.\n", __LINE__); /* Do rollback. */ # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 set_mreq_down(priv); # endif set_h2d_down(priv); altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_SLEEP); } else { /* Transitions to the wake state. */ altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_WAKE); /* Perform processing at the time of state transition. */ exe_callback(MODEM_PM_CB_TYPE_NORMAL, MODEM_PM_CB_WAKE); } } return ret; } /**************************************************************************** * Name: sleep_is_possible * * Description: * Check if the modem can sleep. * ****************************************************************************/ static int sleep_is_possible(FAR struct altmdm_dev_s *priv) { # ifdef CONFIG_MODEM_ALTMDM_KEEP_WAKE_STATE return -EPERM; # else int num_of_lock; uint32_t pm_state; pm_state = altmdm_pm_getinternalstate(); if (pm_state != MODEM_PM_INTERNAL_STATE_WAKE) { return -EBUSY; } num_of_lock = altmdm_pm_getwakelockstate(); if (num_of_lock) { return -EBUSY; } if (g_boot_stat != MODEM_PM_ERR_RESET_BOOTSTAT_DONE) { return -EBUSY; } return 0; # endif } /**************************************************************************** * Name: modem_sleep_req * * Description: * Execute the sleep request. * ****************************************************************************/ static int modem_sleep_req(FAR struct altmdm_dev_s *priv) { int ret; altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_GOING_TO_SLEEP); ret = send_modem_sleep_request(priv); if (ret == 0) { ret = wait_for_sleep_requeset_done(priv); } return ret; } /**************************************************************************** * Name: modem_wakeup_req * * Description: * Execute the wakeup request. * ****************************************************************************/ static int modem_wakeup_req(FAR struct altmdm_dev_s *priv) { int ret; ret = send_modem_wakeup_request(priv); if (ret == 0) { ret = wait_for_wakeup_requeset_done(priv); } return ret; } /**************************************************************************** * Name: pm_task * * Description: * ALTMDM power manager driver task. * ****************************************************************************/ static int pm_task(int argc, FAR char *argv[]) { int ret; uint32_t ptn; FAR struct altmdm_dev_s *priv = g_privdata; while (!g_is_notrun) { ret = altmdm_sys_waitflag(&g_pmtask_flag, EVENT_PM_TASK_WAIT, ALTMDM_SYS_FLAG_WMODEOR, &ptn, ALTMDM_SYS_FLAG_TMOFEVR); if (ret != 0) { m_err("ERR:%04d Wait Flag:%d.\n", __LINE__, ret); } else { m_info("pm_task ptn:%x.\n", ptn); if (ptn & EVENT_MODEM_WAKEUP_REQ) { if (ptn & EVENT_D2H_UP) { /* Modem already wakeup. */ ret = wakeup_modem_itself(priv, true); # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 set_mreq_up(priv); # endif } else { /* Make modem wakeup. */ ret = modem_wakeup_procedure(priv); } send_modem_wakeup_done(priv, ret); if (ptn & EVENT_MODEM_SLEEP_REQ) { /* This condition does not occur, * but it is due to fail safe. */ send_modem_sleep_done(priv, -1); } } else if (ptn & EVENT_MODEM_SLEEP_REQ) { /* Make modem sleep. */ ret = modem_sleep_procedure(priv); send_modem_sleep_done(priv, ret); } else if (ptn & EVENT_D2H_UP) { /* Modem has wake itself. */ wakeup_modem_itself(priv, false); } else if (ptn & EVENT_D2H_DOWN) { /* Modem become sleep itself. */ sleep_modem_itself(priv); } if (ptn & EVENT_MODEM_POWERON_REQ) { /* Modem power on. */ send_modem_poweron_done(priv); } if (ptn & EVENT_MODEM_POWEROFF_REQ) { /* Modem power off. */ sleep_modem_itself(priv); send_modem_poweroff_done(priv); } if (ptn & EVENT_MODEM_RESET_NOTIF) { if (MODEM_PM_ERR_RESET_BOOTSTAT_NONE != g_boot_stat) { exe_callback(MODEM_PM_CB_TYPE_ERROR, g_boot_stat); } else { m_err("ERR:%04d Unexpected boot stat:%d.\n", __LINE__, g_boot_stat); } } if (ptn & EVENT_EXIT) { g_is_notrun = true; } } } task_delete(0); return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: altmdm_pm_init * * Description: * Initialize the ALTMDM power manager driver. * ****************************************************************************/ int altmdm_pm_init(FAR struct altmdm_dev_s *priv) { if (g_is_initdone) { return -EPERM; } g_is_initdone = true; g_privdata = priv; altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_SLEEP); /* Create event flags. */ altmdm_sys_initflag(&g_statetrans_flag); altmdm_sys_initflag(&g_pmtask_flag); altmdm_sys_initflag(&g_wakeup_done_flag); altmdm_sys_initflag(&g_sleep_done_flag); altmdm_sys_initflag(&g_power_done_flag); sq_init(&g_wakelock); /* Initialize GPIO. */ init_h2d_gpio(priv); init_d2h_gpio(priv); /* Create Power management task. */ g_is_notrun = false; g_taskid = task_create(PM_TASK_NAME, PM_TASK_PRI, PM_TASK_STKSIZE, (main_t) pm_task, NULL); if (g_taskid == ERROR) { m_err("Failed to create pm task\n"); } return 0; } /**************************************************************************** * Name: altmdm_pm_uninit * * Description: * Uninitialize the ALTMDM power manager driver. * ****************************************************************************/ int altmdm_pm_uninit(FAR struct altmdm_dev_s *priv) { if (!g_is_initdone) { return -EPERM; } g_is_initdone = false; /* Delete Transfer task. */ send_exit_request(priv); /* Check pm task is deleted or not. */ while (1) { if (g_is_notrun) { break; } nxsig_usleep(10); } /* Delete event flags. */ altmdm_sys_deleteflag(&g_statetrans_flag); altmdm_sys_deleteflag(&g_pmtask_flag); altmdm_sys_deleteflag(&g_wakeup_done_flag); altmdm_sys_deleteflag(&g_sleep_done_flag); altmdm_sys_deleteflag(&g_power_done_flag); /* Uinitialize GPIO. */ uninit_h2d_gpio(priv); uninit_d2h_gpio(priv); altmdm_pm_setinternalstate(MODEM_PM_INTERNAL_STATE_SLEEP); sq_init(&g_wakelock); return 0; } /**************************************************************************** * Name: altmdm_pm_wakeup * * Description: * Make modem wake up. * ****************************************************************************/ int altmdm_pm_wakeup(FAR struct altmdm_dev_s *priv) { int ret; int retval = MODEM_PM_WAKEUP_FAIL; bool is_slept = false; uint32_t modem_state; if (!g_is_initdone) { return -EPERM; } modem_state = altmdm_pm_getinternalstate(); switch (modem_state) { case MODEM_PM_INTERNAL_STATE_WAKE: retval = MODEM_PM_WAKEUP_ALREADY; # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 /* Case where modem spontaneously enters the wake state and already notified by altmdm_spi_setreceiverready(). */ if (altmdm_spi_isreceiverready(priv)) { altmdm_spi_clearreceiverready(priv); retval = MODEM_PM_WAKEUP_DONE; } # endif break; case MODEM_PM_INTERNAL_STATE_SLEEP: is_slept = true; break; case MODEM_PM_INTERNAL_STATE_GOING_TO_SLEEP: case MODEM_PM_INTERNAL_STATE_GOING_TO_WAKE: /* Wait for change modem state. */ wait_for_modem_state_change_notif(priv, &modem_state); if (modem_state == MODEM_PM_INTERNAL_STATE_SLEEP) { is_slept = true; } else if (modem_state == MODEM_PM_INTERNAL_STATE_WAKE) { retval = MODEM_PM_WAKEUP_DONE; } else { m_err("ERR:%04d unexpected event occur. state:%d.\n", __LINE__, modem_state); } break; default: m_err("ERR:%04d unexpected event occur. state:%d.\n", __LINE__, modem_state); } if (is_slept) { /* Wakeup request. */ ret = modem_wakeup_req(priv); if (ret == 0) { retval = MODEM_PM_WAKEUP_DONE; } } return retval; } /**************************************************************************** * Name: altmdm_pm_notify_reset * * Description: * Notify reset has done. * ****************************************************************************/ int altmdm_pm_notify_reset(FAR struct altmdm_dev_s *priv) { if (!g_is_initdone) { return -EPERM; } send_modem_reset_notif(priv); return 0; } # ifdef CONFIG_MODEM_ALTMDM_PROTCOL_V2_1 /**************************************************************************** * Name: altmdm_pm_callgpiohandler * * Description: * Call Device to Host GPIO interrupt handler. * ****************************************************************************/ int altmdm_pm_callgpiohandler(FAR struct altmdm_dev_s *priv) { if (!g_is_initdone) { return -EPERM; } d2h_gpio_isr(priv); return 0; } # endif /**************************************************************************** * Name: altmdm_pm_registercb * * Description: * Register callback for ALTMDM power manager driver. * ****************************************************************************/ int altmdm_pm_registercb(uint32_t type, altmdm_pm_cbfunc_t cb) { if (!g_is_initdone) { return -EPERM; } if (type == MODEM_PM_CB_TYPE_ERROR) { if (g_pm_errcallback == NULL) { g_pm_errcallback = cb; } } else { if (g_pm_callback == NULL) { g_pm_callback = cb; } } return 0; } /**************************************************************************** * Name: altmdm_pm_deregistercb * * Description: * Deregister callback for ALTMDM power manager driver. * ****************************************************************************/ int altmdm_pm_deregistercb(uint32_t type) { altmdm_pm_cbfunc_t *lcallback = NULL; if (!g_is_initdone) { return -EPERM; } if (type == MODEM_PM_CB_TYPE_ERROR) { lcallback = &g_pm_errcallback; } else { lcallback = &g_pm_callback; } if (*lcallback == NULL) { return -EPERM; } *lcallback = NULL; return 0; } /**************************************************************************** * Name: altmdm_pm_sleepmodem * * Description: * Make modem sleep. * ****************************************************************************/ int altmdm_pm_sleepmodem(FAR struct altmdm_dev_s *priv) { int ret; if (!g_is_initdone) { return -EPERM; } ret = modem_sleep_req(priv); return ret; } /**************************************************************************** * Name: altmdm_pm_cansleep * * Description: * Check if modem can sleep. * ****************************************************************************/ int altmdm_pm_cansleep(FAR struct altmdm_dev_s *priv) { int ret; if (!g_is_initdone) { return -EPERM; } ret = sleep_is_possible(priv); if (ret == 0) { m_info("possible to sleep.\n"); ret = 1; } else { m_info("impossible to sleep.\n"); ret = 0; } return ret; } /**************************************************************************** * Name: altmdm_pm_initwakelock * * Description: * Initialize the modem wakelock resource. * ****************************************************************************/ int altmdm_pm_initwakelock(FAR struct altmdm_pm_wakelock_s *lock) { if (!g_is_initdone) { return -EPERM; } if (!lock) { return -EINVAL; } memset(lock, 0, sizeof(struct altmdm_pm_wakelock_s)); return 0; } /**************************************************************************** * Name: altmdm_pm_acquirewakelock * * Description: * Acquire the modem wakelock. * ****************************************************************************/ int altmdm_pm_acquirewakelock(FAR struct altmdm_pm_wakelock_s *lock) { irqstate_t flags; if (!g_is_initdone) { return -EPERM; } if (!lock) { return -EINVAL; } flags = enter_critical_section(); if (lock->count == 0) { sq_addlast((FAR sq_entry_t *) & lock->queue, &g_wakelock); } lock->count++; leave_critical_section(flags); return 0; } /**************************************************************************** * Name: altmdm_pm_releasewakelock * * Description: * Release the modem wakelock. * ****************************************************************************/ int altmdm_pm_releasewakelock(FAR struct altmdm_pm_wakelock_s *lock) { irqstate_t flags; if (!g_is_initdone) { return -EPERM; } if (!lock && (lock->count < 1)) { return -EINVAL; } flags = enter_critical_section(); lock->count--; if (lock->count == 0) { sq_rem((FAR sq_entry_t *) & lock->queue, &g_wakelock); } leave_critical_section(flags); return 0; } /**************************************************************************** * Name: altmdm_pm_getnumofwakelock * * Description: * Get the lock count of the specified wakelock. * ****************************************************************************/ int altmdm_pm_getnumofwakelock(FAR struct altmdm_pm_wakelock_s *lock) { if (!g_is_initdone) { return -EPERM; } if (!lock) { return -EINVAL; } return lock->count; } /**************************************************************************** * Name: altmdm_pm_getwakelockstate * * Description: * Get the wakelock status. If the return value is 0, it means that it is * not locked. Otherwise it means that someone is locking. * ****************************************************************************/ int altmdm_pm_getwakelockstate(void) { int num; irqstate_t flags; if (!g_is_initdone) { return -EPERM; } flags = enter_critical_section(); num = sq_count(&g_wakelock); leave_critical_section(flags); return num; } /**************************************************************************** * Name: altmdm_pm_poweron * * Description: * Modem power on. * ****************************************************************************/ int altmdm_pm_poweron(FAR struct altmdm_dev_s *priv) { int ret; if (!g_is_initdone) { return -EPERM; } ret = send_modem_poweron_request(priv); if (ret == 0) { ret = wait_for_poweron_done(priv); } return 0; } /**************************************************************************** * Name: altmdm_pm_poweroff * * Description: * Modem power off. * ****************************************************************************/ int altmdm_pm_poweroff(FAR struct altmdm_dev_s *priv) { int ret; if (!g_is_initdone) { return -EPERM; } ret = send_modem_poweroff_request(priv); if (ret == 0) { ret = wait_for_poweroff_done(priv); } return 0; } /**************************************************************************** * Name: altmdm_pm_set_bootstatus * * Description: * Set boot status. * ****************************************************************************/ int altmdm_pm_set_bootstatus(FAR struct altmdm_dev_s *priv, uint32_t status) { if (!g_is_initdone) { return -EPERM; } g_boot_stat = status; return 0; } #endif