1 /*
2 * Copyright (c) 2023-2024, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /*
33 * ======== adc_noise.c ========
34 */
35
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <ti/drivers/rcl/RCL_Command.h>
41 #include <ti/drivers/rcl/RCL_Types.h>
42 #include <ti/drivers/rcl/RCL_Scheduler.h>
43
44 #include <ti/drivers/rcl/commands/adc_noise.h>
45
46 #include <ti/log/Log.h>
47
48 #include <ti/devices/DeviceFamily.h>
49 #include DeviceFamily_constructPath(inc/hw_lrfdrfe.h)
50 #include DeviceFamily_constructPath(inc/hw_lrfdrfe32.h)
51 #include DeviceFamily_constructPath(inc/rfe_common_ram_regs.h)
52 #include DeviceFamily_constructPath(inc/hw_lrfds2r.h)
53 #include DeviceFamily_constructPath(inc/hw_lrfdmdm.h)
54 #include DeviceFamily_constructPath(inc/hw_lrfddbell.h)
55
56 /* Storage location of S2R samples */
57 #define ADC_NOISE_SAMPLE_MEM (PBE_RAM_BASE_ADDR)
58 #define ADC_NOISE_SAMPLE_PTR ((uint32_t *)ADC_NOISE_SAMPLE_MEM)
59 /* Start-address of PBE RAM in S2R address-space */
60 #ifdef DeviceFamily_CC23X0R2
61 #define ADC_NOISE_SAMPLE_MEM_S2R_START 1024
62 #else
63 #define ADC_NOISE_SAMPLE_MEM_S2R_START 2048
64 #endif
65
66 struct
67 {
68 uint8_t synthRefsys:1;
69 uint8_t powerUp:1;
70 uint8_t s2rConversion:1;
71 } adcNoiseHandlerState;
72
73 static void RCL_Handler_Adc_Noise_configureS2R(uint32_t numWords);
74 static void RCL_Handler_Adc_Noise_powerUp(void);
75 static void RCL_Handler_Adc_Noise_powerDown(void);
76
77 /*
78 * ======== RCL_Handler_ADC_Noise_getNoise ========
79 */
RCL_Handler_ADC_Noise_getNoise(RCL_Command * cmd,LRF_Events lrfEvents,RCL_Events rclEventsIn)80 RCL_Events RCL_Handler_ADC_Noise_getNoise(RCL_Command *cmd, LRF_Events lrfEvents, RCL_Events rclEventsIn)
81 {
82 RCL_CmdAdcNoiseGet *adcCmd = (RCL_CmdAdcNoiseGet *)cmd;
83 RCL_Events rclEvents = {.value = 0};
84
85 if (rclEventsIn.setup != 0)
86 {
87 uint32_t earliestStartTime;
88 RCL_CommandStatus startTimeStatus;
89
90 /* Verify that length is greater than 0, and does not exceed maximum.
91 * A length of 0 is not handled by the S2R module, so abort here.
92 */
93 if ((adcCmd->numWords == 0) || (adcCmd->numWords > RCL_ADC_NOISE_MAX_NUM_WORDS))
94 {
95 cmd->status = RCL_CommandStatus_Error_Param;
96 rclEvents.lastCmdDone = 1;
97 return rclEvents;
98 }
99
100 /* Start by enabling refsys */
101 earliestStartTime = LRF_enableSynthRefsys();
102
103 /* Schedule new command start-time to wait for refsys */
104 startTimeStatus = RCL_Scheduler_setStartStopTimeEarliestStart(cmd, earliestStartTime);
105
106 if (startTimeStatus >= RCL_CommandStatus_Finished)
107 {
108 cmd->status = startTimeStatus;
109 rclEvents.lastCmdDone = 1;
110 LRF_disableSynthRefsys();
111 }
112 else
113 {
114 /* Initialise handler state */
115 adcNoiseHandlerState.powerUp = 0;
116 adcNoiseHandlerState.s2rConversion = 0;
117
118 /* Indicate that command has been accepted and is active */
119 cmd->status = RCL_CommandStatus_Active;
120 /* Update handler state */
121 adcNoiseHandlerState.synthRefsys = 1;
122 }
123 }
124
125 if (cmd->status == RCL_CommandStatus_Active)
126 {
127 if (rclEventsIn.timerStart != 0)
128 {
129 if (adcNoiseHandlerState.synthRefsys != 0)
130 {
131 /* Power up hardware */
132 RCL_Handler_Adc_Noise_powerUp();
133
134 /* Configure S2R */
135 RCL_Handler_Adc_Noise_configureS2R(adcCmd->numWords);
136
137 Log_printf(RclCore, Log_VERBOSE, "RFE powered up. Configured S2R for %d words, buffer: 0x%x", adcCmd->numWords, adcCmd->output);
138
139 adcNoiseHandlerState.synthRefsys = 0;
140 adcNoiseHandlerState.powerUp = 1;
141 rclEvents.cmdStarted = 1;
142 }
143
144 if (adcNoiseHandlerState.s2rConversion != 0)
145 {
146 /* Make sure S2R is complete */
147 while (HWREG_READ_LRF(LRFDS2R_BASE + LRFDS2R_O_STAT) & LRFDS2R_STAT_RUNNING_M);
148
149 /* Power down hardware */
150 RCL_Handler_Adc_Noise_powerDown();
151
152 if (adcCmd->output == NULL)
153 {
154 /* Update output pointer if it was NULL */
155 adcCmd->output = ADC_NOISE_SAMPLE_PTR;
156 }
157 else
158 {
159 /* Copy data from ADC_NOISE_SAMPLE_MEM to output buffer */
160 memcpy(adcCmd->output, ADC_NOISE_SAMPLE_PTR, adcCmd->numWords * sizeof(uint32_t));
161 /* Clear data from LRF RAM */
162 memset(ADC_NOISE_SAMPLE_PTR, 0, adcCmd->numWords * sizeof(uint32_t));
163 }
164
165 /* Command is complete */
166 adcNoiseHandlerState.s2rConversion = 0;
167 cmd->status = RCL_CommandStatus_Finished;
168 rclEvents.lastCmdDone = 1;
169 }
170 }
171
172 if (lrfEvents.rfedone != 0)
173 {
174 /* Hardware is powered up - proceed to start collecting samples */
175 if (adcNoiseHandlerState.powerUp != 0)
176 {
177 RCL_CommandStatus startTimeStatus;
178
179 /* Clear messagebox */
180 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_MSGBOX) = 0;
181 /* Start RX */
182 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_RFEAPI) = 3;
183 /* Wait until RX is up and running */
184 while ((HWREG_READ_LRF(LRFDMDM_BASE + LRFDMDM_O_RFECMDIN) & 0x08) == 0);
185
186 /* Disable LNA and mixer clocks to reduce impact of any signal received on the antenna */
187 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_LNA) = HWREG_READ_LRF(LRFDRFE_BASE + LRFDRFE_O_LNA) & (~LRFDRFE_LNA_EN_M);
188 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_DIVCTL) = HWREG_READ_LRF(LRFDRFE_BASE + LRFDRFE_O_DIVCTL) & (~(LRFDRFE_DIVCTL_RXPH90DIV_M | LRFDRFE_DIVCTL_RXPH0DIV_M));
189
190 /* Initialize and enable ADC digital interface */
191 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_INIT) = (LRFDMDM_INIT_ADCDIG_RESET);
192 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_ENABLE) = (LRFDMDM_ENABLE_ADCDIG_EN);
193
194 /* Trigger one shot capture */
195 HWREG_WRITE_LRF(LRFDS2R_BASE + LRFDS2R_O_TRIG) = LRFDS2R_TRIG_TRIG_ARM;
196
197 /* Wait for S2R to complete by setting new handler start time.
198 * ADC sampling frequency is ~11.5 MSamples/s, or 3 words / 260 ns.
199 * We wait for (words / 3) * 250 ns, as there is no need to sleep for too long.
200 * The handler will poll for the last few us before reading out the samples
201 */
202 startTimeStatus = RCL_Scheduler_setNewStartAbsTime(RCL_Scheduler_getCurrentTime() + adcCmd->numWords / 3, true);
203
204 if (startTimeStatus >= RCL_CommandStatus_Finished)
205 {
206 cmd->status = startTimeStatus;
207 rclEvents.lastCmdDone = 1;
208 RCL_Handler_Adc_Noise_powerDown();
209 }
210 else
211 {
212 adcNoiseHandlerState.s2rConversion = 1;
213 }
214
215 adcNoiseHandlerState.powerUp = 0;
216 }
217 }
218 }
219
220 return rclEvents;
221 }
222
223 /*
224 * ======== RCL_Handler_Adc_Noise_configureS2R ========
225 */
RCL_Handler_Adc_Noise_configureS2R(uint32_t numWords)226 static void RCL_Handler_Adc_Noise_configureS2R(uint32_t numWords)
227 {
228 /* Enable S2R module */
229 LRF_setRclClockEnable(LRFDDBELL_CLKCTL_S2R_M);
230
231 /* Configure S2R */
232 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_ADCDIGCONF) = (1 << LRFDMDM_ADCDIGCONF_QBRANCHEN_S) | (1 << LRFDMDM_ADCDIGCONF_IBRANCHEN_S);
233
234 /* Setup Sample capture, use PBE RAM for storing samples */
235 HWREG_WRITE_LRF(LRFDS2R_BASE + LRFDS2R_O_CFG) = ((LRFDS2R_CFG_TRIGMODE_ONESHOT) |
236 (LRFDS2R_CFG_SEL_ADCDIG) |
237 (LRFDS2R_CFG_CTL_EN));
238
239 /* Set start-address of where to store samples */
240 HWREG_WRITE_LRF(LRFDS2R_BASE + LRFDS2R_O_START) = ADC_NOISE_SAMPLE_MEM_S2R_START;
241 /* Set stop-address */
242 HWREG_WRITE_LRF(LRFDS2R_BASE + LRFDS2R_O_STOP) = ADC_NOISE_SAMPLE_MEM_S2R_START + numWords - 1;
243 }
244
245 /*
246 * ======== RCL_Handler_Adc_Noise_powerUp ========
247 */
RCL_Handler_Adc_Noise_powerUp(void)248 static void RCL_Handler_Adc_Noise_powerUp(void)
249 {
250 /* Write precomputed frequency words, based on frequency: 2440000000 */
251 HWREGH_WRITE_LRF(LRFD_RFERAM_BASE + RFE_COMMON_RAM_O_K5) = 0x9160;
252 HWREG_WRITE_LRF(LRFDRFE32_BASE + LRFDRFE32_O_CALMMID_CALMCRS) = 0x098604C4;
253 HWREG_WRITE_LRF(LRFDRFE32_BASE + LRFDRFE32_O_PLLM0) = 0x130E0000;
254 HWREG_WRITE_LRF(LRFDRFE32_BASE + LRFDRFE32_O_PLLM1) = 0x163B0000;
255 HWREGH_WRITE_LRF(LRFD_RFERAM_BASE + RFE_COMMON_RAM_O_RXIF) = 0;
256
257 /* Initialize and enable RFE TOPsm */
258 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_INIT) = (1 << LRFDRFE_INIT_TOPSM_S);
259 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_ENABLE) = (1 << LRFDRFE_ENABLE_TOPSM_S);
260
261 /* Wait for boot done */
262 while(HWREG_READ_LRF(LRFDRFE_BASE + LRFDRFE_O_MSGBOX) != 4);
263
264 /* Clear message box */
265 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_MSGBOX) = 0;
266 /* Calibrate PLL */
267 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_RFEAPI) = 4;
268
269 /* Enable RFEDONE interrupt */
270 LRF_enableHwInterrupt(LRFDDBELL_IMASK0_RFEDONE_M);
271 }
272
273 /*
274 * ======== RCL_Handler_Adc_Noise_powerDown ========
275 */
RCL_Handler_Adc_Noise_powerDown(void)276 static void RCL_Handler_Adc_Noise_powerDown(void)
277 {
278 /* Disable RFEDONE interrupt */
279 LRF_disableHwInterrupt(LRFDDBELL_IMASK0_RFEDONE_M);
280
281 /* Clear message box */
282 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_MSGBOX) = 0;
283 /* Stop Radio */
284 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_RFEAPI) = 1;
285
286 /* Wait until radio stops */
287 while (HWREG_READ_LRF(LRFDPBE_BASE + LRFDPBE_O_RFEMSGBOX) == 0);
288
289 /* Clear message box */
290 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_MSGBOX) = 0;
291 /* Stop PLL */
292 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_RFEAPI) = 5;
293
294 /* Wait until radio stops */
295 while (HWREG_READ_LRF(LRFDPBE_BASE + LRFDPBE_O_RFEMSGBOX) == 0);
296
297 /* Disable S2R module */
298 HWREG_WRITE_LRF(LRFDS2R_BASE + LRFDS2R_O_CFG) = 0;
299 /* Initialize/Reset (needed for safe shut down of ADCDIG) */
300 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_INIT) = 0xFFFF;
301 /* Stop modem */
302 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_ENABLE) = 0;
303
304 /* Request RFE powerdown */
305 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_PDREQ) = LRFDRFE_PDREQ_TOPSMPDREQ_M;
306 /* Disable all RFE modules */
307 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_ENABLE) = 0;
308 /* Stop powerdown request */
309 HWREG_WRITE_LRF(LRFDRFE_BASE + LRFDRFE_O_PDREQ) = 0;
310
311 /* Disable S2R module */
312 LRF_clearRclClockEnable(LRFDDBELL_CLKCTL_S2R_M);
313
314 /* Disable refsys */
315 LRF_disableSynthRefsys();
316 }
317