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