1 /*
2 * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /*
8 * This file provides a group of functions that are used to track PHY register
9 * writes by intercepting io_write16 function calls. Once the registers are
10 * tracked, their value can be saved at a given time spot, and restored later
11 * as required. This implementation is useful to capture any PHY register
12 * programing in any function during PHY initialization.
13 */
14
15 #include <stdint.h>
16
17 #include <common/debug.h>
18
19 #include <ddrphy_phyinit.h>
20
21 #include <lib/mmio.h>
22
23 #include <platform_def.h>
24
25 /*
26 * MAX_NUM_RET_REGS default Max number of retention registers.
27 *
28 * This define is only used by the PhyInit Register interface to define the max
29 * amount of registered that can be saved. The user may increase this variable
30 * as desired if a larger number of registers need to be restored.
31 */
32 #if STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE
33 #define MAX_NUM_RET_REGS 129
34 #else /* STM32MP_LPDDR4_TYPE */
35 #define MAX_NUM_RET_REGS 283
36 #endif /* STM32MP_DDR3_TYPE || STM32MP_DDR4_TYPE */
37
38 /*
39 * Array of Address/value pairs used to store register values for the purpose
40 * of retention restore.
41 */
42 #define RETREG_AREA (MAX_NUM_RET_REGS + 1) * sizeof(struct reg_addr_val)
43 #define RETREG_BASE RETRAM_BASE + RETRAM_SIZE - RETREG_AREA
44
45 static int *retregsize = (int *)(RETREG_BASE);
46 static struct reg_addr_val *retreglist = (struct reg_addr_val *)(RETREG_BASE + sizeof(int));
47
48 static int numregsaved; /* Current Number of registers saved. */
49 static int tracken = 1; /* Enabled tracking of registers */
50
51 /*
52 * Tags a register if tracking is enabled in the register
53 * interface.
54 *
55 * During PhyInit registers writes, keeps track of address
56 * for the purpose of restoring the PHY register state during PHY
57 * retention exit process. Tracking can be turned on/off via the
58 * ddrphy_phyinit_reginterface STARTTRACK, STOPTRACK instructions. By
59 * default tracking is always turned on.
60 *
61 * \return 0 on success.
62 */
ddrphy_phyinit_trackreg(uint32_t adr)63 int ddrphy_phyinit_trackreg(uint32_t adr)
64 {
65 int regindx = 0;
66
67 /* Return if tracking is disabled */
68 if (tracken == 0) {
69 return 0;
70 }
71
72 /* Search register address within the array */
73 for (regindx = 0; regindx < numregsaved; regindx++) {
74 if (retreglist[regindx].address == adr) {
75 /* Register found */
76 return 0;
77 }
78 }
79
80 /* Register not found, so add it */
81 if (numregsaved > MAX_NUM_RET_REGS) {
82 ERROR("numregsaved > MAX_NUM_RET_REGS\n");
83 VERBOSE("[ddrphy_phyinit_reginterface:%s]\n", __func__);
84 VERBOSE("Max Number of Restore Registers reached: %d.\n", numregsaved);
85 VERBOSE("Please recompile PhyInit with larger MAX_NUM_RET_REG value.\n");
86 return -1;
87 }
88
89 retreglist[regindx].address = adr;
90 numregsaved++;
91
92 return 0;
93 }
94
95 /*
96 * Register interface function used to track, save and restore retention registers.
97 *
98 * ### Usage
99 * Register tracking is enabled by calling:
100 *
101 * \code
102 * ddrphy_phyinit_reginterface(STARTTRACK,0,0);
103 * \endcode
104 *
105 * from this point on any call to mmio_write_16() in
106 * return will be capture by the register interface via a call to
107 * ddrphy_phyinit_trackreg(). Tracking is disabled by calling:
108 *
109 * \code
110 * ddrphy_phyinit_reginterface(STOPTRACK,0,0);
111 * \endcode
112 *
113 * On calling this function, register write via mmio_write_16 are no longer tracked until a
114 * STARTTRACK call is made. Once all the register write are complete, SAVEREGS
115 * command can be issue to save register values into the internal data array of
116 * the register interface. Upon retention exit RESTOREREGS are command can be
117 * used to issue register write commands to the PHY based on values stored in
118 * the array.
119 * \code
120 * ddrphy_phyinit_reginterface(SAVEREGS,0,0);
121 * ddrphy_phyinit_reginterface(RESTOREREGS,0,0);
122 * \endcode
123 * \return 0 on success.
124 */
ddrphy_phyinit_reginterface(enum reginstr myreginstr,uint32_t adr,uint16_t dat)125 int ddrphy_phyinit_reginterface(enum reginstr myreginstr, uint32_t adr, uint16_t dat)
126 {
127 if (myreginstr == SAVEREGS) {
128 int regindx;
129
130 /*
131 * Go through all the tracked registers, issue a register read and place
132 * the result in the data structure for future recovery.
133 */
134 for (regindx = 0; regindx < numregsaved; regindx++) {
135 uint16_t data;
136
137 data = mmio_read_16((uintptr_t)(DDRPHYC_BASE +
138 (4U * retreglist[regindx].address)));
139 retreglist[regindx].value = data;
140 }
141
142 *retregsize = numregsaved;
143
144 return 0;
145 } else if (myreginstr == RESTOREREGS) {
146 int regindx;
147
148 /*
149 * Write PHY registers based on Address, Data value pairs stores in
150 * retreglist.
151 */
152 for (regindx = 0; regindx < *retregsize; regindx++) {
153 mmio_write_16((uintptr_t)
154 (DDRPHYC_BASE + (4U * retreglist[regindx].address)),
155 retreglist[regindx].value);
156 }
157
158 return 0;
159 } else if (myreginstr == STARTTRACK) {
160 /* Enable tracking */
161 tracken = 1;
162 return 0;
163 } else if (myreginstr == STOPTRACK) {
164 /* Disable tracking */
165 tracken = 0;
166 return 0;
167 } else {
168 return -1;
169 }
170 }
171