6/14/2012

Sleep Mechanism in Qualcom msm8x55 chipset ---- Sleep task

This chapter discusses the deep sleep process in the MSM7x30 system. Since the modem is thepower master, this process is carried out by the sleep task of the modem system, and the device
will enter deep sleep only when the apps processor is already in the Sleep state. Topics covered are:

? Sleep voting � The sleep voting system allows subsystems to vote whether it is okay to enter deep sleep. The sleep task will enter TCXO shutdown only when all subsystems have agreed
to sleep.

? Sleep time � This presents how the sleep time for deep sleep is calculated, including the warmup time needed for exiting deep sleep. This warmup time will be subtracted from the
sleep time while programming the sleep time in the MPM block.

? Procedures for entering and exiting deep sleep � Once the system has determined that it can enter sleep and there is enough time for shutting off XOs, software following a procedure
during which we set up the hardware block for the Sleep state, turn off clock regime, shut down PLLs, configure SDRAM for self-refresh, put the ARM processor in SWFI (Stop and
Wait for Interrupt) state, and the MPM signals the PMIC to turn off XOs. When an interrupt comes, the system follows a reverse procedure to exit from deep sleep.
The sleep code is located at \core\power\sleep\.

Data structure

The following defines the main sleep system data structure.


typedef struct
{


/* Data for logging */
sleep_log_type log;


/* Timer for reporting to the DOG task */
clk_cb_type dog_report_timer;


/* Used to show that the sleep task has been told to start and also
to indicate when it has been told to stop. When stopped, it will
forbid TCXO shutdown and clock shutdown */
boolean task_running;


/* Client OKTS votes, restrictions, and info */
sleep_client_info_type client_info[MAX_NUM_SLEEP_CLIENTS];


/* Number of sleep clients */
uint8 num_clients;


/* Highest index of any allocated sleep client */
uint8 max_client_index;


/* Used to hold off sleep, such as when we're booting up. */
boolean allow_sleep;


/* A mask of all the currently acquired resources. */
uint32 acquired_resources;


/* Shows which voters have voted against local SWFI */
/* Note: only impacts the behavior of the local processor */
uint64 swfi_vote_mask;


/* Shows which voters have voted against TCXO shutdown */
uint64 tcxo_vote_mask;


/* Shows which voters have voted against memory self-refresh */
uint64 memory_vote_mask;


/* Shows which voters have voted against VDD minimization */
uint64 vdd_min_vote_mask;




/* Shows which voters have voted against apps sleep */
/* Note: currently only counted on the apps processor */
uint64 apps_sleep_vote_mask;


/* Shows which voters have voted against apps power collapse */
/* Note: currently only counted on the apps processor */
uint64 apps_pwrc_vote_mask;


/* Shows which voters have voted against RBC1 */
uint64 rbc1_vote_mask;


} sleep_info;
sleep_info gSleepInfo;



This structure contains the sleep log, dog report timer, sleep client, i.e., voter, information, the
current vote mask, other variables indicating the state of the sleep task, etc.

The sleep log stores the following information.


typedef struct
{


/* Sleep task statistics */
sleeplog_task_stats_v2 task_stats;


/* Captures the number of new sleep voters when sleep decision
is made */
uint8 num_clients;


/* Captures the new sleep voters when sleep decision is made */
uint64 curr_not_okts_m;


/* New voters voting to keep TCXO on when last packet was
generated */
uint64 prev_not_okts_m;


/* Names of the sleep voters in shared memory */
char *smem_names;


} sleep_log_type;




The sleep client information has the following structure. It contains the Boolean variables that
record votes for various system resources, client_name, etc.


typedef struct
{
/* true if the client has set the legacy "swfi restriction" */
boolean swfi_restriction;


/* true if the client has set the legacy "vdd min restriction" */
boolean vdd_min_restriction;


/* true if the client has acquired the local SWFI resource */
boolean swfi_vote;


/* true if the client has acquired the TCXO resource */
boolean tcxo_vote;


/* true if the client has acquired the memory resource */
boolean memory_vote;


/* true if the client has acquired the VDD min resource */
boolean vdd_min_vote;


/* true if the client has acquired the apps sleep resource */
boolean apps_sleep_vote;


/* true if the client has acquired the apps power collapse
resource */
boolean apps_pwrc_vote;


/* true if the client has acquired the rbc1 resource */
boolean rbc1_vote;


/* Client name */
char client_name[SLEEP_CLIENT_LIST_MAX_STR + 1];


/* Client Proc ID */
uint32 client_procID;


} sleep_client_info_type;




