nuttx/drivers/modem/altair/altmdm_pm.c

1772 lines
42 KiB
C
Raw Normal View History

/****************************************************************************
* 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 <nuttx/config.h>
#include <string.h>
#include <errno.h>
#include <nuttx/kmalloc.h>
#include <nuttx/modem/altmdm.h>
#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;
}
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 occurr. state:%d.\n",
__LINE__, modem_state);
}
break;
default:
m_err("ERR:%04d unexpected event occurr. 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