| // 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); |
| } |