[6319] | 1 | /** @file wlan_platform_low.c |
---|
| 2 | * @brief Platform abstraction for CPU Low |
---|
| 3 | * |
---|
| 4 | * @copyright Copyright 2014-2019, Mango Communications. All rights reserved. |
---|
| 5 | * Distributed under the Mango Communications Reference Design License |
---|
| 6 | * See LICENSE.txt included in the design archive or |
---|
| 7 | * at http://mangocomm.com/802.11/license |
---|
| 8 | * |
---|
| 9 | * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) |
---|
| 10 | */ |
---|
| 11 | |
---|
| 12 | // SDK includes |
---|
| 13 | #include "xil_types.h" |
---|
| 14 | #include "xil_cache.h" |
---|
| 15 | #include "xil_assert.h" |
---|
| 16 | #include "xstatus.h" |
---|
| 17 | #include "xparameters.h" |
---|
| 18 | #include "stdarg.h" |
---|
| 19 | #include "stdlib.h" |
---|
| 20 | |
---|
| 21 | // Platform includes |
---|
| 22 | #include "w3_mac_phy_regs.h" |
---|
| 23 | #include "w3_low.h" |
---|
| 24 | #include "w3_common.h" |
---|
| 25 | #include "wlan_platform_common.h" |
---|
| 26 | #include "wlan_platform_debug_hdr.h" |
---|
| 27 | #include "wlan_platform_low.h" |
---|
| 28 | #include "w3_userio_util.h" |
---|
| 29 | #include "w3_phy_util.h" |
---|
| 30 | |
---|
| 31 | // Low framework includes |
---|
| 32 | #include "wlan_phy_util.h" |
---|
| 33 | #include "wlan_mac_low.h" |
---|
| 34 | #include "wlan_mac_common.h" |
---|
| 35 | #include "wlan_mac_mailbox_util.h" |
---|
| 36 | #include "wlan_common_types.h" |
---|
| 37 | |
---|
| 38 | // WARP v3 hardware includes |
---|
| 39 | #include "w3_userio.h" |
---|
| 40 | #include "w3_ad_controller.h" |
---|
| 41 | #include "w3_clock_controller.h" |
---|
| 42 | #include "radio_controller.h" |
---|
| 43 | #include "w3_phy_util.h" |
---|
| 44 | |
---|
| 45 | static channel_band_t gl_current_band; |
---|
| 46 | static int gl_enable_phy_cs; |
---|
| 47 | platform_common_dev_info_t platform_common_dev_info; |
---|
| 48 | |
---|
| 49 | /***************************************************************************** |
---|
| 50 | * Public functions - the functions below are exported to the low framework |
---|
| 51 | *****************************************************************************/ |
---|
| 52 | void wlan_platform_cpu_low_init() { |
---|
| 53 | |
---|
| 54 | Xil_AssertSetCallback((Xil_AssertCallback)wlan_assert_print); |
---|
| 55 | |
---|
| 56 | // Disable I and D cache |
---|
| 57 | Xil_DCacheDisable(); |
---|
| 58 | Xil_ICacheDisable(); |
---|
| 59 | |
---|
| 60 | // Enable exceptions - exceptions *must* be enabled on MicroBlaze |
---|
| 61 | // to handle unaligned memory accesses |
---|
| 62 | microblaze_enable_exceptions(); |
---|
| 63 | |
---|
| 64 | return; |
---|
| 65 | } |
---|
| 66 | |
---|
| 67 | void wlan_platform_low_userio_disp_status(userio_disp_low_status_t status, ...){ |
---|
| 68 | va_list valist; |
---|
| 69 | static u8 red_led_index = 0; |
---|
| 70 | static u8 green_led_index = 0; |
---|
| 71 | |
---|
| 72 | /* initialize valist for num number of arguments */ |
---|
| 73 | va_start(valist, status); |
---|
| 74 | |
---|
| 75 | switch(status){ |
---|
| 76 | |
---|
| 77 | case USERIO_DISP_STATUS_GOOD_FCS_EVENT: { |
---|
| 78 | green_led_index = (green_led_index + 1) % 4; |
---|
| 79 | userio_write_leds_green(USERIO_BASEADDR, (1<<green_led_index)); |
---|
| 80 | } break; |
---|
| 81 | |
---|
| 82 | case USERIO_DISP_STATUS_BAD_FCS_EVENT: { |
---|
| 83 | red_led_index = (red_led_index + 1) % 4; |
---|
| 84 | userio_write_leds_red(USERIO_BASEADDR, (1<<red_led_index)); |
---|
| 85 | } break; |
---|
| 86 | case USERIO_DISP_STATUS_CPU_ERROR: { |
---|
| 87 | u32 error_code = va_arg(valist, u32); |
---|
| 88 | if (error_code != WLAN_ERROR_CPU_STOP) { |
---|
| 89 | // Print error message |
---|
| 90 | xil_printf("\n\nERROR: CPU is halting with error code: E%X\n\n", (error_code & 0xF)); |
---|
| 91 | |
---|
| 92 | // Set the error code on the hex display |
---|
| 93 | set_hex_display_error_status(error_code & 0xF); |
---|
| 94 | |
---|
| 95 | // Enter infinite loop blinking the hex display |
---|
| 96 | blink_hex_display(0, 250000); |
---|
| 97 | } else { |
---|
| 98 | // Stop execution |
---|
| 99 | while (1) {}; |
---|
| 100 | } |
---|
| 101 | } break; |
---|
| 102 | |
---|
| 103 | default: |
---|
| 104 | break; |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | /* clean memory reserved for valist */ |
---|
| 108 | va_end(valist); |
---|
| 109 | return; |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | int wlan_platform_low_init() { |
---|
| 113 | int status; |
---|
| 114 | |
---|
| 115 | // Get the device info |
---|
| 116 | platform_common_dev_info = wlan_platform_common_get_dev_info(); |
---|
| 117 | |
---|
| 118 | status = w3_node_init(); |
---|
| 119 | if(status != 0) { |
---|
| 120 | xil_printf("ERROR in w3_node_init(): %d\n", status); |
---|
| 121 | return status; |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | w3_radio_init(); |
---|
| 125 | w3_agc_init(); |
---|
| 126 | |
---|
| 127 | wlan_platform_enable_phy_cs(0); //Call once so we know the global gets set. This will be overwritten when the Low Framework |
---|
| 128 | //finishes booting |
---|
| 129 | |
---|
| 130 | return WLAN_SUCCESS; |
---|
| 131 | } |
---|
| 132 | |
---|
| 133 | |
---|
| 134 | void max2829_tune_custom(u32 band_sel, u32 pll_div_int, u32 pll_div_frac) { |
---|
| 135 | // band_sel: 0=2.4GHz; 1=4.9-5.35GHz; 2=>5.35GHz |
---|
| 136 | // pll_div_int: integer part of PLL divider ratio |
---|
| 137 | // pll_div_frac: fractional part of PLL divider ratio as UFix16_16 |
---|
| 138 | |
---|
| 139 | // Assume only RF A |
---|
| 140 | u32 rfSel = RC_RFA; |
---|
| 141 | |
---|
| 142 | u16 reg3 = (pll_div_int & 0x0FF) | ((pll_div_frac & 0x3) << 12); //reg3[11:8] always 0 |
---|
| 143 | u16 reg4 = (pll_div_frac & 0xFFFC) >> 2; |
---|
| 144 | |
---|
| 145 | if(band_sel == 0) { |
---|
| 146 | //MAX2829 tuning process for 2.4GHz channels: |
---|
| 147 | // -Set reg5[0] to 0 (selects 2.4GHz) |
---|
| 148 | // -Set reg3, reg4 with PLL tuning params |
---|
| 149 | |
---|
| 150 | radio_controller_SPI_setRegBits(RC_BASEADDR, rfSel, 5, 0x1, 0x0); |
---|
| 151 | |
---|
| 152 | //Write the PLL parameters |
---|
| 153 | radio_controller_SPI_write(RC_BASEADDR, rfSel, 3, reg3); |
---|
| 154 | radio_controller_SPI_write(RC_BASEADDR, rfSel, 4, reg4); |
---|
| 155 | |
---|
| 156 | } else { |
---|
| 157 | //MAX2829 tuning process for 5GHz channels: |
---|
| 158 | //(Assumes default config of 5GHz sub-band tuning via FSM; see MAX2829 datasheet for details) |
---|
| 159 | // -Set: |
---|
| 160 | // -reg5[0] to 1 (selects 5GHz) |
---|
| 161 | // -reg5[6] based on selected freq (0:4.9-5.35GHz, 1:5.47-5.875GHz) |
---|
| 162 | // -Set reg3, reg4 with PLL tuning params |
---|
| 163 | |
---|
| 164 | if(band_sel == 1) |
---|
| 165 | radio_controller_SPI_setRegBits(RC_BASEADDR, rfSel, 5, 0x41, 0x01); |
---|
| 166 | else |
---|
| 167 | radio_controller_SPI_setRegBits(RC_BASEADDR, rfSel, 5, 0x41, 0x41); |
---|
| 168 | |
---|
| 169 | //Reset the band select FSM |
---|
| 170 | radio_controller_SPI_setRegBits(RC_BASEADDR, rfSel, 5, 0x80, 0x00); |
---|
| 171 | |
---|
| 172 | //Write the PLL parameters |
---|
| 173 | radio_controller_SPI_write(RC_BASEADDR, rfSel, 3, reg3); |
---|
| 174 | radio_controller_SPI_write(RC_BASEADDR, rfSel, 4, reg4); |
---|
| 175 | |
---|
| 176 | //Start the band select FSM |
---|
| 177 | radio_controller_SPI_setRegBits(RC_BASEADDR, rfSel, 5, 0x80, 0x80); |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | return; |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | void wlan_platform_low_param_handler(u8 mode, u32* payload) { |
---|
| 184 | if(mode != IPC_REG_WRITE_MODE) { |
---|
| 185 | xil_printf("ERROR wlan_platform_low_param_handler: unrecognized mode (%d) - mode must be WRITE\n", mode); |
---|
| 186 | return; |
---|
| 187 | } |
---|
| 188 | |
---|
| 189 | switch(payload[0]) { |
---|
| 190 | case LOW_PARAM_PKT_DET_MIN_POWER: { |
---|
| 191 | int min_pwr_arg = payload[1] & 0xFF; |
---|
| 192 | if(min_pwr_arg == 0) { |
---|
| 193 | // wlan_exp uses 0 to disable min power det logic |
---|
| 194 | wlan_platform_set_pkt_det_min_power(0); |
---|
| 195 | } else { |
---|
| 196 | //The value sent from wlan_exp will be unsigned with 0 representing PKT_DET_MIN_POWER_MIN |
---|
| 197 | wlan_platform_set_pkt_det_min_power(min_pwr_arg + PKT_DET_MIN_POWER_MIN); |
---|
| 198 | } |
---|
| 199 | } |
---|
| 200 | break; |
---|
| 201 | |
---|
| 202 | case LOW_PARAM_CUSTOM_MAX2829_TUNE: { |
---|
| 203 | // Tune MAX2829 PLL to non-standard center frequency |
---|
| 204 | max2829_tune_custom(payload[1], payload[2], payload[3]); |
---|
| 205 | } |
---|
| 206 | break; |
---|
| 207 | |
---|
| 208 | default: |
---|
| 209 | break; |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | return; |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | void wlan_platform_low_set_rx_ant_mode(u32 ant_mode) { |
---|
| 216 | |
---|
| 217 | // Disable PHY control of all RF interfaces - selected interfaces to re-enabled below |
---|
| 218 | radio_controller_setCtrlSource(RC_BASEADDR, RC_ALL_RF, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_REG); |
---|
| 219 | |
---|
| 220 | // Set the antenna mode |
---|
| 221 | // |
---|
| 222 | // For each antenna mode: |
---|
| 223 | // - Enable packet detection |
---|
| 224 | // - Select I/Q stream for Rx PHY |
---|
| 225 | // - Give PHY control of Tx/Rx status |
---|
| 226 | // - Configure AGC |
---|
| 227 | // |
---|
| 228 | switch (ant_mode) { |
---|
| 229 | case RX_ANTMODE_SISO_ANTA: |
---|
| 230 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFA, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 231 | break; |
---|
| 232 | |
---|
| 233 | case RX_ANTMODE_SISO_ANTB: |
---|
| 234 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFB, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 235 | break; |
---|
| 236 | |
---|
| 237 | case RX_ANTMODE_SISO_ANTC: |
---|
| 238 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFC, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 239 | break; |
---|
| 240 | |
---|
| 241 | case RX_ANTMODE_SISO_ANTD: |
---|
| 242 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFD, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 243 | break; |
---|
| 244 | |
---|
| 245 | case RX_ANTMODE_SISO_SELDIV_2ANT: |
---|
| 246 | // By enabling the antenna switching, the I/Q stream is automatically switched for Rx PHY |
---|
| 247 | radio_controller_setCtrlSource(RC_BASEADDR, (RC_RFA | RC_RFB), RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 248 | break; |
---|
| 249 | |
---|
| 250 | case RX_ANTMODE_SISO_SELDIV_4ANT: |
---|
| 251 | // By enabling the antenna switching, the I/Q stream is automatically switched for Rx PHY |
---|
| 252 | radio_controller_setCtrlSource(RC_BASEADDR, RC_ALL_RF, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 253 | break; |
---|
| 254 | |
---|
| 255 | default: |
---|
| 256 | // Default to SISO on A if user provides invalid mode |
---|
| 257 | xil_printf("wlan_platform_low_set_rx_ant_mode ERROR: Invalid Mode - Defaulting to SISO on A\n"); |
---|
| 258 | |
---|
| 259 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFA, RC_REG0_RXEN_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 260 | break; |
---|
| 261 | } |
---|
| 262 | return; |
---|
| 263 | } |
---|
| 264 | |
---|
| 265 | int wlan_platform_low_set_samp_rate(phy_samp_rate_t phy_samp_rate) { |
---|
| 266 | if((phy_samp_rate != PHY_10M) && (phy_samp_rate != PHY_20M) && (phy_samp_rate != PHY_40M)) { |
---|
| 267 | xil_printf("Invalid PHY samp rate (%d)\n", phy_samp_rate); |
---|
| 268 | return WLAN_FAILURE; |
---|
| 269 | } |
---|
| 270 | |
---|
| 271 | // Assert PHY Tx/Rx and MAC Resets |
---|
| 272 | REG_SET_BITS(WLAN_RX_REG_CTRL, WLAN_RX_REG_CTRL_RESET); |
---|
| 273 | REG_SET_BITS(WLAN_TX_REG_CFG, WLAN_TX_REG_CFG_RESET); |
---|
| 274 | wlan_mac_reset(1); |
---|
| 275 | |
---|
| 276 | // Set RF interface clocking and interp/decimation filters |
---|
| 277 | switch(phy_samp_rate){ |
---|
| 278 | case PHY_40M: |
---|
| 279 | // Set ADC_CLK=DAC_CLK=40MHz, interp_rate=decim_rate=1 |
---|
| 280 | clk_config_dividers(CLK_BASEADDR, 2, (CLK_SAMP_OUTSEL_AD_RFA | CLK_SAMP_OUTSEL_AD_RFB)); |
---|
| 281 | ad_config_filters(AD_BASEADDR, AD_ALL_RF, 1, 1); |
---|
| 282 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x32, 0x2F); |
---|
| 283 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x00); |
---|
| 284 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x08); |
---|
| 285 | break; |
---|
| 286 | case PHY_20M: |
---|
| 287 | // Set ADC_CLK=DAC_CLK=40MHz, interp_rate=decim_rate=2 |
---|
| 288 | clk_config_dividers(CLK_BASEADDR, 2, (CLK_SAMP_OUTSEL_AD_RFA | CLK_SAMP_OUTSEL_AD_RFB)); |
---|
| 289 | |
---|
| 290 | ad_config_filters(AD_BASEADDR, AD_ALL_RF, 2, 2); |
---|
| 291 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x32, 0x27); |
---|
| 292 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x00); |
---|
| 293 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x08); |
---|
| 294 | break; |
---|
| 295 | case PHY_10M: |
---|
| 296 | // Set ADC_CLK=DAC_CLK=20MHz, interp_rate=decim_rate=2 |
---|
| 297 | clk_config_dividers(CLK_BASEADDR, 4, (CLK_SAMP_OUTSEL_AD_RFA | CLK_SAMP_OUTSEL_AD_RFB)); |
---|
| 298 | ad_config_filters(AD_BASEADDR, AD_ALL_RF, 2, 2); |
---|
| 299 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x32, 0x27); |
---|
| 300 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x00); |
---|
| 301 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x08); |
---|
| 302 | break; |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | switch(phy_samp_rate){ |
---|
| 306 | case PHY_40M: |
---|
| 307 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXLPF_BW, 3); |
---|
| 308 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_TXLPF_BW, 3); |
---|
| 309 | break; |
---|
| 310 | case PHY_10M: |
---|
| 311 | case PHY_20M: |
---|
| 312 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXLPF_BW, 1); |
---|
| 313 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_TXLPF_BW, 1); |
---|
| 314 | break; |
---|
| 315 | } |
---|
| 316 | |
---|
| 317 | // AGC timing: capt_rssi_1, capt_rssi_2, capt_v_db, agc_done |
---|
| 318 | switch(phy_samp_rate){ |
---|
| 319 | case PHY_40M: |
---|
| 320 | wlan_agc_set_AGC_timing(10, 30, 90, 96); |
---|
| 321 | break; |
---|
| 322 | case PHY_10M: |
---|
| 323 | case PHY_20M: |
---|
| 324 | wlan_agc_set_AGC_timing(1, 30, 90, 96); |
---|
| 325 | break; |
---|
| 326 | } |
---|
| 327 | |
---|
| 328 | |
---|
| 329 | // Configure auto-correlation packet detection |
---|
| 330 | // wlan_phy_rx_pktDet_autoCorr_ofdm_cfg(corr_thresh, energy_thresh, min_dur, post_wait) |
---|
| 331 | switch(phy_samp_rate){ |
---|
| 332 | case PHY_40M: |
---|
| 333 | // TODO: investigate need for lower correlation threshold at 40MSps |
---|
| 334 | wlan_phy_rx_pktDet_autoCorr_ofdm_cfg(100, 15, 4, 0x3F); |
---|
| 335 | break; |
---|
| 336 | case PHY_10M: |
---|
| 337 | case PHY_20M: |
---|
| 338 | wlan_phy_rx_pktDet_autoCorr_ofdm_cfg(100, 15, 4, 0x3F); |
---|
| 339 | break; |
---|
| 340 | } |
---|
| 341 | |
---|
| 342 | // Set post Rx extension |
---|
| 343 | // Number of sample periods post-Rx the PHY waits before asserting Rx END. This value |
---|
| 344 | // should be 6 usec and backdated by the delay through the Rx chain. This aligns the |
---|
| 345 | // Rx END event to the correct sample at the antenna. |
---|
| 346 | switch(phy_samp_rate){ |
---|
| 347 | case PHY_40M: |
---|
| 348 | wlan_phy_rx_set_extension((6*40) - (platform_common_dev_info.rx_analog_latency_100ns*4)); |
---|
| 349 | break; |
---|
| 350 | case PHY_20M: |
---|
| 351 | wlan_phy_rx_set_extension((6*20) - (platform_common_dev_info.rx_analog_latency_100ns*2)); |
---|
| 352 | break; |
---|
| 353 | case PHY_10M: |
---|
| 354 | wlan_phy_rx_set_extension((6*10) - (platform_common_dev_info.rx_analog_latency_100ns)); |
---|
| 355 | break; |
---|
| 356 | } |
---|
| 357 | |
---|
| 358 | // Set Tx duration extension, in units of sample periods. This value should be 6 usec, but |
---|
| 359 | // pushed forward to account for delay through the Tx chain. The results should be a Tx END |
---|
| 360 | // that asserts at the same time as an Rx END on the node receiving the packet (ignoring |
---|
| 361 | // propagation delay). |
---|
| 362 | switch(phy_samp_rate){ |
---|
| 363 | case PHY_40M: |
---|
| 364 | wlan_phy_tx_set_extension((6*40) + (platform_common_dev_info.tx_analog_latency_100ns*4)); |
---|
| 365 | |
---|
| 366 | // Set extension from last samp output to RF Tx -> Rx transition |
---|
| 367 | // This delay allows the Tx pipeline to finish driving samples into DACs |
---|
| 368 | // and for DAC->RF frontend to finish output Tx waveform |
---|
| 369 | wlan_phy_tx_set_txen_extension(100); |
---|
| 370 | |
---|
| 371 | // Set extension from RF Rx -> Tx to un-blocking Rx samples |
---|
| 372 | wlan_phy_tx_set_rx_invalid_extension(300); |
---|
| 373 | break; |
---|
| 374 | case PHY_20M: |
---|
| 375 | wlan_phy_tx_set_extension((6*20) + (platform_common_dev_info.tx_analog_latency_100ns*2)); |
---|
| 376 | |
---|
| 377 | // Set extension from last samp output to RF Tx -> Rx transition |
---|
| 378 | // This delay allows the Tx pipeline to finish driving samples into DACs |
---|
| 379 | // and for DAC->RF frontend to finish output Tx waveform |
---|
| 380 | wlan_phy_tx_set_txen_extension(50); //50 |
---|
| 381 | |
---|
| 382 | // Set extension from RF Rx -> Tx to un-blocking Rx samples |
---|
| 383 | wlan_phy_tx_set_rx_invalid_extension(150); |
---|
| 384 | break; |
---|
| 385 | case PHY_10M: |
---|
| 386 | wlan_phy_tx_set_extension((6*10) + (platform_common_dev_info.tx_analog_latency_100ns)); |
---|
| 387 | |
---|
| 388 | // Set extension from last samp output to RF Tx -> Rx transition |
---|
| 389 | // This delay allows the Tx pipeline to finish driving samples into DACs |
---|
| 390 | // and for DAC->RF frontend to finish output Tx waveform |
---|
| 391 | wlan_phy_tx_set_txen_extension(25); |
---|
| 392 | |
---|
| 393 | // Set extension from RF Rx -> Tx to un-blocking Rx samples |
---|
| 394 | wlan_phy_tx_set_rx_invalid_extension(75); |
---|
| 395 | break; |
---|
| 396 | } |
---|
| 397 | |
---|
| 398 | // Deassert PHY Tx/Rx and MAC Resets |
---|
| 399 | REG_CLEAR_BITS(WLAN_RX_REG_CTRL, WLAN_RX_REG_CTRL_RESET); |
---|
| 400 | REG_CLEAR_BITS(WLAN_TX_REG_CFG, WLAN_TX_REG_CFG_RESET); |
---|
| 401 | wlan_mac_reset(0); |
---|
| 402 | |
---|
| 403 | // Let PHY Tx take control of radio TXEN/RXEN |
---|
| 404 | REG_CLEAR_BITS(WLAN_TX_REG_CFG, WLAN_TX_REG_CFG_SET_RC_RXEN); |
---|
| 405 | REG_SET_BITS(WLAN_TX_REG_CFG, WLAN_TX_REG_CFG_SET_RC_RXEN); |
---|
| 406 | |
---|
| 407 | return WLAN_SUCCESS; |
---|
| 408 | } |
---|
| 409 | |
---|
| 410 | |
---|
| 411 | |
---|
| 412 | /*****************************************************************************/ |
---|
| 413 | /** |
---|
| 414 | * @brief Set the radio channel |
---|
| 415 | * |
---|
| 416 | * This function will set the radio channel for CPU LOW |
---|
| 417 | * |
---|
| 418 | * @param channel - Radio channel |
---|
| 419 | * @return None |
---|
| 420 | * |
---|
| 421 | */ |
---|
| 422 | int wlan_platform_low_set_radio_channel(u32 channel) { |
---|
| 423 | channel_band_t prev_band = gl_current_band; |
---|
| 424 | |
---|
| 425 | if(channel <= 14) { |
---|
| 426 | // 2.4GHz channel |
---|
| 427 | radio_controller_setCenterFrequency(RC_BASEADDR, (RC_ALL_RF), RC_24GHZ, w3_wlan_chan_to_rc_chan(channel)); |
---|
| 428 | gl_current_band = BAND_24GHZ; |
---|
| 429 | } else { |
---|
| 430 | // 5GHz channel |
---|
| 431 | radio_controller_setCenterFrequency(RC_BASEADDR, (RC_ALL_RF), RC_5GHZ, w3_wlan_chan_to_rc_chan(channel)); |
---|
| 432 | gl_current_band = BAND_5GHZ; |
---|
| 433 | } |
---|
| 434 | |
---|
| 435 | if(prev_band != gl_current_band){ |
---|
| 436 | //Update the CS thresh since the RSSI <-> Rx Power mapping as changed |
---|
| 437 | wlan_platform_enable_phy_cs(gl_enable_phy_cs); |
---|
| 438 | } |
---|
| 439 | |
---|
| 440 | return WLAN_SUCCESS; |
---|
| 441 | } |
---|
| 442 | |
---|
| 443 | |
---|
| 444 | /******************************************************************************** |
---|
| 445 | * Private functions - the functions below are not exported to the low framework |
---|
| 446 | ********************************************************************************/ |
---|
| 447 | |
---|
| 448 | |
---|
| 449 | /*****************************************************************************/ |
---|
| 450 | /** |
---|
| 451 | * Initialize the WARP v3 node |
---|
| 452 | * |
---|
| 453 | * @param None |
---|
| 454 | * |
---|
| 455 | * @return int - Status of the command: |
---|
| 456 | * WLAN_SUCCESS - Command completed successfully |
---|
| 457 | * WLAN_FAILURE - There was an error in the command |
---|
| 458 | * |
---|
| 459 | *****************************************************************************/ |
---|
| 460 | int w3_node_init() { |
---|
| 461 | |
---|
| 462 | int ret_val = WLAN_SUCCESS; |
---|
| 463 | int status; |
---|
| 464 | u32 clkmod_status; |
---|
| 465 | |
---|
| 466 | // Initialize w3_clock_controller hardware and AD9512 buffers |
---|
| 467 | // NOTE: The clock initialization will set the clock divider to 2 (for 40MHz clock) to RF A/B AD9963's |
---|
| 468 | status = clk_init(CLK_BASEADDR, 2); |
---|
| 469 | if(status != XST_SUCCESS) { |
---|
| 470 | xil_printf("ERROR: (w3_node_init) Clock initialization failed with error code: %d\n", status); |
---|
| 471 | ret_val = WLAN_FAILURE; |
---|
| 472 | } |
---|
| 473 | |
---|
| 474 | // Check for a clock module and configure clock inputs, outputs and dividers as needed |
---|
| 475 | clkmod_status = clk_config_read_clkmod_status(CLK_BASEADDR); |
---|
| 476 | |
---|
| 477 | switch(clkmod_status & CM_STATUS_SW) { |
---|
| 478 | case CM_STATUS_DET_NOCM: |
---|
| 479 | case CM_STATUS_DET_CMPLL_BYPASS: |
---|
| 480 | // No clock module - default config from HDL/driver is good as-is |
---|
| 481 | xil_printf("No clock module detected - selecting on-board clocks\n\n"); |
---|
| 482 | break; |
---|
| 483 | |
---|
| 484 | case CM_STATUS_DET_CMMMCX_CFG_A: |
---|
| 485 | // CM-MMCX config A: |
---|
| 486 | // Samp clk: on-board, RF clk: on-board |
---|
| 487 | // Samp MMCX output: 80MHz, RF MMCX output: 80MHz |
---|
| 488 | xil_printf("CM-MMCX Config A Detected:\n"); |
---|
| 489 | xil_printf(" RF: On-board\n Samp: On-board\n MMCX Outputs: Enabled\n\n"); |
---|
| 490 | |
---|
| 491 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
| 492 | clk_config_dividers(CLK_BASEADDR, 1, CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR); |
---|
| 493 | break; |
---|
| 494 | |
---|
| 495 | case CM_STATUS_DET_CMMMCX_CFG_B: |
---|
| 496 | // CM-MMCX config B: |
---|
| 497 | // Samp clk: off-board, RF clk: off-board |
---|
| 498 | // Samp MMCX output: 80MHz, RF MMCX output: 80MHz |
---|
| 499 | xil_printf("CM-MMCX Config B Detected:\n"); |
---|
| 500 | xil_printf(" RF: Off-board\n Samp: Off-board\n MMCX Outputs: Enabled\n\n"); |
---|
| 501 | |
---|
| 502 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
| 503 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
| 504 | clk_config_dividers(CLK_BASEADDR, 1, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
| 505 | break; |
---|
| 506 | |
---|
| 507 | case CM_STATUS_DET_CMMMCX_CFG_C: |
---|
| 508 | // CM-MMCX config C: |
---|
| 509 | // Samp clk: off-board, RF clk: off-board |
---|
| 510 | // Samp MMCX output: Off, RF MMCX output: Off |
---|
| 511 | xil_printf("CM-MMCX Config C Detected:\n"); |
---|
| 512 | xil_printf(" RF: Off-board\n Samp: Off-board\n MMCX Outputs: Disabled\n\n"); |
---|
| 513 | |
---|
| 514 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
| 515 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_OFF, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
| 516 | break; |
---|
| 517 | |
---|
| 518 | case CM_STATUS_DET_CMPLL_CFG_A: |
---|
| 519 | // CM-PLL config A: |
---|
| 520 | // Samp clk: clock module PLL |
---|
| 521 | // RF clk: on-board |
---|
| 522 | xil_printf("CM-PLL Config A Detected:\n"); |
---|
| 523 | xil_printf(" RF: On-board\n Samp: clock module PLL\n"); |
---|
| 524 | |
---|
| 525 | // No changes from configuration applied by HDL and clk_init() |
---|
| 526 | break; |
---|
| 527 | |
---|
| 528 | case CM_STATUS_DET_CMPLL_CFG_B: |
---|
| 529 | // CM-PLL config B: |
---|
| 530 | // Samp clk: clock module PLL |
---|
| 531 | // RF clk: clock module PLL |
---|
| 532 | xil_printf("CM-PLL Config B Detected:\n"); |
---|
| 533 | xil_printf(" RF: clock module PLL\n Samp: clock module PLL\n"); |
---|
| 534 | |
---|
| 535 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
| 536 | break; |
---|
| 537 | |
---|
| 538 | case CM_STATUS_DET_CMPLL_CFG_C: |
---|
| 539 | // CM-PLL config C: |
---|
| 540 | // Samp clk: clock module PLL |
---|
| 541 | // RF clk: clock module PLL |
---|
| 542 | xil_printf("CM-PLL Config C Detected:\n"); |
---|
| 543 | xil_printf(" RF: clock module PLL\n Samp: clock module PLL\n"); |
---|
| 544 | |
---|
| 545 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
| 546 | break; |
---|
| 547 | |
---|
| 548 | default: |
---|
| 549 | // Should be impossible |
---|
| 550 | xil_printf("ERROR: (w3_node_init) Invalid clock module switch settings! (0x%08x)\n", clkmod_status); |
---|
| 551 | ret_val = XST_FAILURE; |
---|
| 552 | break; |
---|
| 553 | } |
---|
| 554 | |
---|
| 555 | #ifdef WLAN_4RF_EN |
---|
| 556 | // Turn on clocks to FMC |
---|
| 557 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_FMC | CLK_RFREF_OUTSEL_FMC)); |
---|
| 558 | |
---|
| 559 | // FMC samp clock divider = 2 (40MHz sampling reference, same as on-board AD9963 ref clk) |
---|
| 560 | clk_config_dividers(CLK_BASEADDR, 2, CLK_SAMP_OUTSEL_FMC); |
---|
| 561 | |
---|
| 562 | // FMC RF ref clock divider = 2 (40MHz RF reference, same as on-board MAX2829 ref clk) |
---|
| 563 | clk_config_dividers(CLK_BASEADDR, 2, CLK_RFREF_OUTSEL_FMC); |
---|
| 564 | #endif |
---|
| 565 | |
---|
| 566 | // Initialize the AD9963 ADCs/DACs for on-board RF interfaces |
---|
| 567 | ad_init(AD_BASEADDR, AD_ALL_RF, 3); |
---|
| 568 | |
---|
| 569 | // Disable AD9963 Duty Cycle Stabilizer (recommended when ADCCLK < 75MHz) |
---|
| 570 | ad_config_clocks(AD_BASEADDR, AD_ALL_RF, AD_DACCLKSRC_EXT, AD_ADCCLKSRC_EXT, AD_ADCCLKDIV_1, AD_DCS_OFF); |
---|
| 571 | |
---|
| 572 | if(status != XST_SUCCESS) { |
---|
| 573 | xil_printf("ERROR: (w3_node_init) ADC/DAC initialization failed with error code: %d\n", status); |
---|
| 574 | ret_val = WLAN_FAILURE; |
---|
| 575 | } |
---|
| 576 | |
---|
| 577 | // Initialize the radio_controller core and MAX2829 transceivers for on-board RF interfaces |
---|
| 578 | status = radio_controller_init(RC_BASEADDR, RC_ALL_RF, 1, 1); |
---|
| 579 | |
---|
| 580 | if(status != XST_SUCCESS) { |
---|
| 581 | xil_printf("ERROR: (w3_node_init) Radio controller initialization failed with error code: %d\n", status); |
---|
| 582 | |
---|
| 583 | // Comment out the line below to allow the node to boot even if a radio PLL is unlocked |
---|
| 584 | ret_val = WLAN_FAILURE; |
---|
| 585 | } |
---|
| 586 | |
---|
| 587 | #ifdef WLAN_4RF_EN |
---|
| 588 | //TODO: 4RF mode not supported in 1.7.0. We will add it back in |
---|
| 589 | // in a future dot release. |
---|
| 590 | iic_eeprom_init(FMC_EEPROM_BASEADDR, 0x64); |
---|
| 591 | #endif |
---|
| 592 | |
---|
| 593 | // Give the PHY control of the red user LEDs (PHY counts 1-hot on SIGNAL errors) |
---|
| 594 | // |
---|
| 595 | // NOTE: Uncommenting this line will make the RED LEDs controlled by hardware. |
---|
| 596 | // This will move the LEDs on PHY bad signal events |
---|
| 597 | // |
---|
| 598 | // userio_set_ctrlSrc_hw(USERIO_BASEADDR, W3_USERIO_CTRLSRC_LEDS_RED); |
---|
| 599 | |
---|
| 600 | // Initialize Debug Header |
---|
| 601 | // Configure pins 15:12 as software controlled outputs, pins 11:0 as hardware controlled |
---|
| 602 | // This configuration is applied only by CPU Low to avoid races between CPUs at boot |
---|
| 603 | // Both CPUs can control the software-controlled pins |
---|
| 604 | wlan_mac_set_dbg_hdr_ctrlsrc(DBG_HDR_CTRLSRC_HW, 0x0FFF); |
---|
| 605 | wlan_mac_set_dbg_hdr_ctrlsrc(DBG_HDR_CTRLSRC_SW, 0xF000); |
---|
| 606 | wlan_mac_set_dbg_hdr_dir(DBG_HDR_DIR_OUTPUT, 0xF000); |
---|
| 607 | |
---|
| 608 | return ret_val; |
---|
| 609 | } |
---|
| 610 | |
---|
| 611 | /*****************************************************************************/ |
---|
| 612 | /** |
---|
| 613 | * Initialize the Radio Controller |
---|
| 614 | * |
---|
| 615 | * @param None |
---|
| 616 | * |
---|
| 617 | * @return None |
---|
| 618 | * |
---|
| 619 | * @note This function supports both 2RF and 4RF configurations. Also, there are |
---|
| 620 | * sections (ie #if sections) that have been added to assist if clocking changes |
---|
| 621 | * are needed. |
---|
| 622 | * |
---|
| 623 | *****************************************************************************/ |
---|
| 624 | void w3_radio_init() { |
---|
| 625 | |
---|
| 626 | #if 1 |
---|
| 627 | |
---|
| 628 | // Setup clocking and filtering (20MSps, 2x interp/decimate in AD9963) |
---|
| 629 | clk_config_dividers(CLK_BASEADDR, 2, (CLK_SAMP_OUTSEL_AD_RFA | CLK_SAMP_OUTSEL_AD_RFB)); |
---|
| 630 | ad_config_filters(AD_BASEADDR, AD_ALL_RF, 2, 2); |
---|
| 631 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x32, 0x27); |
---|
| 632 | ad_spi_write(AD_BASEADDR, (AD_ALL_RF), 0x33, 0x08); |
---|
| 633 | |
---|
| 634 | #else |
---|
| 635 | // Setup clocking and filtering: |
---|
| 636 | // 80MHz ref clk to AD9963 |
---|
| 637 | // 20MSps Tx/Rx at FPGA |
---|
| 638 | // 80MHz DAC, 4x interp in AD9963 |
---|
| 639 | // 40MHz ADC, 2x decimate in AD9963 |
---|
| 640 | // |
---|
| 641 | clk_config_dividers(CLK_BASEADDR, 1, (CLK_SAMP_OUTSEL_AD_RFA | CLK_SAMP_OUTSEL_AD_RFB)); |
---|
| 642 | |
---|
| 643 | ad_config_clocks(AD_BASEADDR, AD_ALL_RF, AD_DACCLKSRC_EXT, AD_ADCCLKSRC_EXT, AD_ADCCLKDIV_2, AD_DCS_OFF); |
---|
| 644 | |
---|
| 645 | ad_config_filters(AD_BASEADDR, AD_ALL_RF, 4, 2); |
---|
| 646 | #endif |
---|
| 647 | |
---|
| 648 | // Setup all RF interfaces |
---|
| 649 | radio_controller_TxRxDisable(RC_BASEADDR, RC_ALL_RF); |
---|
| 650 | |
---|
| 651 | radio_controller_apply_TxDCO_calibration(AD_BASEADDR, EEPROM_BASEADDR, (RC_RFA | RC_RFB)); |
---|
| 652 | #ifdef WLAN_4RF_EN |
---|
| 653 | radio_controller_apply_TxDCO_calibration(AD_BASEADDR, FMC_EEPROM_BASEADDR, (RC_RFC | RC_RFD)); |
---|
| 654 | #endif |
---|
| 655 | |
---|
| 656 | radio_controller_setCenterFrequency(RC_BASEADDR, RC_ALL_RF, RC_24GHZ, 4); |
---|
| 657 | |
---|
| 658 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RSSI_HIGH_BW_EN, 0); |
---|
| 659 | |
---|
| 660 | // Filter bandwidths |
---|
| 661 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXHPF_HIGH_CUTOFF_EN, 1); |
---|
| 662 | |
---|
| 663 | #if 0 |
---|
| 664 | // To set the gains manually for all radios: |
---|
| 665 | radio_controller_setCtrlSource(RC_BASEADDR, RC_ALL_RF, RC_REG0_RXHP_CTRLSRC, RC_CTRLSRC_REG); |
---|
| 666 | radio_controller_setRxHP(RC_BASEADDR, RC_ALL_RF, RC_RXHP_OFF); |
---|
| 667 | radio_controller_setRxGainSource(RC_BASEADDR, RC_ALL_RF, RC_GAINSRC_SPI); |
---|
| 668 | |
---|
| 669 | // Set Rx gains |
---|
| 670 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXGAIN_RF, 1); |
---|
| 671 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXGAIN_BB, 8); |
---|
| 672 | |
---|
| 673 | #else |
---|
| 674 | // AGC |
---|
| 675 | radio_controller_setCtrlSource(RC_BASEADDR, RC_ALL_RF, RC_REG0_RXHP_CTRLSRC, RC_CTRLSRC_HW); |
---|
| 676 | radio_controller_setRxGainSource(RC_BASEADDR, RC_ALL_RF, RC_GAINSRC_HW); |
---|
| 677 | #endif |
---|
| 678 | |
---|
| 679 | // Set Tx gains |
---|
| 680 | // |
---|
| 681 | // NOTE: To use software to control the Tx gains, use the following lines: |
---|
| 682 | // radio_controller_setTxGainSource(RC_BASEADDR, RC_ALL_RF, RC_GAINSRC_REG); //Used for software control of gains |
---|
| 683 | // radio_controller_setTxGainTarget(RC_BASEADDR, RC_ALL_RF, 45); |
---|
| 684 | // |
---|
| 685 | radio_controller_setTxGainSource(RC_BASEADDR, RC_ALL_RF, RC_GAINSRC_HW); //Used for hardware control of gains |
---|
| 686 | |
---|
| 687 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_TXGAIN_BB, 1); |
---|
| 688 | |
---|
| 689 | // Set misc radio params |
---|
| 690 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_TXLINEARITY_PADRIVER, 2); |
---|
| 691 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_TXLINEARITY_VGA, 0); |
---|
| 692 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_TXLINEARITY_UPCONV, 0); |
---|
| 693 | |
---|
| 694 | // Set Tx state machine timing |
---|
| 695 | // |
---|
| 696 | // NOTE: radio_controller_setTxDelays(dly_GainRamp, dly_PA, dly_TX, dly_PHY) |
---|
| 697 | // |
---|
| 698 | radio_controller_setTxDelays(RC_BASEADDR, 40, 20, 0, 0); //240 PA time after 180 PHY time is critical point |
---|
| 699 | |
---|
| 700 | // Configure the radio_controller Tx/Rx enable control sources |
---|
| 701 | // The Tx PHY drives a 4-bit TxEn, one bit per RF interface |
---|
| 702 | // The Tx PHY drives a 1-bit RxEn, common to all RF interfaces |
---|
| 703 | // MAC software should select active Rx interface by changing RFA/RFB RxEn ctrl src between _HW and _REG |
---|
| 704 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFA, (RC_REG0_RXEN_CTRLSRC), RC_CTRLSRC_HW); |
---|
| 705 | radio_controller_setCtrlSource(RC_BASEADDR, RC_RFB, (RC_REG0_RXEN_CTRLSRC), RC_CTRLSRC_REG); |
---|
| 706 | |
---|
| 707 | radio_controller_setCtrlSource(RC_BASEADDR, (RC_RFA | RC_RFB), (RC_REG0_TXEN_CTRLSRC), RC_CTRLSRC_HW); |
---|
| 708 | |
---|
| 709 | #ifdef WLAN_4RF_EN |
---|
| 710 | radio_controller_setCtrlSource(RC_BASEADDR, (RC_RFC | RC_RFD), (RC_REG0_TXEN_CTRLSRC), RC_CTRLSRC_HW); |
---|
| 711 | radio_controller_setCtrlSource(RC_BASEADDR, (RC_RFC | RC_RFD), (RC_REG0_RXEN_CTRLSRC), RC_CTRLSRC_REG); |
---|
| 712 | #else |
---|
| 713 | // Disable any hardware control of RFC/RFD |
---|
| 714 | radio_controller_setCtrlSource(RC_BASEADDR, (RC_RFC | RC_RFD), (RC_REG0_RXEN_CTRLSRC | RC_REG0_TXEN_CTRLSRC), RC_CTRLSRC_REG); |
---|
| 715 | #endif |
---|
| 716 | |
---|
| 717 | return; |
---|
| 718 | } |
---|
| 719 | |
---|
| 720 | int w3_agc_init() { |
---|
| 721 | |
---|
| 722 | // Post Rx_done reset delays for [rxhp, g_rf, g_bb] |
---|
| 723 | wlan_agc_set_reset_timing(4, 250, 250); |
---|
| 724 | |
---|
| 725 | // AGC config: |
---|
| 726 | // RFG Thresh 3->2, 2->1, Avg_len_sel, V_DB_Adj, Init G_BB |
---|
| 727 | wlan_agc_set_config((256 - 56), (256 - 37), 0, 6, 24); |
---|
| 728 | |
---|
| 729 | // AGC RSSI->Rx power offsets |
---|
| 730 | wlan_agc_set_RSSI_pwr_calib(100, 85, 70); |
---|
| 731 | |
---|
| 732 | // AGC timing: start_dco, en_iir_filt |
---|
| 733 | wlan_agc_set_DCO_timing(100, (100 + 34)); |
---|
| 734 | |
---|
| 735 | // AGC target output power (log scale) |
---|
| 736 | wlan_agc_set_target((64 - 16)); |
---|
| 737 | |
---|
| 738 | // Configure AGC to keep RXHP asserted between pkt det events |
---|
| 739 | // Note: this mode should be left 0 to prevent false positive OFDM |
---|
| 740 | // detection events immediately following a reception. |
---|
| 741 | wlan_agc_set_rxhp_mode(0); |
---|
| 742 | |
---|
| 743 | // Configure AGC to leave RXHP de-asserted until the AGC is triggered |
---|
| 744 | //wlan_agc_set_rxhp_mode(1); |
---|
| 745 | |
---|
| 746 | #if 0 |
---|
| 747 | // To set the gains manually: |
---|
| 748 | |
---|
| 749 | //xil_printf("Switching to MGC\n"); |
---|
| 750 | radio_controller_setCtrlSource(RC_BASEADDR, RC_ALL_RF, RC_REG0_RXHP_CTRLSRC, RC_CTRLSRC_REG); |
---|
| 751 | radio_controller_setRxHP(RC_BASEADDR, RC_ALL_RF, RC_RXHP_OFF); |
---|
| 752 | radio_controller_setRxGainSource(RC_BASEADDR, RC_ALL_RF, RC_GAINSRC_SPI); |
---|
| 753 | |
---|
| 754 | // Set Rx gains |
---|
| 755 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXGAIN_RF, 2); |
---|
| 756 | radio_controller_setRadioParam(RC_BASEADDR, RC_ALL_RF, RC_PARAMID_RXGAIN_BB, 8); |
---|
| 757 | #endif |
---|
| 758 | |
---|
| 759 | return WLAN_SUCCESS; |
---|
| 760 | } |
---|
| 761 | |
---|
| 762 | |
---|
| 763 | |
---|
| 764 | /*****************************************************************************/ |
---|
| 765 | /** |
---|
| 766 | * @brief Map the WLAN channel frequencies onto the convention used by the radio controller |
---|
| 767 | */ |
---|
| 768 | inline u32 w3_wlan_chan_to_rc_chan(u32 mac_channel) { |
---|
| 769 | int return_value = 0; |
---|
| 770 | |
---|
| 771 | switch(mac_channel){ |
---|
| 772 | // 2.4GHz channels |
---|
| 773 | case 1: |
---|
| 774 | case 2: |
---|
| 775 | case 3: |
---|
| 776 | case 4: |
---|
| 777 | case 5: |
---|
| 778 | case 6: |
---|
| 779 | case 7: |
---|
| 780 | case 8: |
---|
| 781 | case 9: |
---|
| 782 | case 10: |
---|
| 783 | case 11: |
---|
| 784 | return_value = mac_channel; |
---|
| 785 | break; |
---|
| 786 | |
---|
| 787 | // 5GHz channels |
---|
| 788 | case 36: // 5180 MHz |
---|
| 789 | return_value = 1; |
---|
| 790 | break; |
---|
| 791 | case 38: // 5190 MHz |
---|
| 792 | return_value = 2; |
---|
| 793 | break; |
---|
| 794 | case 40: // 5200 MHz |
---|
| 795 | return_value = 3; |
---|
| 796 | break; |
---|
| 797 | case 44: // 5220 MHz |
---|
| 798 | return_value = 4; |
---|
| 799 | break; |
---|
| 800 | case 46: // 5230 MHz |
---|
| 801 | return_value = 5; |
---|
| 802 | break; |
---|
| 803 | case 48: // 5240 MHz |
---|
| 804 | return_value = 6; |
---|
| 805 | break; |
---|
| 806 | #if 0 // Disable these channels by default |
---|
| 807 | case 52: // 5260 MHz |
---|
| 808 | return_value = 7; |
---|
| 809 | break; |
---|
| 810 | case 54: // 5270 MHz |
---|
| 811 | return_value = 8; |
---|
| 812 | break; |
---|
| 813 | case 56: // 5280 MHz |
---|
| 814 | return_value = 9; |
---|
| 815 | break; |
---|
| 816 | case 60: // 5300 MHz |
---|
| 817 | return_value = 10; |
---|
| 818 | break; |
---|
| 819 | case 62: // 5310 MHz |
---|
| 820 | return_value = 11; |
---|
| 821 | break; |
---|
| 822 | case 64: // 5320 MHz |
---|
| 823 | return_value = 12; |
---|
| 824 | break; |
---|
| 825 | case 100: // 5500 MHz |
---|
| 826 | return_value = 13; |
---|
| 827 | break; |
---|
| 828 | case 102: // 5510 MHz |
---|
| 829 | return_value = 14; |
---|
| 830 | break; |
---|
| 831 | case 104: // 5520 MHz |
---|
| 832 | return_value = 15; |
---|
| 833 | break; |
---|
| 834 | case 108: // 5540 MHz |
---|
| 835 | return_value = 16; |
---|
| 836 | break; |
---|
| 837 | case 110: // 5550 MHz |
---|
| 838 | return_value = 17; |
---|
| 839 | break; |
---|
| 840 | case 112: // 5560 MHz |
---|
| 841 | return_value = 18; |
---|
| 842 | break; |
---|
| 843 | case 116: // 5580 MHz |
---|
| 844 | return_value = 19; |
---|
| 845 | break; |
---|
| 846 | case 118: // 5590 MHz |
---|
| 847 | return_value = 20; |
---|
| 848 | break; |
---|
| 849 | case 120: // 5600 MHz |
---|
| 850 | return_value = 21; |
---|
| 851 | break; |
---|
| 852 | case 124: // 5620 MHz |
---|
| 853 | return_value = 22; |
---|
| 854 | break; |
---|
| 855 | case 126: // 5630 MHz |
---|
| 856 | return_value = 23; |
---|
| 857 | break; |
---|
| 858 | case 128: // 5640 MHz |
---|
| 859 | return_value = 24; |
---|
| 860 | break; |
---|
| 861 | case 132: // 5660 MHz |
---|
| 862 | return_value = 25; |
---|
| 863 | break; |
---|
| 864 | case 134: // 5670 MHz |
---|
| 865 | return_value = 26; |
---|
| 866 | break; |
---|
| 867 | case 136: // 5680 MHz |
---|
| 868 | return_value = 27; |
---|
| 869 | break; |
---|
| 870 | case 140: // 5700 MHz |
---|
| 871 | return_value = 28; |
---|
| 872 | break; |
---|
| 873 | case 142: // 5710 MHz |
---|
| 874 | return_value = 29; |
---|
| 875 | break; |
---|
| 876 | case 144: // 5720 MHz |
---|
| 877 | return_value = 30; |
---|
| 878 | break; |
---|
| 879 | case 149: // 5745 MHz |
---|
| 880 | return_value = 31; |
---|
| 881 | break; |
---|
| 882 | case 151: // 5755 MHz |
---|
| 883 | return_value = 32; |
---|
| 884 | break; |
---|
| 885 | case 153: // 5765 MHz |
---|
| 886 | return_value = 33; |
---|
| 887 | break; |
---|
| 888 | case 157: // 5785 MHz |
---|
| 889 | return_value = 34; |
---|
| 890 | break; |
---|
| 891 | case 159: // 5795 MHz |
---|
| 892 | return_value = 35; |
---|
| 893 | break; |
---|
| 894 | case 161: // 5805 MHz |
---|
| 895 | return_value = 36; |
---|
| 896 | break; |
---|
| 897 | case 165: // 5825 MHz |
---|
| 898 | return_value = 37; |
---|
| 899 | break; |
---|
| 900 | case 172: // 5860 MHz |
---|
| 901 | return_value = 38; |
---|
| 902 | break; |
---|
| 903 | case 174: // 5870 MHz |
---|
| 904 | return_value = 39; |
---|
| 905 | break; |
---|
| 906 | case 175: // 5875 MHz |
---|
| 907 | return_value = 40; |
---|
| 908 | break; |
---|
| 909 | case 176: // 5880 MHz |
---|
| 910 | return_value = 41; |
---|
| 911 | break; |
---|
| 912 | case 177: // 5885 MHz |
---|
| 913 | return_value = 42; |
---|
| 914 | break; |
---|
| 915 | case 178: // 5890 MHz |
---|
| 916 | return_value = 43; |
---|
| 917 | break; |
---|
| 918 | #endif |
---|
| 919 | } |
---|
| 920 | |
---|
| 921 | return return_value; |
---|
| 922 | } |
---|
| 923 | |
---|
| 924 | |
---|
| 925 | /*****************************************************************************/ |
---|
| 926 | /** |
---|
| 927 | * @brief Calculates Rx Power (in dBm) |
---|
| 928 | * |
---|
| 929 | * This function calculates receive power for a given band, RSSI and LNA gain. This |
---|
| 930 | * provides a reasonable estimate of Rx power, accurate to a few dB for standard waveforms. |
---|
| 931 | * |
---|
| 932 | * This function does not use the VGA gain setting or I/Q magnitudes. The PHY should use these |
---|
| 933 | * to refine its own power measurement if needed. |
---|
| 934 | * |
---|
| 935 | * NOTE: These lookup tables were developed as part of the RF characterization. See: |
---|
| 936 | * http://warpproject.org/trac/wiki/802.11/Benchmarks/Rx_Char |
---|
| 937 | * |
---|
| 938 | * |
---|
| 939 | * @param rssi - RSSI value from RF frontend |
---|
| 940 | * @param lna_gain - Value of LNA gain stage in RF frontend |
---|
| 941 | * @return int - Power in dBm |
---|
| 942 | */ |
---|
| 943 | static const s8 pow_lookup_B24[128] = {-90, -90, -89, -88, -88, -87, -87, -86, -86, -85, -84, -84, -83, -83, -82, -82, |
---|
| 944 | -81, -81, -80, -79, -79, -78, -78, -77, -77, -76, -75, -75, -74, -74, -73, -73, |
---|
| 945 | -72, -72, -71, -70, -70, -69, -69, -68, -68, -67, -66, -66, -65, -65, -64, -64, |
---|
| 946 | -63, -63, -62, -61, -61, -60, -60, -59, -59, -58, -58, -57, -56, -56, -55, -55, |
---|
| 947 | -54, -54, -53, -52, -52, -51, -51, -50, -50, -49, -49, -48, -47, -47, -46, -46, |
---|
| 948 | -45, -45, -44, -43, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -37, |
---|
| 949 | -36, -36, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -29, -29, -28, -28, |
---|
| 950 | -27, -27, -26, -26, -25, -24, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19}; |
---|
| 951 | |
---|
| 952 | static const s8 pow_lookup_B5[128] = {-97, -97, -96, -96, -95, -94, -94, -93, -93, -92, -92, -91, -90, -90, -89, -89, |
---|
| 953 | -88, -88, -87, -87, -86, -85, -85, -84, -84, -83, -83, -82, -81, -81, -80, -80, |
---|
| 954 | -79, -79, -78, -78, -77, -76, -76, -75, -75, -74, -74, -73, -72, -72, -71, -71, |
---|
| 955 | -70, -70, -69, -69, -68, -67, -67, -66, -66, -65, -65, -64, -63, -63, -62, -62, |
---|
| 956 | -61, -61, -60, -60, -59, -58, -58, -57, -57, -56, -56, -55, -54, -54, -53, -53, |
---|
| 957 | -52, -52, -51, -51, -50, -49, -49, -48, -48, -47, -47, -46, -45, -45, -44, -44, |
---|
| 958 | -43, -43, -42, -42, -41, -40, -40, -39, -39, -38, -38, -37, -36, -36, -35, -35, |
---|
| 959 | -34, -34, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26}; |
---|
| 960 | |
---|
| 961 | |
---|
| 962 | inline int w3_rssi_to_rx_power(u16 rssi, u8 lna_gain, channel_band_t band) { |
---|
| 963 | |
---|
| 964 | int power = -100; |
---|
| 965 | u16 adj_rssi = 0; |
---|
| 966 | |
---|
| 967 | if(band == BAND_24GHZ) { |
---|
| 968 | switch(lna_gain) { |
---|
| 969 | case 0: |
---|
| 970 | case 1: |
---|
| 971 | // Low LNA Gain State |
---|
| 972 | adj_rssi = rssi + (440 << PHY_RX_RSSI_SUM_LEN_BITS); |
---|
| 973 | break; |
---|
| 974 | |
---|
| 975 | case 2: |
---|
| 976 | // Medium LNA Gain State |
---|
| 977 | adj_rssi = rssi + (220 << PHY_RX_RSSI_SUM_LEN_BITS); |
---|
| 978 | break; |
---|
| 979 | |
---|
| 980 | case 3: |
---|
| 981 | // High LNA Gain State |
---|
| 982 | adj_rssi = rssi; |
---|
| 983 | break; |
---|
| 984 | } |
---|
| 985 | |
---|
| 986 | power = pow_lookup_B24[(adj_rssi >> (PHY_RX_RSSI_SUM_LEN_BITS+POW_LOOKUP_SHIFT))]; |
---|
| 987 | |
---|
| 988 | } else if(band == BAND_5GHZ) { |
---|
| 989 | switch(lna_gain){ |
---|
| 990 | case 0: |
---|
| 991 | case 1: |
---|
| 992 | // Low LNA Gain State |
---|
| 993 | adj_rssi = rssi + (540 << PHY_RX_RSSI_SUM_LEN_BITS); |
---|
| 994 | break; |
---|
| 995 | |
---|
| 996 | case 2: |
---|
| 997 | // Medium LNA Gain State |
---|
| 998 | adj_rssi = rssi + (280 << PHY_RX_RSSI_SUM_LEN_BITS); |
---|
| 999 | break; |
---|
| 1000 | |
---|
| 1001 | case 3: |
---|
| 1002 | // High LNA Gain State |
---|
| 1003 | adj_rssi = rssi; |
---|
| 1004 | break; |
---|
| 1005 | } |
---|
| 1006 | |
---|
| 1007 | power = pow_lookup_B5[(adj_rssi >> (PHY_RX_RSSI_SUM_LEN_BITS+POW_LOOKUP_SHIFT))]; |
---|
| 1008 | } |
---|
| 1009 | |
---|
| 1010 | return power; |
---|
| 1011 | } |
---|
| 1012 | |
---|
| 1013 | |
---|
| 1014 | |
---|
| 1015 | /*****************************************************************************/ |
---|
| 1016 | /** |
---|
| 1017 | * @brief Calculates RSSI from Rx power (in dBm) |
---|
| 1018 | * |
---|
| 1019 | * This function calculates receive power for a given band, RSSI and LNA gain. This |
---|
| 1020 | * provides a reasonable estimate of Rx power, accurate to a few dB for standard waveforms. |
---|
| 1021 | * |
---|
| 1022 | * This function does not use the VGA gain setting or I/Q magnitudes. The PHY should use these |
---|
| 1023 | * to refine its own power measurement if needed. |
---|
| 1024 | * |
---|
| 1025 | * NOTE: These lookup tables were developed as part of the RF characterization. See: |
---|
| 1026 | * http://warpproject.org/trac/wiki/802.11/Benchmarks/Rx_Char |
---|
| 1027 | * |
---|
| 1028 | * |
---|
| 1029 | * @param rx_pow - Receive power in dBm |
---|
| 1030 | * @return u16 - RSSI value |
---|
| 1031 | * |
---|
| 1032 | * @note rx_pow must be in the range [PKT_DET_MIN_POWER_MIN, PKT_DET_MIN_POWER_MAX] inclusive |
---|
| 1033 | */ |
---|
| 1034 | static const u16 rssi_lookup_B24[61] = { 1, 16, 24, 40, 56, 72, 80, 96, 112, 128, 144, 152, 168, 184, 200, 208, |
---|
| 1035 | 224, 240, 256, 272, 280, 296, 312, 328, 336, 352, 368, 384, 400, 408, 424, 440, |
---|
| 1036 | 456, 472, 480, 496, 512, 528, 536, 552, 568, 584, 600, 608, 624, 640, 656, 664, |
---|
| 1037 | 680, 696, 712, 728, 736, 752, 768, 784, 792, 808, 824, 840, 856}; |
---|
| 1038 | |
---|
| 1039 | static const u16 rssi_lookup_B5[61] = { 96, 112, 128, 144, 160, 168, 184, 200, 216, 224, 240, 256, 272, 288, 296, 312, |
---|
| 1040 | 328, 344, 352, 368, 384, 400, 416, 424, 440, 456, 472, 480, 496, 512, 528, 544, |
---|
| 1041 | 552, 568, 584, 600, 608, 624, 640, 656, 672, 680, 696, 712, 728, 736, 752, 768, |
---|
| 1042 | 784, 800, 808, 824, 840, 856, 864, 880, 896, 912, 920, 936, 952}; |
---|
| 1043 | |
---|
| 1044 | |
---|
| 1045 | int w3_rx_power_to_rssi(s8 rx_pow, channel_band_t band) { |
---|
| 1046 | u16 rssi_val = 0; |
---|
| 1047 | |
---|
| 1048 | if ((rx_pow <= PKT_DET_MIN_POWER_MAX) && (rx_pow >= PKT_DET_MIN_POWER_MIN)) { |
---|
| 1049 | if(band == BAND_24GHZ){ |
---|
| 1050 | rssi_val = rssi_lookup_B24[rx_pow-PKT_DET_MIN_POWER_MIN]; |
---|
| 1051 | } else if(band == BAND_5GHZ){ |
---|
| 1052 | rssi_val = rssi_lookup_B5[rx_pow-PKT_DET_MIN_POWER_MIN]; |
---|
| 1053 | } |
---|
| 1054 | |
---|
| 1055 | return rssi_val; |
---|
| 1056 | |
---|
| 1057 | } else { |
---|
| 1058 | return WLAN_FAILURE; |
---|
| 1059 | } |
---|
| 1060 | } |
---|
| 1061 | |
---|
| 1062 | int wlan_platform_get_rx_pkt_pwr(u8 ant) { |
---|
| 1063 | u16 rssi; |
---|
| 1064 | u8 lna_gain; |
---|
| 1065 | int pwr; |
---|
| 1066 | |
---|
| 1067 | rssi = wlan_phy_rx_get_pkt_rssi(ant); |
---|
| 1068 | lna_gain = wlan_phy_rx_get_agc_RFG(ant); |
---|
| 1069 | pwr = w3_rssi_to_rx_power(rssi, lna_gain, gl_current_band); |
---|
| 1070 | |
---|
| 1071 | return pwr; |
---|
| 1072 | } |
---|
| 1073 | |
---|
| 1074 | void wlan_platform_enable_phy_cs(u8 enable) { |
---|
| 1075 | |
---|
| 1076 | gl_enable_phy_cs = enable; |
---|
| 1077 | |
---|
| 1078 | if(gl_enable_phy_cs == 0) { |
---|
| 1079 | // Disable physical CS |
---|
| 1080 | wlan_phy_rx_set_cca_thresh(0xFFFF); |
---|
| 1081 | return; |
---|
| 1082 | } |
---|
| 1083 | |
---|
| 1084 | wlan_phy_rx_set_cca_thresh(PHY_RX_RSSI_SUM_LEN * w3_rx_power_to_rssi(-62, gl_current_band)); |
---|
| 1085 | |
---|
| 1086 | return; |
---|
| 1087 | } |
---|
| 1088 | |
---|
| 1089 | /*****************************************************************************/ |
---|
| 1090 | /** |
---|
| 1091 | * @brief Set the minimum power for packet detection |
---|
| 1092 | * |
---|
| 1093 | * @param rx_pow - Receive power in dBm |
---|
| 1094 | * @return int - Status: 0 - Success; -1 - Failure |
---|
| 1095 | */ |
---|
| 1096 | int wlan_platform_set_pkt_det_min_power(int min_power) { |
---|
| 1097 | int rssi_val; |
---|
| 1098 | |
---|
| 1099 | if(min_power == 0) { |
---|
| 1100 | // 0 disables the min-power logic |
---|
| 1101 | wlan_phy_disable_req_both_pkt_det(); |
---|
| 1102 | return WLAN_SUCCESS; |
---|
| 1103 | |
---|
| 1104 | } else { |
---|
| 1105 | wlan_phy_enable_req_both_pkt_det(); |
---|
| 1106 | rssi_val = w3_rx_power_to_rssi(min_power, gl_current_band); |
---|
| 1107 | |
---|
| 1108 | if(rssi_val != -1) { |
---|
| 1109 | wlan_phy_rx_pktDet_RSSI_cfg( (PHY_RX_RSSI_SUM_LEN-1), (rssi_val << PHY_RX_RSSI_SUM_LEN_BITS), 1); |
---|
| 1110 | |
---|
| 1111 | return 0; |
---|
| 1112 | } else { |
---|
| 1113 | xil_printf("wlan_platform_set_pkt_det_min_power: invalid min_power argument: %d\n", min_power); |
---|
| 1114 | return WLAN_FAILURE; |
---|
| 1115 | } |
---|
| 1116 | } |
---|
| 1117 | } |
---|
| 1118 | |
---|
| 1119 | int wlan_platform_get_rx_pkt_gain(u8 ant) { |
---|
| 1120 | u8 bb_gain; |
---|
| 1121 | u8 rf_gain; |
---|
| 1122 | |
---|
| 1123 | switch(ant) { |
---|
| 1124 | case 0: // RF A |
---|
| 1125 | bb_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 0) & 0x1F; |
---|
| 1126 | rf_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 5) & 0x03; |
---|
| 1127 | break; |
---|
| 1128 | case 1: // RF B |
---|
| 1129 | bb_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 8) & 0x1F; |
---|
| 1130 | rf_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 13) & 0x03; |
---|
| 1131 | break; |
---|
| 1132 | case 2: // RF C |
---|
| 1133 | bb_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 16) & 0x1F; |
---|
| 1134 | rf_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 21) & 0x03; |
---|
| 1135 | break; |
---|
| 1136 | case 3: // RF D |
---|
| 1137 | bb_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 24) & 0x1F; |
---|
| 1138 | rf_gain = (Xil_In32(WLAN_RX_PKT_AGC_GAINS) >> 29) & 0x03; |
---|
| 1139 | break; |
---|
| 1140 | default: |
---|
| 1141 | xil_printf("wlan_platform_get_rx_pkt_gain: invalid antenna ID: %d\n", ant); |
---|
| 1142 | return WLAN_FAILURE; |
---|
| 1143 | break; |
---|
| 1144 | } |
---|
| 1145 | |
---|
| 1146 | // For MAX2829 RF interface construct 8-bit gain_index value as: |
---|
| 1147 | // [ 8]: 0 |
---|
| 1148 | // [6:5]: RF gain index (0, 1, 2) |
---|
| 1149 | // [4:0]: BB gain index (0, 1, ..., 31) |
---|
| 1150 | return ((rf_gain << 5) | bb_gain); |
---|
| 1151 | } |
---|
| 1152 | |
---|
| 1153 | |
---|
| 1154 | |
---|
| 1155 | |
---|
| 1156 | int wlan_platform_set_radio_tx_power(s8 power) { |
---|
| 1157 | // Empty function for WARP v3 - all Tx powers are configured per-packet via |
---|
| 1158 | // tx_frame_info and the mac_hw core driving the radio_controller's Tx gain pins |
---|
| 1159 | |
---|
| 1160 | return WLAN_SUCCESS; |
---|
| 1161 | } |
---|
| 1162 | |
---|
| 1163 | /*****************************************************************************/ |
---|
| 1164 | /** |
---|
| 1165 | * @brief Convert dBm to Tx Gain Target |
---|
| 1166 | * |
---|
| 1167 | * This function maps a transmit power (in dBm) to a radio gain target. |
---|
| 1168 | * |
---|
| 1169 | * @param s8 power - Power in dBm |
---|
| 1170 | * @return u32 gain_target - Tx attenuation (in dB) |
---|
| 1171 | */ |
---|
| 1172 | inline u32 wlan_platform_tx_power_to_gain_target(s8 power){ |
---|
| 1173 | s8 power_railed; |
---|
| 1174 | u32 return_value; |
---|
| 1175 | |
---|
| 1176 | if(power > TX_POWER_MAX_DBM){ |
---|
| 1177 | power_railed = TX_POWER_MAX_DBM; |
---|
| 1178 | } else if( power < TX_POWER_MIN_DBM){ |
---|
| 1179 | power_railed = TX_POWER_MIN_DBM; |
---|
| 1180 | } else { |
---|
| 1181 | power_railed = power; |
---|
| 1182 | } |
---|
| 1183 | |
---|
| 1184 | // This is only safe because 'power' is constrained to less than half the dynamic range of an s8 type |
---|
| 1185 | return_value = (u32)((power_railed << 1) + 20); |
---|
| 1186 | |
---|
| 1187 | return return_value; |
---|
| 1188 | } |
---|
| 1189 | |
---|
| 1190 | |
---|