1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2018-2019 NXP
4  *
5  * Brief   CAAM Power state management.
6  */
7 #include <caam_common.h>
8 #include <caam_hal_clk.h>
9 #include <caam_io.h>
10 #include <caam_jr.h>
11 #include <caam_pwr.h>
12 #include <kernel/pm.h>
13 #include <kernel/panic.h>
14 #include <malloc.h>
15 
16 static SLIST_HEAD(,
17 		  backup_data) data_list = SLIST_HEAD_INITIALIZER(backup_data);
18 
caam_pwr_add_backup(vaddr_t baseaddr,const struct reglist * regs,size_t nbentries)19 void caam_pwr_add_backup(vaddr_t baseaddr, const struct reglist *regs,
20 			 size_t nbentries)
21 {
22 	struct backup_data *newelem = NULL;
23 	struct backup_data *elem = NULL;
24 	uint32_t idx = 0;
25 	uint32_t nbregs = 0;
26 
27 	newelem = malloc(sizeof(*newelem));
28 	if (!newelem)
29 		panic();
30 
31 	/* Count the number of registers to save/restore */
32 	for (idx = 0; idx < nbentries; idx++)
33 		nbregs += regs[idx].nbregs;
34 
35 	newelem->baseaddr = baseaddr;
36 	newelem->nbentries = nbentries;
37 	newelem->regs = regs;
38 	newelem->val = malloc(nbregs * sizeof(*newelem->val));
39 
40 	if (!newelem->val)
41 		panic();
42 
43 	/* Add the new backup data element at the end of the list */
44 	elem = SLIST_FIRST(&data_list);
45 	if (elem) {
46 		while (SLIST_NEXT(elem, next))
47 			elem = SLIST_NEXT(elem, next);
48 
49 		SLIST_INSERT_AFTER(elem, newelem, next);
50 	} else {
51 		SLIST_INSERT_HEAD(&data_list, newelem, next);
52 	}
53 }
54 
55 /* Backup all registers present in the data_list */
do_save_regs(void)56 static void do_save_regs(void)
57 {
58 	struct backup_data *elem = NULL;
59 	const struct reglist *reg = NULL;
60 	uint32_t idx = 0;
61 	uint32_t validx = 0;
62 	uint32_t regidx = 0;
63 
64 	SLIST_FOREACH(elem, &data_list, next) {
65 		reg = elem->regs;
66 		validx = 0;
67 		for (idx = 0; idx < elem->nbentries; idx++, reg++) {
68 			for (regidx = 0; regidx < reg->nbregs;
69 			     regidx++, validx++) {
70 				elem->val[validx] =
71 					io_caam_read32(elem->baseaddr +
72 							reg->offset +
73 							(4 * regidx));
74 				elem->val[validx] &= ~reg->mask_clr;
75 
76 				PWR_TRACE("Save @0x%" PRIxPTR "=0x%" PRIx32,
77 					  elem->baseaddr + reg->offset +
78 						  4 * regidx,
79 					  elem->val[validx]);
80 			}
81 		}
82 	}
83 }
84 
85 /* Restore all registers present in the data_list */
do_restore_regs(void)86 static void do_restore_regs(void)
87 {
88 	struct backup_data *elem = NULL;
89 	const struct reglist *reg = NULL;
90 	uint32_t idx = 0;
91 	uint32_t validx = 0;
92 	uint32_t regidx = 0;
93 
94 	SLIST_FOREACH(elem, &data_list, next) {
95 		reg = elem->regs;
96 		validx = 0;
97 		for (idx = 0; idx < elem->nbentries; idx++, reg++) {
98 			for (regidx = 0; regidx < reg->nbregs;
99 			     regidx++, validx++) {
100 				PWR_TRACE("Restore @0x%" PRIxPTR "=0x%" PRIx32,
101 					  elem->baseaddr + reg->offset +
102 						  4 * regidx,
103 					  elem->val[validx]);
104 				io_caam_write32(elem->baseaddr + reg->offset +
105 							4 * regidx,
106 						elem->val[validx] |
107 							reg->mask_set);
108 			}
109 		}
110 	}
111 }
112 
113 /*
114  * CAAM Power state preparation/entry
115  *
116  * @pm_hint   Power mode type
117  */
pm_enter(uint32_t pm_hint)118 static TEE_Result pm_enter(uint32_t pm_hint)
119 {
120 	enum caam_status ret = CAAM_BUSY;
121 
122 	PWR_TRACE("CAAM power mode %" PRIu32 " entry", pm_hint);
123 
124 	if (pm_hint == PM_HINT_CLOCK_STATE) {
125 		ret = caam_jr_halt();
126 	} else if (pm_hint == PM_HINT_CONTEXT_STATE) {
127 		do_save_regs();
128 		ret = caam_jr_flush();
129 	}
130 
131 	if (ret == CAAM_NO_ERROR)
132 		return TEE_SUCCESS;
133 	else
134 		return TEE_ERROR_GENERIC;
135 }
136 
137 /*
138  * CAAM Power state resume
139  *
140  * @pm_hint   Power mode type
141  */
pm_resume(uint32_t pm_hint)142 static TEE_Result pm_resume(uint32_t pm_hint)
143 {
144 	PWR_TRACE("CAAM power mode %" PRIu32 " resume", pm_hint);
145 	if (pm_hint == PM_HINT_CONTEXT_STATE) {
146 		caam_hal_clk_enable(true);
147 		do_restore_regs();
148 	}
149 
150 	caam_jr_resume(pm_hint);
151 
152 	return TEE_SUCCESS;
153 }
154 
155 /*
156  * Power Management Callback function executed when system enter or resume
157  * from a power mode
158  *
159  * @op        Operation mode SUSPEND/RESUME
160  * @pm_hint   Power mode type
161  * @pm_handle Driver private handle (not used)
162  */
163 static TEE_Result
pm_enter_resume(enum pm_op op,uint32_t pm_hint,const struct pm_callback_handle * pm_handle __unused)164 pm_enter_resume(enum pm_op op, uint32_t pm_hint,
165 		const struct pm_callback_handle *pm_handle __unused)
166 {
167 	if (op == PM_OP_SUSPEND)
168 		return pm_enter(pm_hint);
169 	else
170 		return pm_resume(pm_hint);
171 }
172 
caam_pwr_init(void)173 void caam_pwr_init(void)
174 {
175 	register_pm_driver_cb(pm_enter_resume, NULL, "caam_pwr");
176 }
177