1 /*!
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * All rights reserved.
4 *
5 * \file ifr_mkw40z4_radio.c
6 * MKW40Z4 Radio IFR pack/unpack function.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * o Redistributions of source code must retain the above copyright notice, this list
12 *   of conditions and the following disclaimer.
13 *
14 * o Redistributions in binary form must reproduce the above copyright notice, this
15 *   list of conditions and the following disclaimer in the documentation and/or
16 *   other materials provided with the distribution.
17 *
18 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
19 *   contributors may be used to endorse or promote products derived from this
20 *   software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
26 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 /*! *********************************************************************************
35 *************************************************************************************
36 * Include
37 *************************************************************************************
38 ********************************************************************************** */
39 #include "EmbeddedTypes.h"
40 #include "ifr_mkw40z4_radio.h"
41 #include "MKW40Z4.h"
42 #include "KW4xXcvrDrv.h"
43 #include "fsl_device_registers.h"
44 #include "fsl_os_abstraction.h"
45 
46 /*! *********************************************************************************
47 *************************************************************************************
48 * Private macros
49 *************************************************************************************
50 ********************************************************************************** */
51 #define IFR_RAM                 (0)
52 
53 #define mem32(x) (*(volatile uint32_t *)(x))
54 
55 /*! *********************************************************************************
56 *************************************************************************************
57 * Public memory declarations
58 *************************************************************************************
59 ********************************************************************************** */
60 const uint32_t BLOCK_1_IFR[]=
61 {
62     0xABCDFFFE, /* Version #FFFE indicates default trim values */
63     0x4005C47C, /* IQMC_DC_GAIN_ADJ id */
64     0x00000400, /* IQMC_DC_GAIN_ADJ default value */
65     0x4005C050, /* XCVR_IQMC_CAL id */
66     0x00000400, /* IQMC_PHASE_ADJ=0x000 and IQMC_GAIN_ADJ=0x400 (default value) */
67     0x4005C43C, /* XCVR_BGAP_CTRL id */
68     0x00000087, /* XCVR_BGAP_CTRL=0x08 */
69     0x00000002, /* ZB_FILT_TRIM id */
70     0x00440000, /* ZB_FILT_TRIM = BBF_CAP_TUNE<3:0>=0x3, BBF_RES_TUNE2<3:0>=0x3, TZA_CAP_TUNE<3:0>=0x3 */
71     0x00000003, /* BLE_FILT_TRIM id */
72     0x00640004, /* BLE_FILT_TRIM = BBF_CAP_TUNE<3:0>=0x3, BBF_RES_TUNE2<3:0>=0x6, TZA_CAP_TUNE<3:0>=0x3 */
73     0x4005C024, /* BBF_DCOC_STEP */
74     0x00000147,
75     0x4005C028, /* BBF_DCOC_STEP_RCP */
76     0x00000322,
77     0x4005C110, /* TZA_DCOC_STEP & STEP_RCP */
78     0x005C0B21,
79     0x4005C114,
80     0x008D0743,
81     0x4005C118,
82     0x00D304DA,
83     0x4005C11C,
84     0x0136034E,
85     0x4005C120,
86     0x01C30245,
87     0x4005C124,
88     0x02890194,
89     0x4005C128,
90     0x03A3011A,
91     0x4005C12C,
92     0x053000C5,
93     0x4005C130,
94     0x0761008B,
95     0x4005C134,
96     0x0A790062,
97     0x4005C138,
98     0x0ED70045,
99     /* No TRIM_STATUS in SW fallback array. */
100     0xFEED0E0F /* End of File */
101 };
102 
103 /*! *********************************************************************************
104 *************************************************************************************
105 * Private prototypes
106 *************************************************************************************
107 ********************************************************************************** */
108 void store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries, uint32_t addr, uint32_t data);
109 
110 /*! *********************************************************************************
111 *************************************************************************************
112 * Public functions
113 *************************************************************************************
114 ********************************************************************************** */
115 
116 /*! *********************************************************************************
117 * \brief   Read Resource IFR
118 *
119 *  Read command for reading from IFR
120 *
121 * \param[in] read_addr flash address
122 *
123 * \return Packed data containing radio trims only.
124 *
125 ********************************************************************************** */
read_resource_ifr(uint32_t read_addr)126 uint32_t read_resource_ifr(uint32_t read_addr)
127 {
128     uint32_t packed_data;
129     uint8_t flash_addr23_16,flash_addr15_8,flash_addr7_0;
130     uint8_t read_data31_24,read_data23_16,read_data15_8,read_data7_0;
131 
132     flash_addr23_16 = (uint8_t)((read_addr & 0xFF0000)>>16);
133     flash_addr15_8 = (uint8_t)((read_addr & 0x00FF00)>>8);
134     flash_addr7_0 = (uint8_t)(read_addr & 0xFF);
135 
136 #if SILICON
137     while((FTFA_FSTAT_CCIF_MASK & FTFA->FSTAT)==0); /* Wait till CCIF=1 */
138 #endif /* SILICON */
139 
140     if ((FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK)== FTFA_FSTAT_ACCERR_MASK )
141     {
142         FTFA->FSTAT = (1<<FTFA_FSTAT_ACCERR_SHIFT); /* Write 1 to ACCEER to clear errors */
143     }
144 
145     FTFA_WR_FCCOB0(FTFA,RDRSRC);
146     FTFA_WR_FCCOB1(FTFA,flash_addr23_16);
147     FTFA_WR_FCCOB2(FTFA,flash_addr15_8);
148     FTFA_WR_FCCOB3(FTFA,flash_addr7_0);
149     FTFA_WR_FCCOB8(FTFA,0);
150 
151     /* Disable Interrupts */
152     OSA_InterruptDisable();
153 
154     FTFA->FSTAT = FTFA_FSTAT_CCIF_MASK;
155     while((FTFA_FSTAT_CCIF_MASK & FTFA->FSTAT)==0); /* Wait till CCIF=1 */
156 
157     /* Enable Interrupts */
158     OSA_InterruptEnable();
159 
160     /* Start reading */
161     read_data31_24 = FTFA->FCCOB4;
162     read_data23_16 = FTFA->FCCOB5;
163     read_data15_8  = FTFA->FCCOB6;
164     read_data7_0   = FTFA->FCCOB7;
165 
166     packed_data = (read_data31_24<<24)|(read_data23_16<<16)|(read_data15_8<<8)|(read_data7_0<<0);
167 
168     return packed_data;
169 }
170 
read_resource(uint16_t resource_id)171 uint32_t read_resource(uint16_t resource_id)
172 {
173     uint32_t ifr_addr;
174 
175     /* Return the test arrays of packed bits */
176     switch (resource_id)
177     {
178     case 0x84:
179 #if IFR_RAM
180         return 0x4370; /* TZA_CAP_TUNE=0b0100; BBF_CAP_TUNE=4�b0011; BBF_RES_TUNE2=4�b0111 */
181 #else
182         ifr_addr = read_resource_ifr(0x84);
183         return ifr_addr;
184 #endif
185         break;
186     case 0x98:
187 #if IFR_RAM
188         return 0x40000000; /* IQMC_GAIN)ADJ=0b10000000000 */
189 #else
190         ifr_addr = read_resource_ifr(0x98);
191         return ifr_addr;
192 #endif
193         break;
194     case 0x9C:
195 #if IFR_RAM
196         return 0x37000000; /* BGAP_V Trim = 0b0011 & BGAP_I Trim=0b0111 */
197 #else
198         ifr_addr = read_resource_ifr(0x9C);
199         return ifr_addr;
200 #endif
201     case 0x90:
202         ifr_addr = read_resource_ifr(0x90);
203         return ifr_addr;
204 
205     case 0x80:
206         ifr_addr = read_resource_ifr(0x80);
207         return ifr_addr;
208 
209     case 0x88:
210         ifr_addr = read_resource_ifr(0x88);
211         return ifr_addr;
212 
213         break;
214     default:
215         return 0x12345678;
216         break;
217     }
218 }
219 
220 /*! *********************************************************************************
221 * \brief   Store a SW trim value in the table passed in from calling function.
222 *
223 * \param[in]  sw_trim_tbl pointer to the software trim table to hold SW trim values
224 * \param[in]  num_entries the number of entries in the SW trim table
225 * \param[in]  addr the software trim ID
226 * \param[in]  data the value of the software trim
227 *
228 * \return None.
229 *
230 ********************************************************************************** */
store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl,uint16_t num_entries,uint32_t addr,uint32_t data)231 void store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries, uint32_t addr, uint32_t data)
232 {
233     uint16_t i;
234     if (sw_trim_tbl != NULL)
235     {
236         for (i=0;i<num_entries;i++)
237         {
238             if (addr == sw_trim_tbl[i].trim_id)
239             {
240                 sw_trim_tbl[i].trim_value = data;
241                 sw_trim_tbl[i].valid = TRUE;
242                 break;
243             }
244         }
245     }
246 }
247 
248 /*! *********************************************************************************
249 * \brief   Process block 1 IFR data.
250 *
251 * \param[in]  sw_trim_tbl pointer to the software trim table to hold SW trim values
252 * \param[in]  num_entries the number of entries in the SW trim table
253 * \param[in]  addr the software trim ID
254 * \param[in]  data the value of the software trim
255 *
256 * \return None.
257 *
258 * \remarks  Uses a IFR v2 formatted default array if the IFR is blank or corrupted.
259 *           Stores SW trim values to an array passed into this function.
260 *
261 ********************************************************************************** */
handle_ifr(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl,uint16_t num_entries)262 void handle_ifr(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries)
263 {
264     uint32_t read_addr = 0x20000;
265     uint32_t dest_addr, dest_data, packed_data;
266     uint32_t *ifr_ptr;
267 
268     /* Read first entry in IFR table */
269     packed_data=read_resource_ifr(read_addr);
270     read_addr+=4;
271     if ((packed_data&~IFR_VERSION_MASK)==IFR_VERSION_HDR)
272     {
273         /* Valid header was found, process real IFR data */
274         XCVR_OVERWRITE_VER = (uint32_t)(packed_data & IFR_VERSION_MASK);
275         store_sw_trim(sw_trim_tbl, num_entries, 0xABCD, (packed_data & IFR_VERSION_MASK)); /* Place IFR version # in SW trim array */
276         packed_data = read_resource_ifr(read_addr);
277         while (packed_data !=IFR_EOF_SYMBOL)
278         {
279             if(IS_A_SW_ID(packed_data)) /* SW Trim case (non_reg writes) */
280             {
281                 dest_addr = packed_data;
282                 read_addr+=4;
283                 packed_data = read_resource_ifr(read_addr);
284                 dest_data = packed_data;
285                 /* Place SW trim in array for driver SW to use */
286                 store_sw_trim(sw_trim_tbl, num_entries, dest_addr, dest_data);
287             }
288             else
289             {
290                 if (IS_VALID_REG_ADDR(packed_data)) /* Valid register write address */
291                 {
292                     dest_addr = packed_data;
293                     read_addr+=4;
294                     packed_data = read_resource_ifr(read_addr);
295                     dest_data = packed_data;
296                     mem32(dest_addr) = dest_data;
297                 }
298                 else
299                 {
300                     /* Invalid address case */
301                 }
302             }
303             read_addr+=4;
304             packed_data=read_resource_ifr(read_addr);
305         }
306     }
307     else
308     {
309         /*  Valid header is not present, use blind IFR trim table */
310         ifr_ptr = (uint32_t*) BLOCK_1_IFR;
311         packed_data = *ifr_ptr;
312         XCVR_OVERWRITE_VER = (uint32_t)(packed_data & IFR_VERSION_MASK);
313         store_sw_trim(sw_trim_tbl, num_entries, 0xABCD, (packed_data & IFR_VERSION_MASK)); /* Place IFR version # in SW trim array */
314         ifr_ptr++;
315         packed_data= *ifr_ptr;
316         while(packed_data!=IFR_EOF_SYMBOL)
317         {
318             if(IS_A_SW_ID(packed_data)) /* SW Trim case (non_reg writes) */
319             {
320                 dest_addr = packed_data;
321                 ifr_ptr++;
322                 packed_data = *(ifr_ptr);
323                 dest_data = packed_data;
324                 /* Place SW trim in array for driver SW to use */
325                 store_sw_trim(sw_trim_tbl, num_entries, dest_addr, dest_data);
326             }
327             else
328             {
329                 dest_addr = packed_data;
330                 ifr_ptr++;
331                 packed_data = *ifr_ptr;
332                 dest_data = packed_data;
333                 if (IS_VALID_REG_ADDR(dest_addr)) /* Valid register write address */
334                 {
335                     mem32(dest_addr)=dest_data;
336                 }
337                 else
338                 {
339                     /* Invalid address case */
340                 }
341             }
342             ifr_ptr++;
343             packed_data= *ifr_ptr;
344         }
345     }
346 }
347 
handle_ifr_die_id(void)348 uint32_t handle_ifr_die_id(void)
349 {
350     uint32_t id_x,id_y;
351     uint32_t id;
352 
353     id = read_resource_ifr(0x90);
354     id_x = id&0x00FF0000;
355     id_y = id&0x000000FF;
356     return (id_x|id_y);
357 }
358 
handle_ifr_die_kw_type(void)359 uint32_t handle_ifr_die_kw_type(void)
360 {
361     uint32_t zb,ble;
362 
363     zb = read_resource_ifr(0x80)&0x8000;
364     ble= read_resource_ifr(0x88)&0x100000;
365 
366     return (zb|ble);
367 }
368 
369 /*! *********************************************************************************
370 * \brief   Dumps block 1 IFR data to an array.
371 *
372 * \param[in] dump_tbl pointer to the table to hold the dumped IFR values
373 * \param[in] num_entries the number of entries to dump
374 *
375 * \return None.
376 *
377 * \remarks   Starts at the first address in IFR and dumps sequential entries.
378 *
379 ********************************************************************************** */
dump_ifr(uint32_t * dump_tbl,uint8_t num_entries)380 void dump_ifr(uint32_t * dump_tbl, uint8_t num_entries)
381 {
382     uint32_t ifr_address = 0x20000;
383     uint32_t * dump_ptr = dump_tbl;
384     uint8_t i;
385 
386     for (i=0;i<num_entries;i++)
387     {
388         *dump_ptr = read_resource_ifr(ifr_address);
389         dump_ptr++;
390         ifr_address += 4;
391     }
392 }
393