blob: 1df10d166c6455640e5f42cc71c12dd6c646e68e [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright 2018-2019 NXP
*
* Brief CAAM Power state management.
*/
#include <caam_common.h>
#include <caam_hal_clk.h>
#include <caam_io.h>
#include <caam_jr.h>
#include <caam_pwr.h>
#include <kernel/pm.h>
#include <kernel/panic.h>
#include <malloc.h>
static SLIST_HEAD(,
backup_data) data_list = SLIST_HEAD_INITIALIZER(backup_data);
void caam_pwr_add_backup(vaddr_t baseaddr, const struct reglist *regs,
size_t nbentries)
{
struct backup_data *newelem = NULL;
struct backup_data *elem = NULL;
uint32_t idx = 0;
uint32_t nbregs = 0;
newelem = malloc(sizeof(*newelem));
if (!newelem)
panic();
/* Count the number of registers to save/restore */
for (idx = 0; idx < nbentries; idx++)
nbregs += regs[idx].nbregs;
newelem->baseaddr = baseaddr;
newelem->nbentries = nbentries;
newelem->regs = regs;
newelem->val = malloc(nbregs * sizeof(*newelem->val));
if (!newelem->val)
panic();
/* Add the new backup data element at the end of the list */
elem = SLIST_FIRST(&data_list);
if (elem) {
while (SLIST_NEXT(elem, next))
elem = SLIST_NEXT(elem, next);
SLIST_INSERT_AFTER(elem, newelem, next);
} else {
SLIST_INSERT_HEAD(&data_list, newelem, next);
}
}
/* Backup all registers present in the data_list */
static void do_save_regs(void)
{
struct backup_data *elem = NULL;
const struct reglist *reg = NULL;
uint32_t idx = 0;
uint32_t validx = 0;
uint32_t regidx = 0;
SLIST_FOREACH(elem, &data_list, next) {
reg = elem->regs;
validx = 0;
for (idx = 0; idx < elem->nbentries; idx++, reg++) {
for (regidx = 0; regidx < reg->nbregs;
regidx++, validx++) {
elem->val[validx] =
io_caam_read32(elem->baseaddr +
reg->offset +
(4 * regidx));
elem->val[validx] &= ~reg->mask_clr;
PWR_TRACE("Save @0x%" PRIxPTR "=0x%" PRIx32,
elem->baseaddr + reg->offset +
4 * regidx,
elem->val[validx]);
}
}
}
}
/* Restore all registers present in the data_list */
static void do_restore_regs(void)
{
struct backup_data *elem = NULL;
const struct reglist *reg = NULL;
uint32_t idx = 0;
uint32_t validx = 0;
uint32_t regidx = 0;
SLIST_FOREACH(elem, &data_list, next) {
reg = elem->regs;
validx = 0;
for (idx = 0; idx < elem->nbentries; idx++, reg++) {
for (regidx = 0; regidx < reg->nbregs;
regidx++, validx++) {
PWR_TRACE("Restore @0x%" PRIxPTR "=0x%" PRIx32,
elem->baseaddr + reg->offset +
4 * regidx,
elem->val[validx]);
io_caam_write32(elem->baseaddr + reg->offset +
4 * regidx,
elem->val[validx] |
reg->mask_set);
}
}
}
}
/*
* CAAM Power state preparation/entry
*
* @pm_hint Power mode type
*/
static TEE_Result pm_enter(uint32_t pm_hint)
{
enum caam_status ret = CAAM_BUSY;
PWR_TRACE("CAAM power mode %" PRIu32 " entry", pm_hint);
if (pm_hint == PM_HINT_CLOCK_STATE) {
ret = caam_jr_halt();
} else if (pm_hint == PM_HINT_CONTEXT_STATE) {
do_save_regs();
ret = caam_jr_flush();
}
if (ret == CAAM_NO_ERROR)
return TEE_SUCCESS;
else
return TEE_ERROR_GENERIC;
}
/*
* CAAM Power state resume
*
* @pm_hint Power mode type
*/
static TEE_Result pm_resume(uint32_t pm_hint)
{
PWR_TRACE("CAAM power mode %" PRIu32 " resume", pm_hint);
if (pm_hint == PM_HINT_CONTEXT_STATE) {
caam_hal_clk_enable(true);
do_restore_regs();
}
caam_jr_resume(pm_hint);
return TEE_SUCCESS;
}
/*
* Power Management Callback function executed when system enter or resume
* from a power mode
*
* @op Operation mode SUSPEND/RESUME
* @pm_hint Power mode type
* @pm_handle Driver private handle (not used)
*/
static TEE_Result
pm_enter_resume(enum pm_op op, uint32_t pm_hint,
const struct pm_callback_handle *pm_handle __unused)
{
if (op == PM_OP_SUSPEND)
return pm_enter(pm_hint);
else
return pm_resume(pm_hint);
}
void caam_pwr_init(void)
{
register_pm_driver_cb(pm_enter_resume, NULL);
}