Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Commit

Permalink
UART power saving support for LEXI-R10 (#1203)
Browse files Browse the repository at this point in the history
The UART power saving/32 kHz sleep is updated for LEXI-R10. Mode-1 of UART power saving is supported for LEXI-R10 in which the PWR_ON pin must be toggled to get the module out of sleep.  The deep sleep limit can be adjusted by setting the `U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10` to the desired value from `uCellPwrSleepModeR10_t`.
  • Loading branch information
SaqibAkram-Doit authored Oct 3, 2024
1 parent 06de0e5 commit 4561c45
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 131 deletions.
26 changes: 17 additions & 9 deletions cell/api/u_cell_pwr.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,20 @@
*
* The sleep states are as follows:
*
* "UART sleep"/"32 kHz sleep": in this sleep state the speed of the
* module's clocks are reduced to save a lot of power. Because of
* "UART sleep"/"32 kHz sleep"/"sleep-1": in this sleep state the speed
* of the module's clocks are reduced to save a lot of power. Because of
* these reduced clock rates the module is not able to drive the
* UART HW, hence this is often termed "UART sleep". However, all
* of the module's RAM is still on, state is fully retained, the module
* is still actually running, is still connected to the network, and
* it can be woken-up quickly by toggling lines of the UART AT interface.
* it can be woken-up quickly by toggling lines of the UART AT interface,
* except for LEXI-R10 where a configured GPIO line or PWR_ON must be toggled.
*
* "deep sleep": in this sleep state the module is basically off,
* almost all state is lost, what is retained is only a basic notion
* of time and whether the module was attached to the cellular
* network when deep sleep began. The IP stack on the module, the
* MQTT client on the module, etc, are all reset by deep sleep.
* "deep sleep"/"sleep-2"/"hibernate": in this sleep state the module
* is basically off, almost all state is lost, what is retained is
* only a basic notion of time and whether the module was attached to
* the cellular network when deep sleep began. The IP stack on the
* module, the MQTT client on the module, etc, are all reset by deep sleep.
*
* The ways of entering these sleep states are as follows:
*
Expand All @@ -85,7 +86,7 @@
* go into 32 kHz sleep during the E-DRX sleep periods the application
* never has to worry about state being lost.
*
* "3GPP power saving made (PSM)": also a 3GPP-defined mechanism, this
* "3GPP power saving mode (PSM)": also a 3GPP-defined mechanism, this
* forms an agreement with the network that the module will be out of
* contact for long periods (think hours or days). The functions below
* with "3gppPowerSaving" in the name allow you to initiate and manage
Expand Down Expand Up @@ -179,6 +180,13 @@ extern "C" {
# define U_CELL_PWR_UART_POWER_SAVING_DTR_HYSTERESIS_MS 20
#endif

#ifndef U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10
/** The deep sleep limit of UART power saving that can be achieved for
* LEXI-R10 (see uCellPwrSleepModeR10_t).
*/
# define U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10 U_CELL_PWR_SLEEP_MODE_R10_1
#endif

/* ----------------------------------------------------------------
* TYPES
* -------------------------------------------------------------- */
Expand Down
73 changes: 17 additions & 56 deletions cell/src/u_cell_private.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,6 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = {
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_MQTT_KEEP_ALIVE) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_MQTT_SECURITY) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SECURITY_ZTP) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_DTR_POWER_SAVING) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_DEEP_SLEEP_URC) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_3GPP_POWER_SAVING) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_3GPP_POWER_SAVING_PAGING_WINDOW_SET) |
Expand All @@ -529,6 +528,7 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = {
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_FOTA) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SNR_REPORTED) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UCGED) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_HTTP) /* features */
),
Expand Down Expand Up @@ -921,6 +921,21 @@ int32_t privateActivateProfile(const uCellPrivateInstance_t *pInstance,
return errorCode;
}

// Check whether the module is LEXI-R10 and
// we have the mode 1 for UPSV sleep.
static bool uCellPrivateIsR10SleepMode1(uCellPrivateInstance_t *pInstance)
{
bool success = false;

if (pInstance != NULL) {
if ((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) &&
(pInstance->uartSleepCache.mode == 1)) {
success = true;
}
}
return success;
}

/* ----------------------------------------------------------------
* PUBLIC FUNCTIONS THAT ARE PRIVATE TO CELLULAR
* -------------------------------------------------------------- */
Expand Down Expand Up @@ -1374,7 +1389,7 @@ int32_t uCellPrivateWakeUpCallback(uAtClientHandle_t atHandle, void *pInstance)
uPortUartCtsSuspend(stream.handle.int32);
}

if (uCellPrivateIsDeepSleepActive(_pInstance)) {
if (uCellPrivateIsDeepSleepActive(_pInstance) || uCellPrivateIsR10SleepMode1(_pInstance)) {
// We know that the module has gone into 3GPP sleep, wake it up.
errorCode = deepSleepWakeUp(_pInstance);
} else {
Expand Down Expand Up @@ -1449,60 +1464,6 @@ void uCellPrivateSetDeepSleepState(uCellPrivateInstance_t *pInstance)
}
}

