1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * o Redistributions of source code must retain the above copyright notice, this list
9  *   of conditions and the following disclaimer.
10  *
11  * o Redistributions in binary form must reproduce the above copyright notice, this
12  *   list of conditions and the following disclaimer in the documentation and/or
13  *   other materials provided with the distribution.
14  *
15  * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
16  *   contributors may be used to endorse or promote products derived from this
17  *   software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "fsl_device_registers.h"
32 #include "fsl_xcvr.h"
33 #include "ifr_radio.h"
34 #include "fsl_os_abstraction.h"
35 /*******************************************************************************
36  * Definitions
37  ******************************************************************************/
38 #define IFR_RAM             (0)
39 
40 #if RADIO_IS_GEN_3P0
41 #define RDINDX              (0x41U)
42 #define K3_BASE_INDEX       (0x11U) /* Based for read index */
43 #else
44 #define RDRSRC              (0x03U)
45 #define KW4x_512_BASE       (0x20000U)
46 #define KW4x_256_BASE       (0x10000U)
47 #endif /* RADIO_IS_GEN_3P0 */
48 
49 #if RADIO_IS_GEN_2P1
50 #define FTFA    (FTFE)
51 #endif /* RADIO_IS_GEN_2P1 */
52 
53 /*******************************************************************************
54  * Prototypes
55  ******************************************************************************/
56 uint32_t read_another_ifr_word(void);
57 uint32_t read_first_ifr_word(uint32_t read_addr);
58 
59 #if RADIO_IS_GEN_3P0
60 uint64_t read_index_ifr(uint32_t read_addr);
61 #else
62 /*! *********************************************************************************
63  * @brief  Reads a location in block 1 IFR for use by the radio.
64  *
65  * This function handles reading IFR data from flash memory for trim loading.
66  *
67  * @param read_addr the address in the IFR to be read.
68  *
69  * @details This function wraps both the Gen2 read_resource command and the Gen2.1 and Gen3 read_index
70 ***********************************************************************************/
71 #if RADIO_IS_GEN_2P1
72 uint64_t read_resource_ifr(uint32_t read_addr);
73 #else
74 uint32_t read_resource_ifr(uint32_t read_addr);
75 #endif /* RADIO_IS_GEN_2P1 */
76 #endif /* RADIO_IS_GEN_3P0 */
77 
78 void store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries, uint32_t addr, uint32_t data);
79 
80 /*******************************************************************************
81  * Variables
82  ******************************************************************************/
83 static uint32_t ifr_read_addr;
84 
85 #if RADIO_IS_GEN_3P0
86 static uint64_t packed_data_long; /* Storage for 2 32 bit values to be read by read_index */
87 static uint8_t num_words_avail; /* Number of 32 bit words available in packed_data_long storage */
88 const uint32_t BLOCK_1_IFR[]=
89 {
90     /* Revised fallback table which should work with untrimmed parts */
91     0xABCDFFFEU, /* Version #FFFE indicates default trim values */
92 
93     /* Trim table is empty for Gen3 by default */
94 
95     /* No TRIM_STATUS in SW fallback array. */
96     0xFEED0E0FU /* End of File */
97 };
98 #else
99 #if RADIO_IS_GEN_2P0
100 const uint32_t BLOCK_1_IFR[]=
101 {
102     /* Revised fallback table which should work with untrimmed parts */
103     0xABCDFFFEU, /* Version #FFFE indicates default trim values */
104 
105     0x4005912CU, /* RSIM_ANA_TRIM address */
106     0x784B0000U, /* RSIM_ANA_TRIM default value */
107 
108     /* No TRIM_STATUS in SW fallback array. */
109     0xFEED0E0FU /* End of File */
110 };
111 #else
112 static uint64_t packed_data_long; /* Storage for 2 32 bit values to be read by read_index */
113 static uint8_t num_words_avail; /* Number of 32 bit words available in packed_data_long storage */
114 const uint32_t BLOCK_1_IFR[]=
115 {
116     /* Revised fallback table which should work with untrimmed parts */
117     0xABCDFFFEU, /* Version #FFFE indicates default trim values */
118 
119     0x4005912CU, /* RSIM_ANA_TRIM address */
120     0x784B0000U, /* RSIM_ANA_TRIM default value */
121 
122     /* No TRIM_STATUS in SW fallback array. */
123     0xFEED0E0FU /* End of File */
124 };
125 #endif /* RADIO_IS_GEN_2P0 */
126 #endif /* RADIO_IS_GEN_3P0 */
127 
128 /*******************************************************************************
129  * Code
130  ******************************************************************************/
131 
132 /*! *********************************************************************************
133  * \brief  Read command for reading the first 32bit word from IFR, encapsulates different
134  *  flash IFR read mechanisms for multiple generations of SOC
135  *
136  * \param read_addr flash address
137  *
138  * \return 8 bytes of packed data containing radio trims only
139  *
140 ***********************************************************************************/
read_first_ifr_word(uint32_t read_addr)141 uint32_t read_first_ifr_word(uint32_t read_addr)
142 {
143     ifr_read_addr = read_addr;
144     return read_another_ifr_word();
145 }
146 
147 /*! *********************************************************************************
148  * \brief  Read command for reading additional 32bit words from IFR. Encapsulates multiple IFR read mechanisms.
149  *
150  * \param read_addr flash address
151  *
152  * \return 8 bytes of packed data containing radio trims only
153  *
154  * \remarks PRE-CONDITIONS:
155  *  The function read_first_ifr_word() must have been called so that the ifr_read_addr variable is setup prior to use.
156  *
157 ***********************************************************************************/
read_another_ifr_word(void)158 uint32_t read_another_ifr_word(void)
159 {
160     uint32_t packed_data;
161 
162 #if (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1)
163     /* Using some static storage and alternating reads to read_index_ifr to replace read_resource_ifr */
164     if (num_words_avail == 0)
165     {
166 #if RADIO_IS_GEN_3P0
167         packed_data_long = read_index_ifr(ifr_read_addr);
168 #else /* Use 64 bit return version of read_resource */
169         packed_data_long = read_resource_ifr(ifr_read_addr);
170 #endif /* RADIO_IS_GEN_3P0 */
171 
172         num_words_avail = 2;
173         ifr_read_addr++; /* Read index addresses increment by 1 */
174     }
175 
176     packed_data = (uint32_t)(packed_data_long & 0xFFFFFFFF);
177     packed_data_long = packed_data_long >> 32;
178     num_words_avail--;
179 #else
180     packed_data = read_resource_ifr(ifr_read_addr);
181     ifr_read_addr += 4; /* Read resource addresses increment by 4 */
182 #endif /* (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1) */
183 
184     return packed_data;
185 }
186 
187 #if RADIO_IS_GEN_3P0
188 /*! *********************************************************************************
189  * \brief  Read command for reading from IFR using RDINDEX command
190  *
191  * \param read_addr flash address
192  *
193  * \return 8 bytes of packed data containing radio trims only
194  *
195 ***********************************************************************************/
read_index_ifr(uint32_t read_addr)196 uint64_t read_index_ifr(uint32_t read_addr)
197 {
198     uint8_t rdindex = read_addr;
199     uint64_t read_data;
200     uint8_t i;
201 
202     while ((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 to make sure not interrupting a prior operation */
203 
204     if ((FTFE->FSTAT & FTFE_FSTAT_ACCERR_MASK) == FTFE_FSTAT_ACCERR_MASK )
205     {
206         FTFE->FSTAT = (1 << FTFE_FSTAT_ACCERR_SHIFT); /* Write 1 to ACCEER to clear errors */
207     }
208 
209     FTFE->FCCOB[0] = RDINDX;
210     FTFE->FCCOB[1] = rdindex;
211 
212     OSA_InterrupDisable();
213     FTFE->FSTAT = FTFE_FSTAT_CCIF_MASK;
214     while((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 */
215     OSA_InterruptEnable();
216 
217     /* Pack read data back into 64 bit type */
218     read_data = FTFE->FCCOB[11]; /* MSB goes in first, will be shifted left sequentially */
219     for (i = 10; i > 3; i--)
220     {
221         read_data = read_data << 8;
222         read_data |= FTFE->FCCOB[i];
223     }
224 
225     return read_data;
226 }
227 #else
228 
229 /*! *********************************************************************************
230  * \brief  Read command for reading from IFR
231  *
232  * \param read_addr flash address
233  *
234  * \return packed data containing radio trims only
235  *
236 ***********************************************************************************/
237 #if RADIO_IS_GEN_2P0
read_resource_ifr(uint32_t read_addr)238 uint32_t read_resource_ifr(uint32_t read_addr)
239 {
240 
241     uint32_t packed_data;
242     uint8_t flash_addr23_16, flash_addr15_8, flash_addr7_0;
243     uint32_t read_data31_24, read_data23_16, read_data15_8, read_data7_0;
244 
245     flash_addr23_16 = (uint8_t)((read_addr & 0xFF0000) >> 16);
246     flash_addr15_8 = (uint8_t)((read_addr & 0x00FF00) >> 8);
247     flash_addr7_0 = (uint8_t)(read_addr & 0xFF);
248 
249     while ((FTFA_FSTAT_CCIF_MASK & FTFA->FSTAT) == 0); /* Wait till CCIF=1 */
250 
251     if ((FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) == FTFA_FSTAT_ACCERR_MASK )
252     {
253         FTFA->FSTAT = (1<<FTFA_FSTAT_ACCERR_SHIFT); /* Write 1 to ACCEER to clear errors */
254     }
255 
256     FTFA->FCCOB0 = RDRSRC;
257     FTFA->FCCOB1 = flash_addr23_16;
258     FTFA->FCCOB2 = flash_addr15_8;
259     FTFA->FCCOB3 = flash_addr7_0;
260     FTFA->FCCOB8 = 0x00;
261 
262     OSA_InterruptDisable();
263     FTFA->FSTAT = FTFA_FSTAT_CCIF_MASK;
264     while ((FTFA_FSTAT_CCIF_MASK & FTFA->FSTAT) == 0); /* Wait till CCIF=1 */
265     OSA_InterruptEnable();
266 
267     /* Start reading */
268     read_data31_24 = FTFA->FCCOB4; /* FTFA->FCCOB[4] */
269     read_data23_16 = FTFA->FCCOB5; /* FTFA->FCCOB[5] */
270     read_data15_8  = FTFA->FCCOB6; /* FTFA->FCCOB[6] */
271     read_data7_0   = FTFA->FCCOB7; /* FTFA->FCCOB[7] */
272 
273     packed_data = (read_data31_24 << 24) | (read_data23_16 << 16) | (read_data15_8 << 8) | (read_data7_0 << 0);
274 
275     return packed_data;
276 }
277 #else
read_resource_ifr(uint32_t read_addr)278 uint64_t read_resource_ifr(uint32_t read_addr)
279 {
280 
281     uint64_t packed_data;
282     uint8_t flash_addr23_16, flash_addr15_8, flash_addr7_0;
283     uint8_t read_data[8];
284     uint64_t temp_64;
285     uint8_t i;
286 
287     flash_addr23_16 = (uint8_t)((read_addr & 0xFF0000) >> 16);
288     flash_addr15_8 = (uint8_t)((read_addr & 0x00FF00) >> 8);
289     flash_addr7_0 = (uint8_t)(read_addr & 0xFF);
290     while((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 */
291 
292     if ((FTFE->FSTAT & FTFE_FSTAT_ACCERR_MASK) == FTFE_FSTAT_ACCERR_MASK )
293     {
294         FTFE->FSTAT = (1<<FTFE_FSTAT_ACCERR_SHIFT); /* Write 1 to ACCEER to clear errors */
295     }
296 
297     FTFE->FCCOB0 = RDRSRC;
298     FTFE->FCCOB1 = flash_addr23_16;
299     FTFE->FCCOB2 = flash_addr15_8;
300     FTFE->FCCOB3 = flash_addr7_0;
301     FTFE->FCCOB4 = 0x00;
302 
303     OSA_InterruptDisable();
304     FTFE->FSTAT = FTFE_FSTAT_CCIF_MASK;
305     while ((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 */
306     OSA_InterruptEnable();
307 
308     /* Start reading */
309     read_data[7] = FTFE->FCCOB4;
310     read_data[6] = FTFE->FCCOB5;
311     read_data[5] = FTFE->FCCOB6;
312     read_data[4] = FTFE->FCCOB7;
313     read_data[3] = FTFE->FCCOB8;
314     read_data[2] = FTFE->FCCOB9;
315     read_data[1] = FTFE->FCCOBA;
316     read_data[0] = FTFE->FCCOBB;
317 
318     packed_data = 0;
319     for (i = 0; i < 8; i++)
320     {
321         temp_64 = read_data[i];
322         packed_data |= temp_64 << (i * 8);
323     }
324 
325     return packed_data;
326 }
327 
328 #endif /* RADIO_IS_GEN_2P0 */
329 #endif /* RADIO_IS_GEN_3P0 */
330 
331 /*! *********************************************************************************
332  * \brief  Store a SW trim value in the table passed in from calling function.
333  *
334  * \param sw_trim_tbl pointer to the software trim table to hold SW trim values
335  * \param num_entries the number of entries in the SW trim table
336  * \param addr the software trim ID
337  * \param data the value of the software trim
338  *
339 ***********************************************************************************/
store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl,uint16_t num_entries,uint32_t addr,uint32_t data)340 void store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries, uint32_t addr, uint32_t data)
341 {
342     uint16_t i;
343 
344     if (sw_trim_tbl != NULL)
345     {
346         for (i = 0; i < num_entries; i++)
347         {
348             if (addr == sw_trim_tbl[i].trim_id)
349             {
350                 sw_trim_tbl[i].trim_value = data;
351                 sw_trim_tbl[i].valid = 1;
352                 break; /* Don't need to scan the array any further... */
353             }
354         }
355     }
356 }
357 
358 /*! *********************************************************************************
359  * \brief  Process block 1 IFR data.
360  *
361  * \param sw_trim_tbl pointer to the software trim table to hold SW trim values
362  * \param num_entries the number of entries in the SW trim table
363  *
364  * \remarks
365  *  Uses a IFR v2 formatted default array if the IFR is blank or corrupted.
366  *  Stores SW trim values to an array passed into this function.
367  *
368 ***********************************************************************************/
handle_ifr(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl,uint16_t num_entries)369 void handle_ifr(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries)
370 {
371     uint32_t dest_addr;
372     uint32_t read_addr;
373     uint32_t dest_data;
374     uint32_t packed_data;
375     uint32_t *ifr_ptr;
376 
377 #if RADIO_IS_GEN_3P0
378     num_words_avail = 0; /* Prep for handling 64 bit words from flash */
379 #endif /* RADIO_IS_GEN_3P0 */
380 
381 #if RADIO_IS_GEN_3P0
382     read_addr = K3_BASE_INDEX;
383 #else
384 #ifdef CPU_MKW41Z256VHT4
385     read_addr = KW4x_256_BASE;
386 #else
387     read_addr = KW4x_512_BASE;
388 #endif /* CPU_MKW41Z256VHT4 */
389 #endif /* RADIO_IS_GEN_3P0 */
390 
391     /* Read first entry in IFR table */
392     packed_data = read_first_ifr_word(read_addr);
393     if ((packed_data&~IFR_VERSION_MASK) == IFR_VERSION_HDR)
394     {
395         /* Valid header was found, process real IFR data */
396         XCVR_MISC->OVERWRITE_VER = (packed_data & IFR_VERSION_MASK);
397         store_sw_trim(sw_trim_tbl, num_entries, 0xABCD, (packed_data & IFR_VERSION_MASK)); /* Place IFR version # in SW trim array*/
398         packed_data = read_another_ifr_word();
399 
400         while (packed_data !=IFR_EOF_SYMBOL)
401         {
402             if (IS_A_SW_ID(packed_data)) /* SW Trim case (non_reg writes) */
403             {
404                 dest_addr = packed_data;
405                 packed_data = read_another_ifr_word();
406                 dest_data = packed_data;
407                 /* Place SW trim in array for driver SW to use */
408                 store_sw_trim(sw_trim_tbl, num_entries, dest_addr, dest_data);
409             }
410             else
411             {
412                 if (IS_VALID_REG_ADDR(packed_data)) /* Valid register write address */
413                 {
414                     dest_addr = packed_data;
415                     packed_data = read_another_ifr_word();
416                     dest_data = packed_data;
417                     *(uint32_t *)(dest_addr) = dest_data;
418                 }
419                 else
420                 { /* Invalid address case */
421 
422                 }
423             }
424 
425         packed_data=read_another_ifr_word();
426         }
427     }
428     else
429     {
430         /* Valid header is not present, use blind IFR trim table */
431         ifr_ptr = (void *)BLOCK_1_IFR;
432         packed_data = *ifr_ptr;
433         XCVR_MISC->OVERWRITE_VER = (packed_data & IFR_VERSION_MASK);
434         store_sw_trim(sw_trim_tbl, num_entries, 0xABCD, (packed_data & IFR_VERSION_MASK)); /* Place IFR version # in SW trim array */
435         ifr_ptr++;
436         packed_data= *ifr_ptr;
437 
438         while (packed_data != IFR_EOF_SYMBOL)
439         {
440             if (IS_A_SW_ID(packed_data))
441             {
442                 /* SW Trim case (non_reg writes) */
443                 dest_addr = packed_data;
444                 ifr_ptr++;
445                 packed_data = *(ifr_ptr);
446                 dest_data = packed_data;
447                 /* Place SW trim in array for driver SW to use */
448                 store_sw_trim(sw_trim_tbl, num_entries, dest_addr, dest_data);
449             }
450             else
451             {
452                 dest_addr = packed_data;
453                 ifr_ptr++;
454                 packed_data = *ifr_ptr;
455                 dest_data = packed_data;
456 
457                 /* Valid register write address */
458                 if (IS_VALID_REG_ADDR(dest_addr))
459                 {
460                     *(uint32_t *)(dest_addr) = dest_data;
461                 }
462                 else
463                 {
464                     /* Invalid address case */
465                 }
466             }
467 
468             ifr_ptr++;
469             packed_data= *ifr_ptr;
470         }
471     }
472 }
473 
474 #if RADIO_IS_GEN_3P0
475 
476 #else
handle_ifr_die_id(void)477 uint32_t handle_ifr_die_id(void)
478 {
479     uint32_t id_x, id_y;
480     uint32_t id;
481 
482     id = read_resource_ifr(0x90);
483     id_x = id & 0x00FF0000;
484     id_y = id & 0x000000FF;
485 
486     return (id_x | id_y);
487 }
488 
handle_ifr_die_kw_type(void)489 uint32_t handle_ifr_die_kw_type(void)
490 {
491     uint32_t zb, ble;
492 
493     zb = read_resource_ifr(0x80) & 0x8000;
494     ble= read_resource_ifr(0x88) & 0x100000;
495 
496     return (zb | ble);
497 }
498 
499 #endif /* RADIO_IS_GEN_3P0 */
500 
501 /*! *********************************************************************************
502  * \brief  Dumps block 1 IFR data to an array.
503  *
504  * \param dump_tbl pointer to the table to hold the dumped IFR values
505  * \param num_entries the number of entries to dump
506  *
507  * \remarks
508  *  Starts at the first address in IFR and dumps sequential entries.
509  *
510 ***********************************************************************************/
dump_ifr(uint32_t * dump_tbl,uint8_t num_entries)511 void dump_ifr(uint32_t * dump_tbl, uint8_t num_entries)
512 {
513 #if RADIO_IS_GEN_3P0
514     uint32_t ifr_address = 0x20000;
515 #else
516     uint32_t ifr_address = 0x20000;
517 #endif /* RADIO_IS_GEN_3P0 */
518     uint32_t * dump_ptr = dump_tbl;
519     uint8_t i;
520 
521     *dump_ptr = read_first_ifr_word(ifr_address);
522     dump_ptr++;
523 
524     for (i = 0; i < num_entries - 1; i++)
525     {
526         *dump_ptr = read_another_ifr_word();
527         dump_ptr++;
528     }
529 }
530 
531