1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <assert.h>
21 #include <string.h>
22 #include <DA1469xAB.h>
23 #include <da1469x_config.h>
24 #include <da1469x_trimv.h>
25 #include <da1469x_otp.h>
26
27 #define min(a, b) (((a) < (b)) ? (a) : (b))
28
29 #define GROUP_ID_MAX (MCU_TRIMV_GROUP_ID_MAX)
30
31 #define CS_OFFSET (MCU_OTPM_CS_OFFSET)
32 #define CS_LENGTH (MCU_OTPM_CS_LENGTH)
33
34 #define CS_WORD_START 0xa5a5a5a5
35 #define CS_WORD_END 0x00000000
36 #define CS_WORD_INVALID 0xffffffff
37 #define CS_WORD_TYPE_MASK 0xf0000000
38 #define CS_WORD_TYPE_BOOTER 0x60000000
39 #define CS_WORD_TYPE_TRIM 0x90000000
40
41 struct da1469x_trimv_group {
42 uint8_t index;
43 uint8_t num_words;
44 };
45
46 static struct da1469x_trimv_group g_mcu_trimv_groups[GROUP_ID_MAX + 1];
47
48 void
da1469x_trimv_is_reg_pairs_in_otp(const uint32_t * regs_buf,uint32_t num_entries,bool * status_buf)49 da1469x_trimv_is_reg_pairs_in_otp(const uint32_t *regs_buf, uint32_t num_entries, bool *status_buf)
50 {
51 uint32_t offset_start;
52 uint32_t offset_end;
53 uint32_t offset;
54 uint32_t ow;
55
56 assert(regs_buf);
57 assert(status_buf);
58
59 /* Initialize the status buffer to indicate invalid entries */
60 memset(status_buf, 0, num_entries);
61
62 if (!num_entries) {
63 return;
64 }
65
66 offset_start = CS_OFFSET;
67 offset_end = CS_OFFSET + CS_LENGTH;
68 offset = offset_start;
69
70 da1469x_otp_read(offset, &ow, sizeof(ow));
71 /* Check if there is a valid CS entry in OTP */
72 if (ow != CS_WORD_START) {
73 return;
74 }
75
76 offset += 4;
77
78 /* Iterate the whole CS section */
79 while (offset < offset_end) {
80 da1469x_otp_read(offset, &ow, sizeof(ow));
81
82 offset += 4;
83
84 /* Check if the end of the CS section has been reached */
85 if (ow == CS_WORD_END || ow == CS_WORD_INVALID) {
86 return;
87 /* Check if the entry accommodates a TCS section and compute its size (num. of words) so it can be skipped */
88 } else if ((ow & CS_WORD_TYPE_MASK) == CS_WORD_TYPE_TRIM) {
89 offset += ((ow & 0x0000ff00) >> 8) << 2;
90 /* Check if the entry accommodates a register-value pair and compare it with the register buffer provided */
91 } else if (ow <= MEMCTRL_BASE + 0x100) {
92 for (int i = 0 ; i < num_entries; i++) {
93 if (ow == regs_buf[i]) {
94 status_buf[i] = true;
95 break;
96 }
97 }
98 offset += 4; // Skip register's value
99 }
100 }
101 }
102
103 void
da1469x_trimv_init_from_otp(void)104 da1469x_trimv_init_from_otp(void)
105 {
106 uint32_t offset_start;
107 uint32_t offset_end;
108 uint32_t offset;
109 uint32_t ow;
110 uint8_t trimv_group;
111 uint8_t trimv_num_words;
112
113 /* Clear groups if anything was previously loaded */
114 memset(&g_mcu_trimv_groups, 0, sizeof(g_mcu_trimv_groups));
115
116 /* Start of configuration script */
117 offset_start = CS_OFFSET;
118 offset_end = CS_OFFSET + CS_LENGTH;
119 offset = offset_start;
120
121 da1469x_otp_read(offset, &ow, sizeof(ow));
122 if (ow != CS_WORD_START) {
123 return;
124 }
125
126 offset += 4;
127
128 while (offset < offset_end) {
129 da1469x_otp_read(offset, &ow, sizeof(ow));
130
131 offset += 4;
132
133 if ((ow == CS_WORD_END) || (ow == CS_WORD_INVALID)) {
134 /* End of CS or empty word */
135 break;
136 } else if ((ow & CS_WORD_TYPE_MASK) < CS_WORD_TYPE_BOOTER) {
137 /* This is a register+value configuration, skip one more word */
138 offset += 4;
139 } else if ((ow & CS_WORD_TYPE_MASK) == CS_WORD_TYPE_TRIM) {
140 trimv_num_words = (ow & 0x0000ff00) >> 8;
141 trimv_group = ow & 0x000000ff;
142
143 if (trimv_group <= GROUP_ID_MAX) {
144 /*
145 * XXX not sure if each group can be present only once in OTP,
146 * but our implementation requires it for now and it works
147 */
148 assert(g_mcu_trimv_groups[trimv_group].num_words == 0);
149
150 g_mcu_trimv_groups[trimv_group].index = (offset - offset_start) / 4;
151 g_mcu_trimv_groups[trimv_group].num_words = trimv_num_words;
152 }
153
154 offset += trimv_num_words * 4;
155 }
156 }
157 }
158
159 uint8_t
da1469x_trimv_group_num_words_get(uint8_t group)160 da1469x_trimv_group_num_words_get(uint8_t group)
161 {
162 struct da1469x_trimv_group *grp = &g_mcu_trimv_groups[group];
163
164 if (group > GROUP_ID_MAX) {
165 return 0;
166 }
167
168 return grp->num_words;
169 }
170
171 uint8_t
da1469x_trimv_group_read(uint8_t group,uint32_t * buf,uint8_t num_words)172 da1469x_trimv_group_read(uint8_t group, uint32_t *buf, uint8_t num_words)
173 {
174 struct da1469x_trimv_group *grp = &g_mcu_trimv_groups[group];
175 uint32_t offset;
176 int rc;
177
178 /* Silence warning if built without asserts */
179 (void)rc;
180
181 num_words = min(num_words, da1469x_trimv_group_num_words_get(group));
182
183 if (!num_words) {
184 return 0;
185 }
186
187 offset = CS_OFFSET + grp->index * 4;
188
189 rc = da1469x_otp_read(offset, buf, num_words * 4);
190 assert(rc == 0);
191
192 return num_words;
193 }
194