// Suspend "32 kHz" or UART/AT+UPSV sleep.
int32_t uCellPrivateSuspendUartPowerSaving(const uCellPrivateInstance_t *pInstance,
int32_t *pMode, int32_t *pTimeout)
{
int32_t errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
uAtClientHandle_t atHandle = pInstance->atHandle;

if ((pMode != NULL) && (pTimeout != NULL)) {
// First, read the current AT+UPSV mode
uAtClientLock(atHandle);
uAtClientCommandStart(atHandle, "AT+UPSV?");
uAtClientCommandStop(atHandle);
uAtClientResponseStart(atHandle, "+UPSV:");
*pMode = uAtClientReadInt(atHandle);
*pTimeout = -1;
if (!U_CELL_PRIVATE_MODULE_IS_R4(pInstance->pModule->moduleType) &&
((*pMode == 1) || (*pMode == 4))) {
// Only non-SARA-R4 modules have a timeout value and
// only for AT+UPSV modes 1 and 4
*pTimeout = uAtClientReadInt(atHandle);
}
uAtClientResponseStop(atHandle);
errorCode = uAtClientUnlock(atHandle);
if ((errorCode == 0) && (*pMode > 0)) {
// If that was successful and the current mode was
// not already zero then we now disable AT+UPSV
uAtClientLock(atHandle);
uAtClientCommandStart(atHandle, "AT+UPSV=");
uAtClientWriteInt(atHandle, 0);
uAtClientCommandStopReadResponse(atHandle);
errorCode = uAtClientUnlock(atHandle);
}
}

return errorCode;
}

// Resume "32 kHz" or UART/AT+UPSV sleep.
int32_t uCellPrivateResumeUartPowerSaving(const uCellPrivateInstance_t *pInstance,
int32_t mode, int32_t timeout)
{
uAtClientHandle_t atHandle = pInstance->atHandle;

uAtClientLock(atHandle);
uAtClientCommandStart(atHandle, "AT+UPSV=");
uAtClientWriteInt(atHandle, mode);
if (timeout >= 0) {
uAtClientWriteInt(atHandle, timeout);
}
uAtClientCommandStopReadResponse(atHandle);

return uAtClientUnlock(atHandle);
}

// Delete file on file system.
int32_t uCellPrivateFileDelete(const uCellPrivateInstance_t *pInstance,
const char *pFileName)
Expand Down
39 changes: 1 addition & 38 deletions cell/src/u_cell_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ typedef struct {
typedef struct {
int32_t mode;
int32_t sleepTime;
int32_t maxSleepMode;
} uCellPrivateUartSleepCache_t;

/** Track the state of the profile that is mapped to the
Expand Down Expand Up @@ -895,44 +896,6 @@ int32_t uCellPrivateWakeUpCallback(uAtClientHandle_t atHandle,
*/
void uCellPrivateSetDeepSleepState(uCellPrivateInstance_t *pInstance);

/** Suspend "32 kHz" or UART/AT+UPSV sleep. This function reads the
* current AT+UPSV state, which it returns in pMode and pTimeout, then
* sets AT+UPSV=0. uCellPrivateResumeUartPowerSaving() should be used,
* with the values placed in pMode and pTimeout, to resume UART power
* saving.
*
* Note: gUCellPrivateMutex should be locked before this is called.
*
* @param[in] pInstance a pointer to the cellular instance.
* @param[out] pMode a pointer to a place to put the current AT+UPSV
* mode; cannot be NULL.
* @param[out] pTimeout a pointer to a place to put the current AT+UPSV
* timesout; cannot be NULL, if the AT+UPSV mode in
* pMode does not have a timeout then -1 will be
* returned.
* @return zero on successful wake-up, else negative error.
*/
int32_t uCellPrivateSuspendUartPowerSaving(const uCellPrivateInstance_t *pInstance,
int32_t *pMode, int32_t *pTimeout);

/** Resume "32 kHz" or UART/AT+UPSV sleep, the counterpart to
* uCellPrivateSuspendUartPowerSaving().
*
* Note: gUCellPrivateMutex should be locked before this is called.
*
* @param[in] pInstance a pointer to the cellular instance.
* @param mode the AT+UPSV mode to apply.
* @param timeout the timeout for the AT+UPSV mode; if the mode in
* question does not have a timeout value then
* a negative value should be used, in other words
* the value returned by
* uCellPrivateSuspendUartPowerSaving() can be used
* directly.
* @return zero on successful wake-up, else negative error.
*/
int32_t uCellPrivateResumeUartPowerSaving(const uCellPrivateInstance_t *pInstance,
int32_t mode, int32_t timeout);

/** Delete a file from the file system. If the file does not exist an
* error will be returned.
*
Expand Down
Loading

0 comments on commit 4561c45

Please sign in to comment.