Sleep Voting APIs

SLEEP_REGISTER

This function is generally called at init/boot time by any client that wants a vote for sleep.


sleep_okts_handle sleep_register
(
const char *pszName,
boolean bOkts
);



The client provides a unique char* to identify itself. It is important that this is unique. The string
that it points to will be used for logging, so that one can look at a log and determine who is voting
for sleep without translating from an enum. The second parameter is the initial vote indicating
whether the client is for or against sleep. After this function completes, a new client record in the
internal sleep array structure (client_info[]) is reserved for the calling client.

This function returns a sleep client handle, which will be used for subsequent calls to vote for or
against sleep. Besides, when sleep vote is no longer needed for a particular client, the following
API can be used to deregister from the sleep voting.


void sleep_deregister(sleep_okts_handle handle);


SLEEP_ASSERT_OKTS

Using the handle provided from sleep_register, the client calls this function to assert OKTS, i.e.,
indicate that as far as that client is concerned, it is okay to sleep.


void sleep_assert_okts
(
sleep_okts_handle handle
);

Using the handle provided from sleep_register, the client calls this function to negate OKTS, i.e.,
indicate that as far as that client is concerned, it is not okay to sleep.


void sleep_negate_okts
(
sleep_okts_handle handle
);



SLEEP_SET_SWFI_RESTRICTION

This function allows the sleep client to place a restriction on SWFI. If the client requires that
SWFI not happen, then it is prevented using this function.


void sleep_set_swfi_restriction
(
sleep_okts_handle handle,
sleep_swfi_restrict_type swfi_restriction
);



With:


typedef enum
{
SLEEP_SWFI_RESTRICT_OFF = 0x0,
SLEEP_SWFI_RESTRICT_ON
} sleep_swfi_restrict_type;


SLEEP_SET_VDD_MIN_RESTRICTION

This API allows the sleep client to place a restriction on VDD minimization. This would only
occur if XO shutdown (sleep) occurs, so is only relevant if the clients sleep vote is okts.


void sleep_set_vdd_min_restriction
(
sleep_okts_handle handle,
sleep_vdd_min_restrict_type vdd_min_restriction
);


SLEEP_ACQUIRE_RESOURCE

The above APIs such as sleep_assert_okts and sleep_set_vdd_min_restriction actually calls the
following APIs to acquire specific system resources so that these resources will not enter low
power state or release the resources when they are no longer needed.


void sleep_acquire_resources
(
sleep_okts_handle handle,
uint32 resources
);
void sleep_release_resources
(
sleep_okts_handle handle,
uint32 resources
);



Tallying sleep votes

The sleep votes are tallied in the following API to decide whether it is okay to go into deep sleep.


static boolean sleepmod_ok_tcxo_shutdown(void);

It returns TRUE if deep sleep is allowed, and FALSE if not. When deep sleep is not allowed, it
will write to a shared memory log to record which modules have voted against sleep.

If deep sleep is not allowed, the system will also check relevant votes to see whether it is okay to
put memory in self-refresh or do ARM halt, i.e., SWFI, in the following APIs.


static boolean sleepmod_allow_self_refresh
(
/* Input and Output. Notify function if timer groups have been
disabled. Notify caller if timer groups were disabled inside this
function */
boolean *pBDisabledTimerGroups,
/* Return Min sclk's until next wakeup */
uint32 *puMinSclk
);
static boolean sleepmod_allow_micro_proc_sleep(void);

This way, the system can enter an appropriate low power state (self-refresh or ARM halt) to
conserve power even if deep sleep is not allowed.


Sleep time

When all votes are okay to enter deep sleep, the system proceeds to find out how long it can sleep
and whether the sleep time is enough for a deep sleep. If yes, it will calculate the actual sleep time
by subtracting warmup time from the sleep time and program the MAO time so that an
MPM_INT will fire to wake up the system when the sleep time expires.


Calculating sleep time

The sleep time is determined from the two sources, sleep controller and software timers.

The following API returns the next wakeup point of the sleep controller.


uint32 sleepctl_get_next_wakeup(void)

Depending on what base station the phone camps on, the sleep cycle will vary. When the device
is put in Airplane mode, the API should return the maximum allowable time, 0xFFFFFFFFuL.

The nearest expiring undeferable timer is checked in the following API.


timetick_type timer_defer_match_interrupt( void );





If the sleep time is less than the following time, there is not sufficient time for performing a deep
sleep. A corresponding INSUF_TIME log will be recorded in the shared memory log.


/* The amount of time that must be left in the sleep controller
(in milliseconds) to allow switching of the uP clock to the
sleep clock and shutting down the uP, if the MSM is currently
sleeping */


#define SLEEP_CHECK_TIME_MS 30


System warmup time

When system is in deep sleep, it takes some time to reenable XOs, turn on PLLs, and bring the
system to a normal running state. This needs to be taken into account when planning the deep
sleep so that when the system comes up it will not be too late to handle any pending events.

When the system does VDD minimization during deep sleep, the warmup time is determined by
the following BSP data structure for MPM.


/*
* Target specific MPM Hardware configuration.
*/
BSP_mao_ConfigDataType MAO_BSP_DATA =
{

/* Wakeup Delays */
{











},
...
}


/* The total warmup time that is subtracted off of the requested
* sleep time is the sum of these three values. */
/* Warmup Delay Rampup Delay Software Overhead */
0x00C5 /* 6 ms */, 0x0008 /* .24 ms */, 0x130 /* 9.2 ms */




The total warmup delay is subtracted from the sleep time inside the following API when
programming the sleep time into MAO hardware.


void mao_set_wakeup_time
(
uint32 wakeup_time
);




When XO shutdown is performed without VDD minimization during deep sleep, the XO warmup
time is actually enforced by the PMIC, not MPM. So we also program the PMIC to make sure its
XO warmup time is set to 0xC5.


/* Set PMIC TCXO turnon delay to 1ms, 32KHz clock counts. */
if ( !pmic_configured )
{
pm_config_tcxo_ctrl( PM_POLARITY_ACTIVE_HIGH, 0xc5 );
pmic_configured = TRUE;
}


Sleep process

This section summarizes the steps when the system enters and exits deep sleep. This is by no
means an exhaustive list. Besides essential sleep steps, it also lists some smem log events that are
useful for debugging.

? Lock IRQ and FIQ.

? Tally sleep votes. Proceed if all votes are okay to sleep, otherwise, record NO_SLEEP to
smem_log (sleepmod_allow_tcxo_shutdown).

? Calculate sleep time. Proceed if there is sufficient time for deep sleep, otherise, record
INSUF_TIME to smem_log (sleepmod_allow_tcxo_shutdown).

? Check whether VDD minimization and XO shutdown is allowed (sleepmod_tcxo_shutdown).

? Record ENTER_TCXO to smem log.

? Set up wakeup interrupt time in MAO (mao_setup_sleep).

? Program MPM block from appropriate low power mode, VDD_MIN or XO down
(mao_enable_low_power_mode).

? Set PMIC TCXO turn on delay (pm_config_tcxo_ctrl).

? Configure GPIOs for sleep (gpio_config_gpios_for_sleep).

? Put interrupt controller into sleep (tramp_sleep).

? Program clock regime for sleep (clk_regime_tcxo_shutdown).

? Execute clk_regime_tcxo_shutdown_asm of clkrgm_asm_tcxo_shutdown.s in
\AMSS\products\7x30\core\systemdrivers\clkregim\src\proc\mpss inside IRAM. This
function also calls HAL_clk_TCXOShutdown of HALclk7630TCXOShutdown.s in
\AMSS\products\7x30\core\systemdrivers\hal\clk\src\7630.


clk_regime_execute_in_iram(CLKRGM_IRAM_TCXO_SHUTDOWN);



Altogether, the following steps are performed inside the IRAM.

a. Disable CDC autocalibration by hardware (SMI and EBI1).

b. Put the SDRAM devices in Self-Refresh mode (SMI and EBI1).

c. Switch the ARM9 clock to TCXO.



d. Turn off the DCPLL0 (global PLL).

e. Issue a STBYWFI command.

f. Wait for the wakeup interrupt.

g. When the interrupt comes, turn on the DCPLL0 (Global PLL).

h. Switch the ARM9 clock to high speed.

i. Take the SDRAM out of Self-Refresh mode.

j. Recalibrate the CDC (SMI and EBI1).

? Exit IRAM and enter external memory.

? Restore clock regime after sleep.

? Wake up tramp.

? Restore GPIOs after sleep.

? Disable MPM settings.

? Record TCXO_END in smem log.

? Disable MAO wakeup interrupts.

? Reenable deferred software timers.

? Free IRQ and FIQ.

No comments:

Post a Comment