Index: head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300.h =================================================================== --- head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300.h (revision 361485) +++ head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300.h (revision 361486) @@ -1,1715 +1,1715 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef _ATH_AR9300_H_ #define _ATH_AR9300_H_ #include "ar9300_freebsd_inc.h" /* XXX doesn't belong here */ #define AR_EEPROM_MODAL_SPURS 5 /* Ensure that AH_BYTE_ORDER is defined */ #ifndef AH_BYTE_ORDER #error AH_BYTE_ORDER needs to be defined! #endif /* * (a) this should be N(a), * (b) FreeBSD does define nitems, * (c) it doesn't have an AH_ prefix, sigh. */ #define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) #include "ah_internal.h" #include "ah_eeprom.h" #include "ah_devid.h" #include "ar9300eep.h" /* For Eeprom definitions */ #define AR9300_MAGIC 0x19741014 /* MAC register values */ #define INIT_CONFIG_STATUS 0x00000000 #define INIT_RSSI_THR 0x7 /* Missed beacon counter initialized to 0x7 (max is 0xff) */ #define INIT_RSSI_BEACON_WEIGHT 8 /* ave beacon rssi weight (0-16) */ /* * Various fifo fill before Tx start, in 64-byte units * i.e. put the frame in the air while still DMAing */ #define MIN_TX_FIFO_THRESHOLD 0x1 #define MAX_TX_FIFO_THRESHOLD (( 4096 / 64) - 1) #define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD #define CHANSEL_DIV 15 #define FCLK 40 #define COEFF ((FCLK * 5) / 2) #define CHANSEL_2G(_freq) (((_freq) * 0x10000) / CHANSEL_DIV) #define CHANSEL_5G(_freq) (((_freq) * 0x8000) / CHANSEL_DIV) #define CHANSEL_5G_DOT5MHZ 2188 /* * Receive Queue Fifo depth. */ enum RX_FIFO_DEPTH { HAL_HP_RXFIFO_DEPTH = 16, HAL_LP_RXFIFO_DEPTH = 128, }; /* * Gain support. */ #define NUM_CORNER_FIX_BITS_2133 7 #define CCK_OFDM_GAIN_DELTA 15 enum GAIN_PARAMS { GP_TXCLIP, GP_PD90, GP_PD84, GP_GSEL }; enum GAIN_PARAMS_2133 { GP_MIXGAIN_OVR, GP_PWD_138, GP_PWD_137, GP_PWD_136, GP_PWD_132, GP_PWD_131, GP_PWD_130, }; typedef struct _gain_opt_step { int16_t paramVal[NUM_CORNER_FIX_BITS_2133]; int32_t stepGain; int8_t stepName[16]; } GAIN_OPTIMIZATION_STEP; typedef struct { u_int32_t numStepsInLadder; u_int32_t defaultStepNum; GAIN_OPTIMIZATION_STEP optStep[10]; } GAIN_OPTIMIZATION_LADDER; typedef struct { u_int32_t currStepNum; u_int32_t currGain; u_int32_t targetGain; u_int32_t loTrig; u_int32_t hiTrig; u_int32_t gainFCorrection; u_int32_t active; GAIN_OPTIMIZATION_STEP *curr_step; } GAIN_VALUES; typedef struct { u_int16_t synth_center; u_int16_t ctl_center; u_int16_t ext_center; } CHAN_CENTERS; /* RF HAL structures */ typedef struct rf_hal_funcs { HAL_BOOL (*set_channel)(struct ath_hal *, struct ieee80211_channel *); HAL_BOOL (*get_chip_power_lim)(struct ath_hal *ah, struct ieee80211_channel *chan); } RF_HAL_FUNCS; struct ar9300_ani_default { u_int16_t m1_thresh_low; u_int16_t m2_thresh_low; u_int16_t m1_thresh; u_int16_t m2_thresh; u_int16_t m2_count_thr; u_int16_t m2_count_thr_low; u_int16_t m1_thresh_low_ext; u_int16_t m2_thresh_low_ext; u_int16_t m1_thresh_ext; u_int16_t m2_thresh_ext; u_int16_t firstep; u_int16_t firstep_low; u_int16_t cycpwr_thr1; u_int16_t cycpwr_thr1_ext; }; /* * Per-channel ANI state private to the driver. */ struct ar9300_ani_state { struct ieee80211_channel c; /* XXX ew? */ HAL_BOOL must_restore; HAL_BOOL ofdms_turn; u_int8_t ofdm_noise_immunity_level; u_int8_t cck_noise_immunity_level; u_int8_t spur_immunity_level; u_int8_t firstep_level; u_int8_t ofdm_weak_sig_detect_off; u_int8_t mrc_cck_off; /* Thresholds */ u_int32_t listen_time; u_int32_t ofdm_trig_high; u_int32_t ofdm_trig_low; int32_t cck_trig_high; int32_t cck_trig_low; int32_t rssi_thr_low; int32_t rssi_thr_high; int32_t rssi; /* The current RSSI */ u_int32_t tx_frame_count; /* Last tx_frame_count */ u_int32_t rx_frame_count; /* Last rx Frame count */ u_int32_t rx_busy_count; /* Last rx busy count */ u_int32_t rx_ext_busy_count; /* Last rx busy count; extension channel */ u_int32_t cycle_count; /* Last cycle_count (can detect wrap-around) */ u_int32_t ofdm_phy_err_count;/* OFDM err count since last reset */ u_int32_t cck_phy_err_count; /* CCK err count since last reset */ struct ar9300_ani_default ini_def; /* INI default values for ANI registers */ HAL_BOOL phy_noise_spur; /* based on OFDM/CCK Phy errors */ }; #define AR9300_ANI_POLLINTERVAL 1000 /* 1000 milliseconds between ANI poll */ #define AR9300_CHANNEL_SWITCH_TIME_USEC 1000 /* 1 millisecond needed to change channels */ #define HAL_PROCESS_ANI 0x00000001 /* ANI state setup */ #define HAL_RADAR_EN 0x80000000 /* Radar detect is capable */ #define HAL_AR_EN 0x40000000 /* AR detect is capable */ #define DO_ANI(ah) \ ((AH9300(ah)->ah_proc_phy_err & HAL_PROCESS_ANI)) #if 0 struct ar9300_stats { u_int32_t ast_ani_niup; /* ANI increased noise immunity */ u_int32_t ast_ani_nidown; /* ANI decreased noise immunity */ u_int32_t ast_ani_spurup; /* ANI increased spur immunity */ u_int32_t ast_ani_spurdown;/* ANI descreased spur immunity */ u_int32_t ast_ani_ofdmon; /* ANI OFDM weak signal detect on */ u_int32_t ast_ani_ofdmoff;/* ANI OFDM weak signal detect off */ u_int32_t ast_ani_cckhigh;/* ANI CCK weak signal threshold high */ u_int32_t ast_ani_ccklow; /* ANI CCK weak signal threshold low */ u_int32_t ast_ani_stepup; /* ANI increased first step level */ u_int32_t ast_ani_stepdown;/* ANI decreased first step level */ u_int32_t ast_ani_ofdmerrs;/* ANI cumulative ofdm phy err count */ u_int32_t ast_ani_cckerrs;/* ANI cumulative cck phy err count */ u_int32_t ast_ani_reset; /* ANI parameters zero'd for non-STA */ u_int32_t ast_ani_lzero; /* ANI listen time forced to zero */ u_int32_t ast_ani_lneg; /* ANI listen time calculated < 0 */ HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ }; #endif struct ar9300_rad_reader { u_int16_t rd_index; u_int16_t rd_expSeq; u_int32_t rd_resetVal; u_int8_t rd_start; }; struct ar9300_rad_writer { u_int16_t wr_index; u_int16_t wr_seq; }; struct ar9300_radar_event { u_int32_t re_ts; /* 32 bit time stamp */ u_int8_t re_rssi; /* rssi of radar event */ u_int8_t re_dur; /* duration of radar pulse */ u_int8_t re_chanIndex; /* Channel of event */ }; struct ar9300_radar_q_elem { u_int32_t rq_seqNum; u_int32_t rq_busy; /* 32 bit to insure atomic read/write */ struct ar9300_radar_event rq_event; /* Radar event */ }; struct ar9300_radar_q_info { u_int16_t ri_qsize; /* q size */ u_int16_t ri_seqSize; /* Size of sequence ring */ struct ar9300_rad_reader ri_reader; /* State for the q reader */ struct ar9300_rad_writer ri_writer; /* state for the q writer */ }; #define HAL_MAX_ACK_RADAR_DUR 511 #define HAL_MAX_NUM_PEAKS 3 #define HAL_ARQ_SIZE 4096 /* 8K AR events for buffer size */ #define HAL_ARQ_SEQSIZE 4097 /* Sequence counter wrap for AR */ #define HAL_RADARQ_SIZE 1024 /* 1K radar events for buffer size */ #define HAL_RADARQ_SEQSIZE 1025 /* Sequence counter wrap for radar */ #define HAL_NUMRADAR_STATES 64 /* Number of radar channels we keep state for */ struct ar9300_ar_state { u_int16_t ar_prev_time_stamp; u_int32_t ar_prev_width; u_int32_t ar_phy_err_count[HAL_MAX_ACK_RADAR_DUR]; u_int32_t ar_ack_sum; u_int16_t ar_peak_list[HAL_MAX_NUM_PEAKS]; u_int32_t ar_packet_threshold; /* Thresh to determine traffic load */ u_int32_t ar_par_threshold; /* Thresh to determine peak */ u_int32_t ar_radar_rssi; /* Rssi threshold for AR event */ }; struct ar9300_radar_state { struct ieee80211_channel *rs_chan; /* Channel info */ u_int8_t rs_chan_index; /* Channel index in radar structure */ u_int32_t rs_num_radar_events; /* Number of radar events */ int32_t rs_firpwr; /* Thresh to check radar sig is gone */ u_int32_t rs_radar_rssi; /* Thresh to start radar det (dB) */ u_int32_t rs_height; /* Thresh for pulse height (dB)*/ u_int32_t rs_pulse_rssi; /* Thresh to check if pulse is gone (dB) */ u_int32_t rs_inband; /* Thresh to check if pusle is inband (0.5 dB) */ }; typedef struct { u_int8_t uc_receiver_errors; u_int8_t uc_bad_tlp_errors; u_int8_t uc_bad_dllp_errors; u_int8_t uc_replay_timeout_errors; u_int8_t uc_replay_number_rollover_errors; } ar_pcie_error_moniter_counters; #define AR9300_OPFLAGS_11A 0x01 /* if set, allow 11a */ #define AR9300_OPFLAGS_11G 0x02 /* if set, allow 11g */ #define AR9300_OPFLAGS_N_5G_HT40 0x04 /* if set, disable 5G HT40 */ #define AR9300_OPFLAGS_N_2G_HT40 0x08 /* if set, disable 2G HT40 */ #define AR9300_OPFLAGS_N_5G_HT20 0x10 /* if set, disable 5G HT20 */ #define AR9300_OPFLAGS_N_2G_HT20 0x20 /* if set, disable 2G HT20 */ /* * For Kite and later chipsets, the following bits are not being programmed in EEPROM * and so need to be enabled always. * Bit 0: en_fcc_mid, Bit 1: en_jap_mid, Bit 2: en_fcc_dfs_ht40 * Bit 3: en_jap_ht40, Bit 4: en_jap_dfs_ht40 */ #define AR9300_RDEXT_DEFAULT 0x1F #define AR9300_MAX_CHAINS 3 #define AR9300_NUM_CHAINS(chainmask) \ (((chainmask >> 2) & 1) + ((chainmask >> 1) & 1) + (chainmask & 1)) #define AR9300_CHAIN0_MASK 0x1 #define AR9300_CHAIN1_MASK 0x2 #define AR9300_CHAIN2_MASK 0x4 /* Support for multiple INIs */ struct ar9300_ini_array { const u_int32_t *ia_array; u_int32_t ia_rows; u_int32_t ia_columns; }; #define INIT_INI_ARRAY(iniarray, array, rows, columns) do { \ (iniarray)->ia_array = (const u_int32_t *)(array); \ (iniarray)->ia_rows = (rows); \ (iniarray)->ia_columns = (columns); \ } while (0) #define INI_RA(iniarray, row, column) (((iniarray)->ia_array)[(row) * ((iniarray)->ia_columns) + (column)]) #define INIT_CAL(_perCal) \ (_perCal)->cal_state = CAL_WAITING; \ (_perCal)->cal_next = AH_NULL; #define INSERT_CAL(_ahp, _perCal) \ do { \ if ((_ahp)->ah_cal_list_last == AH_NULL) { \ (_ahp)->ah_cal_list = (_ahp)->ah_cal_list_last = (_perCal); \ ((_ahp)->ah_cal_list_last)->cal_next = (_perCal); \ } else { \ ((_ahp)->ah_cal_list_last)->cal_next = (_perCal); \ (_ahp)->ah_cal_list_last = (_perCal); \ (_perCal)->cal_next = (_ahp)->ah_cal_list; \ } \ } while (0) typedef enum cal_types { IQ_MISMATCH_CAL = 0x1, TEMP_COMP_CAL = 0x2, } HAL_CAL_TYPES; typedef enum cal_state { CAL_INACTIVE, CAL_WAITING, CAL_RUNNING, CAL_DONE } HAL_CAL_STATE; /* Calibrate state */ #define MIN_CAL_SAMPLES 1 #define MAX_CAL_SAMPLES 64 #define INIT_LOG_COUNT 5 #define PER_MIN_LOG_COUNT 2 #define PER_MAX_LOG_COUNT 10 #define AR9300_NUM_BT_WEIGHTS 4 #define AR9300_NUM_WLAN_WEIGHTS 4 /* Per Calibration data structure */ typedef struct per_cal_data { HAL_CAL_TYPES cal_type; // Type of calibration u_int32_t cal_num_samples; // Number of SW samples to collect u_int32_t cal_count_max; // Number of HW samples to collect void (*cal_collect)(struct ath_hal *, u_int8_t); // Accumulator func void (*cal_post_proc)(struct ath_hal *, u_int8_t); // Post-processing func } HAL_PERCAL_DATA; /* List structure for calibration data */ typedef struct cal_list { const HAL_PERCAL_DATA *cal_data; HAL_CAL_STATE cal_state; struct cal_list *cal_next; } HAL_CAL_LIST; #define AR9300_NUM_CAL_TYPES 2 #define AR9300_PAPRD_TABLE_SZ 24 #define AR9300_PAPRD_GAIN_TABLE_SZ 32 #define AR9382_MAX_GPIO_PIN_NUM (16) #define AR9382_GPIO_PIN_8_RESERVED (8) #define AR9382_GPIO_9_INPUT_ONLY (9) #define AR9382_MAX_GPIO_INPUT_PIN_NUM (13) #define AR9382_GPIO_PIN_11_RESERVED (11) #define AR9382_MAX_JTAG_GPIO_PIN_NUM (3) /* Paprd tx power adjust data structure */ struct ar9300_paprd_pwr_adjust { u_int32_t target_rate; // rate index u_int32_t reg_addr; // register offset u_int32_t reg_mask; // mask of register u_int32_t reg_mask_offset; // mask offset of register u_int32_t sub_db; // offset value unit of dB }; struct ar9300NfLimits { int16_t max; int16_t min; int16_t nominal; }; #define AR9300_MAX_RATES 36 /* legacy(4) + ofdm(8) + HTSS(8) + HTDS(8) + HTTS(8)*/ struct ath_hal_9300 { struct ath_hal_private ah_priv; /* base class */ /* * Information retrieved from EEPROM. */ ar9300_eeprom_t ah_eeprom; GAIN_VALUES ah_gain_values; u_int8_t ah_macaddr[IEEE80211_ADDR_LEN]; u_int8_t ah_bssid[IEEE80211_ADDR_LEN]; u_int8_t ah_bssid_mask[IEEE80211_ADDR_LEN]; u_int16_t ah_assoc_id; /* * Runtime state. */ u_int32_t ah_mask_reg; /* copy of AR_IMR */ u_int32_t ah_mask2Reg; /* copy of AR_IMR_S2 */ u_int32_t ah_msi_reg; /* copy of AR_PCIE_MSI */ os_atomic_t ah_ier_ref_count; /* reference count for enabling interrupts */ HAL_ANI_STATS ah_stats; /* various statistics */ RF_HAL_FUNCS ah_rf_hal; u_int32_t ah_tx_desc_mask; /* mask for TXDESC */ u_int32_t ah_tx_ok_interrupt_mask; u_int32_t ah_tx_err_interrupt_mask; u_int32_t ah_tx_desc_interrupt_mask; u_int32_t ah_tx_eol_interrupt_mask; u_int32_t ah_tx_urn_interrupt_mask; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; HAL_SMPS_MODE ah_sm_power_mode; HAL_BOOL ah_chip_full_sleep; u_int32_t ah_atim_window; HAL_ANT_SETTING ah_diversity_control; /* antenna setting */ u_int16_t ah_antenna_switch_swap; /* Controls mapping of OID request */ u_int8_t ah_tx_chainmask_cfg; /* chain mask config */ u_int8_t ah_rx_chainmask_cfg; u_int32_t ah_beacon_rssi_threshold; /* cache beacon rssi threshold */ /* Calibration related fields */ HAL_CAL_TYPES ah_supp_cals; HAL_CAL_LIST ah_iq_cal_data; /* IQ Cal Data */ HAL_CAL_LIST ah_temp_comp_cal_data; /* Temperature Compensation Cal Data */ HAL_CAL_LIST *ah_cal_list; /* ptr to first cal in list */ HAL_CAL_LIST *ah_cal_list_last; /* ptr to last cal in list */ HAL_CAL_LIST *ah_cal_list_curr; /* ptr to current cal */ // IQ Cal aliases #define ah_total_power_meas_i ah_meas0.unsign #define ah_total_power_meas_q ah_meas1.unsign #define ah_total_iq_corr_meas ah_meas2.sign union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas0; union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas1; union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas2; union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas3; u_int16_t ah_cal_samples; /* end - Calibration related fields */ u_int32_t ah_tx6_power_in_half_dbm; /* power output for 6Mb tx */ u_int32_t ah_sta_id1_defaults; /* STA_ID1 default settings */ u_int32_t ah_misc_mode; /* MISC_MODE settings */ HAL_BOOL ah_get_plcp_hdr; /* setting about MISC_SEL_EVM */ enum { AUTO_32KHZ, /* use it if 32kHz crystal present */ USE_32KHZ, /* do it regardless */ DONT_USE_32KHZ, /* don't use it regardless */ } ah_enable32k_hz_clock; /* whether to sleep at 32kHz */ u_int32_t ah_ofdm_tx_power; int16_t ah_tx_power_index_offset; u_int ah_slot_time; /* user-specified slot time */ u_int ah_ack_timeout; /* user-specified ack timeout */ /* * XXX * 11g-specific stuff; belongs in the driver. */ u_int8_t ah_g_beacon_rate; /* fixed rate for G beacons */ u_int32_t ah_gpio_mask; /* copy of enabled GPIO mask */ u_int32_t ah_gpio_cause; /* copy of GPIO cause (sync and async) */ /* * RF Silent handling; setup according to the EEPROM. */ u_int32_t ah_gpio_select; /* GPIO pin to use */ u_int32_t ah_polarity; /* polarity to disable RF */ u_int32_t ah_gpio_bit; /* after init, prev value */ HAL_BOOL ah_eep_enabled; /* EEPROM bit for capability */ #ifdef ATH_BT_COEX /* * Bluetooth coexistence static setup according to the registry */ HAL_BT_MODULE ah_bt_module; /* Bluetooth module identifier */ u_int8_t ah_bt_coex_config_type; /* BT coex configuration */ u_int8_t ah_bt_active_gpio_select; /* GPIO pin for BT_ACTIVE */ u_int8_t ah_bt_priority_gpio_select; /* GPIO pin for BT_PRIORITY */ u_int8_t ah_wlan_active_gpio_select; /* GPIO pin for WLAN_ACTIVE */ u_int8_t ah_bt_active_polarity; /* Polarity of BT_ACTIVE */ HAL_BOOL ah_bt_coex_single_ant; /* Single or dual antenna configuration */ u_int8_t ah_bt_wlan_isolation; /* Isolation between BT and WLAN in dB */ /* * Bluetooth coexistence runtime settings */ HAL_BOOL ah_bt_coex_enabled; /* If Bluetooth coexistence is enabled */ u_int32_t ah_bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */ u_int32_t ah_bt_coex_bt_weight[AR9300_NUM_BT_WEIGHTS]; /* Register setting for AR_BT_COEX_WEIGHT */ u_int32_t ah_bt_coex_wlan_weight[AR9300_NUM_WLAN_WEIGHTS]; /* Register setting for AR_BT_COEX_WEIGHT */ u_int32_t ah_bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */ u_int32_t ah_bt_coex_flag; /* Special tuning flags for BT coex */ #endif /* * Generic timer support */ u_int32_t ah_avail_gen_timers; /* mask of available timers */ u_int32_t ah_intr_gen_timer_trigger; /* generic timer trigger interrupt state */ u_int32_t ah_intr_gen_timer_thresh; /* generic timer trigger interrupt state */ HAL_BOOL ah_enable_tsf2; /* enable TSF2 for gen timer 8-15. */ /* * ANI & Radar support. */ u_int32_t ah_proc_phy_err; /* Process Phy errs */ u_int32_t ah_ani_period; /* ani update list period */ struct ar9300_ani_state *ah_curani; /* cached last reference */ struct ar9300_ani_state ah_ani[255]; /* per-channel state */ struct ar9300_radar_state ah_radar[HAL_NUMRADAR_STATES]; /* Per-Channel Radar detector state */ struct ar9300_radar_q_elem *ah_radarq; /* radar event queue */ struct ar9300_radar_q_info ah_radarq_info; /* radar event q read/write state */ struct ar9300_ar_state ah_ar; /* AR detector state */ struct ar9300_radar_q_elem *ah_arq; /* AR event queue */ struct ar9300_radar_q_info ah_arq_info; /* AR event q read/write state */ /* * Transmit power state. Note these are maintained * here so they can be retrieved by diagnostic tools. */ u_int16_t ah_rates_array[16]; /* * Tx queue interrupt state. */ u_int32_t ah_intr_txqs; HAL_BOOL ah_intr_mitigation_rx; /* rx Interrupt Mitigation Settings */ HAL_BOOL ah_intr_mitigation_tx; /* tx Interrupt Mitigation Settings */ /* * Extension Channel Rx Clear State */ u_int32_t ah_cycle_count; u_int32_t ah_ctl_busy; u_int32_t ah_ext_busy; /* HT CWM state */ HAL_HT_EXTPROTSPACING ah_ext_prot_spacing; u_int8_t ah_tx_chainmask; /* tx chain mask */ u_int8_t ah_rx_chainmask; /* rx chain mask */ /* optional tx chainmask */ u_int8_t ah_tx_chainmaskopt; u_int8_t ah_tx_cal_chainmask; /* tx cal chain mask */ u_int8_t ah_rx_cal_chainmask; /* rx cal chain mask */ int ah_hwp; void *ah_cal_mem; HAL_BOOL ah_emu_eeprom; HAL_ANI_CMD ah_ani_function; HAL_BOOL ah_rifs_enabled; u_int32_t ah_rifs_reg[11]; u_int32_t ah_rifs_sec_cnt; /* open-loop power control */ u_int32_t original_gain[22]; int32_t init_pdadc; int32_t pdadc_delta; /* cycle counts for beacon stuck diagnostics */ u_int32_t ah_cycles; u_int32_t ah_rx_clear; u_int32_t ah_rx_frame; u_int32_t ah_tx_frame; #define BB_HANG_SIG1 0 #define BB_HANG_SIG2 1 #define BB_HANG_SIG3 2 #define BB_HANG_SIG4 3 #define MAC_HANG_SIG1 4 #define MAC_HANG_SIG2 5 /* bb hang detection */ int ah_hang[6]; hal_hw_hangs_t ah_hang_wars; /* * Keytable type table */ #define AR_KEYTABLE_SIZE 128 /* XXX! */ uint8_t ah_keytype[AR_KEYTABLE_SIZE]; #undef AR_KEYTABLE_SIZE /* * Support for ar9300 multiple INIs */ struct ar9300_ini_array ah_ini_pcie_serdes; struct ar9300_ini_array ah_ini_pcie_serdes_low_power; struct ar9300_ini_array ah_ini_modes_additional; struct ar9300_ini_array ah_ini_modes_additional_40mhz; struct ar9300_ini_array ah_ini_modes_rxgain; struct ar9300_ini_array ah_ini_modes_rxgain_bounds; struct ar9300_ini_array ah_ini_modes_txgain; struct ar9300_ini_array ah_ini_japan2484; struct ar9300_ini_array ah_ini_radio_post_sys2ant; struct ar9300_ini_array ah_ini_BTCOEX_MAX_TXPWR; struct ar9300_ini_array ah_ini_modes_rxgain_xlna; struct ar9300_ini_array ah_ini_modes_rxgain_bb_core; struct ar9300_ini_array ah_ini_modes_rxgain_bb_postamble; /* * New INI format starting with Osprey 2.0 INI. * Pre, core, post arrays for each sub-system (mac, bb, radio, soc) */ #define ATH_INI_PRE 0 #define ATH_INI_CORE 1 #define ATH_INI_POST 2 #define ATH_INI_NUM_SPLIT (ATH_INI_POST + 1) struct ar9300_ini_array ah_ini_mac[ATH_INI_NUM_SPLIT]; /* New INI format */ struct ar9300_ini_array ah_ini_bb[ATH_INI_NUM_SPLIT]; /* New INI format */ struct ar9300_ini_array ah_ini_radio[ATH_INI_NUM_SPLIT]; /* New INI format */ struct ar9300_ini_array ah_ini_soc[ATH_INI_NUM_SPLIT]; /* New INI format */ /* * Added to support DFS postamble array in INI that we need to apply * in DFS channels */ struct ar9300_ini_array ah_ini_dfs; #if ATH_WOW struct ar9300_ini_array ah_ini_pcie_serdes_wow; /* SerDes values during WOW sleep */ #endif /* To indicate EEPROM mapping used */ u_int32_t ah_immunity_vals[6]; HAL_BOOL ah_immunity_on; /* * snap shot of counter register for debug purposes */ #ifdef AH_DEBUG u_int32_t last_tf; u_int32_t last_rf; u_int32_t last_rc; u_int32_t last_cc; #endif HAL_BOOL ah_dma_stuck; /* Set to AH_TRUE when RX/TX DMA failed to stop. */ u_int32_t nf_tsf32; /* timestamp for NF calibration duration */ u_int32_t reg_dmn; /* Regulatory Domain */ int16_t twice_antenna_gain; /* Antenna Gain */ u_int16_t twice_antenna_reduction; /* Antenna Gain Allowed */ /* * Upper limit after factoring in the regulatory max, antenna gain and * multichain factor. No TxBF, CDD or STBC gain factored */ int16_t upper_limit[AR9300_MAX_CHAINS]; /* adjusted power for descriptor-based TPC for 1, 2, or 3 chains */ int16_t txpower[AR9300_MAX_RATES][AR9300_MAX_CHAINS]; /* adjusted power for descriptor-based TPC for 1, 2, or 3 chains with STBC*/ int16_t txpower_stbc[AR9300_MAX_RATES][AR9300_MAX_CHAINS]; /* Transmit Status ring support */ struct ar9300_txs *ts_ring; u_int16_t ts_tail; u_int16_t ts_size; u_int32_t ts_paddr_start; u_int32_t ts_paddr_end; /* Receive Buffer size */ #define HAL_RXBUFSIZE_DEFAULT 0xfff u_int16_t rx_buf_size; u_int32_t ah_wa_reg_val; // Store the permanent value of Reg 0x4004 so we dont have to R/M/W. (We should not be reading this register when in sleep states). /* Indicate the PLL source clock rate is 25Mhz or not. * clk_25mhz = 0 by default. */ u_int8_t clk_25mhz; /* For PAPRD uses */ u_int16_t small_signal_gain[AH_MAX_CHAINS]; u_int32_t pa_table[AH_MAX_CHAINS][AR9300_PAPRD_TABLE_SZ]; u_int32_t paprd_gain_table_entries[AR9300_PAPRD_GAIN_TABLE_SZ]; u_int32_t paprd_gain_table_index[AR9300_PAPRD_GAIN_TABLE_SZ]; u_int32_t ah_2g_paprd_rate_mask_ht20; /* Copy of eep->modal_header_2g.paprd_rate_mask_ht20 */ u_int32_t ah_2g_paprd_rate_mask_ht40; /* Copy of eep->modal_header_2g.paprd_rate_mask_ht40 */ u_int32_t ah_5g_paprd_rate_mask_ht20; /* Copy of eep->modal_header_5g.paprd_rate_mask_ht20 */ u_int32_t ah_5g_paprd_rate_mask_ht40; /* Copy of eep->modal_header_5g.paprd_rate_mask_ht40 */ u_int32_t paprd_training_power; /* For GreenTx use to store the default tx power */ u_int8_t ah_default_tx_power[ar9300_rate_size]; HAL_BOOL ah_paprd_broken; /* To store offsets of host interface registers */ struct { u_int32_t AR_RC; u_int32_t AR_WA; u_int32_t AR_PM_STATE; u_int32_t AR_H_INFOL; u_int32_t AR_H_INFOH; u_int32_t AR_PCIE_PM_CTRL; u_int32_t AR_HOST_TIMEOUT; u_int32_t AR_EEPROM; u_int32_t AR_SREV; u_int32_t AR_INTR_SYNC_CAUSE; u_int32_t AR_INTR_SYNC_CAUSE_CLR; u_int32_t AR_INTR_SYNC_ENABLE; u_int32_t AR_INTR_ASYNC_MASK; u_int32_t AR_INTR_SYNC_MASK; u_int32_t AR_INTR_ASYNC_CAUSE_CLR; u_int32_t AR_INTR_ASYNC_CAUSE; u_int32_t AR_INTR_ASYNC_ENABLE; u_int32_t AR_PCIE_SERDES; u_int32_t AR_PCIE_SERDES2; u_int32_t AR_GPIO_OUT; u_int32_t AR_GPIO_IN; u_int32_t AR_GPIO_OE_OUT; u_int32_t AR_GPIO_OE1_OUT; u_int32_t AR_GPIO_INTR_POL; u_int32_t AR_GPIO_INPUT_EN_VAL; u_int32_t AR_GPIO_INPUT_MUX1; u_int32_t AR_GPIO_INPUT_MUX2; u_int32_t AR_GPIO_OUTPUT_MUX1; u_int32_t AR_GPIO_OUTPUT_MUX2; u_int32_t AR_GPIO_OUTPUT_MUX3; u_int32_t AR_INPUT_STATE; u_int32_t AR_SPARE; u_int32_t AR_PCIE_CORE_RESET_EN; u_int32_t AR_CLKRUN; u_int32_t AR_EEPROM_STATUS_DATA; u_int32_t AR_OBS; u_int32_t AR_RFSILENT; u_int32_t AR_GPIO_PDPU; u_int32_t AR_GPIO_DS; u_int32_t AR_MISC; u_int32_t AR_PCIE_MSI; u_int32_t AR_TSF_SNAPSHOT_BT_ACTIVE; u_int32_t AR_TSF_SNAPSHOT_BT_PRIORITY; u_int32_t AR_TSF_SNAPSHOT_BT_CNTL; u_int32_t AR_PCIE_PHY_LATENCY_NFTS_ADJ; u_int32_t AR_TDMA_CCA_CNTL; u_int32_t AR_TXAPSYNC; u_int32_t AR_TXSYNC_INIT_SYNC_TMR; u_int32_t AR_INTR_PRIO_SYNC_CAUSE; u_int32_t AR_INTR_PRIO_SYNC_ENABLE; u_int32_t AR_INTR_PRIO_ASYNC_MASK; u_int32_t AR_INTR_PRIO_SYNC_MASK; u_int32_t AR_INTR_PRIO_ASYNC_CAUSE; u_int32_t AR_INTR_PRIO_ASYNC_ENABLE; } ah_hostifregs; u_int32_t ah_enterprise_mode; u_int32_t ah_radar1; u_int32_t ah_dc_offset; HAL_BOOL ah_hw_green_tx_enable; /* 1:enalbe H/W Green Tx */ HAL_BOOL ah_smartantenna_enable; /* 1:enalbe H/W */ u_int32_t ah_disable_cck; HAL_BOOL ah_lna_div_use_bt_ant_enable; /* 1:enable Rx(LNA) Diversity */ /* * Different types of memory where the calibration data might be stored. * All types are searched in Ar9300EepromRestore() in the order flash, eeprom, otp. * To disable searching a type, set its parameter to 0. */ int try_dram; int try_flash; int try_eeprom; int try_otp; #ifdef ATH_CAL_NAND_FLASH int try_nand; #endif /* * This is where we found the calibration data. */ int calibration_data_source; int calibration_data_source_address; /* * This is where we look for the calibration data. must be set before ath_attach() is called */ int calibration_data_try; int calibration_data_try_address; u_int8_t tx_iq_cal_enable : 1, tx_iq_cal_during_agc_cal : 1, tx_cl_cal_enable : 1; #if ATH_SUPPORT_MCI /* For MCI */ HAL_BOOL ah_mci_ready; u_int32_t ah_mci_int_raw; u_int32_t ah_mci_int_rx_msg; u_int32_t ah_mci_rx_status; u_int32_t ah_mci_cont_status; u_int8_t ah_mci_bt_state; u_int32_t ah_mci_gpm_addr; u_int8_t *ah_mci_gpm_buf; u_int32_t ah_mci_gpm_len; u_int32_t ah_mci_gpm_idx; u_int32_t ah_mci_sched_addr; u_int8_t *ah_mci_sched_buf; u_int8_t ah_mci_coex_major_version_wlan; u_int8_t ah_mci_coex_minor_version_wlan; u_int8_t ah_mci_coex_major_version_bt; u_int8_t ah_mci_coex_minor_version_bt; HAL_BOOL ah_mci_coex_bt_version_known; HAL_BOOL ah_mci_coex_wlan_channels_update; u_int32_t ah_mci_coex_wlan_channels[4]; HAL_BOOL ah_mci_coex_2g5g_update; HAL_BOOL ah_mci_coex_is_2g; HAL_BOOL ah_mci_query_bt; HAL_BOOL ah_mci_unhalt_bt_gpm; /* need send UNHALT */ HAL_BOOL ah_mci_halted_bt_gpm; /* HALT sent */ HAL_BOOL ah_mci_need_flush_btinfo; HAL_BOOL ah_mci_concur_tx_en; u_int8_t ah_mci_stomp_low_tx_pri; u_int8_t ah_mci_stomp_all_tx_pri; u_int8_t ah_mci_stomp_none_tx_pri; u_int32_t ah_mci_wlan_cal_seq; u_int32_t ah_mci_wlan_cal_done; #if ATH_SUPPORT_AIC HAL_BOOL ah_aic_enabled; u_int32_t ah_aic_sram[ATH_AIC_MAX_BT_CHANNEL]; #endif #endif /* ATH_SUPPORT_MCI */ u_int8_t ah_cac_quiet_enabled; #if ATH_WOW_OFFLOAD u_int32_t ah_mcast_filter_l32_set; u_int32_t ah_mcast_filter_u32_set; #endif HAL_BOOL ah_reduced_self_gen_mask; HAL_BOOL ah_chip_reset_done; HAL_BOOL ah_abort_txdma_norx; /* store previous passive RX Cal info */ HAL_BOOL ah_skip_rx_iq_cal; HAL_BOOL ah_rx_cal_complete; /* previous rx cal completed or not */ u_int32_t ah_rx_cal_chan; /* chan on which rx cal is done */ u_int32_t ah_rx_cal_chan_flag; u_int32_t ah_rx_cal_corr[AR9300_MAX_CHAINS]; /* Local additions for FreeBSD */ /* * These fields are in the top level HAL in the atheros * codebase; here we place them in the AR9300 HAL and * access them via accessor methods if the driver requires them. */ u_int32_t ah_ob_db1[3]; u_int32_t ah_db2[3]; u_int32_t ah_bb_panic_timeout_ms; u_int32_t ah_bb_panic_last_status; u_int32_t ah_tx_trig_level; u_int16_t ath_hal_spur_chans[AR_EEPROM_MODAL_SPURS][2]; int16_t nf_cw_int_delta; /* diff btwn nominal NF and CW interf threshold */ int ah_phyrestart_disabled; HAL_RSSI_TX_POWER green_tx_status; int green_ap_ps_on; int ah_enable_keysearch_always; int ah_fccaifs; int ah_reset_reason; int ah_dcs_enable; HAL_ANI_STATE ext_ani_state; /* FreeBSD; external facing ANI state */ struct ar9300NfLimits nf_2GHz; struct ar9300NfLimits nf_5GHz; struct ar9300NfLimits *nfp; uint32_t ah_beaconInterval; }; #define AH9300(_ah) ((struct ath_hal_9300 *)(_ah)) #define IS_9300_EMU(ah) \ (AH_PRIVATE(ah)->ah_devid == AR9300_DEVID_EMU_PCIE) #define ar9300_eep_data_in_flash(_ah) \ (!(AH_PRIVATE(_ah)->ah_flags & AH_USE_EEPROM)) #ifdef notyet // Need these additional conditions for IS_5GHZ_FAST_CLOCK_EN when we have valid eeprom contents. && \ ((ar9300_eeprom_get(AH9300(_ah), EEP_MINOR_REV) <= AR9300_EEP_MINOR_VER_16) || \ (ar9300_eeprom_get(AH9300(_ah), EEP_FSTCLK_5G)))) #endif /* * WAR for bug 6773. OS_DELAY() does a PIO READ on the PCI bus which allows * other cards' DMA reads to complete in the middle of our reset. */ #define WAR_6773(x) do { \ if ((++(x) % 64) == 0) \ OS_DELAY(1); \ } while (0) #define REG_WRITE_ARRAY(iniarray, column, regWr) do { \ int r; \ for (r = 0; r < ((iniarray)->ia_rows); r++) { \ OS_REG_WRITE(ah, INI_RA((iniarray), (r), 0), INI_RA((iniarray), r, (column)));\ WAR_6773(regWr); \ } \ } while (0) #define UPPER_5G_SUB_BANDSTART 5700 #define MID_5G_SUB_BANDSTART 5400 #define TRAINPOWER_DB_OFFSET 6 #define AH_PAPRD_GET_SCALE_FACTOR(_scale, _eep, _is2G, _channel) do{ if(_is2G) { _scale = (_eep->modal_header_2g.paprd_rate_mask_ht20>>25)&0x7; \ } else { \ if(_channel >= UPPER_5G_SUB_BANDSTART){ _scale = (_eep->modal_header_5g.paprd_rate_mask_ht20>>25)&0x7;} \ else if((UPPER_5G_SUB_BANDSTART < _channel) && (_channel >= MID_5G_SUB_BANDSTART)) \ { _scale = (_eep->modal_header_5g.paprd_rate_mask_ht40>>28)&0x7;} \ else { _scale = (_eep->modal_header_5g.paprd_rate_mask_ht40>>25)&0x7;} } }while(0) #ifdef AH_ASSERT #define ar9300FeatureNotSupported(feature, ah, func) \ ath_hal_printf(ah, # feature \ " not supported but called from %s\n", (func)), \ hal_assert(0) #else #define ar9300FeatureNotSupported(feature, ah, func) \ ath_hal_printf(ah, # feature \ " not supported but called from %s\n", (func)) #endif /* AH_ASSERT */ /* * Green Tx, Based on different RSSI of Received Beacon thresholds, * using different tx power by modified register tx power related values. * The thresholds are decided by system team. */ #define WB225_SW_GREEN_TX_THRES1_DB 56 /* in dB */ #define WB225_SW_GREEN_TX_THRES2_DB 41 /* in dB */ #define WB225_OB_CALIBRATION_VALUE 5 /* For Green Tx OLPC Delta Calibration Offset */ #define WB225_OB_GREEN_TX_SHORT_VALUE 1 /* For Green Tx OB value in short distance*/ #define WB225_OB_GREEN_TX_MIDDLE_VALUE 3 /* For Green Tx OB value in middle distance */ #define WB225_OB_GREEN_TX_LONG_VALUE 5 /* For Green Tx OB value in long distance */ #define WB225_BBPWRTXRATE9_SW_GREEN_TX_SHORT_VALUE 0x06060606 /* For SwGreen Tx BB_powertx_rate9 reg value in short distance */ #define WB225_BBPWRTXRATE9_SW_GREEN_TX_MIDDLE_VALUE 0x0E0E0E0E /* For SwGreen Tx BB_powertx_rate9 reg value in middle distance */ /* Tx power for short distacnce in SwGreenTx.*/ static const u_int8_t wb225_sw_gtx_tp_distance_short[ar9300_rate_size] = { 6, /*ALL_TARGET_LEGACY_6_24*/ 6, /*ALL_TARGET_LEGACY_36*/ 6, /*ALL_TARGET_LEGACY_48*/ 4, /*ALL_TARGET_LEGACY_54*/ 6, /*ALL_TARGET_LEGACY_1L_5L*/ 6, /*ALL_TARGET_LEGACY_5S*/ 6, /*ALL_TARGET_LEGACY_11L*/ 6, /*ALL_TARGET_LEGACY_11S*/ 6, /*ALL_TARGET_HT20_0_8_16*/ 6, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 4, /*ALL_TARGET_HT20_4*/ 4, /*ALL_TARGET_HT20_5*/ 4, /*ALL_TARGET_HT20_6*/ 2, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 6, /*ALL_TARGET_HT40_0_8_16*/ 6, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 4, /*ALL_TARGET_HT40_4*/ 4, /*ALL_TARGET_HT40_5*/ 4, /*ALL_TARGET_HT40_6*/ 2, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* Tx power for middle distacnce in SwGreenTx.*/ static const u_int8_t wb225_sw_gtx_tp_distance_middle[ar9300_rate_size] = { 14, /*ALL_TARGET_LEGACY_6_24*/ 14, /*ALL_TARGET_LEGACY_36*/ 14, /*ALL_TARGET_LEGACY_48*/ 12, /*ALL_TARGET_LEGACY_54*/ 14, /*ALL_TARGET_LEGACY_1L_5L*/ 14, /*ALL_TARGET_LEGACY_5S*/ 14, /*ALL_TARGET_LEGACY_11L*/ 14, /*ALL_TARGET_LEGACY_11S*/ 14, /*ALL_TARGET_HT20_0_8_16*/ 14, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 14, /*ALL_TARGET_HT20_4*/ 14, /*ALL_TARGET_HT20_5*/ 12, /*ALL_TARGET_HT20_6*/ 10, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 14, /*ALL_TARGET_HT40_0_8_16*/ 14, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 14, /*ALL_TARGET_HT40_4*/ 14, /*ALL_TARGET_HT40_5*/ 12, /*ALL_TARGET_HT40_6*/ 10, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* OLPC DeltaCalibration Offset unit in half dB.*/ static const u_int8_t wb225_gtx_olpc_cal_offset[6] = { 0, /* OB0*/ 16, /* OB1*/ 9, /* OB2*/ 5, /* OB3*/ 2, /* OB4*/ 0, /* OB5*/ }; /* * Definitions for HwGreenTx */ #define AR9485_HW_GREEN_TX_THRES1_DB 56 /* in dB */ #define AR9485_HW_GREEN_TX_THRES2_DB 41 /* in dB */ #define AR9485_BBPWRTXRATE9_HW_GREEN_TX_SHORT_VALUE 0x0C0C0A0A /* For HwGreen Tx BB_powertx_rate9 reg value in short distance */ #define AR9485_BBPWRTXRATE9_HW_GREEN_TX_MIDDLE_VALUE 0x10100E0E /* For HwGreenTx BB_powertx_rate9 reg value in middle distance */ /* Tx power for short distacnce in HwGreenTx.*/ static const u_int8_t ar9485_hw_gtx_tp_distance_short[ar9300_rate_size] = { 14, /*ALL_TARGET_LEGACY_6_24*/ 14, /*ALL_TARGET_LEGACY_36*/ 8, /*ALL_TARGET_LEGACY_48*/ 2, /*ALL_TARGET_LEGACY_54*/ 14, /*ALL_TARGET_LEGACY_1L_5L*/ 14, /*ALL_TARGET_LEGACY_5S*/ 14, /*ALL_TARGET_LEGACY_11L*/ 14, /*ALL_TARGET_LEGACY_11S*/ 12, /*ALL_TARGET_HT20_0_8_16*/ 12, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 12, /*ALL_TARGET_HT20_4*/ 12, /*ALL_TARGET_HT20_5*/ 8, /*ALL_TARGET_HT20_6*/ 2, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 10, /*ALL_TARGET_HT40_0_8_16*/ 10, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 10, /*ALL_TARGET_HT40_4*/ 10, /*ALL_TARGET_HT40_5*/ 6, /*ALL_TARGET_HT40_6*/ 2, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* Tx power for middle distacnce in HwGreenTx.*/ static const u_int8_t ar9485_hw_gtx_tp_distance_middle[ar9300_rate_size] = { 18, /*ALL_TARGET_LEGACY_6_24*/ 18, /*ALL_TARGET_LEGACY_36*/ 14, /*ALL_TARGET_LEGACY_48*/ 12, /*ALL_TARGET_LEGACY_54*/ 18, /*ALL_TARGET_LEGACY_1L_5L*/ 18, /*ALL_TARGET_LEGACY_5S*/ 18, /*ALL_TARGET_LEGACY_11L*/ 18, /*ALL_TARGET_LEGACY_11S*/ 16, /*ALL_TARGET_HT20_0_8_16*/ 16, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 16, /*ALL_TARGET_HT20_4*/ 16, /*ALL_TARGET_HT20_5*/ 14, /*ALL_TARGET_HT20_6*/ 12, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 14, /*ALL_TARGET_HT40_0_8_16*/ 14, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 14, /*ALL_TARGET_HT40_4*/ 14, /*ALL_TARGET_HT40_5*/ 14, /*ALL_TARGET_HT40_6*/ 12, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* MIMO Modes used in TPC calculations */ typedef enum { AR9300_DEF_MODE = 0, /* Could be CDD or Direct */ AR9300_TXBF_MODE, AR9300_STBC_MODE } AR9300_TXMODES; typedef enum { POSEIDON_STORED_REG_OBDB = 0, /* default OB/DB setting from ini */ POSEIDON_STORED_REG_TPC = 1, /* default txpower value in TPC reg */ POSEIDON_STORED_REG_BB_PWRTX_RATE9 = 2, /* default txpower value in * BB_powertx_rate9 reg */ POSEIDON_STORED_REG_SZ /* Can not add anymore */ } POSEIDON_STORED_REGS; typedef enum { POSEIDON_STORED_REG_G2_OLPC_OFFSET = 0,/* default OB/DB setting from ini */ POSEIDON_STORED_REG_G2_SZ /* should not exceed 3 */ } POSEIDON_STORED_REGS_G2; #if AH_NEED_TX_DATA_SWAP #if AH_NEED_RX_DATA_SWAP #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB | AR_CFG_SWRB,0) #else #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB,0) #endif #elif AH_NEED_RX_DATA_SWAP #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWRB,0) #else #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD,0) #endif extern HAL_BOOL ar9300_rf_attach(struct ath_hal *, HAL_STATUS *); struct ath_hal; extern struct ath_hal_9300 * ar9300_new_state(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status); extern struct ath_hal * ar9300_attach(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status); extern void ar9300_detach(struct ath_hal *ah); extern void ar9300_read_revisions(struct ath_hal *ah); extern HAL_BOOL ar9300_chip_test(struct ath_hal *ah); extern HAL_BOOL ar9300_get_channel_edges(struct ath_hal *ah, u_int16_t flags, u_int16_t *low, u_int16_t *high); extern HAL_BOOL ar9300_fill_capability_info(struct ath_hal *ah); extern void ar9300_beacon_init(struct ath_hal *ah, u_int32_t next_beacon, u_int32_t beacon_period, u_int32_t beacon_period_fraction, HAL_OPMODE opmode); extern void ar9300_set_sta_beacon_timers(struct ath_hal *ah, const HAL_BEACON_STATE *); extern HAL_BOOL ar9300_is_interrupt_pending(struct ath_hal *ah); extern HAL_BOOL ar9300_get_pending_interrupts(struct ath_hal *ah, HAL_INT *, HAL_INT_TYPE, u_int8_t, HAL_BOOL); extern HAL_INT ar9300_get_interrupts(struct ath_hal *ah); extern HAL_INT ar9300_set_interrupts(struct ath_hal *ah, HAL_INT ints, HAL_BOOL); extern void ar9300_set_intr_mitigation_timer(struct ath_hal* ah, HAL_INT_MITIGATION reg, u_int32_t value); extern u_int32_t ar9300_get_intr_mitigation_timer(struct ath_hal* ah, HAL_INT_MITIGATION reg); extern u_int32_t ar9300_get_key_cache_size(struct ath_hal *); extern HAL_BOOL ar9300_is_key_cache_entry_valid(struct ath_hal *, u_int16_t entry); extern HAL_BOOL ar9300_reset_key_cache_entry(struct ath_hal *ah, u_int16_t entry); extern HAL_CHANNEL_INTERNAL * ar9300_check_chan(struct ath_hal *ah, const struct ieee80211_channel *chan); extern HAL_BOOL ar9300_set_key_cache_entry_mac(struct ath_hal *, u_int16_t entry, const u_int8_t *mac); extern HAL_BOOL ar9300_set_key_cache_entry(struct ath_hal *ah, u_int16_t entry, const HAL_KEYVAL *k, const u_int8_t *mac, int xor_key); extern HAL_BOOL ar9300_print_keycache(struct ath_hal *ah); #if ATH_SUPPORT_KEYPLUMB_WAR extern HAL_BOOL ar9300_check_key_cache_entry(struct ath_hal *ah, u_int16_t entry, const HAL_KEYVAL *k, int xorKey); #endif extern void ar9300_get_mac_address(struct ath_hal *ah, u_int8_t *mac); extern HAL_BOOL ar9300_set_mac_address(struct ath_hal *ah, const u_int8_t *); extern void ar9300_get_bss_id_mask(struct ath_hal *ah, u_int8_t *mac); extern HAL_BOOL ar9300_set_bss_id_mask(struct ath_hal *, const u_int8_t *); extern HAL_STATUS ar9300_select_ant_config(struct ath_hal *ah, u_int32_t cfg); #if 0 extern u_int32_t ar9300_ant_ctrl_common_get(struct ath_hal *ah, HAL_BOOL is_2ghz); #endif extern HAL_BOOL ar9300_ant_swcom_sel(struct ath_hal *ah, u_int8_t ops, u_int32_t *common_tbl1, u_int32_t *common_tbl2); extern HAL_BOOL ar9300_set_regulatory_domain(struct ath_hal *ah, u_int16_t reg_domain, HAL_STATUS *stats); extern u_int ar9300_get_wireless_modes(struct ath_hal *ah); extern void ar9300_enable_rf_kill(struct ath_hal *); extern HAL_BOOL ar9300_gpio_cfg_output(struct ath_hal *, u_int32_t gpio, HAL_GPIO_MUX_TYPE signalType); extern HAL_BOOL ar9300_gpio_cfg_output_led_off(struct ath_hal *, u_int32_t gpio, HAL_GPIO_MUX_TYPE signalType); extern HAL_BOOL ar9300_gpio_cfg_input(struct ath_hal *, u_int32_t gpio); extern HAL_BOOL ar9300_gpio_set(struct ath_hal *, u_int32_t gpio, u_int32_t val); extern u_int32_t ar9300_gpio_get(struct ath_hal *ah, u_int32_t gpio); extern u_int32_t ar9300_gpio_get_intr(struct ath_hal *ah); extern void ar9300_gpio_set_intr(struct ath_hal *ah, u_int, u_int32_t ilevel); extern u_int32_t ar9300_gpio_get_polarity(struct ath_hal *ah); extern void ar9300_gpio_set_polarity(struct ath_hal *ah, u_int32_t, u_int32_t); extern u_int32_t ar9300_gpio_get_mask(struct ath_hal *ah); extern int ar9300_gpio_set_mask(struct ath_hal *ah, u_int32_t mask, u_int32_t pol_map); extern void ar9300_set_led_state(struct ath_hal *ah, HAL_LED_STATE state); extern void ar9300_set_power_led_state(struct ath_hal *ah, u_int8_t enable); extern void ar9300_set_network_led_state(struct ath_hal *ah, u_int8_t enable); extern void ar9300_write_associd(struct ath_hal *ah, const u_int8_t *bssid, u_int16_t assoc_id); extern u_int32_t ar9300_ppm_get_rssi_dump(struct ath_hal *); extern u_int32_t ar9300_ppm_arm_trigger(struct ath_hal *); extern int ar9300_ppm_get_trigger(struct ath_hal *); extern u_int32_t ar9300_ppm_force(struct ath_hal *); extern void ar9300_ppm_un_force(struct ath_hal *); extern u_int32_t ar9300_ppm_get_force_state(struct ath_hal *); extern void ar9300_set_dcs_mode(struct ath_hal *ah, u_int32_t); extern u_int32_t ar9300_get_dcs_mode(struct ath_hal *ah); extern u_int32_t ar9300_get_tsf32(struct ath_hal *ah); extern u_int64_t ar9300_get_tsf64(struct ath_hal *ah); extern u_int32_t ar9300_get_tsf2_32(struct ath_hal *ah); extern void ar9300_set_tsf64(struct ath_hal *ah, u_int64_t tsf); extern void ar9300_reset_tsf(struct ath_hal *ah); extern void ar9300_set_basic_rate(struct ath_hal *ah, HAL_RATE_SET *pSet); extern u_int32_t ar9300_get_random_seed(struct ath_hal *ah); extern HAL_BOOL ar9300_detect_card_present(struct ath_hal *ah); extern void ar9300_update_mib_mac_stats(struct ath_hal *ah); extern void ar9300_get_mib_mac_stats(struct ath_hal *ah, HAL_MIB_STATS* stats); extern HAL_BOOL ar9300_is_japan_channel_spread_supported(struct ath_hal *ah); extern u_int32_t ar9300_get_cur_rssi(struct ath_hal *ah); extern u_int32_t ar9300_get_rssi_chain0(struct ath_hal *ah); extern u_int ar9300_get_def_antenna(struct ath_hal *ah); extern void ar9300_set_def_antenna(struct ath_hal *ah, u_int antenna); extern HAL_BOOL ar9300_set_antenna_switch(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *chan, u_int8_t *, u_int8_t *, u_int8_t *); extern HAL_BOOL ar9300_is_sleep_after_beacon_broken(struct ath_hal *ah); extern HAL_BOOL ar9300_set_slot_time(struct ath_hal *, u_int); extern HAL_BOOL ar9300_set_ack_timeout(struct ath_hal *, u_int); extern u_int ar9300_get_ack_timeout(struct ath_hal *); extern HAL_STATUS ar9300_set_quiet(struct ath_hal *ah, u_int32_t period, u_int32_t duration, u_int32_t next_start, HAL_QUIET_FLAG flag); extern void ar9300_set_pcu_config(struct ath_hal *); extern HAL_STATUS ar9300_get_capability(struct ath_hal *, HAL_CAPABILITY_TYPE, u_int32_t, u_int32_t *); extern HAL_BOOL ar9300_set_capability(struct ath_hal *, HAL_CAPABILITY_TYPE, u_int32_t, u_int32_t, HAL_STATUS *); extern HAL_BOOL ar9300_get_diag_state(struct ath_hal *ah, int request, const void *args, u_int32_t argsize, void **result, u_int32_t *resultsize); extern void ar9300_get_desc_info(struct ath_hal *ah, HAL_DESC_INFO *desc_info); extern uint32_t ar9300_get_11n_ext_busy(struct ath_hal *ah); extern void ar9300_set_11n_mac2040(struct ath_hal *ah, HAL_HT_MACMODE mode); extern HAL_HT_RXCLEAR ar9300_get_11n_rx_clear(struct ath_hal *ah); extern void ar9300_set_11n_rx_clear(struct ath_hal *ah, HAL_HT_RXCLEAR rxclear); extern HAL_BOOL ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip); extern HAL_POWER_MODE ar9300_get_power_mode(struct ath_hal *ah); extern HAL_BOOL ar9300_set_power_mode_awake(struct ath_hal *ah, int set_chip); extern void ar9300_set_sm_power_mode(struct ath_hal *ah, HAL_SMPS_MODE mode); extern void ar9300_config_pci_power_save(struct ath_hal *ah, int restore, int power_off); extern void ar9300_force_tsf_sync(struct ath_hal *ah, const u_int8_t *bssid, u_int16_t assoc_id); #if ATH_WOW extern void ar9300_wow_apply_pattern(struct ath_hal *ah, u_int8_t *p_ath_pattern, u_int8_t *p_ath_mask, int32_t pattern_count, u_int32_t ath_pattern_len); //extern u_int32_t ar9300_wow_wake_up(struct ath_hal *ah,u_int8_t *chipPatternBytes); extern u_int32_t ar9300_wow_wake_up(struct ath_hal *ah, HAL_BOOL offloadEnable); extern bool ar9300_wow_enable(struct ath_hal *ah, u_int32_t pattern_enable, u_int32_t timeout_in_seconds, int clearbssid, HAL_BOOL offloadEnable); #if ATH_WOW_OFFLOAD /* ARP offload */ #define WOW_OFFLOAD_ARP_INFO_MAX 2 struct hal_wow_offload_arp_info { u_int32_t valid; u_int32_t id; u_int32_t Flags; union { u_int8_t u8[4]; u_int32_t u32; } RemoteIPv4Address; union { u_int8_t u8[4]; u_int32_t u32; } HostIPv4Address; union { u_int8_t u8[6]; u_int32_t u32[2]; } MacAddress; }; /* NS offload */ #define WOW_OFFLOAD_NS_INFO_MAX 2 struct hal_wow_offload_ns_info { u_int32_t valid; u_int32_t id; u_int32_t Flags; union { u_int8_t u8[16]; u_int32_t u32[4]; } RemoteIPv6Address; union { u_int8_t u8[16]; u_int32_t u32[4]; } SolicitedNodeIPv6Address; union { u_int8_t u8[6]; u_int32_t u32[2]; } MacAddress; union { u_int8_t u8[16]; u_int32_t u32[4]; } TargetIPv6Addresses[2]; }; extern void ar9300_wowoffload_prep(struct ath_hal *ah); extern void ar9300_wowoffload_post(struct ath_hal *ah); extern u_int32_t ar9300_wowoffload_download_rekey_data(struct ath_hal *ah, u_int32_t *data, u_int32_t size); extern void ar9300_wowoffload_retrieve_data(struct ath_hal *ah, void *buf, u_int32_t param); extern void ar9300_wowoffload_download_acer_magic(struct ath_hal *ah, HAL_BOOL valid, u_int8_t* datap, u_int32_t bytes); extern void ar9300_wowoffload_download_acer_swka(struct ath_hal *ah, u_int32_t id, HAL_BOOL valid, u_int32_t period, u_int32_t size, u_int32_t* datap); extern void ar9300_wowoffload_download_arp_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data); extern void ar9300_wowoffload_download_ns_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data); #endif /* ATH_WOW_OFFLOAD */ #endif extern HAL_BOOL ar9300_reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask, HAL_HT_EXTPROTSPACING extprotspacing, - HAL_BOOL b_channel_change, HAL_STATUS *status, int is_scan); + HAL_BOOL b_channel_change, HAL_STATUS *status, HAL_RESET_TYPE reset_type, int is_scan); extern HAL_BOOL ar9300_lean_channel_change(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask); extern HAL_BOOL ar9300_set_reset_reg(struct ath_hal *ah, u_int32_t type); extern void ar9300_init_pll(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar9300_green_ap_ps_on_off( struct ath_hal *ah, u_int16_t rxMask); extern u_int16_t ar9300_is_single_ant_power_save_possible(struct ath_hal *ah); extern void ar9300_set_operating_mode(struct ath_hal *ah, int opmode); extern HAL_BOOL ar9300_phy_disable(struct ath_hal *ah); extern HAL_BOOL ar9300_disable(struct ath_hal *ah); -extern HAL_BOOL ar9300_chip_reset(struct ath_hal *ah, struct ieee80211_channel *); +extern HAL_BOOL ar9300_chip_reset(struct ath_hal *ah, struct ieee80211_channel *, HAL_RESET_TYPE type); extern HAL_BOOL ar9300_calibration(struct ath_hal *ah, struct ieee80211_channel *chan, u_int8_t rxchainmask, HAL_BOOL longcal, HAL_BOOL *isIQdone, int is_scan, u_int32_t *sched_cals); extern void ar9300_reset_cal_valid(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL *isIQdone, u_int32_t cal_type); extern void ar9300_iq_cal_collect(struct ath_hal *ah, u_int8_t num_chains); extern void ar9300_iq_calibration(struct ath_hal *ah, u_int8_t num_chains); extern void ar9300_temp_comp_cal_collect(struct ath_hal *ah); extern void ar9300_temp_comp_calibration(struct ath_hal *ah, u_int8_t num_chains); extern int16_t ar9300_get_min_cca_pwr(struct ath_hal *ah); extern void ar9300_upload_noise_floor(struct ath_hal *ah, int is2G, int16_t nfarray[]); extern HAL_BOOL ar9300_set_tx_power_limit(struct ath_hal *ah, u_int32_t limit, u_int16_t extra_txpow, u_int16_t tpc_in_db); extern void ar9300_chain_noise_floor(struct ath_hal *ah, int16_t *nf_buf, struct ieee80211_channel *chan, int is_scan); extern int16_t ar9300_get_nf_from_reg(struct ath_hal *ah, struct ieee80211_channel *chan, int wait_time); extern int ar9300_get_rx_nf_offset(struct ath_hal *ah, struct ieee80211_channel *chan, int8_t *nf_pwr, int8_t *nf_cal); extern HAL_BOOL ar9300_load_nf(struct ath_hal *ah, int16_t nf[]); extern HAL_RFGAIN ar9300_get_rfgain(struct ath_hal *ah); extern const HAL_RATE_TABLE *ar9300_get_rate_table(struct ath_hal *, u_int mode); extern int16_t ar9300_get_rate_txpower(struct ath_hal *ah, u_int mode, u_int8_t rate_index, u_int8_t chainmask, u_int8_t mimo_mode); extern void ar9300_init_rate_txpower(struct ath_hal *ah, u_int mode, const struct ieee80211_channel *chan, u_int8_t powerPerRate[], u_int8_t chainmask); extern void ar9300_adjust_reg_txpower_cdd(struct ath_hal *ah, u_int8_t powerPerRate[]); extern HAL_STATUS ath_hal_get_rate_power_limit_from_eeprom(struct ath_hal *ah, u_int16_t freq, int8_t *max_rate_power, int8_t *min_rate_power); extern void ar9300_reset_tx_status_ring(struct ath_hal *ah); extern void ar9300_enable_mib_counters(struct ath_hal *); extern void ar9300_disable_mib_counters(struct ath_hal *); extern void ar9300_ani_attach(struct ath_hal *); extern void ar9300_ani_detach(struct ath_hal *); extern struct ar9300_ani_state *ar9300_ani_get_current_state(struct ath_hal *); extern HAL_ANI_STATS *ar9300_ani_get_current_stats(struct ath_hal *); extern HAL_BOOL ar9300_ani_control(struct ath_hal *, HAL_ANI_CMD cmd, int param); struct ath_rx_status; extern void ar9300_process_mib_intr(struct ath_hal *, const HAL_NODE_STATS *); extern void ar9300_ani_ar_poll(struct ath_hal *, const HAL_NODE_STATS *, const struct ieee80211_channel *, HAL_ANISTATS *); extern void ar9300_ani_reset(struct ath_hal *, HAL_BOOL is_scanning); extern void ar9300_ani_init_defaults(struct ath_hal *ah, HAL_HT_MACMODE macmode); extern void ar9300_enable_tpc(struct ath_hal *); extern HAL_BOOL ar9300_rf_gain_cap_apply(struct ath_hal *ah, int is2GHz); extern void ar9300_rx_gain_table_apply(struct ath_hal *ah); extern void ar9300_tx_gain_table_apply(struct ath_hal *ah); extern void ar9300_mat_enable(struct ath_hal *ah, int enable); extern void ar9300_dump_keycache(struct ath_hal *ah, int n, u_int32_t *entry); extern HAL_BOOL ar9300_ant_ctrl_set_lna_div_use_bt_ant(struct ath_hal * ah, HAL_BOOL enable, const struct ieee80211_channel * chan); /* BB Panic Watchdog declarations */ #define HAL_BB_PANIC_WD_TMO 25 /* in ms, 0 to disable */ #define HAL_BB_PANIC_WD_TMO_HORNET 85 extern void ar9300_config_bb_panic_watchdog(struct ath_hal *); extern void ar9300_handle_bb_panic(struct ath_hal *); extern int ar9300_get_bb_panic_info(struct ath_hal *ah, struct hal_bb_panic_info *bb_panic); extern HAL_BOOL ar9300_handle_radar_bb_panic(struct ath_hal *ah); extern void ar9300_set_hal_reset_reason(struct ath_hal *ah, u_int8_t resetreason); /* DFS declarations */ extern void ar9300_check_dfs(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar9300_dfs_found(struct ath_hal *ah, struct ieee80211_channel *chan, u_int64_t nolTime); extern void ar9300_enable_dfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern void ar9300_get_dfs_thresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar9300_radar_wait(struct ath_hal *ah, struct ieee80211_channel *chan); extern struct dfs_pulse * ar9300_get_dfs_radars(struct ath_hal *ah, u_int32_t dfsdomain, int *numradars, struct dfs_bin5pulse **bin5pulses, int *numb5radars, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar9300_get_default_dfs_thresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern void ar9300_adjust_difs(struct ath_hal *ah, u_int32_t val); extern u_int32_t ar9300_dfs_config_fft(struct ath_hal *ah, HAL_BOOL is_enable); extern void ar9300_cac_tx_quiet(struct ath_hal *ah, HAL_BOOL enable); extern void ar9300_dfs_cac_war(struct ath_hal *ah, u_int32_t start); extern struct ieee80211_channel * ar9300_get_extension_channel(struct ath_hal *ah); extern HAL_BOOL ar9300_is_fast_clock_enabled(struct ath_hal *ah); extern void ar9300_mark_phy_inactive(struct ath_hal *ah); /* Spectral scan declarations */ extern void ar9300_configure_spectral_scan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); extern void ar9300_set_cca_threshold(struct ath_hal *ah, u_int8_t thresh62); extern void ar9300_get_spectral_params(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); extern HAL_BOOL ar9300_is_spectral_active(struct ath_hal *ah); extern HAL_BOOL ar9300_is_spectral_enabled(struct ath_hal *ah); extern void ar9300_start_spectral_scan(struct ath_hal *ah); extern void ar9300_stop_spectral_scan(struct ath_hal *ah); extern u_int32_t ar9300_get_spectral_config(struct ath_hal *ah); extern void ar9300_restore_spectral_config(struct ath_hal *ah, u_int32_t restoreval); int16_t ar9300_get_ctl_chan_nf(struct ath_hal *ah); int16_t ar9300_get_ext_chan_nf(struct ath_hal *ah); /* End spectral scan declarations */ /* Raw ADC capture functions */ extern void ar9300_enable_test_addac_mode(struct ath_hal *ah); extern void ar9300_disable_test_addac_mode(struct ath_hal *ah); extern void ar9300_begin_adc_capture(struct ath_hal *ah, int auto_agc_gain); extern HAL_STATUS ar9300_retrieve_capture_data(struct ath_hal *ah, u_int16_t chain_mask, int disable_dc_filter, void *sample_buf, u_int32_t *max_samples); extern HAL_STATUS ar9300_calc_adc_ref_powers(struct ath_hal *ah, int freq_mhz, int16_t *sample_min, int16_t *sample_max, int32_t *chain_ref_pwr, int num_chain_ref_pwr); extern HAL_STATUS ar9300_get_min_agc_gain(struct ath_hal *ah, int freq_mhz, int32_t *chain_gain, int num_chain_gain); extern HAL_BOOL ar9300_reset_11n(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL b_channel_change, HAL_STATUS *status); extern void ar9300_set_coverage_class(struct ath_hal *ah, u_int8_t coverageclass, int now); extern void ar9300_get_channel_centers(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers); extern u_int16_t ar9300_get_ctl_center(struct ath_hal *ah, const struct ieee80211_channel *chan); extern u_int16_t ar9300_get_ext_center(struct ath_hal *ah, const struct ieee80211_channel *chan); extern u_int32_t ar9300_get_mib_cycle_counts_pct(struct ath_hal *, u_int32_t*, u_int32_t*, u_int32_t*); extern void ar9300_dma_reg_dump(struct ath_hal *); extern HAL_BOOL ar9300_set_11n_rx_rifs(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_set_rifs_delay(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_set_smart_antenna(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_detect_bb_hang(struct ath_hal *ah); extern HAL_BOOL ar9300_detect_mac_hang(struct ath_hal *ah); #ifdef ATH_BT_COEX extern void ar9300_set_bt_coex_info(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo); extern void ar9300_bt_coex_config(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf); extern void ar9300_bt_coex_set_qcu_thresh(struct ath_hal *ah, int qnum); extern void ar9300_bt_coex_set_weights(struct ath_hal *ah, u_int32_t stomp_type); extern void ar9300_bt_coex_setup_bmiss_thresh(struct ath_hal *ah, u_int32_t thresh); extern void ar9300_bt_coex_set_parameter(struct ath_hal *ah, u_int32_t type, u_int32_t value); extern void ar9300_bt_coex_disable(struct ath_hal *ah); extern int ar9300_bt_coex_enable(struct ath_hal *ah); extern void ar9300_init_bt_coex(struct ath_hal *ah); extern u_int32_t ar9300_get_bt_active_gpio(struct ath_hal *ah, u_int32_t reg); extern u_int32_t ar9300_get_wlan_active_gpio(struct ath_hal *ah, u_int32_t reg,u_int32_t bOn); #endif extern int ar9300_alloc_generic_timer(struct ath_hal *ah, HAL_GEN_TIMER_DOMAIN tsf); extern void ar9300_free_generic_timer(struct ath_hal *ah, int index); extern void ar9300_start_generic_timer(struct ath_hal *ah, int index, u_int32_t timer_next, u_int32_t timer_period); extern void ar9300_stop_generic_timer(struct ath_hal *ah, int index); extern void ar9300_get_gen_timer_interrupts(struct ath_hal *ah, u_int32_t *trigger, u_int32_t *thresh); extern void ar9300_start_tsf2(struct ath_hal *ah); extern void ar9300_chk_rssi_update_tx_pwr(struct ath_hal *ah, int rssi); extern HAL_BOOL ar9300_is_skip_paprd_by_greentx(struct ath_hal *ah); extern void ar9300_control_signals_for_green_tx_mode(struct ath_hal *ah); extern void ar9300_hwgreentx_set_pal_spare(struct ath_hal *ah, int value); extern HAL_BOOL ar9300_is_ani_noise_spur(struct ath_hal *ah); extern void ar9300_reset_hw_beacon_proc_crc(struct ath_hal *ah); extern int32_t ar9300_get_hw_beacon_rssi(struct ath_hal *ah); extern void ar9300_set_hw_beacon_rssi_threshold(struct ath_hal *ah, u_int32_t rssi_threshold); extern void ar9300_reset_hw_beacon_rssi(struct ath_hal *ah); extern void ar9300_set_hw_beacon_proc(struct ath_hal *ah, HAL_BOOL on); extern void ar9300_get_vow_stats(struct ath_hal *ah, HAL_VOWSTATS *p_stats, u_int8_t); extern int ar9300_get_spur_info(struct ath_hal * ah, int *enable, int len, u_int16_t *freq); extern int ar9300_set_spur_info(struct ath_hal * ah, int enable, int len, u_int16_t *freq); extern void ar9300_wow_set_gpio_reset_low(struct ath_hal * ah); extern HAL_BOOL ar9300_get_mib_cycle_counts(struct ath_hal *, HAL_SURVEY_SAMPLE *); extern void ar9300_clear_mib_counters(struct ath_hal *ah); /* EEPROM interface functions */ /* Common Interface functions */ extern HAL_STATUS ar9300_eeprom_attach(struct ath_hal *); extern u_int32_t ar9300_eeprom_get(struct ath_hal_9300 *ahp, EEPROM_PARAM param); extern u_int32_t ar9300_ini_fixup(struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, u_int32_t reg, u_int32_t val); extern HAL_STATUS ar9300_eeprom_set_transmit_power(struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, const struct ieee80211_channel *chan, u_int16_t cfg_ctl, u_int16_t twice_antenna_reduction, u_int16_t twice_max_regulatory_power, u_int16_t power_limit); extern void ar9300_eeprom_set_addac(struct ath_hal *, struct ieee80211_channel *); extern HAL_BOOL ar9300_eeprom_set_param(struct ath_hal *ah, EEPROM_PARAM param, u_int32_t value); extern HAL_BOOL ar9300_eeprom_set_board_values(struct ath_hal *, const struct ieee80211_channel *); extern HAL_BOOL ar9300_eeprom_read_word(struct ath_hal *, u_int off, u_int16_t *data); extern HAL_BOOL ar9300_eeprom_read(struct ath_hal *ah, long address, u_int8_t *buffer, int many); extern HAL_BOOL ar9300_otp_read(struct ath_hal *ah, u_int off, u_int32_t *data, HAL_BOOL is_wifi); extern HAL_BOOL ar9300_flash_read(struct ath_hal *, u_int off, u_int16_t *data); extern HAL_BOOL ar9300_flash_write(struct ath_hal *, u_int off, u_int16_t data); extern u_int ar9300_eeprom_dump_support(struct ath_hal *ah, void **pp_e); extern u_int8_t ar9300_eeprom_get_num_ant_config(struct ath_hal_9300 *ahp, HAL_FREQ_BAND freq_band); extern HAL_STATUS ar9300_eeprom_get_ant_cfg(struct ath_hal_9300 *ahp, const struct ieee80211_channel *chan, u_int8_t index, u_int16_t *config); extern u_int8_t* ar9300_eeprom_get_cust_data(struct ath_hal_9300 *ahp); extern u_int8_t *ar9300_eeprom_get_spur_chans_ptr(struct ath_hal *ah, HAL_BOOL is_2ghz); extern HAL_BOOL ar9300_interference_is_present(struct ath_hal *ah); extern HAL_BOOL ar9300_tuning_caps_apply(struct ath_hal *ah); extern void ar9300_disp_tpc_tables(struct ath_hal *ah); extern u_int8_t *ar9300_get_tpc_tables(struct ath_hal *ah); extern u_int8_t ar9300_eeprom_set_tx_gain_cap(struct ath_hal *ah, int *tx_gain_max); extern u_int8_t ar9300_eeprom_tx_gain_table_index_max_apply(struct ath_hal *ah, u_int16_t channel); /* Common EEPROM Help function */ extern void ar9300_set_immunity(struct ath_hal *ah, HAL_BOOL enable); extern void ar9300_get_hw_hangs(struct ath_hal *ah, hal_hw_hangs_t *hangs); extern u_int ar9300_mac_to_clks(struct ath_hal *ah, u_int clks); /* tx_bf interface */ #define ar9300_init_txbf(ah) #define ar9300_set_11n_txbf_sounding(ah, ds, series, cec, opt) #define ar9300_set_11n_txbf_cal(ah, ds, cal_pos, code_rate, cec, opt) #define ar9300_txbf_save_cv_from_compress( \ ah, key_idx, mimo_control, compress_rpt) \ false #define ar9300_txbf_save_cv_from_non_compress( \ ah, key_idx, mimo_control, non_compress_rpt) \ false #define ar9300_txbf_rc_update( \ ah, rx_status, local_h, csi_frame, ness_a, ness_b, bw) \ false #define ar9300_fill_csi_frame( \ ah, rx_status, bandwidth, local_h, csi_frame_body) \ 0 #define ar9300_fill_txbf_capabilities(ah) #define ar9300_get_txbf_capabilities(ah) NULL #define ar9300_txbf_set_key( \ ah, entry, rx_staggered_sounding, channel_estimation_cap, mmss) #define ar9300_read_key_cache_mac(ah, entry, mac) false #define ar9300_txbf_get_cv_cache_nr(ah, key_idx, nr) #define ar9300_set_selfgenrate_limit(ah, ts_ratecode) #define ar9300_reset_lowest_txrate(ah) #define ar9300_txbf_set_basic_set(ah) extern void ar9300_crdc_rx_notify(struct ath_hal *ah, struct ath_rx_status *rxs); extern void ar9300_chain_rssi_diff_compensation(struct ath_hal *ah); #if ATH_SUPPORT_MCI extern void ar9300_mci_bt_coex_set_weights(struct ath_hal *ah, u_int32_t stomp_type); extern void ar9300_mci_bt_coex_disable(struct ath_hal *ah); extern int ar9300_mci_bt_coex_enable(struct ath_hal *ah); extern void ar9300_mci_setup (struct ath_hal *ah, u_int32_t gpm_addr, void *gpm_buf, u_int16_t len, u_int32_t sched_addr); extern void ar9300_mci_remote_reset(struct ath_hal *ah, HAL_BOOL wait_done); extern void ar9300_mci_send_lna_transfer(struct ath_hal *ah, HAL_BOOL wait_done); extern void ar9300_mci_send_sys_waking(struct ath_hal *ah, HAL_BOOL wait_done); extern HAL_BOOL ar9300_mci_send_message (struct ath_hal *ah, u_int8_t header, u_int32_t flag, u_int32_t *payload, u_int8_t len, HAL_BOOL wait_done, HAL_BOOL check_bt); extern u_int32_t ar9300_mci_get_interrupt (struct ath_hal *ah, u_int32_t *mci_int, u_int32_t *mci_int_rx_msg); extern u_int32_t ar9300_mci_state (struct ath_hal *ah, u_int32_t state_type, u_int32_t *p_data); extern void ar9300_mci_reset (struct ath_hal *ah, HAL_BOOL en_int, HAL_BOOL is_2g, HAL_BOOL is_full_sleep); extern void ar9300_mci_send_coex_halt_bt_gpm(struct ath_hal *ah, HAL_BOOL halt, HAL_BOOL wait_done); extern void ar9300_mci_mute_bt(struct ath_hal *ah); extern u_int32_t ar9300_mci_wait_for_gpm(struct ath_hal *ah, u_int8_t gpm_type, u_int8_t gpm_opcode, int32_t time_out); extern void ar9300_mci_enable_interrupt(struct ath_hal *ah); extern void ar9300_mci_disable_interrupt(struct ath_hal *ah); extern void ar9300_mci_detach (struct ath_hal *ah); extern u_int32_t ar9300_mci_check_int (struct ath_hal *ah, u_int32_t ints); extern void ar9300_mci_sync_bt_state (struct ath_hal *ah); extern void ar9300_mci_2g5g_changed(struct ath_hal *ah, HAL_BOOL is_2g); extern void ar9300_mci_2g5g_switch(struct ath_hal *ah, HAL_BOOL wait_done); #if ATH_SUPPORT_AIC extern u_int32_t ar9300_aic_calibration (struct ath_hal *ah); extern u_int32_t ar9300_aic_start_normal (struct ath_hal *ah); #endif #endif extern HAL_STATUS ar9300_set_proxy_sta(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_regulatory_domain_override( struct ath_hal *ah, u_int16_t regdmn); #if ATH_ANT_DIV_COMB extern void ar9300_ant_div_comb_get_config(struct ath_hal *ah, HAL_ANT_COMB_CONFIG* div_comb_conf); extern void ar9300_ant_div_comb_set_config(struct ath_hal *ah, HAL_ANT_COMB_CONFIG* div_comb_conf); #endif /* ATH_ANT_DIV_COMB */ extern void ar9300_disable_phy_restart(struct ath_hal *ah, int disable_phy_restart); extern void ar9300_enable_keysearch_always(struct ath_hal *ah, int enable); extern HAL_BOOL ar9300ForceVCS( struct ath_hal *ah); extern HAL_BOOL ar9300SetDfs3StreamFix(struct ath_hal *ah, u_int32_t val); extern HAL_BOOL ar9300Get3StreamSignature( struct ath_hal *ah); #ifdef ATH_TX99_DIAG #ifndef ATH_SUPPORT_HTC extern void ar9300_tx99_channel_pwr_update(struct ath_hal *ah, struct ieee80211_channel *c, u_int32_t txpower); extern void ar9300_tx99_chainmsk_setup(struct ath_hal *ah, int tx_chainmask); extern void ar9300_tx99_set_single_carrier(struct ath_hal *ah, int tx_chain_mask, int chtype); extern void ar9300_tx99_start(struct ath_hal *ah, u_int8_t *data); extern void ar9300_tx99_stop(struct ath_hal *ah); #endif /* ATH_SUPPORT_HTC */ #endif /* ATH_TX99_DIAG */ extern HAL_BOOL ar9300_set_ctl_pwr(struct ath_hal *ah, u_int8_t *ctl_array); extern void ar9300_set_txchainmaskopt(struct ath_hal *ah, u_int8_t mask); enum { AR9300_COEFF_TX_TYPE = 0, AR9300_COEFF_RX_TYPE }; #endif /* _ATH_AR9300_H_ */ Index: head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c =================================================================== --- head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c (revision 361485) +++ head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c (revision 361486) @@ -1,999 +1,1000 @@ /* * Copyright (c) 2012, 2013 Adrian Chadd . * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_desc.h" #include "ar9300.h" #include "ar9300reg.h" #include "ar9300phy.h" #include "ar9300desc.h" #include "ar9300_freebsd.h" #include "ar9300_stub.h" #include "ar9300_stub_funcs.h" #define FIX_NOISE_FLOOR 1 #define NEXT_TBTT_NOW 5 static HAL_BOOL ar9300ClrMulticastFilterIndex(struct ath_hal *ah, uint32_t ix); static HAL_BOOL ar9300SetMulticastFilterIndex(struct ath_hal *ah, uint32_t ix); static void ar9300_beacon_set_beacon_timers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt); static void ar9300SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask, uint32_t rx_chainmask) { AH9300(ah)->ah_tx_chainmask = tx_chainmask & AH_PRIVATE(ah)->ah_caps.halTxChainMask; AH9300(ah)->ah_rx_chainmask = rx_chainmask & AH_PRIVATE(ah)->ah_caps.halRxChainMask; } static u_int ar9300GetSlotTime(struct ath_hal *ah) { u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SLOT) & 0xffff; return (ath_hal_mac_usec(ah, clks)); /* convert from system clocks */ } static HAL_BOOL ar9300_freebsd_set_tx_power_limit(struct ath_hal *ah, uint32_t limit) { return (ar9300_set_tx_power_limit(ah, limit, 0, 0)); } static uint64_t ar9300_get_next_tbtt(struct ath_hal *ah) { return (OS_REG_READ(ah, AR_NEXT_TBTT_TIMER)); } /* * TODO: implement the antenna diversity control for AR9485 and * other LNA mixing based NICs. * * For now we'll just go with the HAL default and make these no-ops. */ static HAL_ANT_SETTING ar9300_freebsd_get_antenna_switch(struct ath_hal *ah) { return (HAL_ANT_VARIABLE); } static HAL_BOOL ar9300_freebsd_set_antenna_switch(struct ath_hal *ah, HAL_ANT_SETTING setting) { return (AH_TRUE); } static u_int ar9300_freebsd_get_cts_timeout(struct ath_hal *ah) { u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_CTS); return ath_hal_mac_usec(ah, clks); /* convert from system clocks */ } static void ar9300_freebsd_set_tsf64(struct ath_hal *ah, uint64_t tsf64) { /* * XXX TODO: read ar5416SetTsf64() - we should wait before we do * this. */ OS_REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff); OS_REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff); } /* Flags for pulse_bw_info */ #define PRI_CH_RADAR_FOUND 0x01 #define EXT_CH_RADAR_FOUND 0x02 #define EXT_CH_RADAR_EARLY_FOUND 0x04 static HAL_BOOL ar9300_freebsd_proc_radar_event(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) { HAL_BOOL doDfsExtCh; HAL_BOOL doDfsEnhanced; HAL_BOOL doDfsCombinedRssi; uint8_t rssi = 0, ext_rssi = 0; uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0; uint32_t dur = 0; int pri_found = 1, ext_found = 0; int early_ext = 0; int is_dc = 0; uint16_t datalen; /* length from the RX status field */ /* Check whether the given phy error is a radar event */ if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) && (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) { return AH_FALSE; } /* Grab copies of the capabilities; just to make the code clearer */ doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport; doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport; doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi; datalen = rxs->rs_datalen; /* If hardware supports it, use combined RSSI, else use chain 0 RSSI */ if (doDfsCombinedRssi) rssi = (uint8_t) rxs->rs_rssi; else rssi = (uint8_t) rxs->rs_rssi_ctl[0]; /* Set this; but only use it if doDfsExtCh is set */ ext_rssi = (uint8_t) rxs->rs_rssi_ext[0]; /* Cap it at 0 if the RSSI is a negative number */ if (rssi & 0x80) rssi = 0; if (ext_rssi & 0x80) ext_rssi = 0; /* * Fetch the relevant data from the frame */ if (doDfsExtCh) { if (datalen < 3) return AH_FALSE; /* Last three bytes of the frame are of interest */ pulse_length_pri = *(buf + datalen - 3); pulse_length_ext = *(buf + datalen - 2); pulse_bw_info = *(buf + datalen - 1); HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d," " pulse_length_ext=%d, pulse_bw_info=%x\n", __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext, pulse_bw_info); } else { /* The pulse width is byte 0 of the data */ if (datalen >= 1) dur = ((uint8_t) buf[0]) & 0xff; else dur = 0; if (dur == 0 && rssi == 0) { HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur); /* Single-channel only */ pri_found = 1; ext_found = 0; } /* * If doing extended channel data, pulse_bw_info must * have one of the flags set. */ if (doDfsExtCh && pulse_bw_info == 0x0) return AH_FALSE; /* * If the extended channel data is available, calculate * which to pay attention to. */ if (doDfsExtCh) { /* If pulse is on DC, take the larger duration of the two */ if ((pulse_bw_info & EXT_CH_RADAR_FOUND) && (pulse_bw_info & PRI_CH_RADAR_FOUND)) { is_dc = 1; if (pulse_length_ext > pulse_length_pri) { dur = pulse_length_ext; pri_found = 0; ext_found = 1; } else { dur = pulse_length_pri; pri_found = 1; ext_found = 0; } } else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) { dur = pulse_length_ext; pri_found = 0; ext_found = 1; early_ext = 1; } else if (pulse_bw_info & PRI_CH_RADAR_FOUND) { dur = pulse_length_pri; pri_found = 1; ext_found = 0; } else if (pulse_bw_info & EXT_CH_RADAR_FOUND) { dur = pulse_length_ext; pri_found = 0; ext_found = 1; } } /* * For enhanced DFS (Merlin and later), pulse_bw_info has * implications for selecting the correct RSSI value. */ if (doDfsEnhanced) { switch (pulse_bw_info & 0x03) { case 0: /* No radar? */ rssi = 0; break; case PRI_CH_RADAR_FOUND: /* Radar in primary channel */ /* Cannot use ctrl channel RSSI if ext channel is stronger */ if (ext_rssi >= (rssi + 3)) { rssi = 0; } break; case EXT_CH_RADAR_FOUND: /* Radar in extended channel */ /* Cannot use ext channel RSSI if ctrl channel is stronger */ if (rssi >= (ext_rssi + 12)) { rssi = 0; } else { rssi = ext_rssi; } break; case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): /* When both are present, use stronger one */ if (rssi < ext_rssi) rssi = ext_rssi; break; } } /* * If not doing enhanced DFS, choose the ext channel if * it is stronger than the main channel */ if (doDfsExtCh && !doDfsEnhanced) { if ((ext_rssi > rssi) && (ext_rssi < 128)) rssi = ext_rssi; } /* * XXX what happens if the above code decides the RSSI * XXX wasn't valid, an sets it to 0? */ /* * Fill out dfs_event structure. */ event->re_full_ts = fulltsf; event->re_ts = rxs->rs_tstamp; event->re_rssi = rssi; event->re_dur = dur; event->re_flags = 0; if (pri_found) event->re_flags |= HAL_DFS_EVENT_PRICH; if (ext_found) event->re_flags |= HAL_DFS_EVENT_EXTCH; if (early_ext) event->re_flags |= HAL_DFS_EVENT_EXTEARLY; if (is_dc) event->re_flags |= HAL_DFS_EVENT_ISDC; return AH_TRUE; } void ar9300_attach_freebsd_ops(struct ath_hal *ah) { /* Global functions */ ah->ah_detach = ar9300_detach; ah->ah_getRateTable = ar9300_get_rate_table; /* Reset functions */ ah->ah_reset = ar9300_reset_freebsd; ah->ah_phyDisable = ar9300_phy_disable; ah->ah_disable = ar9300_disable; ah->ah_configPCIE = ar9300_config_pcie_freebsd; // ah->ah_disablePCIE = ar9300_disable_pcie_phy; ah->ah_setPCUConfig = ar9300_set_pcu_config; // perCalibration ah->ah_perCalibrationN = ar9300_per_calibration_freebsd; ah->ah_resetCalValid = ar9300_reset_cal_valid_freebsd; ah->ah_setTxPowerLimit = ar9300_freebsd_set_tx_power_limit; ah->ah_getChanNoise = ath_hal_getChanNoise; /* Transmit functions */ ah->ah_setupTxQueue = ar9300_setup_tx_queue; ah->ah_setTxQueueProps = ar9300_set_tx_queue_props; ah->ah_getTxQueueProps = ar9300_get_tx_queue_props; ah->ah_releaseTxQueue = ar9300_release_tx_queue; ah->ah_resetTxQueue = ar9300_reset_tx_queue; ah->ah_getTxDP = ar9300_get_tx_dp; ah->ah_setTxDP = ar9300_set_tx_dp; ah->ah_numTxPending = ar9300_num_tx_pending; ah->ah_startTxDma = ar9300_start_tx_dma; ah->ah_stopTxDma = ar9300_stop_tx_dma_freebsd; ah->ah_setupTxDesc = ar9300_freebsd_setup_tx_desc; ah->ah_setupXTxDesc = ar9300_freebsd_setup_x_tx_desc; ah->ah_fillTxDesc = ar9300_freebsd_fill_tx_desc; ah->ah_procTxDesc = ar9300_freebsd_proc_tx_desc; ah->ah_getTxIntrQueue = ar9300_get_tx_intr_queue; // reqTxIntrDesc ah->ah_getTxCompletionRates = ar9300_freebsd_get_tx_completion_rates; ah->ah_setTxDescLink = ar9300_set_desc_link; ah->ah_getTxDescLink = ar9300_freebsd_get_desc_link; ah->ah_getTxDescLinkPtr = ar9300_get_desc_link_ptr; ah->ah_setupTxStatusRing = ar9300_setup_tx_status_ring; ah->ah_getTxRawTxDesc = ar9300_get_raw_tx_desc; ah->ah_updateTxTrigLevel = ar9300_update_tx_trig_level; /* RX functions */ ah->ah_getRxDP = ar9300_get_rx_dp; ah->ah_setRxDP = ar9300_set_rx_dp; ah->ah_enableReceive = ar9300_enable_receive; ah->ah_stopDmaReceive = ar9300_stop_dma_receive_freebsd; ah->ah_startPcuReceive = ar9300_start_pcu_receive; ah->ah_stopPcuReceive = ar9300_stop_pcu_receive; ah->ah_setMulticastFilter = ar9300_set_multicast_filter; ah->ah_setMulticastFilterIndex = ar9300SetMulticastFilterIndex; ah->ah_clrMulticastFilterIndex = ar9300ClrMulticastFilterIndex; ah->ah_getRxFilter = ar9300_get_rx_filter; ah->ah_setRxFilter = ar9300_set_rx_filter; /* setupRxDesc */ ah->ah_procRxDesc = ar9300_proc_rx_desc_freebsd; ah->ah_rxMonitor = ar9300_ani_rxmonitor_freebsd; ah->ah_aniPoll = ar9300_ani_poll_freebsd; ah->ah_procMibEvent = ar9300_process_mib_intr; /* Misc functions */ ah->ah_getCapability = ar9300_get_capability; ah->ah_setCapability = ar9300_set_capability; ah->ah_getDiagState = ar9300_get_diag_state; ah->ah_getMacAddress = ar9300_get_mac_address; ah->ah_setMacAddress = ar9300_set_mac_address; ah->ah_getBssIdMask = ar9300_get_bss_id_mask; ah->ah_setBssIdMask = ar9300_set_bss_id_mask; ah->ah_setRegulatoryDomain = ar9300_set_regulatory_domain; ah->ah_setLedState = ar9300_set_led_state; ah->ah_writeAssocid = ar9300_write_associd; ah->ah_gpioCfgInput = ar9300_gpio_cfg_input; ah->ah_gpioCfgOutput = ar9300_gpio_cfg_output; ah->ah_gpioGet = ar9300_gpio_get; ah->ah_gpioSet = ar9300_gpio_set; ah->ah_gpioSetIntr = ar9300_gpio_set_intr; /* polarity */ /* mask */ ah->ah_getTsf32 = ar9300_get_tsf32; ah->ah_getTsf64 = ar9300_get_tsf64; ah->ah_resetTsf = ar9300_reset_tsf; ah->ah_setTsf64 = ar9300_freebsd_set_tsf64; ah->ah_detectCardPresent = ar9300_detect_card_present; // ah->ah_updateMibCounters = ar9300_update_mib_counters; ah->ah_getRfGain = ar9300_get_rfgain; ah->ah_getDefAntenna = ar9300_get_def_antenna; ah->ah_setDefAntenna = ar9300_set_def_antenna; ah->ah_getAntennaSwitch = ar9300_freebsd_get_antenna_switch; ah->ah_setAntennaSwitch = ar9300_freebsd_set_antenna_switch; // ah->ah_setSifsTime = ar9300_set_sifs_time; // ah->ah_getSifsTime = ar9300_get_sifs_time; ah->ah_setSlotTime = ar9300_set_slot_time; ah->ah_getSlotTime = ar9300GetSlotTime; ah->ah_getAckTimeout = ar9300_get_ack_timeout; ah->ah_setAckTimeout = ar9300_set_ack_timeout; // XXX ack/ctsrate // XXX CTS timeout ah->ah_getCTSTimeout = ar9300_freebsd_get_cts_timeout; // XXX decompmask // coverageclass ah->ah_setQuiet = ar9300_set_quiet; ah->ah_getMibCycleCounts = ar9300_freebsd_get_mib_cycle_counts; /* DFS functions */ ah->ah_enableDfs = ar9300_enable_dfs; ah->ah_getDfsThresh = ar9300_get_dfs_thresh; ah->ah_getDfsDefaultThresh = ar9300_get_default_dfs_thresh; ah->ah_procRadarEvent = ar9300_freebsd_proc_radar_event; ah->ah_isFastClockEnabled = ar9300_is_fast_clock_enabled; ah->ah_get11nExtBusy = ar9300_get_11n_ext_busy; ah->ah_setDfsCacTxQuiet = ar9300_cac_tx_quiet; /* Spectral Scan Functions */ ah->ah_spectralConfigure = ar9300_configure_spectral_scan; ah->ah_spectralGetConfig = ar9300_get_spectral_params; ah->ah_spectralStart = ar9300_start_spectral_scan; ah->ah_spectralStop = ar9300_stop_spectral_scan; ah->ah_spectralIsEnabled = ar9300_is_spectral_enabled; ah->ah_spectralIsActive = ar9300_is_spectral_active; /* Key cache functions */ ah->ah_getKeyCacheSize = ar9300_get_key_cache_size; ah->ah_resetKeyCacheEntry = ar9300_reset_key_cache_entry; ah->ah_isKeyCacheEntryValid = ar9300_is_key_cache_entry_valid; ah->ah_setKeyCacheEntry = ar9300_set_key_cache_entry; ah->ah_setKeyCacheEntryMac = ar9300_set_key_cache_entry_mac; /* Power management functions */ ah->ah_setPowerMode = ar9300_set_power_mode; ah->ah_getPowerMode = ar9300_get_power_mode; /* Beacon functions */ /* ah_setBeaconTimers */ ah->ah_beaconInit = ar9300_freebsd_beacon_init; ah->ah_setBeaconTimers = ar9300_beacon_set_beacon_timers; ah->ah_setStationBeaconTimers = ar9300_set_sta_beacon_timers; /* ah_resetStationBeaconTimers */ ah->ah_getNextTBTT = ar9300_get_next_tbtt; /* Interrupt functions */ ah->ah_isInterruptPending = ar9300_is_interrupt_pending; ah->ah_getPendingInterrupts = ar9300_get_pending_interrupts_freebsd; ah->ah_getInterrupts = ar9300_get_interrupts; ah->ah_setInterrupts = ar9300_set_interrupts_freebsd; /* Regulatory/internal functions */ // AH_PRIVATE(ah)->ah_getNfAdjust = ar9300_get_nf_adjust; AH_PRIVATE(ah)->ah_eepromRead = ar9300_eeprom_read_word; // AH_PRIVATE(ah)->ah_getChipPowerLimits = ar9300_get_chip_power_limits; AH_PRIVATE(ah)->ah_getWirelessModes = ar9300_get_wireless_modes; AH_PRIVATE(ah)->ah_getChannelEdges = ar9300_get_channel_edges; AH_PRIVATE(ah)->ah_eepromRead = ar9300_eeprom_read_word; /* XXX ah_eeprom */ /* XXX ah_eeversion */ /* XXX ah_eepromDetach */ /* XXX ah_eepromGet */ AH_PRIVATE(ah)->ah_eepromGet = ar9300_eeprom_get_freebsd; /* XXX ah_eepromSet */ /* XXX ah_getSpurChan */ /* XXX ah_eepromDiag */ /* 802.11n functions */ ah->ah_chainTxDesc = ar9300_freebsd_chain_tx_desc; ah->ah_setupFirstTxDesc= ar9300_freebsd_setup_first_tx_desc; ah->ah_setupLastTxDesc = ar9300_freebsd_setup_last_tx_desc; ah->ah_set11nRateScenario = ar9300_freebsd_set_11n_rate_scenario; ah->ah_set11nTxDesc = ar9300_freebsd_setup_11n_desc; ah->ah_set11nAggrFirst = ar9300_set_11n_aggr_first; ah->ah_set11nAggrMiddle = ar9300_set_11n_aggr_middle; ah->ah_set11nAggrLast = ar9300_set_11n_aggr_last; ah->ah_clr11nAggr = ar9300_clr_11n_aggr; ah->ah_set11nBurstDuration = ar9300_set_11n_burst_duration; /* ah_get11nExtBusy */ ah->ah_set11nMac2040 = ar9300_set_11n_mac2040; ah->ah_setChainMasks = ar9300SetChainMasks; /* ah_get11nRxClear */ /* ah_set11nRxClear */ /* bluetooth coexistence functions */ ah->ah_btCoexSetInfo = ar9300_set_bt_coex_info; ah->ah_btCoexSetConfig = ar9300_bt_coex_config; ah->ah_btCoexSetQcuThresh = ar9300_bt_coex_set_qcu_thresh; ah->ah_btCoexSetWeights = ar9300_bt_coex_set_weights; ah->ah_btCoexSetBmissThresh = ar9300_bt_coex_setup_bmiss_thresh; ah->ah_btCoexSetParameter = ar9300_bt_coex_set_parameter; ah->ah_btCoexDisable = ar9300_bt_coex_disable; ah->ah_btCoexEnable = ar9300_bt_coex_enable; /* MCI bluetooth functions */ if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { /* * Note: these are done in attach too for now, because * at this point we haven't yet setup the mac/bb revision * values, so this code is effectively NULL. * However, I'm leaving this here so people digging * into the code (a) see the MCI bits here, and (b) * are now told they should look elsewhere for * these methods. */ ah->ah_btCoexSetWeights = ar9300_mci_bt_coex_set_weights; ah->ah_btCoexDisable = ar9300_mci_bt_coex_disable; ah->ah_btCoexEnable = ar9300_mci_bt_coex_enable; } ah->ah_btMciSetup = ar9300_mci_setup; ah->ah_btMciSendMessage = ar9300_mci_send_message; ah->ah_btMciGetInterrupt = ar9300_mci_get_interrupt; ah->ah_btMciState = ar9300_mci_state; ah->ah_btMciDetach = ar9300_mci_detach; /* LNA diversity functions */ ah->ah_divLnaConfGet = ar9300_ant_div_comb_get_config; ah->ah_divLnaConfSet = ar9300_ant_div_comb_set_config; } HAL_BOOL ar9300_reset_freebsd(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_RESET_TYPE resetType, HAL_STATUS *status) { HAL_BOOL r; HAL_HT_MACMODE macmode; struct ath_hal_private *ap = AH_PRIVATE(ah); macmode = IEEE80211_IS_CHAN_HT40(chan) ? HAL_HT_MACMODE_2040 : HAL_HT_MACMODE_20; r = ar9300_reset(ah, opmode, chan, macmode, ap->ah_caps.halTxChainMask, ap->ah_caps.halRxChainMask, HAL_HT_EXTPROTSPACING_20, /* always 20Mhz channel spacing */ bChannelChange, status, + resetType, AH_FALSE); /* XXX should really extend ath_hal_reset() */ return (r); } void ar9300_config_pcie_freebsd(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL powerOff) { ar9300_config_pci_power_save(ah, restore ? 1 : 0, powerOff ? 1 : 0); } /* * This is a copy from ar9300_eeprom_get(), purely because the FreeBSD * API is very silly and inconsistent. * * The AR93xx HAL doesn't call the eepromGetFlag() function, so this * only occurs for FreeBSD code. * * When I fix this particular API, I'll undo this. */ HAL_STATUS ar9300_eeprom_get_freebsd(struct ath_hal *ah, int param, void *val) { switch (param) { case AR_EEP_FSTCLK_5G: return HAL_OK; default: ath_hal_printf(ah, "%s: called, param=%d\n", __func__, param); return HAL_EIO; } } HAL_BOOL ar9300_stop_tx_dma_freebsd(struct ath_hal *ah, u_int q) { return ar9300_stop_tx_dma(ah, q, 1000); } void ar9300_ani_poll_freebsd(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_NODE_STATS stats; HAL_ANISTATS anistats; HAL_SURVEY_SAMPLE survey; OS_MEMZERO(&stats, sizeof(stats)); OS_MEMZERO(&anistats, sizeof(anistats)); OS_MEMZERO(&survey, sizeof(survey)); ar9300_ani_ar_poll(ah, &stats, chan, &anistats); /* * If ANI stats are valid, use them to update the * channel survey. */ if (anistats.valid) { survey.cycle_count = anistats.cyclecnt_diff; survey.chan_busy = anistats.rxclr_cnt; survey.ext_chan_busy = anistats.extrxclr_cnt; survey.tx_busy = anistats.txframecnt_diff; survey.rx_busy = anistats.rxframecnt_diff; ath_hal_survey_add_sample(ah, &survey); } } /* * Setup the configuration parameters in the style the AR9300 HAL * wants. */ void ar9300_config_defaults_freebsd(struct ath_hal *ah, HAL_OPS_CONFIG *ah_config) { /* Until FreeBSD's HAL does this by default - just copy */ OS_MEMCPY(&ah->ah_config, ah_config, sizeof(HAL_OPS_CONFIG)); ah->ah_config.ath_hal_enable_ani = AH_TRUE; } HAL_BOOL ar9300_stop_dma_receive_freebsd(struct ath_hal *ah) { return ar9300_stop_dma_receive(ah, 1000); } HAL_BOOL ar9300_get_pending_interrupts_freebsd(struct ath_hal *ah, HAL_INT *masked) { /* Non-MSI, so no MSI vector; and 'nortc' = 0 */ return ar9300_get_pending_interrupts(ah, masked, HAL_INT_LINE, 0, 0); } HAL_INT ar9300_set_interrupts_freebsd(struct ath_hal *ah, HAL_INT ints) { /* nortc = 0 */ return ar9300_set_interrupts(ah, ints, 0); } HAL_BOOL ar9300_per_calibration_freebsd(struct ath_hal *ah, struct ieee80211_channel *chan, u_int rxchainmask, HAL_BOOL long_cal, HAL_BOOL *isCalDone) { /* XXX fake scheduled calibrations for now */ u_int32_t sched_cals = 0xfffffff; return ar9300_calibration(ah, chan, AH_PRIVATE(ah)->ah_caps.halRxChainMask, long_cal, isCalDone, 0, /* is_scan */ &sched_cals); } HAL_BOOL ar9300_reset_cal_valid_freebsd(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_BOOL is_cal_done = AH_TRUE; ar9300_reset_cal_valid(ah, chan, &is_cal_done, 0xffffffff); return (is_cal_done); } /* * FreeBSD will just pass in the descriptor value as 'pa'. * The Atheros HAL treats 'pa' as the physical address of the RX * descriptor and 'bufaddr' as the physical address of the RX buffer. * I'm not sure why they didn't collapse them - the AR9300 RX descriptor * routine doesn't check 'pa'. */ HAL_STATUS ar9300_proc_rx_desc_freebsd(struct ath_hal *ah, struct ath_desc *ds, uint32_t pa, struct ath_desc *ds_next, uint64_t tsf, struct ath_rx_status *rxs) { return (ar9300_proc_rx_desc_fast(ah, ds, 0, ds_next, rxs, (void *) ds)); } /* * This is the primary way the ANI code gets the node statistics per packet. */ void ar9300_ani_rxmonitor_freebsd(struct ath_hal *ah, const HAL_NODE_STATS *stats, const struct ieee80211_channel *chan) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; } void ar9300_freebsd_get_desc_link(struct ath_hal *ah, void *ds, uint32_t *link) { struct ar9300_txc *ads = AR9300TXC(ds); (*link) = ads->ds_link; } /* * TX descriptor field setting wrappers - eek. */ HAL_BOOL ar9300_freebsd_setup_tx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, u_int txRate0, u_int txTries0, u_int keyIx, u_int antMode, u_int flags, u_int rtsctsRate, u_int rtsCtsDuration, u_int compicvLen, u_int compivLen, u_int comp) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_KEY_TYPE keyType = 0; /* XXX No padding */ if (keyIx != HAL_TXKEYIX_INVALID) keyType = ahp->ah_keytype[keyIx]; /* XXX bounds check keyix */ ar9300_set_11n_tx_desc(ah, ds, pktLen, type, txPower, keyIx, keyType, flags); return AH_TRUE; } HAL_BOOL ar9300_freebsd_setup_x_tx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int txRate1, u_int txTries1, u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3) { #if 0 ath_hal_printf(ah, "%s: called, 0x%x/%d, 0x%x/%d, 0x%x/%d\n", __func__, txRate1, txTries1, txRate2, txTries2, txRate3, txTries3); #endif /* XXX should only be called during probe */ return (AH_TRUE); } HAL_BOOL ar9300_freebsd_fill_tx_desc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufListPtr, uint32_t *segLenPtr, u_int descId, u_int qid, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { HAL_KEY_TYPE keyType = 0; const struct ar9300_txc *ads = AR9300TXC_CONST(ds0); /* * FreeBSD's HAL doesn't pass the keytype to fill_tx_desc(); * it's copied as part of the descriptor chaining. * * So, extract it from ds0. */ keyType = MS(ads->ds_ctl17, AR_encr_type); return ar9300_fill_tx_desc(ah, ds, bufListPtr, segLenPtr, descId, qid, keyType, firstSeg, lastSeg, ds0); } HAL_BOOL ar9300_freebsd_get_tx_completion_rates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries) { ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; /* XXX for now */ } /* * 802.11n TX descriptor wrappers */ void ar9300_freebsd_set_11n_rate_scenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[], u_int nseries, u_int flags) { /* lastds=NULL, rtscts_duration is 0, smart antenna is 0 */ ar9300_set_11n_rate_scenario(ah, (void *) ds, (void *)ds, durUpdateEn, rtsctsRate, 0, series, nseries, flags, 0); } /* chaintxdesc */ HAL_BOOL ar9300_freebsd_chain_tx_desc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufLenList, uint32_t *segLenList, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, HAL_CIPHER cipher, uint8_t numDelims, HAL_BOOL firstSeg, HAL_BOOL lastSeg, HAL_BOOL lastAggr) { ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; } /* setupfirsttxdesc */ HAL_BOOL ar9300_freebsd_setup_first_tx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0, u_int antMode, u_int rtsctsRate, u_int rtsctsDuration) { ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; } /* setuplasttxdesc */ /* * This gets called but for now let's not log anything; * it's only used to update the rate control information. */ HAL_BOOL ar9300_freebsd_setup_last_tx_desc(struct ath_hal *ah, struct ath_desc *ds, const struct ath_desc *ds0) { // ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; } void ar9300_freebsd_setup_11n_desc(struct ath_hal *ah, void *ds, u_int pktLen, HAL_PKT_TYPE type, u_int txPower, u_int keyIx, u_int flags) { ath_hal_printf(ah, "%s: called\n", __func__); #if 0 struct ath_hal_9300 *ahp = AH9300(ah); HAL_KEY_TYPE keyType = 0; /* XXX No padding */ if (keyIx != HAL_TXKEYIX_INVALID) keyType = ahp->ah_keytype[keyIx]; /* XXX bounds check keyix */ ar9300_set_11n_tx_desc(ah, ds, pktLen, type, txPower, keyIx, keyType, flags); #endif } HAL_STATUS ar9300_freebsd_proc_tx_desc(struct ath_hal *ah, struct ath_desc *ds, struct ath_tx_status *ts) { return ar9300_proc_tx_desc(ah, ts); } void ar9300_freebsd_beacon_init(struct ath_hal *ah, uint32_t next_beacon, uint32_t beacon_period) { ar9300_beacon_init(ah, next_beacon, beacon_period, 0, AH_PRIVATE(ah)->ah_opmode); } HAL_BOOL ar9300_freebsd_get_mib_cycle_counts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) { return (AH_FALSE); } /* * Clear multicast filter by index - from FreeBSD ar5212_recv.c */ static HAL_BOOL ar9300ClrMulticastFilterIndex(struct ath_hal *ah, uint32_t ix) { uint32_t val; if (ix >= 64) return (AH_FALSE); if (ix >= 32) { val = OS_REG_READ(ah, AR_MCAST_FIL1); OS_REG_WRITE(ah, AR_MCAST_FIL1, (val &~ (1<<(ix-32)))); } else { val = OS_REG_READ(ah, AR_MCAST_FIL0); OS_REG_WRITE(ah, AR_MCAST_FIL0, (val &~ (1<= 64) return (AH_FALSE); if (ix >= 32) { val = OS_REG_READ(ah, AR_MCAST_FIL1); OS_REG_WRITE(ah, AR_MCAST_FIL1, (val | (1<<(ix-32)))); } else { val = OS_REG_READ(ah, AR_MCAST_FIL0); OS_REG_WRITE(ah, AR_MCAST_FIL0, (val | (1<bt_nexttbtt)); OS_REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, ONE_EIGHTH_TU_TO_USEC(bt->bt_nextdba)); OS_REG_WRITE(ah, AR_NEXT_SWBA, ONE_EIGHTH_TU_TO_USEC(bt->bt_nextswba)); OS_REG_WRITE(ah, AR_NEXT_NDP_TIMER, TU_TO_USEC(bt->bt_nextatim)); bperiod = TU_TO_USEC(bt->bt_intval & HAL_BEACON_PERIOD); AH9300(ah)->ah_beaconInterval = bt->bt_intval & HAL_BEACON_PERIOD; OS_REG_WRITE(ah, AR_BEACON_PERIOD, bperiod); OS_REG_WRITE(ah, AR_DMA_BEACON_PERIOD, bperiod); OS_REG_WRITE(ah, AR_SWBA_PERIOD, bperiod); OS_REG_WRITE(ah, AR_NDP_PERIOD, bperiod); /* * Reset TSF if required. */ if (bt->bt_intval & HAL_BEACON_RESET_TSF) ar9300_reset_tsf(ah); /* enable timers */ /* NB: flags == 0 handled specially for backwards compatibility */ OS_REG_SET_BIT(ah, AR_TIMER_MODE, bt->bt_flags != 0 ? bt->bt_flags : AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN); } /* * RF attach stubs */ static HAL_BOOL rf9330_attach(struct ath_hal *ah, HAL_STATUS *status) { (*status) = HAL_EINVAL; return (AH_FALSE); } static HAL_BOOL rf9330_probe(struct ath_hal *ah) { return (AH_FALSE); } AH_RF(RF9330, rf9330_probe, rf9330_attach); Index: head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c =================================================================== --- head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c (revision 361485) +++ head/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c (revision 361486) @@ -1,6550 +1,6555 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_desc.h" #include "ar9300.h" #include "ar9300reg.h" #include "ar9300phy.h" #include "ar9300desc.h" #define FIX_NOISE_FLOOR 1 /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* usec */ #define RTC_PLL_SETTLE_DELAY 100 /* usec */ #define COEF_SCALE_S 24 #define HT40_CHANNEL_CENTER_SHIFT 10 /* MHz */ #define DELPT 32 /* XXX Duplicates! (in ar9300desc.h) */ #if 0 extern HAL_BOOL ar9300_reset_tx_queue(struct ath_hal *ah, u_int q); extern u_int32_t ar9300_num_tx_pending(struct ath_hal *ah, u_int q); #endif #define MAX_MEASUREMENT 8 #define MAXIQCAL 3 struct coeff_t { int32_t mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int32_t phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int32_t iqc_coeff[2]; int last_nmeasurement; HAL_BOOL last_cal; }; static HAL_BOOL ar9300_tx_iq_cal_hw_run(struct ath_hal *ah); static void ar9300_tx_iq_cal_post_proc(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, int iqcal_idx, int max_iqcal, HAL_BOOL is_cal_reusable, HAL_BOOL apply_last_corr); static void ar9300_tx_iq_cal_outlier_detection(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, u_int32_t num_chains, struct coeff_t *coeff, HAL_BOOL is_cal_reusable); #if ATH_SUPPORT_CAL_REUSE static void ar9300_tx_iq_cal_apply(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan); #endif static inline void ar9300_prog_ini(struct ath_hal *ah, struct ar9300_ini_array *ini_arr, int column); static inline void ar9300_set_rf_mode(struct ath_hal *ah, struct ieee80211_channel *chan); static inline HAL_BOOL ar9300_init_cal(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL skip_if_none, HAL_BOOL apply_last_corr); static inline void ar9300_init_user_settings(struct ath_hal *ah); #ifdef HOST_OFFLOAD /* * For usb offload solution, some USB registers must be tuned * to gain better stability/performance but these registers * might be changed while doing wlan reset so do this here */ #define WAR_USB_DISABLE_PLL_LOCK_DETECT(__ah) \ do { \ if (AR_SREV_HORNET(__ah) || AR_SREV_WASP(__ah)) { \ volatile u_int32_t *usb_ctrl_r1 = (u_int32_t *) 0xb8116c84; \ volatile u_int32_t *usb_ctrl_r2 = (u_int32_t *) 0xb8116c88; \ *usb_ctrl_r1 = (*usb_ctrl_r1 & 0xffefffff); \ *usb_ctrl_r2 = (*usb_ctrl_r2 & 0xfc1fffff) | (1 << 21) | (3 << 22); \ } \ } while (0) #else #define WAR_USB_DISABLE_PLL_LOCK_DETECT(__ah) #endif /* * Note: the below is the version that ships with ath9k. * The original HAL version is above. */ static void ar9300_disable_pll_lock_detect(struct ath_hal *ah) { /* * On AR9330 and AR9340 devices, some PHY registers must be * tuned to gain better stability/performance. These registers * might be changed while doing wlan reset so the registers must * be reprogrammed after each reset. */ if (AR_SREV_HORNET(ah) || AR_SREV_WASP(ah)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: called\n", __func__); OS_REG_CLR_BIT(ah, AR_PHY_USB_CTRL1, (1 << 20)); OS_REG_RMW(ah, AR_PHY_USB_CTRL2, (1 << 21) | (0xf << 22), (1 << 21) | (0x3 << 22)); } } static inline void ar9300_attach_hw_platform(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ah_hwp = HAL_TRUE_CHIP; return; } /* Adjust various register settings based on half/quarter rate clock setting. * This includes: +USEC, TX/RX latency, * + IFS params: slot, eifs, misc etc. * SIFS stays the same. */ static void ar9300_set_ifs_timing(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t tx_lat, rx_lat, usec, slot, regval, eifs; regval = OS_REG_READ(ah, AR_USEC); regval &= ~(AR_USEC_RX_LATENCY | AR_USEC_TX_LATENCY | AR_USEC_USEC); if (IEEE80211_IS_CHAN_HALF(chan)) { /* half rates */ slot = ar9300_mac_to_clks(ah, AR_SLOT_HALF); eifs = ar9300_mac_to_clks(ah, AR_EIFS_HALF); if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* fast clock */ rx_lat = SM(AR_RX_LATENCY_HALF_FAST_CLOCK, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_HALF_FAST_CLOCK, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_HALF_FAST_CLOCK, AR_USEC_USEC); } else { rx_lat = SM(AR_RX_LATENCY_HALF, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_HALF, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_HALF, AR_USEC_USEC); } } else { /* quarter rate */ slot = ar9300_mac_to_clks(ah, AR_SLOT_QUARTER); eifs = ar9300_mac_to_clks(ah, AR_EIFS_QUARTER); if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* fast clock */ rx_lat = SM(AR_RX_LATENCY_QUARTER_FAST_CLOCK, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_QUARTER_FAST_CLOCK, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_QUARTER_FAST_CLOCK, AR_USEC_USEC); } else { rx_lat = SM(AR_RX_LATENCY_QUARTER, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_QUARTER, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_QUARTER, AR_USEC_USEC); } } OS_REG_WRITE(ah, AR_USEC, (usec | regval | tx_lat | rx_lat)); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); } /* * This inline function configures the chip either * to encrypt/decrypt management frames or pass thru */ static inline void ar9300_init_mfp(struct ath_hal * ah) { u_int32_t mfpcap, mfp_qos; ath_hal_getcapability(ah, HAL_CAP_MFP, 0, &mfpcap); if (mfpcap == HAL_MFP_QOSDATA) { /* Treat like legacy hardware. Do not touch the MFP registers. */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s forced to use QOSDATA\n", __func__); return; } /* MFP support (Sowl 1.0 or greater) */ if (mfpcap == HAL_MFP_HW_CRYPTO) { /* configure hardware MFP support */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s using HW crypto\n", __func__); OS_REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT, AR_AES_MUTE_MASK1_FC_MGMT_MFP); OS_REG_RMW(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE, AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT); /* * Mask used to construct AAD for CCMP-AES * Cisco spec defined bits 0-3 as mask * IEEE802.11w defined as bit 4. */ if (ath_hal_get_mfp_qos(ah)) { mfp_qos = AR_MFP_QOS_MASK_IEEE; } else { mfp_qos = AR_MFP_QOS_MASK_CISCO; } OS_REG_RMW_FIELD(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_QOS, mfp_qos); } else if (mfpcap == HAL_MFP_PASSTHRU) { /* Disable en/decrypt by hardware */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s using passthru\n", __func__); OS_REG_RMW(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT, AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE); } } void ar9300_get_channel_centers(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers) { int8_t extoff; struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (!IEEE80211_IS_CHAN_HT40(chan)) { centers->ctl_center = centers->ext_center = centers->synth_center = ichan->channel; return; } HALASSERT(IEEE80211_IS_CHAN_HT40(chan)); /* * In 20/40 phy mode, the center frequency is * "between" the primary and extension channels. */ if (IEEE80211_IS_CHAN_HT40U(chan)) { centers->synth_center = ichan->channel + HT40_CHANNEL_CENTER_SHIFT; extoff = 1; } else { centers->synth_center = ichan->channel - HT40_CHANNEL_CENTER_SHIFT; extoff = -1; } centers->ctl_center = centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT); centers->ext_center = centers->synth_center + (extoff * ((ahp->ah_ext_prot_spacing == HAL_HT_EXTPROTSPACING_20) ? HT40_CHANNEL_CENTER_SHIFT : 15)); } /* * Read the noise-floor values from the HW. * Specifically, read the minimum clear-channel assessment value for * each chain, for both the control and extension channels. * (The received power level during clear-channel periods is the * noise floor.) * These noise floor values computed by the HW will be stored in the * NF history buffer. * The HW sometimes produces bogus NF values. To avoid using these * bogus values, the NF data is (a) range-limited, and (b) filtered. * However, this data-processing is done when reading the NF values * out of the history buffer. The history buffer stores the raw values. * This allows the NF history buffer to be used to check for interference. * A single high NF reading might be a bogus HW value, but if the NF * readings are consistently high, it must be due to interference. * This is the purpose of storing raw NF values in the history buffer, * rather than processed values. By looking at a history of NF values * that have not been range-limited, we can check if they are consistently * high (due to interference). */ #define AH_NF_SIGN_EXTEND(nf) \ ((nf) & 0x100) ? \ 0 - (((nf) ^ 0x1ff) + 1) : \ (nf) void ar9300_upload_noise_floor(struct ath_hal *ah, int is_2g, int16_t nfarray[HAL_NUM_NF_READINGS]) { int16_t nf; int chan, chain; u_int32_t regs[HAL_NUM_NF_READINGS] = { /* control channel */ AR_PHY_CCA_0, /* chain 0 */ AR_PHY_CCA_1, /* chain 1 */ AR_PHY_CCA_2, /* chain 2 */ /* extension channel */ AR_PHY_EXT_CCA, /* chain 0 */ AR_PHY_EXT_CCA_1, /* chain 1 */ AR_PHY_EXT_CCA_2, /* chain 2 */ }; u_int8_t chainmask; /* * Within a given channel (ctl vs. ext), the CH0, CH1, and CH2 * masks and shifts are the same, though they differ for the * control vs. extension channels. */ u_int32_t masks[2] = { AR_PHY_MINCCA_PWR, /* control channel */ AR_PHY_EXT_MINCCA_PWR, /* extention channel */ }; u_int8_t shifts[2] = { AR_PHY_MINCCA_PWR_S, /* control channel */ AR_PHY_EXT_MINCCA_PWR_S, /* extention channel */ }; /* * Force NF calibration for all chains. */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { chainmask = 0x01; } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah) || AR_SREV_HONEYBEE(ah)) { chainmask = 0x03; } else { chainmask = 0x07; } for (chan = 0; chan < 2 /*ctl,ext*/; chan++) { for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { int i; if (!((chainmask >> chain) & 0x1)) { continue; } i = chan * AR9300_MAX_CHAINS + chain; nf = (OS_REG_READ(ah, regs[i]) & masks[chan]) >> shifts[chan]; nfarray[i] = AH_NF_SIGN_EXTEND(nf); } } } /* ar9300_get_min_cca_pwr - * Used by the scan function for a quick read of the noise floor. * This is used to detect presence of CW interference such as video bridge. * The noise floor is assumed to have been already started during reset * called during channel change. The function checks if the noise floor * reading is done. In case it has been done, it reads the noise floor value. * If the noise floor calibration has not been finished, it assumes this is * due to presence of CW interference an returns a high value for noise floor, * derived from the CW interference threshold + margin fudge factor. */ #define BAD_SCAN_NF_MARGIN (30) int16_t ar9300_get_min_cca_pwr(struct ath_hal *ah) { int16_t nf; // struct ath_hal_private *ahpriv = AH_PRIVATE(ah); if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0) { nf = MS(OS_REG_READ(ah, AR_PHY_CCA_0), AR9280_PHY_MINCCA_PWR); if (nf & 0x100) { nf = 0 - ((nf ^ 0x1ff) + 1); } } else { /* NF calibration is not done, assume CW interference */ nf = AH9300(ah)->nfp->nominal + AH9300(ah)->nf_cw_int_delta + BAD_SCAN_NF_MARGIN; } return nf; } /* * Noise Floor values for all chains. * Most recently updated values from the NF history buffer are used. */ void ar9300_chain_noise_floor(struct ath_hal *ah, int16_t *nf_buf, struct ieee80211_channel *chan, int is_scan) { struct ath_hal_9300 *ahp = AH9300(ah); int i, nf_hist_len, recent_nf_index = 0; HAL_NFCAL_HIST_FULL *h; u_int8_t rx_chainmask = ahp->ah_rx_chainmask | (ahp->ah_rx_chainmask << 3); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HALASSERT(ichan); #ifdef ATH_NF_PER_CHAN /* Fill 0 if valid internal channel is not found */ if (ichan == AH_NULL) { OS_MEMZERO(nf_buf, sizeof(nf_buf[0])*HAL_NUM_NF_READINGS); return; } h = &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; #else /* * If a scan is not in progress, then the most recent value goes * into ahpriv->nf_cal_hist. If a scan is in progress, then * the most recent value goes into ichan->nf_cal_hist. * Thus, return the value from ahpriv->nf_cal_hist if there's * no scan, and if the specified channel is the current channel. * Otherwise, return the noise floor from ichan->nf_cal_hist. */ if ((!is_scan) && chan == AH_PRIVATE(ah)->ah_curchan) { h = &AH_PRIVATE(ah)->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; } else { /* Fill 0 if valid internal channel is not found */ if (ichan == AH_NULL) { OS_MEMZERO(nf_buf, sizeof(nf_buf[0])*HAL_NUM_NF_READINGS); return; } /* * It is okay to treat a HAL_NFCAL_HIST_SMALL struct as if it were a * HAL_NFCAL_HIST_FULL struct, as long as only the index 0 of the * nf_cal_buffer is used (nf_cal_buffer[0][0:HAL_NUM_NF_READINGS-1]) */ h = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; } #endif /* Get most recently updated values from nf cal history buffer */ recent_nf_index = (h->base.curr_index) ? h->base.curr_index - 1 : nf_hist_len - 1; for (i = 0; i < HAL_NUM_NF_READINGS; i++) { /* Fill 0 for unsupported chains */ if (!(rx_chainmask & (1 << i))) { nf_buf[i] = 0; continue; } nf_buf[i] = h->nf_cal_buffer[recent_nf_index][i]; } } /* * Return the current NF value in register. * If the current NF cal is not completed, return 0. */ int16_t ar9300_get_nf_from_reg(struct ath_hal *ah, struct ieee80211_channel *chan, int wait_time) { int16_t nfarray[HAL_NUM_NF_READINGS] = {0}; int is_2g = 0; HAL_CHANNEL_INTERNAL *ichan = NULL; ichan = ath_hal_checkchannel(ah, chan); if (ichan == NULL) return (0); if (wait_time <= 0) { return 0; } if (!ath_hal_waitfor(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF, 0, wait_time)) { ath_hal_printf(ah, "%s: NF cal is not complete in %dus", __func__, wait_time); return 0; } is_2g = !! (IS_CHAN_2GHZ(ichan)); ar9300_upload_noise_floor(ah, is_2g, nfarray); return nfarray[0]; } /* * Pick up the medium one in the noise floor buffer and update the * corresponding range for valid noise floor values */ static int16_t ar9300_get_nf_hist_mid(struct ath_hal *ah, HAL_NFCAL_HIST_FULL *h, int reading, int hist_len) { int16_t nfval; int16_t sort[HAL_NF_CAL_HIST_LEN_FULL]; /* upper bound for hist_len */ int i, j; for (i = 0; i < hist_len; i++) { sort[i] = h->nf_cal_buffer[i][reading]; HALDEBUG(ah, HAL_DEBUG_NFCAL, "nf_cal_buffer[%d][%d] = %d\n", i, reading, (int)sort[i]); } for (i = 0; i < hist_len - 1; i++) { for (j = 1; j < hist_len - i; j++) { if (sort[j] > sort[j - 1]) { nfval = sort[j]; sort[j] = sort[j - 1]; sort[j - 1] = nfval; } } } nfval = sort[(hist_len - 1) >> 1]; return nfval; } static int16_t ar9300_limit_nf_range(struct ath_hal *ah, int16_t nf) { if (nf < AH9300(ah)->nfp->min) { return AH9300(ah)->nfp->nominal; } else if (nf > AH9300(ah)->nfp->max) { return AH9300(ah)->nfp->max; } return nf; } #ifndef ATH_NF_PER_CHAN inline static void ar9300_reset_nf_hist_buff(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { HAL_CHAN_NFCAL_HIST *h = &ichan->nf_cal_hist; HAL_NFCAL_HIST_FULL *home = &AH_PRIVATE(ah)->nf_cal_hist; int i; /* * Copy the value for the channel in question into the home-channel * NF history buffer. The channel NF is probably a value filled in by * a prior background channel scan, but if no scan has been done then * it is the nominal noise floor filled in by ath_hal_init_NF_buffer * for this chip and the channel's band. * Replicate this channel NF into all entries of the home-channel NF * history buffer. * If the channel NF was filled in by a channel scan, it has not had * bounds limits applied to it yet - do so now. It is important to * apply bounds limits to the priv_nf value that gets loaded into the * WLAN chip's min_cca_pwr register field. It is also necessary to * apply bounds limits to the nf_cal_buffer[] elements. Since we are * replicating a single NF reading into all nf_cal_buffer elements, * if the single reading were above the CW_INT threshold, the CW_INT * check in ar9300_get_nf would immediately conclude that CW interference * is present, even though we're not supposed to set CW_INT unless * NF values are _consistently_ above the CW_INT threshold. * Applying the bounds limits to the nf_cal_buffer contents fixes this * problem. */ for (i = 0; i < HAL_NUM_NF_READINGS; i ++) { int j; int16_t nf; /* * No need to set curr_index, since it already has a value in * the range [0..HAL_NF_CAL_HIST_LEN_FULL), and all nf_cal_buffer * values will be the same. */ nf = ar9300_limit_nf_range(ah, h->nf_cal_buffer[0][i]); for (j = 0; j < HAL_NF_CAL_HIST_LEN_FULL; j++) { home->nf_cal_buffer[j][i] = nf; } AH_PRIVATE(ah)->nf_cal_hist.base.priv_nf[i] = nf; } } #endif /* * Update the noise floor buffer as a ring buffer */ static int16_t ar9300_update_nf_hist_buff(struct ath_hal *ah, HAL_NFCAL_HIST_FULL *h, int16_t *nfarray, int hist_len) { int i, nr; int16_t nf_no_lim_chain0; nf_no_lim_chain0 = ar9300_get_nf_hist_mid(ah, h, 0, hist_len); HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s[%d] BEFORE\n", __func__, __LINE__); for (nr = 0; nr < HAL_NF_CAL_HIST_LEN_FULL; nr++) { for (i = 0; i < HAL_NUM_NF_READINGS; i++) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "nf_cal_buffer[%d][%d] = %d\n", nr, i, (int)h->nf_cal_buffer[nr][i]); } } for (i = 0; i < HAL_NUM_NF_READINGS; i++) { h->nf_cal_buffer[h->base.curr_index][i] = nfarray[i]; h->base.priv_nf[i] = ar9300_limit_nf_range( ah, ar9300_get_nf_hist_mid(ah, h, i, hist_len)); } HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s[%d] AFTER\n", __func__, __LINE__); for (nr = 0; nr < HAL_NF_CAL_HIST_LEN_FULL; nr++) { for (i = 0; i < HAL_NUM_NF_READINGS; i++) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "nf_cal_buffer[%d][%d] = %d\n", nr, i, (int)h->nf_cal_buffer[nr][i]); } } if (++h->base.curr_index >= hist_len) { h->base.curr_index = 0; } return nf_no_lim_chain0; } #ifdef UNUSED static HAL_BOOL get_noise_floor_thresh(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *chan, int16_t *nft) { struct ath_hal_9300 *ahp = AH9300(ah); switch (chan->channel_flags & CHANNEL_ALL_NOTURBO) { case CHANNEL_A: case CHANNEL_A_HT20: case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: *nft = (int8_t)ar9300_eeprom_get(ahp, EEP_NFTHRESH_5); break; case CHANNEL_B: case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: *nft = (int8_t)ar9300_eeprom_get(ahp, EEP_NFTHRESH_2); break; default: HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel flags 0x%x\n", __func__, chan->channel_flags); return AH_FALSE; } return AH_TRUE; } #endif /* * Read the NF and check it against the noise floor threshhold */ #define IS(_c, _f) (((_c)->channel_flags & _f) || 0) static int ar9300_store_new_nf(struct ath_hal *ah, struct ieee80211_channel *chan, int is_scan) { // struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int nf_hist_len; int16_t nf_no_lim; int16_t nfarray[HAL_NUM_NF_READINGS] = {0}; HAL_NFCAL_HIST_FULL *h; int is_2g = 0; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); struct ath_hal_9300 *ahp = AH9300(ah); if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { u_int32_t tsf32, nf_cal_dur_tsf; /* * The reason the NF calibration did not complete may just be that * not enough time has passed since the NF calibration was started, * because under certain conditions (when first moving to a new * channel) the NF calibration may be checked very repeatedly. * Or, there may be CW interference keeping the NF calibration * from completing. Check the delta time between when the NF * calibration was started and now to see whether the NF calibration * should have already completed (but hasn't, probably due to CW * interference), or hasn't had enough time to finish yet. */ /* * AH_NF_CAL_DUR_MAX_TSF - A conservative maximum time that the * HW should need to finish a NF calibration. If the HW * does not complete a NF calibration within this time period, * there must be a problem - probably CW interference. * AH_NF_CAL_PERIOD_MAX_TSF - A conservative maximum time between * check of the HW's NF calibration being finished. * If the difference between the current TSF and the TSF * recorded when the NF calibration started is larger than this * value, the TSF must have been reset. * In general, we expect the TSF to only be reset during * regular operation for STAs, not for APs. However, an * AP's TSF could be reset when joining an IBSS. * There's an outside chance that this could result in the * CW_INT flag being erroneously set, if the TSF adjustment * is smaller than AH_NF_CAL_PERIOD_MAX_TSF but larger than * AH_NF_CAL_DUR_TSF. However, even if this does happen, * it shouldn't matter, as the IBSS case shouldn't be * concerned about CW_INT. */ /* AH_NF_CAL_DUR_TSF - 90 sec in usec units */ #define AH_NF_CAL_DUR_TSF (90 * 1000 * 1000) /* AH_NF_CAL_PERIOD_MAX_TSF - 180 sec in usec units */ #define AH_NF_CAL_PERIOD_MAX_TSF (180 * 1000 * 1000) /* wraparound handled by using unsigned values */ tsf32 = ar9300_get_tsf32(ah); nf_cal_dur_tsf = tsf32 - AH9300(ah)->nf_tsf32; if (nf_cal_dur_tsf > AH_NF_CAL_PERIOD_MAX_TSF) { /* * The TSF must have gotten reset during the NF cal - * just reset the NF TSF timestamp, so the next time * this function is called, the timestamp comparison * will be valid. */ AH9300(ah)->nf_tsf32 = tsf32; } else if (nf_cal_dur_tsf > AH_NF_CAL_DUR_TSF) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: NF did not complete in calibration window\n", __func__); /* the NF incompletion is probably due to CW interference */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; } return 0; /* HW's NF measurement not finished */ } HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s[%d] chan %d\n", __func__, __LINE__, ichan->channel); is_2g = !! IS_CHAN_2GHZ(ichan); ar9300_upload_noise_floor(ah, is_2g, nfarray); /* Update the NF buffer for each chain masked by chainmask */ #ifdef ATH_NF_PER_CHAN h = &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; #else if (is_scan) { /* * This channel's NF cal info is just a HAL_NFCAL_HIST_SMALL struct * rather than a HAL_NFCAL_HIST_FULL struct. * As long as we only use the first history element of nf_cal_buffer * (nf_cal_buffer[0][0:HAL_NUM_NF_READINGS-1]), we can use * HAL_NFCAL_HIST_SMALL and HAL_NFCAL_HIST_FULL interchangeably. */ h = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; } else { h = &AH_PRIVATE(ah)->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; } #endif /* * nf_no_lim = median value from NF history buffer without bounds limits, * priv_nf = median value from NF history buffer with bounds limits. */ nf_no_lim = ar9300_update_nf_hist_buff(ah, h, nfarray, nf_hist_len); ichan->rawNoiseFloor = h->base.priv_nf[0]; /* check if there is interference */ // ichan->channel_flags &= (~CHANNEL_CW_INT); /* * Use AR9300_EMULATION to check for emulation purpose as PCIE Device ID * 0xABCD is recognized as valid Osprey as WAR in some EVs. */ if (nf_no_lim > ahp->nfp->nominal + ahp->nf_cw_int_delta) { /* * Since this CW interference check is being applied to the * median element of the NF history buffer, this indicates that * the CW interference is persistent. A single high NF reading * will not show up in the median, and thus will not cause the * CW_INT flag to be set. */ HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: NF Cal: CW interferer detected through NF: %d\n", __func__, nf_no_lim); chan->ic_state |= IEEE80211_CHANSTATE_CWINT; } return 1; /* HW's NF measurement finished */ } #undef IS static inline void ar9300_get_delta_slope_values(struct ath_hal *ah, u_int32_t coef_scaled, u_int32_t *coef_mantissa, u_int32_t *coef_exponent) { u_int32_t coef_exp, coef_man; /* * ALGO -> coef_exp = 14-floor(log2(coef)); * floor(log2(x)) is the highest set bit position */ for (coef_exp = 31; coef_exp > 0; coef_exp--) { if ((coef_scaled >> coef_exp) & 0x1) { break; } } /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ HALASSERT(coef_exp); coef_exp = 14 - (coef_exp - COEF_SCALE_S); /* * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); * The coefficient is already shifted up for scaling */ coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); *coef_exponent = coef_exp - 16; } #define MAX_ANALOG_START 319 /* XXX */ /* * Delta slope coefficient computation. * Required for OFDM operation. */ static void ar9300_set_delta_slope(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t coef_scaled, ds_coef_exp, ds_coef_man; u_int32_t fclk = COEFF; /* clock * 2.5 */ u_int32_t clock_mhz_scaled = 0x1000000 * fclk; CHAN_CENTERS centers; /* * half and quarter rate can divide the scaled clock by 2 or 4 * scale for selected channel bandwidth */ if (IEEE80211_IS_CHAN_HALF(chan)) { clock_mhz_scaled = clock_mhz_scaled >> 1; } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { clock_mhz_scaled = clock_mhz_scaled >> 2; } /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ ar9300_get_channel_centers(ah, chan, ¢ers); coef_scaled = clock_mhz_scaled / centers.synth_center; ar9300_get_delta_slope_values(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); /* * For Short GI, * scaled coeff is 9/10 that of normal coeff */ coef_scaled = (9 * coef_scaled) / 10; ar9300_get_delta_slope_values(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); /* for short gi */ OS_REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_EXP, ds_coef_exp); } #define IS(_c, _f) (IEEE80211_IS_ ## _f(_c)) /* * XXX FreeBSD: This should be turned into something generic in ath_hal! */ HAL_CHANNEL_INTERNAL * ar9300_check_chan(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (chan == NULL) { return AH_NULL; } if ((IS(chan, CHAN_2GHZ) ^ IS(chan, CHAN_5GHZ)) == 0) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; not marked as 2GHz or 5GHz\n", __func__, chan->ic_freq , chan->ic_flags); return AH_NULL; } /* * FreeBSD sets multiple flags, so this will fail. */ #if 0 if ((IS(chan, CHAN_OFDM) ^ IS(chan, CHAN_CCK) ^ IS(chan, CHAN_DYN) ^ IS(chan, CHAN_HT20) ^ IS(chan, CHAN_HT40U) ^ IS(chan, CHAN_HT40D)) == 0) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; not marked as " "OFDM or CCK or DYN or HT20 or HT40PLUS or HT40MINUS\n", __func__, chan->ic_freq , chan->ic_flags); return AH_NULL; } #endif return (ath_hal_checkchannel(ah, chan)); } #undef IS static void ar9300_set_11n_regs(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode) { u_int32_t phymode; // struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t enable_dac_fifo; /* XXX */ enable_dac_fifo = OS_REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO; /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 | AR_PHY_GC_SHORT_GI_40 | enable_dac_fifo; /* Configure baseband for dynamic 20/40 operation */ if (IEEE80211_IS_CHAN_HT40(chan)) { phymode |= AR_PHY_GC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ if (IEEE80211_IS_CHAN_HT40U(chan)) { phymode |= AR_PHY_GC_DYN2040_PRI_CH; } #if 0 /* Configure 20/25 spacing */ if (ahp->ah_ext_prot_spacing == HAL_HT_EXTPROTSPACING_25) { phymode |= AR_PHY_GC_DYN2040_EXT_CH; } #endif } /* make sure we preserve INI settings */ phymode |= OS_REG_READ(ah, AR_PHY_GEN_CTRL); /* EV 62881/64991 - turn off Green Field detection for Maverick STA beta */ phymode &= ~AR_PHY_GC_GF_DETECT_EN; OS_REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode); /* Set IFS timing for half/quarter rates */ if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { u_int32_t modeselect = OS_REG_READ(ah, AR_PHY_MODE); if (IEEE80211_IS_CHAN_HALF(chan)) { modeselect |= AR_PHY_MS_HALF_RATE; } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { modeselect |= AR_PHY_MS_QUARTER_RATE; } OS_REG_WRITE(ah, AR_PHY_MODE, modeselect); ar9300_set_ifs_timing(ah, chan); OS_REG_RMW_FIELD( ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW, 0x3); } /* Configure MAC for 20/40 operation */ ar9300_set_11n_mac2040(ah, macmode); /* global transmit timeout (25 TUs default)*/ /* XXX - put this elsewhere??? */ OS_REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); /* carrier sense timeout */ OS_REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); } /* * Spur mitigation for MRC CCK */ static void ar9300_spur_mitigate_mrc_cck(struct ath_hal *ah, struct ieee80211_channel *chan) { int i; /* spur_freq_for_osprey - hardcoded by Systems team for now. */ u_int32_t spur_freq_for_osprey[4] = { 2420, 2440, 2464, 2480 }; u_int32_t spur_freq_for_jupiter[2] = { 2440, 2464}; int cur_bb_spur, negative = 0, cck_spur_freq; u_int8_t* spur_fbin_ptr = NULL; int synth_freq; int range = 10; int max_spurcounts = OSPREY_EEPROM_MODAL_SPURS; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); /* * Need to verify range +/- 10 MHz in control channel, otherwise spur * is out-of-band and can be ignored. */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { spur_fbin_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 1); if (spur_fbin_ptr[0] == 0) { return; /* No spur in the mode */ } if (IEEE80211_IS_CHAN_HT40(chan)) { range = 19; if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { synth_freq = ichan->channel + 10; } else { synth_freq = ichan->channel - 10; } } else { range = 10; synth_freq = ichan->channel; } } else if(AR_SREV_JUPITER(ah)) { range = 5; max_spurcounts = 2; /* Hardcoded by Jupiter Systems team for now. */ synth_freq = ichan->channel; } else { range = 10; max_spurcounts = 4; /* Hardcoded by Osprey Systems team for now. */ synth_freq = ichan->channel; } for (i = 0; i < max_spurcounts; i++) { negative = 0; if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { cur_bb_spur = FBIN2FREQ(spur_fbin_ptr[i], HAL_FREQ_BAND_2GHZ) - synth_freq; } else if(AR_SREV_JUPITER(ah)) { cur_bb_spur = spur_freq_for_jupiter[i] - synth_freq; } else { cur_bb_spur = spur_freq_for_osprey[i] - synth_freq; } if (cur_bb_spur < 0) { negative = 1; cur_bb_spur = -cur_bb_spur; } if (cur_bb_spur < range) { cck_spur_freq = (int)((cur_bb_spur << 19) / 11); if (negative == 1) { cck_spur_freq = -cck_spur_freq; } cck_spur_freq = cck_spur_freq & 0xfffff; /*OS_REG_WRITE_field(ah, BB_agc_control.ycok_max, 0x7);*/ OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x7); /*OS_REG_WRITE_field(ah, BB_cck_spur_mit.spur_rssi_thr, 0x7f);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR, 0x7f); /*OS_REG_WRITE(ah, BB_cck_spur_mit.spur_filter_type, 0x2);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE, 0x2); /*OS_REG_WRITE(ah, BB_cck_spur_mit.use_cck_spur_mit, 0x1);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x1); /*OS_REG_WRITE(ah, BB_cck_spur_mit.cck_spur_freq, cck_spur_freq);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, cck_spur_freq); return; } } /*OS_REG_WRITE(ah, BB_agc_control.ycok_max, 0x5);*/ OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x5); /*OS_REG_WRITE(ah, BB_cck_spur_mit.use_cck_spur_mit, 0x0);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x0); /*OS_REG_WRITE(ah, BB_cck_spur_mit.cck_spur_freq, 0x0);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, 0x0); } /* Spur mitigation for OFDM */ static void ar9300_spur_mitigate_ofdm(struct ath_hal *ah, struct ieee80211_channel *chan) { int synth_freq; int range = 10; int freq_offset = 0; int spur_freq_sd = 0; int spur_subchannel_sd = 0; int spur_delta_phase = 0; int mask_index = 0; int i; int mode; u_int8_t* spur_chans_ptr; struct ath_hal_9300 *ahp; ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (IS_CHAN_5GHZ(ichan)) { spur_chans_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 0); mode = 0; } else { spur_chans_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 1); mode = 1; } if (IEEE80211_IS_CHAN_HT40(chan)) { range = 19; if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { synth_freq = ichan->channel - 10; } else { synth_freq = ichan->channel + 10; } } else { range = 10; synth_freq = ichan->channel; } /* Clean all spur register fields */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0); i = 0; while (spur_chans_ptr[i] && i < 5) { freq_offset = FBIN2FREQ(spur_chans_ptr[i], mode) - synth_freq; if (abs(freq_offset) < range) { /* printf( "Spur Mitigation for OFDM: Synth Frequency = %d, " "Spur Frequency = %d\n", synth_freq, FBIN2FREQ(spur_chans_ptr[i], mode)); */ if (IEEE80211_IS_CHAN_HT40(chan)) { if (freq_offset < 0) { if (OS_REG_READ_FIELD( ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { spur_subchannel_sd = 1; } else { spur_subchannel_sd = 0; } spur_freq_sd = ((freq_offset + 10) << 9) / 11; } else { if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { spur_subchannel_sd = 0; } else { spur_subchannel_sd = 1; } spur_freq_sd = ((freq_offset - 10) << 9) / 11; } spur_delta_phase = (freq_offset << 17) / 5; } else { spur_subchannel_sd = 0; spur_freq_sd = (freq_offset << 9) / 11; spur_delta_phase = (freq_offset << 18) / 5; } spur_freq_sd = spur_freq_sd & 0x3ff; spur_delta_phase = spur_delta_phase & 0xfffff; /* printf( "spur_subchannel_sd = %d, spur_freq_sd = 0x%x, " "spur_delta_phase = 0x%x\n", spur_subchannel_sd, spur_freq_sd, spur_delta_phase); */ /* OFDM Spur mitigation */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, spur_freq_sd); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, spur_delta_phase); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, spur_subchannel_sd); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH, 34); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 1); /* * Do not subtract spur power from noise floor for wasp. * This causes the maximum client test (on Veriwave) to fail * when run on spur channel (2464 MHz). * Refer to ev#82746 and ev#82744. */ if (!AR_SREV_WASP(ah) && (OS_REG_READ_FIELD(ah, AR_PHY_MODE, AR_PHY_MODE_DYNAMIC) == 0x1)) { OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 1); } mask_index = (freq_offset << 4) / 5; if (mask_index < 0) { mask_index = mask_index - 1; } mask_index = mask_index & 0x7f; /*printf("Bin 0x%x\n", mask_index);*/ OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, mask_index); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, mask_index); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, mask_index); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0xc); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0xc); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0xa0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0xff); /* printf("BB_timing_control_4 = 0x%x\n", OS_REG_READ(ah, AR_PHY_TIMING4)); printf("BB_timing_control_11 = 0x%x\n", OS_REG_READ(ah, AR_PHY_TIMING11)); printf("BB_ext_chan_scorr_thr = 0x%x\n", OS_REG_READ(ah, AR_PHY_SFCORR_EXT)); printf("BB_spur_mask_controls = 0x%x\n", OS_REG_READ(ah, AR_PHY_SPUR_REG)); printf("BB_pilot_spur_mask = 0x%x\n", OS_REG_READ(ah, AR_PHY_PILOT_SPUR_MASK)); printf("BB_chan_spur_mask = 0x%x\n", OS_REG_READ(ah, AR_PHY_CHAN_SPUR_MASK)); printf("BB_vit_spur_mask_A = 0x%x\n", OS_REG_READ(ah, AR_PHY_SPUR_MASK_A)); */ break; } i++; } } /* * Convert to baseband spur frequency given input channel frequency * and compute register settings below. */ static void ar9300_spur_mitigate(struct ath_hal *ah, struct ieee80211_channel *chan) { ar9300_spur_mitigate_ofdm(ah, chan); ar9300_spur_mitigate_mrc_cck(ah, chan); } /************************************************************** * ar9300_channel_change * Assumes caller wants to change channel, and not reset. */ static inline HAL_BOOL ar9300_channel_change(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_CHANNEL_INTERNAL *ichan, HAL_HT_MACMODE macmode) { u_int32_t synth_delay, qnum; struct ath_hal_9300 *ahp = AH9300(ah); /* TX must be stopped by now */ for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { if (ar9300_num_tx_pending(ah, qnum)) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: Transmit frames pending on queue %d\n", __func__, qnum); HALASSERT(0); return AH_FALSE; } } /* * Kill last Baseband Rx Frame - Request analog bus grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); if (!ath_hal_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN)) { HALDEBUG(ah, HAL_DEBUG_PHYIO, "%s: Could not kill baseband RX\n", __func__); return AH_FALSE; } /* Setup 11n MAC/Phy mode registers */ ar9300_set_11n_regs(ah, chan, macmode); /* * Change the synth */ if (!ahp->ah_rf_hal.set_channel(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: failed to set channel\n", __func__); return AH_FALSE; } /* * Some registers get reinitialized during ATH_INI_POST INI programming. */ ar9300_init_user_settings(ah); /* * Setup the transmit power values. * * After the public to private hal channel mapping, ichan contains the * valid regulatory power value. * ath_hal_getctl and ath_hal_getantennaallowed look up ichan from chan. */ if (ar9300_eeprom_set_transmit_power( ah, &ahp->ah_eeprom, chan, ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), ath_hal_get_twice_max_regpower(AH_PRIVATE(ah), ichan, chan), AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit)) != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: error init'ing transmit power\n", __func__); return AH_FALSE; } /* * Release the RFBus Grant. */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* * Write spur immunity and delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan) || IEEE80211_IS_CHAN_HT(chan)) { ar9300_set_delta_slope(ah, chan); } else { /* Set to Ini default */ OS_REG_WRITE(ah, AR_PHY_TIMING3, 0x9c0a9f6b); OS_REG_WRITE(ah, AR_PHY_SGI_DELTA, 0x00046384); } ar9300_spur_mitigate(ah, chan); /* * Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN). * Read the phy active delay register. Value is in 100ns increments. */ synth_delay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_CCK(chan)) { synth_delay = (4 * synth_delay) / 22; } else { synth_delay /= 10; } OS_DELAY(synth_delay + BASE_ACTIVATE_DELAY); /* * Do calibration. */ return AH_TRUE; } void ar9300_set_operating_mode(struct ath_hal *ah, int opmode) { u_int32_t val; val = OS_REG_READ(ah, AR_STA_ID1); val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); switch (opmode) { case HAL_M_HOSTAP: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP | AR_STA_ID1_KSRCH_MODE); OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_IBSS: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC | AR_STA_ID1_KSRCH_MODE); OS_REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_STA: case HAL_M_MONITOR: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); break; } } /* XXX need the logic for Osprey */ void ar9300_init_pll(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t pll; u_int8_t clk_25mhz = AH9300(ah)->clk_25mhz; HAL_CHANNEL_INTERNAL *ichan = NULL; if (chan) ichan = ath_hal_checkchannel(ah, chan); if (AR_SREV_HORNET(ah)) { if (clk_25mhz) { /* Hornet uses PLL_CONTROL_2. Xtal is 25MHz for Hornet. * REFDIV set to 0x1. * $xtal_freq = 25; * $PLL2_div = (704/$xtal_freq); # 176 * 4 = 704. * MAC and BB run at 176 MHz. * $PLL2_divint = int($PLL2_div); * $PLL2_divfrac = $PLL2_div - $PLL2_divint; * $PLL2_divfrac = int($PLL2_divfrac * 0x4000); # 2^14 * $PLL2_Val = ($PLL2_divint & 0x3f) << 19 | (0x1) << 14 | * $PLL2_divfrac & 0x3fff; * Therefore, $PLL2_Val = 0xe04a3d */ #define DPLL2_KD_VAL 0x1D #define DPLL2_KI_VAL 0x06 #define DPLL3_PHASE_SHIFT_VAL 0x1 /* Rewrite DDR PLL2 and PLL3 */ /* program DDR PLL ki and kd value, ki=0x6, kd=0x1d */ OS_REG_WRITE(ah, AR_HORNET_CH0_DDR_DPLL2, 0x18e82f01); /* program DDR PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_DDR_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); OS_DELAY(1000); /* program refdiv, nint, frac to RTC register */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0xe04a3d); /* program BB PLL ki and kd value, ki=0x6, kd=0x1d */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KD, DPLL2_KD_VAL); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KI, DPLL2_KI_VAL); /* program BB PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); } else { /* 40MHz */ #undef DPLL2_KD_VAL #undef DPLL2_KI_VAL #define DPLL2_KD_VAL 0x3D #define DPLL2_KI_VAL 0x06 /* Rewrite DDR PLL2 and PLL3 */ /* program DDR PLL ki and kd value, ki=0x6, kd=0x3d */ OS_REG_WRITE(ah, AR_HORNET_CH0_DDR_DPLL2, 0x19e82f01); /* program DDR PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_DDR_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); OS_DELAY(1000); /* program refdiv, nint, frac to RTC register */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0x886666); /* program BB PLL ki and kd value, ki=0x6, kd=0x3d */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KD, DPLL2_KD_VAL); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KI, DPLL2_KI_VAL); /* program BB PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); } OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); OS_DELAY(1000); } else if (AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_PLL_PWD, 0x1); /* program BB PLL ki and kd value, ki=0x4, kd=0x40 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KD, 0x40); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KI, 0x4); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, AR_PHY_BB_DPLL1_REFDIV, 0x5); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, AR_PHY_BB_DPLL1_NINI, 0x58); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, AR_PHY_BB_DPLL1_NFRAC, 0x0); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_OUTDIV, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_LOCAL_PLL, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_EN_NEGTRIG, 0x1); /* program BB PLL phase_shift to 0x6 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, 0x6); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_PLL_PWD, 0x0); OS_DELAY(1000); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); OS_DELAY(1000); } else if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah) || AR_SREV_HONEYBEE(ah)) { #define SRIF_PLL 1 u_int32_t regdata, pll2_divint, pll2_divfrac; #ifndef SRIF_PLL u_int32_t pll2_clkmode; #endif #ifdef SRIF_PLL u_int32_t refdiv; #endif if (clk_25mhz) { #ifndef SRIF_PLL pll2_divint = 0x1c; pll2_divfrac = 0xa3d7; #else if (AR_SREV_HONEYBEE(ah)) { pll2_divint = 0x1c; pll2_divfrac = 0xa3d2; refdiv = 1; } else { pll2_divint = 0x54; pll2_divfrac = 0x1eb85; refdiv = 3; } #endif } else { #ifndef SRIF_PLL pll2_divint = 0x11; pll2_divfrac = 0x26666; #else if (AR_SREV_WASP(ah)) { pll2_divint = 88; pll2_divfrac = 0; refdiv = 5; } else { pll2_divint = 0x11; pll2_divfrac = 0x26666; refdiv = 1; } #endif } #ifndef SRIF_PLL pll2_clkmode = 0x3d; #endif /* PLL programming through SRIF Local Mode */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); /* Bypass mode */ OS_DELAY(1000); do { regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); if (AR_SREV_HONEYBEE(ah)) { regdata = regdata | (0x1 << 22); } else { regdata = regdata | (0x1 << 16); } OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); /* PWD_PLL set to 1 */ OS_DELAY(100); /* override int, frac, refdiv */ #ifndef SRIF_PLL OS_REG_WRITE(ah, AR_PHY_PLL_CONTROL, ((1 << 27) | (pll2_divint << 18) | pll2_divfrac)); #else OS_REG_WRITE(ah, AR_PHY_PLL_CONTROL, ((refdiv << 27) | (pll2_divint << 18) | pll2_divfrac)); #endif OS_DELAY(100); regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); #ifndef SRIF_PLL regdata = (regdata & 0x80071fff) | (0x1 << 30) | (0x1 << 13) | (0x6 << 26) | (pll2_clkmode << 19); #else if (AR_SREV_WASP(ah)) { regdata = (regdata & 0x80071fff) | (0x1 << 30) | (0x1 << 13) | (0x4 << 26) | (0x18 << 19); } else if (AR_SREV_HONEYBEE(ah)) { /* * Kd=10, Ki=2, Outdiv=1, Local PLL=0, Phase Shift=4 */ regdata = (regdata & 0x01c00fff) | (0x1 << 31) | (0x2 << 29) | (0xa << 25) | (0x1 << 19) | (0x6 << 12); } else { regdata = (regdata & 0x80071fff) | (0x3 << 30) | (0x1 << 13) | (0x4 << 26) | (0x60 << 19); } #endif /* Ki, Kd, Local PLL, Outdiv */ OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); if (AR_SREV_HONEYBEE(ah)) { regdata = (regdata & 0xffbfffff); } else { regdata = (regdata & 0xfffeffff); } OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); /* PWD_PLL set to 0 */ OS_DELAY(1000); if (AR_SREV_WASP(ah)) { /* clear do measure */ regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); regdata &= ~(1 << 30); OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); OS_DELAY(100); /* set do measure */ regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); regdata |= (1 << 30); OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); /* wait for measure done */ do { regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL4); } while ((regdata & (1 << 3)) == 0); /* clear do measure */ regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); regdata &= ~(1 << 30); OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); /* get measure sqsum dvc */ regdata = (OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3) & 0x007FFFF8) >> 3; } else { break; } } while (regdata >= 0x40000); /* Remove from Bypass mode */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); OS_DELAY(1000); } else { pll = SM(0x5, AR_RTC_PLL_REFDIV); /* Supposedly not needed on Osprey */ #if 0 if (chan && IS_CHAN_HALF_RATE(chan)) { pll |= SM(0x1, AR_RTC_PLL_CLKSEL); } else if (chan && IS_CHAN_QUARTER_RATE(chan)) { pll |= SM(0x2, AR_RTC_PLL_CLKSEL); } #endif if (ichan && IS_CHAN_5GHZ(ichan)) { pll |= SM(0x28, AR_RTC_PLL_DIV); /* * When doing fast clock, set PLL to 0x142c */ if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { pll = 0x142c; } } else { pll |= SM(0x2c, AR_RTC_PLL_DIV); } OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); } /* TODO: * For multi-band owl, switch between bands by reiniting the PLL. */ OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK | AR_RTC_PCIE_RST_PWDN_EN); /* XXX TODO: honeybee? */ if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { if (clk_25mhz) { OS_REG_WRITE(ah, AR_RTC_DERIVED_RTC_CLK, (0x17c << 1)); /* 32KHz sleep clk */ OS_REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); OS_REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae); } else { OS_REG_WRITE(ah, AR_RTC_DERIVED_RTC_CLK, (0x261 << 1)); /* 32KHz sleep clk */ OS_REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400); OS_REG_WRITE(ah, AR_SLP32_INC, 0x0001e800); } OS_DELAY(100); } } static inline HAL_BOOL ar9300_set_reset(struct ath_hal *ah, int type) { u_int32_t rst_flags; u_int32_t tmp_reg; struct ath_hal_9300 *ahp = AH9300(ah); HALASSERT(type == HAL_RESET_WARM || type == HAL_RESET_COLD); /* * RTC Force wake should be done before resetting the MAC. * MDK/ART does it that way. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* Reset AHB */ /* Bug26871 */ tmp_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE)); if (AR_SREV_WASP(ah)) { if (tmp_reg & (AR9340_INTR_SYNC_LOCAL_TIMEOUT)) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); } } else { if (tmp_reg & (AR9300_INTR_SYNC_LOCAL_TIMEOUT | AR9300_INTR_SYNC_RADM_CPL_TIMEOUT)) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); } else { /* NO AR_RC_AHB in Osprey */ /*OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_AHB);*/ } } rst_flags = AR_RTC_RC_MAC_WARM; if (type == HAL_RESET_COLD) { rst_flags |= AR_RTC_RC_MAC_COLD; } #ifdef AH_SUPPORT_HORNET /* Hornet WAR: trigger SoC to reset WMAC if ... * (1) doing cold reset. Ref: EV 69254 * (2) beacon pending. Ref: EV 70983 */ if (AR_SREV_HORNET(ah) && (ar9300_num_tx_pending( ah, AH_PRIVATE(ah)->ah_caps.halTotalQueues - 1) != 0 || type == HAL_RESET_COLD)) { u_int32_t time_out; #define AR_SOC_RST_RESET 0xB806001C #define AR_SOC_BOOT_STRAP 0xB80600AC #define AR_SOC_WLAN_RST 0x00000800 /* WLAN reset */ #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Hornet SoC reset WMAC.\n", __func__); REG_WRITE(AR_SOC_RST_RESET, REG_READ(AR_SOC_RST_RESET) | AR_SOC_WLAN_RST); REG_WRITE(AR_SOC_RST_RESET, REG_READ(AR_SOC_RST_RESET) & (~AR_SOC_WLAN_RST)); time_out = 0; while (1) { tmp_reg = REG_READ(AR_SOC_BOOT_STRAP); if ((tmp_reg & 0x10) == 0) { break; } if (time_out > 20) { break; } OS_DELAY(10000); time_out++; } OS_REG_WRITE(ah, AR_RTC_RESET, 1); #undef REG_READ #undef REG_WRITE #undef AR_SOC_WLAN_RST #undef AR_SOC_RST_RESET #undef AR_SOC_BOOT_STRAP } #endif /* AH_SUPPORT_HORNET */ #ifdef AH_SUPPORT_SCORPION if (AR_SREV_SCORPION(ah)) { #define DDR_CTL_CONFIG_ADDRESS 0xb8000000 #define DDR_CTL_CONFIG_OFFSET 0x0108 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_MSB 29 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB 21 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK 0x3fe00000 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_GET(x) (((x) & DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK) >> DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB) #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_SET(x) (((x) << DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB) & DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK) #define MAC_DMA_CFG_ADDRESS 0xb8100000 #define MAC_DMA_CFG_OFFSET 0x0014 #define MAC_DMA_CFG_HALT_REQ_MSB 11 #define MAC_DMA_CFG_HALT_REQ_LSB 11 #define MAC_DMA_CFG_HALT_REQ_MASK 0x00000800 #define MAC_DMA_CFG_HALT_REQ_GET(x) (((x) & MAC_DMA_CFG_HALT_REQ_MASK) >> MAC_DMA_CFG_HALT_REQ_LSB) #define MAC_DMA_CFG_HALT_REQ_SET(x) (((x) << MAC_DMA_CFG_HALT_REQ_LSB) & MAC_DMA_CFG_HALT_REQ_MASK) #define MAC_DMA_CFG_HALT_ACK_MSB 12 #define MAC_DMA_CFG_HALT_ACK_LSB 12 #define MAC_DMA_CFG_HALT_ACK_MASK 0x00001000 #define MAC_DMA_CFG_HALT_ACK_GET(x) (((x) & MAC_DMA_CFG_HALT_ACK_MASK) >> MAC_DMA_CFG_HALT_ACK_LSB) #define MAC_DMA_CFG_HALT_ACK_SET(x) (((x) << MAC_DMA_CFG_HALT_ACK_LSB) & MAC_DMA_CFG_HALT_ACK_MASK) #define RST_RESET 0xB806001c #define RTC_RESET (1<<27) #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define DDR_REG_READ(_ah, _reg) \ *((volatile u_int32_t *)( DDR_CTL_CONFIG_ADDRESS + (_reg))) #define DDR_REG_WRITE(_ah, _reg, _val) \ *((volatile u_int32_t *)(DDR_CTL_CONFIG_ADDRESS + (_reg))) = (_val) OS_REG_WRITE(ah,MAC_DMA_CFG_OFFSET, (OS_REG_READ(ah,MAC_DMA_CFG_OFFSET) & ~MAC_DMA_CFG_HALT_REQ_MASK) | MAC_DMA_CFG_HALT_REQ_SET(1)); { int count; u_int32_t data; count = 0; while (!MAC_DMA_CFG_HALT_ACK_GET(OS_REG_READ(ah, MAC_DMA_CFG_OFFSET) )) { count++; if (count > 10) { ath_hal_printf(ah, "Halt ACK timeout\n"); break; } OS_DELAY(10); } data = DDR_REG_READ(ah,DDR_CTL_CONFIG_OFFSET); HALDEBUG(ah, HAL_DEBUG_RESET, "check DDR Activity - HIGH\n"); count = 0; while (DDR_CTL_CONFIG_CLIENT_ACTIVITY_GET(data)) { // AVE_DEBUG(0,"DDR Activity - HIGH\n"); HALDEBUG(ah, HAL_DEBUG_RESET, "DDR Activity - HIGH\n"); count++; OS_DELAY(10); data = DDR_REG_READ(ah,DDR_CTL_CONFIG_OFFSET); if (count > 10) { ath_hal_printf(ah, "DDR Activity timeout\n"); break; } } } { //Force RTC reset REG_WRITE(RST_RESET, (REG_READ(RST_RESET) | RTC_RESET)); OS_DELAY(10); REG_WRITE(RST_RESET, (REG_READ(RST_RESET) & ~RTC_RESET)); OS_DELAY(10); OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(10); OS_REG_WRITE(ah, AR_RTC_RESET, 1); OS_DELAY(10); HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Scorpion SoC RTC reset done.\n", __func__); } #undef REG_READ #undef REG_WRITE } #endif /* AH_SUPPORT_SCORPION */ /* * Set Mac(BB,Phy) Warm Reset */ OS_REG_WRITE(ah, AR_RTC_RC, rst_flags); OS_DELAY(50); /* XXX 50 usec */ /* * Clear resets and force wakeup */ OS_REG_WRITE(ah, AR_RTC_RC, 0); if (!ath_hal_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: RTC stuck in MAC reset\n", __FUNCTION__); HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: AR_RTC_RC = 0x%x\n", __func__, OS_REG_READ(ah, AR_RTC_RC)); return AH_FALSE; } /* Clear AHB reset */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), 0); ar9300_disable_pll_lock_detect(ah); ar9300_attach_hw_platform(ah); ahp->ah_chip_reset_done = 1; return AH_TRUE; } static inline HAL_BOOL ar9300_set_reset_power_on(struct ath_hal *ah) { /* Force wake */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* * RTC reset and clear. Some delay in between is needed * to give the chip time to settle. */ OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(2); OS_REG_WRITE(ah, AR_RTC_RESET, 1); /* * Poll till RTC is ON */ if (!ath_hal_wait(ah, AR_RTC_STATUS, AR_RTC_STATUS_M, AR_RTC_STATUS_ON)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: RTC not waking up for %d\n", __FUNCTION__, 1000); return AH_FALSE; } /* * Read Revisions from Chip right after RTC is on for the first time. * This helps us detect the chip type early and initialize it accordingly. */ ar9300_read_revisions(ah); /* * Warm reset if we aren't really powering on, * just restarting the driver. */ return ar9300_set_reset(ah, HAL_RESET_WARM); } /* * Write the given reset bit mask into the reset register */ HAL_BOOL ar9300_set_reset_reg(struct ath_hal *ah, u_int32_t type) { HAL_BOOL ret = AH_FALSE; /* * Set force wake */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); switch (type) { case HAL_RESET_POWER_ON: ret = ar9300_set_reset_power_on(ah); break; case HAL_RESET_WARM: case HAL_RESET_COLD: ret = ar9300_set_reset(ah, type); break; default: break; } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif return ret; } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar9300_phy_disable(struct ath_hal *ah) { if (!ar9300_set_reset_reg(ah, HAL_RESET_WARM)) { return AH_FALSE; } #ifdef ATH_SUPPORT_LED #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define ATH_GPIO_OE 0xB8040000 #define ATH_GPIO_OUT 0xB8040008 /* GPIO Ouput Value reg.*/ if (AR_SREV_WASP(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 13))); } else { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); } } else if (AR_SREV_SCORPION(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 13))); } else { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); } /* Turn off JMPST led */ REG_WRITE(ATH_GPIO_OUT, (REG_READ(ATH_GPIO_OUT) | (0x1 << 15))); } else if (AR_SREV_HONEYBEE(ah)) { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); } #undef REG_READ #undef REG_WRITE #endif if ( AR_SREV_OSPREY(ah) ) { OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1), 0x0, 0x1f); } ar9300_init_pll(ah, AH_NULL); ar9300_disable_pll_lock_detect(ah); return AH_TRUE; } /* * Places all of hardware into reset */ HAL_BOOL ar9300_disable(struct ath_hal *ah) { if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { return AH_FALSE; } if (!ar9300_set_reset_reg(ah, HAL_RESET_COLD)) { return AH_FALSE; } ar9300_init_pll(ah, AH_NULL); return AH_TRUE; } /* * TODO: Only write the PLL if we're changing to or from CCK mode * * WARNING: The order of the PLL and mode registers must be correct. */ static inline void ar9300_set_rf_mode(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t rf_mode = 0; if (chan == AH_NULL) { return; } switch (AH9300(ah)->ah_hwp) { case HAL_TRUE_CHIP: rf_mode |= (IEEE80211_IS_CHAN_B(chan) || IEEE80211_IS_CHAN_G(chan)) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; break; default: HALASSERT(0); break; } /* Phy mode bits for 5GHz channels requiring Fast Clock */ if ( IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { rf_mode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); } OS_REG_WRITE(ah, AR_PHY_MODE, rf_mode); } /* * Places the hardware into reset and then pulls it out of reset */ HAL_BOOL -ar9300_chip_reset(struct ath_hal *ah, struct ieee80211_channel *chan) +ar9300_chip_reset(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_RESET_TYPE reset_type) { struct ath_hal_9300 *ahp = AH9300(ah); int type = HAL_RESET_WARM; OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Warm reset is optimistic. * * If the TX/RX DMA engines aren't shut down (eg, they're * wedged) then we're better off doing a full cold reset * to try and shake that condition. */ if (ahp->ah_chip_full_sleep || (ah->ah_config.ah_force_full_reset == 1) || + (reset_type == HAL_RESET_FORCE_COLD) || + (reset_type == HAL_RESET_BBPANIC) || OS_REG_READ(ah, AR_Q_TXE) || (OS_REG_READ(ah, AR_CR) & AR_CR_RXE)) { + HALDEBUG(ah, HAL_DEBUG_RESET, + "%s: full reset; reset_type=%d, full_sleep=%d\n", + __func__, reset_type, ahp->ah_chip_full_sleep); type = HAL_RESET_COLD; } if (!ar9300_set_reset_reg(ah, type)) { return AH_FALSE; } /* Bring out of sleep mode (AGAIN) */ if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { return AH_FALSE; } ahp->ah_chip_full_sleep = AH_FALSE; if (AR_SREV_HORNET(ah)) { ar9300_internal_regulator_apply(ah); } ar9300_init_pll(ah, chan); /* * Perform warm reset before the mode/PLL/turbo registers * are changed in order to deactivate the radio. Mode changes * with an active radio can result in corrupted shifts to the * radio device. */ ar9300_set_rf_mode(ah, chan); return AH_TRUE; } /* ar9300_setup_calibration * Setup HW to collect samples used for current cal */ inline static void ar9300_setup_calibration(struct ath_hal *ah, HAL_CAL_LIST *curr_cal) { /* Select calibration to run */ switch (curr_cal->cal_data->cal_type) { case IQ_MISMATCH_CAL: /* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, curr_cal->cal_data->cal_count_max); OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: starting IQ Mismatch Calibration\n", __func__); /* Kick-off cal */ OS_REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); break; case TEMP_COMP_CAL: if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); } else if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM_JUPITER, AR_PHY_65NM_CH0_THERM_LOCAL, 1); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM_JUPITER, AR_PHY_65NM_CH0_THERM_START, 1); } else { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: starting Temperature Compensation Calibration\n", __func__); break; default: HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s called with incorrect calibration type.\n", __func__); } } /* ar9300_reset_calibration * Initialize shared data structures and prepare a cal to be run. */ inline static void ar9300_reset_calibration(struct ath_hal *ah, HAL_CAL_LIST *curr_cal) { struct ath_hal_9300 *ahp = AH9300(ah); int i; /* Setup HW for new calibration */ ar9300_setup_calibration(ah, curr_cal); /* Change SW state to RUNNING for this calibration */ curr_cal->cal_state = CAL_RUNNING; /* Reset data structures shared between different calibrations */ for (i = 0; i < AR9300_MAX_CHAINS; i++) { ahp->ah_meas0.sign[i] = 0; ahp->ah_meas1.sign[i] = 0; ahp->ah_meas2.sign[i] = 0; ahp->ah_meas3.sign[i] = 0; } ahp->ah_cal_samples = 0; } #ifdef XXX_UNUSED_FUNCTION /* * Find out which of the RX chains are enabled */ static u_int32_t ar9300_get_rx_chain_mask(struct ath_hal *ah) { u_int32_t ret_val = OS_REG_READ(ah, AR_PHY_RX_CHAINMASK); /* The bits [2:0] indicate the rx chain mask and are to be * interpreted as follows: * 00x => Only chain 0 is enabled * 01x => Chain 1 and 0 enabled * 1xx => Chain 2,1 and 0 enabled */ return (ret_val & 0x7); } #endif static void ar9300_get_nf_hist_base(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, int is_scan, int16_t nf[]) { HAL_NFCAL_BASE *h_base; #ifdef ATH_NF_PER_CHAN h_base = &chan->nf_cal_hist.base; #else if (is_scan) { /* * The channel we are currently on is not the home channel, * so we shouldn't use the home channel NF buffer's values on * this channel. Instead, use the NF single value already * read for this channel. (Or, if we haven't read the NF for * this channel yet, the SW default for this chip/band will * be used.) */ h_base = &chan->nf_cal_hist.base; } else { /* use the home channel NF info */ h_base = &AH_PRIVATE(ah)->nf_cal_hist.base; } #endif OS_MEMCPY(nf, h_base->priv_nf, sizeof(h_base->priv_nf)); } HAL_BOOL ar9300_load_nf(struct ath_hal *ah, int16_t nf[]) { int i, j; int32_t val; /* XXX where are EXT regs defined */ const u_int32_t ar9300_cca_regs[] = { AR_PHY_CCA_0, AR_PHY_CCA_1, AR_PHY_CCA_2, AR_PHY_EXT_CCA, AR_PHY_EXT_CCA_1, AR_PHY_EXT_CCA_2, }; u_int8_t chainmask; /* * Force NF calibration for all chains, otherwise Vista station * would conduct a bad performance */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { chainmask = 0x9; } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah) || AR_SREV_HONEYBEE(ah)) { chainmask = 0x1b; } else { chainmask = 0x3F; } /* * Write filtered NF values into max_cca_pwr register parameter * so we can load below. */ for (i = 0; i < HAL_NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { val = OS_REG_READ(ah, ar9300_cca_regs[i]); val &= 0xFFFFFE00; val |= (((u_int32_t)(nf[i]) << 1) & 0x1ff); OS_REG_WRITE(ah, ar9300_cca_regs[i], val); } } HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: load %d %d %d %d %d %d\n", __func__, nf[0], nf[1], nf[2], nf[3], nf[4], nf[5]); /* * Load software filtered NF value into baseband internal min_cca_pwr * variable. */ OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); /* Wait for load to complete, should be fast, a few 10s of us. */ /* Changed the max delay 250us back to 10000us, since 250us often * results in NF load timeout and causes deaf condition * during stress testing 12/12/2009 */ for (j = 0; j < 10000; j++) { if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0){ break; } OS_DELAY(10); } if (j == 10000) { /* * We timed out waiting for the noisefloor to load, probably * due to an in-progress rx. Simply return here and allow * the load plenty of time to complete before the next * calibration interval. We need to avoid trying to load -50 * (which happens below) while the previous load is still in * progress as this can cause rx deafness (see EV 66368,62830). * Instead by returning here, the baseband nf cal will * just be capped by our present noisefloor until the next * calibration timer. */ HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: *** TIMEOUT while waiting for nf to load: " "AR_PHY_AGC_CONTROL=0x%x ***\n", __func__, OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); return AH_FALSE; } /* * Restore max_cca_power register parameter again so that we're not capped * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ for (i = 0; i < HAL_NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { val = OS_REG_READ(ah, ar9300_cca_regs[i]); val &= 0xFFFFFE00; val |= (((u_int32_t)(-50) << 1) & 0x1ff); OS_REG_WRITE(ah, ar9300_cca_regs[i], val); } } return AH_TRUE; } /* ar9300_per_calibration * Generic calibration routine. * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ inline static void ar9300_per_calibration(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, u_int8_t rxchainmask, HAL_CAL_LIST *curr_cal, HAL_BOOL *is_cal_done) { struct ath_hal_9300 *ahp = AH9300(ah); /* Cal is assumed not done until explicitly set below */ *is_cal_done = AH_FALSE; /* Calibration in progress. */ if (curr_cal->cal_state == CAL_RUNNING) { /* Check to see if it has finished. */ if (!(OS_REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) { int i, num_chains = 0; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (rxchainmask & (1 << i)) { num_chains++; } } /* * Accumulate cal measures for active chains */ curr_cal->cal_data->cal_collect(ah, num_chains); ahp->ah_cal_samples++; if (ahp->ah_cal_samples >= curr_cal->cal_data->cal_num_samples) { /* * Process accumulated data */ curr_cal->cal_data->cal_post_proc(ah, num_chains); /* Calibration has finished. */ ichan->calValid |= curr_cal->cal_data->cal_type; curr_cal->cal_state = CAL_DONE; *is_cal_done = AH_TRUE; } else { /* Set-up collection of another sub-sample until we * get desired number */ ar9300_setup_calibration(ah, curr_cal); } } } else if (!(ichan->calValid & curr_cal->cal_data->cal_type)) { /* If current cal is marked invalid in channel, kick it off */ ar9300_reset_calibration(ah, curr_cal); } } static void ar9300_start_nf_cal(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); AH9300(ah)->nf_tsf32 = ar9300_get_tsf32(ah); /* * We are reading the NF values before we start the NF operation, because * of that we are getting very high values like -45. * This triggers the CW_INT detected and EACS module triggers the channel change * chip_reset_done value is used to fix this issue. * chip_reset_flag is set during the RTC reset. * chip_reset_flag is cleared during the starting NF operation. * if flag is set we will clear the flag and will not read the NF values. */ ahp->ah_chip_reset_done = 0; } /* ar9300_calibration * Wrapper for a more generic Calibration routine. Primarily to abstract to * upper layers whether there is 1 or more calibrations to be run. */ HAL_BOOL ar9300_calibration(struct ath_hal *ah, struct ieee80211_channel *chan, u_int8_t rxchainmask, HAL_BOOL do_nf_cal, HAL_BOOL *is_cal_done, int is_scan, u_int32_t *sched_cals) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CAL_LIST *curr_cal = ahp->ah_cal_list_curr; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int16_t nf_buf[HAL_NUM_NF_READINGS]; *is_cal_done = AH_TRUE; /* XXX: For initial wasp bringup - disable periodic calibration */ /* Invalid channel check */ if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Entering, Doing NF Cal = %d\n", __func__, do_nf_cal); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 0 Rx IQ Cal Correction 0x%08x\n", __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); if (!AR_SREV_HORNET(ah) && !AR_SREV_POSEIDON(ah) && !AR_SREV_APHRODITE(ah)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 1 Rx IQ Cal Correction 0x%08x\n", __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B1)); if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah) && !AR_SREV_HONEYBEE(ah)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 2 Rx IQ Cal Correction 0x%08x\n", __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B2)); } } OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq); /* For given calibration: * 1. Call generic cal routine * 2. When this cal is done (is_cal_done) if we have more cals waiting * (eg after reset), mask this to upper layers by not propagating * is_cal_done if it is set to TRUE. * Instead, change is_cal_done to FALSE and setup the waiting cal(s) * to be run. */ if (curr_cal && (curr_cal->cal_data->cal_type & *sched_cals) && (curr_cal->cal_state == CAL_RUNNING || curr_cal->cal_state == CAL_WAITING)) { ar9300_per_calibration(ah, ichan, rxchainmask, curr_cal, is_cal_done); if (*is_cal_done == AH_TRUE) { ahp->ah_cal_list_curr = curr_cal = curr_cal->cal_next; if (curr_cal && curr_cal->cal_state == CAL_WAITING) { *is_cal_done = AH_FALSE; ar9300_reset_calibration(ah, curr_cal); } else { *sched_cals &= ~IQ_MISMATCH_CAL; } } } /* Do NF cal only at longer intervals */ if (do_nf_cal) { int nf_done; /* Get the value from the previous NF cal and update history buffer */ nf_done = ar9300_store_new_nf(ah, chan, is_scan); #if 0 if (ichan->channel_flags & CHANNEL_CW_INT) { chan->channel_flags |= CHANNEL_CW_INT; } #endif chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; if (nf_done) { int ret; /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical value. */ ar9300_get_nf_hist_base(ah, ichan, is_scan, nf_buf); ret = ar9300_load_nf(ah, nf_buf); /* start NF calibration, without updating BB NF register*/ ar9300_start_nf_cal(ah); /* * If we failed the NF cal then tell the upper layer that we * failed so we can do a full reset */ if (! ret) return AH_FALSE; } } return AH_TRUE; } /* ar9300_iq_cal_collect * Collect data from HW to later perform IQ Mismatch Calibration */ void ar9300_iq_cal_collect(struct ath_hal *ah, u_int8_t num_chains) { struct ath_hal_9300 *ahp = AH9300(ah); int i; /* * Accumulate IQ cal measures for active chains */ for (i = 0; i < num_chains; i++) { ahp->ah_total_power_meas_i[i] = OS_REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); ahp->ah_total_power_meas_q[i] = OS_REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ahp->ah_total_iq_corr_meas[i] = (int32_t) OS_REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%d: Chn %d " "Reg Offset(0x%04x)pmi=0x%08x; " "Reg Offset(0x%04x)pmq=0x%08x; " "Reg Offset (0x%04x)iqcm=0x%08x;\n", ahp->ah_cal_samples, i, (unsigned) AR_PHY_CAL_MEAS_0(i), ahp->ah_total_power_meas_i[i], (unsigned) AR_PHY_CAL_MEAS_1(i), ahp->ah_total_power_meas_q[i], (unsigned) AR_PHY_CAL_MEAS_2(i), ahp->ah_total_iq_corr_meas[i]); } } /* ar9300_iq_calibration * Use HW data to perform IQ Mismatch Calibration */ void ar9300_iq_calibration(struct ath_hal *ah, u_int8_t num_chains) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t power_meas_q, power_meas_i, iq_corr_meas; u_int32_t q_coff_denom, i_coff_denom; int32_t q_coff, i_coff; int iq_corr_neg, i; HAL_CHANNEL_INTERNAL *ichan; static const u_int32_t offset_array[3] = { AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B1, AR_PHY_RX_IQCAL_CORR_B2, }; ichan = ath_hal_checkchannel(ah, AH_PRIVATE(ah)->ah_curchan); for (i = 0; i < num_chains; i++) { power_meas_i = ahp->ah_total_power_meas_i[i]; power_meas_q = ahp->ah_total_power_meas_q[i]; iq_corr_meas = ahp->ah_total_iq_corr_meas[i]; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Starting IQ Cal and Correction for Chain %d\n", i); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Orignal: Chn %diq_corr_meas = 0x%08x\n", i, ahp->ah_total_iq_corr_meas[i]); iq_corr_neg = 0; /* iq_corr_meas is always negative. */ if (iq_corr_meas > 0x80000000) { iq_corr_meas = (0xffffffff - iq_corr_meas) + 1; iq_corr_neg = 1; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", i, power_meas_i); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", i, power_meas_q); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "iq_corr_neg is 0x%08x\n", iq_corr_neg); i_coff_denom = (power_meas_i / 2 + power_meas_q / 2) / 256; q_coff_denom = power_meas_q / 64; /* Protect against divide-by-0 */ if ((i_coff_denom != 0) && (q_coff_denom != 0)) { /* IQ corr_meas is already negated if iqcorr_neg == 1 */ i_coff = iq_corr_meas / i_coff_denom; q_coff = power_meas_i / q_coff_denom - 64; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d i_coff = 0x%08x\n", i, i_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d q_coff = 0x%08x\n", i, q_coff); /* Force bounds on i_coff */ if (i_coff >= 63) { i_coff = 63; } else if (i_coff <= -63) { i_coff = -63; } /* Negate i_coff if iq_corr_neg == 0 */ if (iq_corr_neg == 0x0) { i_coff = -i_coff; } /* Force bounds on q_coff */ if (q_coff >= 63) { q_coff = 63; } else if (q_coff <= -63) { q_coff = -63; } i_coff = i_coff & 0x7f; q_coff = q_coff & 0x7f; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d : i_coff = 0x%x q_coff = 0x%x\n", i, i_coff, q_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Register offset (0x%04x) before update = 0x%x\n", offset_array[i], OS_REG_READ(ah, offset_array[i])); OS_REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, i_coff); OS_REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, q_coff); /* store the RX cal results */ if (ichan != NULL) { ahp->ah_rx_cal_corr[i] = OS_REG_READ(ah, offset_array[i]) & 0x7fff; ahp->ah_rx_cal_complete = AH_TRUE; ahp->ah_rx_cal_chan = ichan->channel; // ahp->ah_rx_cal_chan_flag = ichan->channel_flags &~ CHANNEL_PASSIVE; ahp->ah_rx_cal_chan_flag = 0; /* XXX */ } else { /* XXX? Is this what I should do? */ ahp->ah_rx_cal_complete = AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Register offset (0x%04x) QI COFF (bitfields 0x%08x) " "after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, OS_REG_READ(ah, offset_array[i])); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) " "after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, OS_REG_READ(ah, offset_array[i])); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i); } } OS_REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "IQ Cal and Correction (offset 0x%04x) enabled " "(bit position 0x%08x). New Value 0x%08x\n", (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); } /* * When coming back from offchan, we do not perform RX IQ Cal. * But the chip reset will clear all previous results * We store the previous results and restore here. */ static void ar9300_rx_iq_cal_restore(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t i_coff, q_coff; HAL_BOOL is_restore = AH_FALSE; int i; static const u_int32_t offset_array[3] = { AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B1, AR_PHY_RX_IQCAL_CORR_B2, }; for (i=0; iah_rx_cal_corr[i]) { i_coff = (ahp->ah_rx_cal_corr[i] & AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF) >> AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF_S; q_coff = (ahp->ah_rx_cal_corr[i] & AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF) >> AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF_S; OS_REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, i_coff); OS_REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, q_coff); is_restore = AH_TRUE; } } if (is_restore) OS_REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: IQ Cal and Correction (offset 0x%04x) enabled " "(bit position 0x%08x). New Value 0x%08x\n", __func__, (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); } /* * Set a limit on the overall output power. Used for dynamic * transmit power control and the like. * * NB: limit is in units of 0.5 dbM. */ HAL_BOOL ar9300_set_tx_power_limit(struct ath_hal *ah, u_int32_t limit, u_int16_t extra_txpow, u_int16_t tpc_in_db) { struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ahpriv = AH_PRIVATE(ah); const struct ieee80211_channel *chan = ahpriv->ah_curchan; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (NULL == chan) { return AH_FALSE; } ahpriv->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); ahpriv->ah_extraTxPow = extra_txpow; if(chan == NULL) { return AH_FALSE; } if (ar9300_eeprom_set_transmit_power(ah, &ahp->ah_eeprom, chan, ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), ath_hal_get_twice_max_regpower(ahpriv, ichan, chan), AH_MIN(MAX_RATE_POWER, ahpriv->ah_powerLimit)) != HAL_OK) { return AH_FALSE; } return AH_TRUE; } /* * Exported call to check for a recent gain reading and return * the current state of the thermal calibration gain engine. */ HAL_RFGAIN ar9300_get_rfgain(struct ath_hal *ah) { return HAL_RFGAIN_INACTIVE; } #define HAL_GREEN_AP_RX_MASK 0x1 static inline void ar9300_init_chain_masks(struct ath_hal *ah, int rx_chainmask, int tx_chainmask) { if (AH9300(ah)->green_ap_ps_on) { rx_chainmask = HAL_GREEN_AP_RX_MASK; } if (rx_chainmask == 0x5) { OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); } OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); /* * Adaptive Power Management: * Some 3 stream chips exceed the PCIe power requirements. * This workaround will reduce power consumption by using 2 tx chains * for 1 and 2 stream rates (5 GHz only). * * Set the self gen mask to 2 tx chains when APM is enabled. * */ if (AH_PRIVATE(ah)->ah_caps.halApmEnable && (tx_chainmask == 0x7)) { OS_REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); } else { OS_REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); } if (tx_chainmask == 0x5) { OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); } } /* * Override INI values with chip specific configuration. */ static inline void ar9300_override_ini(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t val; HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; /* * Set the RX_ABORT and RX_DIS and clear it only after * RXE is set for MAC. This prevents frames with * corrupted descriptor status. */ OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); /* * For Merlin and above, there is a new feature that allows Multicast * search based on both MAC Address and Key ID. * By default, this feature is enabled. * But since the driver is not using this feature, we switch it off; * otherwise multicast search based on MAC addr only will fail. */ val = OS_REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, val | AR_BUG_58603_FIX_ENABLE | AR_AGG_WEP_ENABLE); /* Osprey revision specific configuration */ /* Osprey 2.0+ - if SW RAC support is disabled, must also disable * the Osprey 2.0 hardware RAC fix. */ if (p_cap->halIsrRacSupport == AH_FALSE) { OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_MISSING_TX_INTR_FIX_ENABLE); } /* try to enable old pal if it is needed for h/w green tx */ ar9300_hwgreentx_set_pal_spare(ah, 1); } static inline void ar9300_prog_ini(struct ath_hal *ah, struct ar9300_ini_array *ini_arr, int column) { int i, reg_writes = 0; /* New INI format: Array may be undefined (pre, core, post arrays) */ if (ini_arr->ia_array == NULL) { return; } /* * New INI format: Pre, core, and post arrays for a given subsystem may be * modal (> 2 columns) or non-modal (2 columns). * Determine if the array is non-modal and force the column to 1. */ if (column >= ini_arr->ia_columns) { column = 1; } for (i = 0; i < ini_arr->ia_rows; i++) { u_int32_t reg = INI_RA(ini_arr, i, 0); u_int32_t val = INI_RA(ini_arr, i, column); /* ** Determine if this is a shift register value ** (reg >= 0x16000 && reg < 0x17000 for Osprey) , ** and insert the configured delay if so. ** -this delay is not required for Osprey (EV#71410) */ OS_REG_WRITE(ah, reg, val); WAR_6773(reg_writes); } } static inline HAL_STATUS ar9300_process_ini(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_CHANNEL_INTERNAL *ichan, HAL_HT_MACMODE macmode) { int reg_writes = 0; struct ath_hal_9300 *ahp = AH9300(ah); u_int modes_index, modes_txgaintable_index = 0; int i; HAL_STATUS status; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); /* Setup the indices for the next set of register array writes */ /* TODO: * If the channel marker is indicative of the current mode rather * than capability, we do not need to check the phy mode below. */ #if 0 switch (chan->channel_flags & CHANNEL_ALL) { case CHANNEL_A: case CHANNEL_A_HT20: if (AR_SREV_SCORPION(ah)){ if (chan->channel <= 5350){ modes_txgaintable_index = 1; }else if ((chan->channel > 5350) && (chan->channel <= 5600)){ modes_txgaintable_index = 3; }else if (chan->channel > 5600){ modes_txgaintable_index = 5; } } modes_index = 1; freq_index = 1; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: if (AR_SREV_SCORPION(ah)){ if (chan->channel <= 5350){ modes_txgaintable_index = 2; }else if ((chan->channel > 5350) && (chan->channel <= 5600)){ modes_txgaintable_index = 4; }else if (chan->channel > 5600){ modes_txgaintable_index = 6; } } modes_index = 2; freq_index = 1; break; case CHANNEL_PUREG: case CHANNEL_G_HT20: case CHANNEL_B: if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 8; }else if (AR_SREV_HONEYBEE(ah)){ modes_txgaintable_index = 1; } modes_index = 4; freq_index = 2; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 7; }else if (AR_SREV_HONEYBEE(ah)){ modes_txgaintable_index = 1; } modes_index = 3; freq_index = 2; break; case CHANNEL_108G: modes_index = 5; freq_index = 2; break; default: HALASSERT(0); return HAL_EINVAL; } #endif /* FreeBSD */ if (IS_CHAN_5GHZ(ichan)) { if (IEEE80211_IS_CHAN_HT40U(chan) || IEEE80211_IS_CHAN_HT40D(chan)) { if (AR_SREV_SCORPION(ah)){ if (ichan->channel <= 5350){ modes_txgaintable_index = 2; }else if ((ichan->channel > 5350) && (ichan->channel <= 5600)){ modes_txgaintable_index = 4; }else if (ichan->channel > 5600){ modes_txgaintable_index = 6; } } modes_index = 2; } else if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_HT20(chan)) { if (AR_SREV_SCORPION(ah)){ if (ichan->channel <= 5350){ modes_txgaintable_index = 1; }else if ((ichan->channel > 5350) && (ichan->channel <= 5600)){ modes_txgaintable_index = 3; }else if (ichan->channel > 5600){ modes_txgaintable_index = 5; } } modes_index = 1; } else return HAL_EINVAL; } else if (IS_CHAN_2GHZ(ichan)) { if (IEEE80211_IS_CHAN_108G(chan)) { modes_index = 5; } else if (IEEE80211_IS_CHAN_HT40U(chan) || IEEE80211_IS_CHAN_HT40D(chan)) { if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 7; } else if (AR_SREV_HONEYBEE(ah)){ modes_txgaintable_index = 1; } modes_index = 3; } else if (IEEE80211_IS_CHAN_HT20(chan) || IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_B(chan) || IEEE80211_IS_CHAN_PUREG(chan)) { if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 8; } else if (AR_SREV_HONEYBEE(ah)){ modes_txgaintable_index = 1; } modes_index = 4; } else return HAL_EINVAL; } else return HAL_EINVAL; #if 0 /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); #endif HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: " "Skipping OS-REG-WRITE(ah, AR-PHY(0), 0x00000007)\n"); HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: no ADDac programming\n"); /* * Osprey 2.0+ - new INI format. * Each subsystem has a pre, core, and post array. */ for (i = 0; i < ATH_INI_NUM_SPLIT; i++) { ar9300_prog_ini(ah, &ahp->ah_ini_soc[i], modes_index); ar9300_prog_ini(ah, &ahp->ah_ini_mac[i], modes_index); ar9300_prog_ini(ah, &ahp->ah_ini_bb[i], modes_index); ar9300_prog_ini(ah, &ahp->ah_ini_radio[i], modes_index); if ((i == ATH_INI_POST) && (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah))) { ar9300_prog_ini(ah, &ahp->ah_ini_radio_post_sys2ant, modes_index); } } if (!(AR_SREV_SOC(ah))) { /* Doubler issue : Some board doesn't work well with MCS15. Turn off doubler after freq locking is complete*/ //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ OS_REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ OS_DELAY(200); //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH0_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH1_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH2_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_DELAY(1); //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_DELAY(200); //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_SYNTH12, OS_REG_READ(ah, AR_PHY_65NM_CH0_SYNTH12)); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_SYNTH12, AR_PHY_65NM_CH0_SYNTH12_VREFMUL3, 0xf); //OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH0_SYNTH12, 1<< 16); /* clr charge pump */ //ath_hal_printf(ah, "%s[%d] ==== After reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_SYNTH12, OS_REG_READ(ah, AR_PHY_65NM_CH0_SYNTH12)); OS_REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ OS_REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ OS_REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); } /* Write rxgain Array Parameters */ REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain, 1, reg_writes); HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Rx Gain programming\n"); if (AR_SREV_JUPITER_20_OR_LATER(ah)) { /* * CUS217 mix LNA mode. */ if (ar9300_rx_gain_index_get(ah) == 2) { REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain_bb_core, 1, reg_writes); REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain_bb_postamble, modes_index, reg_writes); } /* * 5G-XLNA */ if ((ar9300_rx_gain_index_get(ah) == 2) || (ar9300_rx_gain_index_get(ah) == 3)) { REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain_xlna, modes_index, reg_writes); } } if (AR_SREV_SCORPION(ah)) { /* Write rxgain bounds Array */ REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain_bounds, modes_index, reg_writes); HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Rx Gain table bounds programming\n"); } /* UB124 xLNA settings */ if (AR_SREV_WASP(ah) && ar9300_rx_gain_index_get(ah) == 2) { #define REG_WRITE(_reg,_val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) u_int32_t val; /* B8040000: bit[0]=0, bit[3]=0; */ val = REG_READ(0xB8040000); val &= 0xfffffff6; REG_WRITE(0xB8040000, val); /* B804002c: bit[31:24]=0x2e; bit[7:0]=0x2f; */ val = REG_READ(0xB804002c); val &= 0x00ffff00; val |= 0x2e00002f; REG_WRITE(0xB804002c, val); /* B804006c: bit[1]=1; */ val = REG_READ(0xB804006c); val |= 0x2; REG_WRITE(0xB804006c, val); #undef REG_READ #undef REG_WRITE } /* Write txgain Array Parameters */ if (AR_SREV_SCORPION(ah) || AR_SREV_HONEYBEE(ah)) { REG_WRITE_ARRAY(&ahp->ah_ini_modes_txgain, modes_txgaintable_index, reg_writes); }else{ REG_WRITE_ARRAY(&ahp->ah_ini_modes_txgain, modes_index, reg_writes); } HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Tx Gain programming\n"); /* For 5GHz channels requiring Fast Clock, apply different modal values */ if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Fast clock enabled, use special ini values\n", __func__); REG_WRITE_ARRAY(&ahp->ah_ini_modes_additional, modes_index, reg_writes); } if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: use xtal ini for AH9300(ah)->clk_25mhz: %d\n", __func__, AH9300(ah)->clk_25mhz); REG_WRITE_ARRAY( &ahp->ah_ini_modes_additional, 1/*modes_index*/, reg_writes); } if (AR_SREV_WASP(ah) && (AH9300(ah)->clk_25mhz == 0)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Apply 40MHz ini settings\n", __func__); REG_WRITE_ARRAY( &ahp->ah_ini_modes_additional_40mhz, 1/*modesIndex*/, reg_writes); } /* Handle Japan Channel 14 channel spreading */ if (2484 == ichan->channel) { ar9300_prog_ini(ah, &ahp->ah_ini_japan2484, 1); } #if 0 /* XXX TODO! */ if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) { ar9300_prog_ini(ah, &ahp->ah_ini_BTCOEX_MAX_TXPWR, 1); } #endif /* Override INI with chip specific configuration */ ar9300_override_ini(ah, chan); /* Setup 11n MAC/Phy mode registers */ ar9300_set_11n_regs(ah, chan, macmode); /* * Moved ar9300_init_chain_masks() here to ensure the swap bit is set before * the pdadc table is written. Swap must occur before any radio dependent * replicated register access. The pdadc curve addressing in particular * depends on the consistent setting of the swap bit. */ ar9300_init_chain_masks(ah, ahp->ah_rx_chainmask, ahp->ah_tx_chainmask); /* * Setup the transmit power values. * * After the public to private hal channel mapping, ichan contains the * valid regulatory power value. * ath_hal_getctl and ath_hal_getantennaallowed look up ichan from chan. */ status = ar9300_eeprom_set_transmit_power(ah, &ahp->ah_eeprom, chan, ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), ath_hal_get_twice_max_regpower(ahpriv, ichan, chan), AH_MIN(MAX_RATE_POWER, ahpriv->ah_powerLimit)); if (status != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: error init'ing transmit power\n", __func__); return HAL_EIO; } return HAL_OK; #undef N } /* ar9300_is_cal_supp * Determine if calibration is supported by device and channel flags */ inline static HAL_BOOL ar9300_is_cal_supp(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_CAL_TYPES cal_type) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_BOOL retval = AH_FALSE; switch (cal_type & ahp->ah_supp_cals) { case IQ_MISMATCH_CAL: /* Run IQ Mismatch for non-CCK only */ if (!IEEE80211_IS_CHAN_B(chan)) { retval = AH_TRUE; } break; case TEMP_COMP_CAL: retval = AH_TRUE; break; } return retval; } #if 0 /* ar9285_pa_cal * PA Calibration for Kite 1.1 and later versions of Kite. * - from system's team. */ static inline void ar9285_pa_cal(struct ath_hal *ah) { u_int32_t reg_val; int i, lo_gn, offs_6_1, offs_0; u_int8_t reflo; u_int32_t phy_test2_reg_val, phy_adc_ctl_reg_val; u_int32_t an_top2_reg_val, phy_tst_dac_reg_val; /* Kite 1.1 WAR for Bug 35666 * Increase the LDO value to 1.28V before accessing analog Reg */ if (AR_SREV_KITE_11(ah)) { OS_REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14) ); } an_top2_reg_val = OS_REG_READ(ah, AR9285_AN_TOP2); /* set pdv2i pdrxtxbb */ reg_val = OS_REG_READ(ah, AR9285_AN_RXTXBB1); reg_val |= ((0x1 << 5) | (0x1 << 7)); OS_REG_WRITE(ah, AR9285_AN_RXTXBB1, reg_val); /* clear pwddb */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G7); reg_val &= 0xfffffffd; OS_REG_WRITE(ah, AR9285_AN_RF2G7, reg_val); /* clear enpacal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val &= 0xfffff7ff; OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* set offcal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G2); reg_val |= (0x1 << 12); OS_REG_WRITE(ah, AR9285_AN_RF2G2, reg_val); /* set pdpadrv1=pdpadrv2=pdpaout=1 */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val |= (0x7 << 23); OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* Read back reflo, increase it by 1 and write it. */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = ((reg_val >> 26) & 0x7); if (reflo < 0x7) { reflo++; } reg_val = ((reg_val & 0xe3ffffff) | (reflo << 26)); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = ((reg_val >> 26) & 0x7); /* use TX single carrier to transmit * dac const * reg. 15 */ phy_tst_dac_reg_val = OS_REG_READ(ah, AR_PHY_TSTDAC_CONST); OS_REG_WRITE(ah, AR_PHY_TSTDAC_CONST, ((0x7ff << 11) | 0x7ff)); reg_val = OS_REG_READ(ah, AR_PHY_TSTDAC_CONST); /* source is dac const * reg. 2 */ phy_test2_reg_val = OS_REG_READ(ah, AR_PHY_TEST2); OS_REG_WRITE(ah, AR_PHY_TEST2, ((0x1 << 7) | (0x1 << 1))); reg_val = OS_REG_READ(ah, AR_PHY_TEST2); /* set dac on * reg. 11 */ phy_adc_ctl_reg_val = OS_REG_READ(ah, AR_PHY_ADC_CTL); OS_REG_WRITE(ah, AR_PHY_ADC_CTL, 0x80008000); reg_val = OS_REG_READ(ah, AR_PHY_ADC_CTL); OS_REG_WRITE(ah, AR9285_AN_TOP2, (0x1 << 27) | (0x1 << 17) | (0x1 << 16) | (0x1 << 14) | (0x1 << 12) | (0x1 << 11) | (0x1 << 7) | (0x1 << 5)); OS_DELAY(10); /* 10 usec */ /* clear off[6:0] */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); reg_val &= 0xfc0fffff; OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reg_val &= 0xfdffffff; OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); offs_6_1 = 0; for (i = 6; i > 0; i--) { /* sef off[$k]==1 */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); reg_val &= 0xfc0fffff; reg_val = reg_val | (0x1 << (19 + i)) | ((offs_6_1) << 20); OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); lo_gn = (OS_REG_READ(ah, AR9285_AN_RF2G9)) & 0x1; offs_6_1 = offs_6_1 | (lo_gn << (i - 1)); } reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); reg_val &= 0xfc0fffff; reg_val = reg_val | ((offs_6_1 - 1) << 20); OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); /* set off_0=1; */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reg_val &= 0xfdffffff; reg_val = reg_val | (0x1 << 25); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); lo_gn = OS_REG_READ(ah, AR9285_AN_RF2G9) & 0x1; offs_0 = lo_gn; reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reg_val &= 0xfdffffff; reg_val = reg_val | (offs_0 << 25); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); /* clear pdv2i */ reg_val = OS_REG_READ(ah, AR9285_AN_RXTXBB1); reg_val &= 0xffffff5f; OS_REG_WRITE(ah, AR9285_AN_RXTXBB1, reg_val); /* set enpacal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val |= (0x1 << 11); OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* clear offcal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G2); reg_val &= 0xffffefff; OS_REG_WRITE(ah, AR9285_AN_RF2G2, reg_val); /* set pdpadrv1=pdpadrv2=pdpaout=0 */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val &= 0xfc7fffff; OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* Read back reflo, decrease it by 1 and write it. */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = (reg_val >> 26) & 0x7; if (reflo) { reflo--; } reg_val = ((reg_val & 0xe3ffffff) | (reflo << 26)); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = (reg_val >> 26) & 0x7; /* write back registers */ OS_REG_WRITE(ah, AR_PHY_TSTDAC_CONST, phy_tst_dac_reg_val); OS_REG_WRITE(ah, AR_PHY_TEST2, phy_test2_reg_val); OS_REG_WRITE(ah, AR_PHY_ADC_CTL, phy_adc_ctl_reg_val); OS_REG_WRITE(ah, AR9285_AN_TOP2, an_top2_reg_val); /* Kite 1.1 WAR for Bug 35666 * Decrease the LDO value back to 1.20V */ if (AR_SREV_KITE_11(ah)) { OS_REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); } } #endif /* ar9300_run_init_cals * Runs non-periodic calibrations */ inline static HAL_BOOL ar9300_run_init_cals(struct ath_hal *ah, int init_cal_count) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL ichan; /* bogus */ HAL_BOOL is_cal_done; HAL_CAL_LIST *curr_cal; const HAL_PERCAL_DATA *cal_data; int i; curr_cal = ahp->ah_cal_list_curr; if (curr_cal == AH_NULL) { return AH_FALSE; } cal_data = curr_cal->cal_data; ichan.calValid = 0; for (i = 0; i < init_cal_count; i++) { /* Reset this Cal */ ar9300_reset_calibration(ah, curr_cal); /* Poll for offset calibration complete */ if (!ath_hal_wait( ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Cal %d failed to complete in 100ms.\n", __func__, curr_cal->cal_data->cal_type); /* Re-initialize list pointers for periodic cals */ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; return AH_FALSE; } /* Run this cal */ ar9300_per_calibration( ah, &ichan, ahp->ah_rx_chainmask, curr_cal, &is_cal_done); if (is_cal_done == AH_FALSE) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Not able to run Init Cal %d.\n", __func__, curr_cal->cal_data->cal_type); } if (curr_cal->cal_next) { curr_cal = curr_cal->cal_next; } } /* Re-initialize list pointers for periodic cals */ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; return AH_TRUE; } #if 0 static void ar9300_tx_carrier_leak_war(struct ath_hal *ah) { unsigned long tx_gain_table_max; unsigned long reg_bb_cl_map_0_b0 = 0xffffffff; unsigned long reg_bb_cl_map_1_b0 = 0xffffffff; unsigned long reg_bb_cl_map_2_b0 = 0xffffffff; unsigned long reg_bb_cl_map_3_b0 = 0xffffffff; unsigned long tx_gain, cal_run = 0; unsigned long cal_gain[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; unsigned long cal_gain_index[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; unsigned long new_gain[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; int i, j; OS_MEMSET(new_gain, 0, sizeof(new_gain)); /*printf(" Running TxCarrierLeakWAR\n");*/ /* process tx gain table, we use cl_map_hw_gen=0. */ OS_REG_RMW_FIELD(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_MAP_HW_GEN, 0); //the table we used is txbb_gc[2:0], 1dB[2:1]. tx_gain_table_max = OS_REG_READ_FIELD(ah, AR_PHY_TPC_7, AR_PHY_TPC_7_TX_GAIN_TABLE_MAX); for (i = 0; i <= tx_gain_table_max; i++) { tx_gain = OS_REG_READ(ah, AR_PHY_TXGAIN_TAB(1) + i * 4); cal_gain[i] = (((tx_gain >> 5)& 0x7) << 2) | (((tx_gain >> 1) & 0x3) << 0); if (i == 0) { cal_gain_index[i] = cal_run; new_gain[i] = 1; cal_run++; } else { new_gain[i] = 1; for (j = 0; j < i; j++) { /* printf("i=%d, j=%d cal_gain[$i]=0x%04x\n", i, j, cal_gain[i]); */ if (new_gain[i]) { if ((cal_gain[i] != cal_gain[j])) { new_gain[i] = 1; } else { /* if old gain found, use old cal_run value. */ new_gain[i] = 0; cal_gain_index[i] = cal_gain_index[j]; } } } /* if new gain found, increase cal_run */ if (new_gain[i] == 1) { cal_gain_index[i] = cal_run; cal_run++; } } reg_bb_cl_map_0_b0 = (reg_bb_cl_map_0_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 0 & 0x1) << i); reg_bb_cl_map_1_b0 = (reg_bb_cl_map_1_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 1 & 0x1) << i); reg_bb_cl_map_2_b0 = (reg_bb_cl_map_2_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 2 & 0x1) << i); reg_bb_cl_map_3_b0 = (reg_bb_cl_map_3_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 3 & 0x1) << i); /* printf("i=%2d, cal_gain[$i]= 0x%04x, cal_run= %d, " "cal_gain_index[i]=%d, new_gain[i] = %d\n", i, cal_gain[i], cal_run, cal_gain_index[i], new_gain[i]); */ } OS_REG_WRITE(ah, AR_PHY_CL_MAP_0_B0, reg_bb_cl_map_0_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_1_B0, reg_bb_cl_map_1_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_2_B0, reg_bb_cl_map_2_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_3_B0, reg_bb_cl_map_3_b0); if (AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_CL_MAP_0_B1, reg_bb_cl_map_0_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_1_B1, reg_bb_cl_map_1_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_2_B1, reg_bb_cl_map_2_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_3_B1, reg_bb_cl_map_3_b0); } } #endif static inline void ar9300_invalidate_saved_cals(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { #if ATH_SUPPORT_CAL_REUSE if (AH_PRIVATE(ah)->ah_config.ath_hal_cal_reuse & ATH_CAL_REUSE_REDO_IN_FULL_RESET) { ichan->one_time_txiqcal_done = AH_FALSE; ichan->one_time_txclcal_done = AH_FALSE; } #endif } static inline HAL_BOOL ar9300_restore_rtt_cals(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { HAL_BOOL restore_status = AH_FALSE; return restore_status; } /* ar9300_init_cal * Initialize Calibration infrastructure */ static inline HAL_BOOL ar9300_init_cal_internal(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_CHANNEL_INTERNAL *ichan, HAL_BOOL enable_rtt, HAL_BOOL do_rtt_cal, HAL_BOOL skip_if_none, HAL_BOOL apply_last_iqcorr) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_BOOL txiqcal_success_flag = AH_FALSE; HAL_BOOL cal_done = AH_FALSE; int iqcal_idx = 0; HAL_BOOL do_sep_iq_cal = AH_FALSE; HAL_BOOL do_agc_cal = do_rtt_cal; HAL_BOOL is_cal_reusable = AH_TRUE; #if ATH_SUPPORT_CAL_REUSE HAL_BOOL cal_reuse_enable = AH_PRIVATE(ah)->ah_config.ath_hal_cal_reuse & ATH_CAL_REUSE_ENABLE; HAL_BOOL clc_success = AH_FALSE; int32_t ch_idx, j, cl_tab_reg; u_int32_t BB_cl_tab_entry = MAX_BB_CL_TABLE_ENTRY; u_int32_t BB_cl_tab_b[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, AR_PHY_CL_TAB_1, AR_PHY_CL_TAB_2 }; #endif if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { /* Hornet: 1 x 1 */ ahp->ah_rx_cal_chainmask = 0x1; ahp->ah_tx_cal_chainmask = 0x1; } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah) || AR_SREV_HONEYBEE(ah)) { /* Wasp/Jupiter: 2 x 2 */ ahp->ah_rx_cal_chainmask = 0x3; ahp->ah_tx_cal_chainmask = 0x3; } else { /* * Osprey needs to be configured for the correct chain mode * before running AGC/TxIQ cals. */ if (ahp->ah_enterprise_mode & AR_ENT_OTP_CHAIN2_DISABLE) { /* chain 2 disabled - 2 chain mode */ ahp->ah_rx_cal_chainmask = 0x3; ahp->ah_tx_cal_chainmask = 0x3; } else { ahp->ah_rx_cal_chainmask = 0x7; ahp->ah_tx_cal_chainmask = 0x7; } } ar9300_init_chain_masks(ah, ahp->ah_rx_cal_chainmask, ahp->ah_tx_cal_chainmask); if (ahp->tx_cl_cal_enable) { #if ATH_SUPPORT_CAL_REUSE /* disable Carrie Leak or set do_agc_cal accordingly */ if (cal_reuse_enable && ichan->one_time_txclcal_done) { OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); } else #endif /* ATH_SUPPORT_CAL_REUSE */ { OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); do_agc_cal = AH_TRUE; } } /* Do Tx IQ Calibration here for osprey hornet and wasp */ /* XXX: For initial wasp bringup - check and enable this */ /* EV 74233: Tx IQ fails to complete for half/quarter rates */ if (!(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan))) { if (ahp->tx_iq_cal_enable) { /* this should be eventually moved to INI file */ OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1(ah), AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, DELPT); /* * For poseidon and later chips, * Tx IQ cal HW run will be a part of AGC calibration */ if (ahp->tx_iq_cal_during_agc_cal) { /* * txiqcal_success_flag always set to 1 to run * ar9300_tx_iq_cal_post_proc * if following AGC cal passes */ #if ATH_SUPPORT_CAL_REUSE if (!cal_reuse_enable || !ichan->one_time_txiqcal_done) { txiqcal_success_flag = AH_TRUE; OS_REG_WRITE(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), OS_REG_READ(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah)) | AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); } else { OS_REG_WRITE(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), OS_REG_READ(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah)) & (~AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)); } #else if (OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)){ if (apply_last_iqcorr == AH_TRUE) { OS_REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); txiqcal_success_flag = AH_FALSE; } else { txiqcal_success_flag = AH_TRUE; } }else{ txiqcal_success_flag = AH_FALSE; } #endif if (txiqcal_success_flag) { do_agc_cal = AH_TRUE; } } else #if ATH_SUPPORT_CAL_REUSE if (!cal_reuse_enable || !ichan->one_time_txiqcal_done) #endif { do_sep_iq_cal = AH_TRUE; do_agc_cal = AH_TRUE; } } } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && IS_CHAN_2GHZ(ichan) && (ahp->ah_mci_bt_state == MCI_BT_AWAKE) && do_agc_cal && !(ah->ah_config.ath_hal_mci_config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) { u_int32_t payload[4] = {0, 0, 0, 0}; /* Send CAL_REQ only when BT is AWAKE. */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_REQ 0x%X\n", __func__, ahp->ah_mci_wlan_cal_seq); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_REQ); payload[MCI_GPM_WLAN_CAL_W_SEQUENCE] = ahp->ah_mci_wlan_cal_seq++; ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); /* Wait BT_CAL_GRANT for 50ms */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Wait for BT_CAL_GRANT\n", __func__); if (ar9300_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Got BT_CAL_GRANT.\n", __func__); } else { is_cal_reusable = AH_FALSE; HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: BT is not responding.\n", __func__); } } #endif /* ATH_SUPPORT_MCI */ if (do_sep_iq_cal) { /* enable Tx IQ Calibration HW for osprey/hornet/wasp */ txiqcal_success_flag = ar9300_tx_iq_cal_hw_run(ah); OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); OS_DELAY(5); OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); } #if 0 if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { ar9300_tx_carrier_leak_war(ah); } #endif /* * Calibrate the AGC * * Tx IQ cal is a part of AGC cal for Jupiter/Poseidon, etc. * please enable the bit of txiqcal_control_0[31] in INI file * for Jupiter/Poseidon/etc. */ if(!AR_SREV_SCORPION(ah)) { if (do_agc_cal || !skip_if_none) { OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ cal_done = ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0); if (!cal_done) { HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) CAL NOT DONE!!! - %d\n", ichan->channel); } } else { cal_done = AH_TRUE; } /* * Tx IQ cal post-processing in SW * This part of code should be common to all chips, * no chip specific code for Jupiter/Posdeion except for register names. */ if (txiqcal_success_flag) { ar9300_tx_iq_cal_post_proc(ah,ichan, 1, 1,is_cal_reusable, AH_FALSE); } } else { if (!txiqcal_success_flag) { OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: offset calibration failed to complete in 1ms; " "noisy environment?\n", __func__); return AH_FALSE; } if (apply_last_iqcorr == AH_TRUE) { ar9300_tx_iq_cal_post_proc(ah, ichan, 0, 0, is_cal_reusable, AH_TRUE); } } else { for (iqcal_idx=0;iqcal_idxah_caps.halMciSupport && IS_CHAN_2GHZ(ichan) && (ahp->ah_mci_bt_state == MCI_BT_AWAKE) && do_agc_cal && !(ah->ah_config.ath_hal_mci_config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) { u_int32_t payload[4] = {0, 0, 0, 0}; HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_DONE 0x%X\n", __func__, ahp->ah_mci_wlan_cal_done); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); payload[MCI_GPM_WLAN_CAL_W_SEQUENCE] = ahp->ah_mci_wlan_cal_done++; ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); } #endif /* ATH_SUPPORT_MCI */ if (!cal_done && !AR_SREV_SCORPION(ah) ) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: offset calibration failed to complete in 1ms; " "noisy environment?\n", __func__); return AH_FALSE; } #if 0 /* Beacon stuck fix, refer to EV 120056 */ if(IS_CHAN_2GHZ(chan) && AR_SREV_SCORPION(ah)) OS_REG_WRITE(ah, AR_PHY_TIMING5, OS_REG_READ(ah,AR_PHY_TIMING5) & ~AR_PHY_TIMING5_CYCPWR_THR1_ENABLE); #endif #if 0 /* Do PA Calibration */ if (AR_SREV_KITE(ah) && AR_SREV_KITE_11_OR_LATER(ah)) { ar9285_pa_cal(ah); } #endif #if ATH_SUPPORT_CAL_REUSE if (ichan->one_time_txiqcal_done) { ar9300_tx_iq_cal_apply(ah, ichan); HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TXIQCAL applied - %d\n", ichan->channel); } #endif /* ATH_SUPPORT_CAL_REUSE */ #if ATH_SUPPORT_CAL_REUSE if (cal_reuse_enable && ahp->tx_cl_cal_enable) { clc_success = (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_CLC_SUCCESS) ? 1 : 0; if (ichan->one_time_txclcal_done) { /* reapply CL cal results */ for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { continue; } cl_tab_reg = BB_cl_tab_b[ch_idx]; for (j = 0; j < BB_cl_tab_entry; j++) { OS_REG_WRITE(ah, cl_tab_reg, ichan->tx_clcal[ch_idx][j]); cl_tab_reg += 4;; } } HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TX CL CAL applied - %d\n", ichan->channel); } else if (is_cal_reusable && clc_success) { /* save CL cal results */ for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { continue; } cl_tab_reg = BB_cl_tab_b[ch_idx]; for (j = 0; j < BB_cl_tab_entry; j++) { ichan->tx_clcal[ch_idx][j] = OS_REG_READ(ah, cl_tab_reg); cl_tab_reg += 4; } } ichan->one_time_txclcal_done = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TX CL CAL saved - %d\n", ichan->channel); } } #endif /* ATH_SUPPORT_CAL_REUSE */ /* Revert chainmasks to their original values before NF cal */ ar9300_init_chain_masks(ah, ahp->ah_rx_chainmask, ahp->ah_tx_chainmask); #if !FIX_NOISE_FLOOR /* * Do NF calibration after DC offset and other CALs. * Per system engineers, noise floor value can sometimes be 20 dB * higher than normal value if DC offset and noise floor cal are * triggered at the same time. */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); #endif /* Initialize list pointers */ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; /* * Enable IQ, ADC Gain, ADC DC Offset Cals */ /* Setup all non-periodic, init time only calibrations */ /* XXX: Init DC Offset not working yet */ #ifdef not_yet if (AH_TRUE == ar9300_is_cal_supp(ah, chan, ADC_DC_INIT_CAL)) { INIT_CAL(&ahp->ah_adc_dc_cal_init_data); INSERT_CAL(ahp, &ahp->ah_adc_dc_cal_init_data); } /* Initialize current pointer to first element in list */ ahp->ah_cal_list_curr = ahp->ah_cal_list; if (ahp->ah_cal_list_curr) { if (ar9300_run_init_cals(ah, 0) == AH_FALSE) { return AH_FALSE; } } #endif /* end - Init time calibrations */ /* Do not do RX cal in case of offchan, or cal data already exists on same channel*/ if (ahp->ah_skip_rx_iq_cal) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Skip RX IQ Cal\n"); return AH_TRUE; } /* If Cals are supported, add them to list via INIT/INSERT_CAL */ if (AH_TRUE == ar9300_is_cal_supp(ah, chan, IQ_MISMATCH_CAL)) { INIT_CAL(&ahp->ah_iq_cal_data); INSERT_CAL(ahp, &ahp->ah_iq_cal_data); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: enabling IQ Calibration.\n", __func__); } if (AH_TRUE == ar9300_is_cal_supp(ah, chan, TEMP_COMP_CAL)) { INIT_CAL(&ahp->ah_temp_comp_cal_data); INSERT_CAL(ahp, &ahp->ah_temp_comp_cal_data); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: enabling Temperature Compensation Calibration.\n", __func__); } /* Initialize current pointer to first element in list */ ahp->ah_cal_list_curr = ahp->ah_cal_list; /* Reset state within current cal */ if (ahp->ah_cal_list_curr) { ar9300_reset_calibration(ah, ahp->ah_cal_list_curr); } /* Mark all calibrations on this channel as being invalid */ ichan->calValid = 0; return AH_TRUE; } static inline HAL_BOOL ar9300_init_cal(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL skip_if_none, HAL_BOOL apply_last_iqcorr) { HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HAL_BOOL do_rtt_cal = AH_TRUE; HAL_BOOL enable_rtt = AH_FALSE; HALASSERT(ichan); return ar9300_init_cal_internal(ah, chan, ichan, enable_rtt, do_rtt_cal, skip_if_none, apply_last_iqcorr); } /* ar9300_reset_cal_valid * Entry point for upper layers to restart current cal. * Reset the calibration valid bit in channel. */ void ar9300_reset_cal_valid(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL *is_cal_done, u_int32_t cal_type) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HAL_CAL_LIST *curr_cal = ahp->ah_cal_list_curr; *is_cal_done = AH_TRUE; if (curr_cal == AH_NULL) { return; } if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return; } if (!(cal_type & IQ_MISMATCH_CAL)) { *is_cal_done = AH_FALSE; return; } /* Expected that this calibration has run before, post-reset. * Current state should be done */ if (curr_cal->cal_state != CAL_DONE) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Calibration state incorrect, %d\n", __func__, curr_cal->cal_state); return; } /* Verify Cal is supported on this channel */ if (ar9300_is_cal_supp(ah, chan, curr_cal->cal_data->cal_type) == AH_FALSE) { return; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Resetting Cal %d state for channel %u/0x%x\n", __func__, curr_cal->cal_data->cal_type, chan->ic_freq, chan->ic_flags); /* Disable cal validity in channel */ ichan->calValid &= ~curr_cal->cal_data->cal_type; curr_cal->cal_state = CAL_WAITING; /* Indicate to upper layers that we need polling */ *is_cal_done = AH_FALSE; } static inline void ar9300_set_dma(struct ath_hal *ah) { u_int32_t regval; struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; #if 0 /* * set AHB_MODE not to do cacheline prefetches */ regval = OS_REG_READ(ah, AR_AHB_MODE); OS_REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN); #endif /* * let mac dma reads be in 128 byte chunks */ regval = OS_REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK; OS_REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); /* * Restore TX Trigger Level to its pre-reset value. * The initial value depends on whether aggregation is enabled, and is * adjusted whenever underruns are detected. */ /* OS_REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, AH_PRIVATE(ah)->ah_tx_trig_level); */ /* * Osprey 1.0 bug (EV 61936). Don't change trigger level from .ini default. * Osprey 2.0 - hardware recommends using the default INI settings. */ #if 0 OS_REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, 0x3f); #endif /* * let mac dma writes be in 128 byte chunks */ regval = OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK; OS_REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B); /* * Setup receive FIFO threshold to hold off TX activities */ OS_REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); /* * reduce the number of usable entries in PCU TXBUF to avoid * wrap around bugs. (bug 20428) */ if (AR_SREV_WASP(ah) && (AH_PRIVATE((ah))->ah_macRev > AR_SREV_REVISION_WASP_12)) { /* Wasp 1.3 fix for EV#85395 requires usable entries * to be set to 0x500 */ OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, 0x500); } else { OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } /* * Enable HPQ for UAPSD */ if (pCap->halHwUapsdTrig == AH_TRUE) { /* Only enable this if HAL capabilities says it is OK */ if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { OS_REG_WRITE(ah, AR_HP_Q_CONTROL, AR_HPQ_ENABLE | AR_HPQ_UAPSD | AR_HPQ_UAPSD_TRIGGER_EN); } } else { /* use default value from ini file - which disable HPQ queue usage */ } /* * set the transmit status ring */ ar9300_reset_tx_status_ring(ah); /* * set rxbp threshold. Must be non-zero for RX_EOL to occur. * For Osprey 2.0+, keep the original thresholds * otherwise performance is lost due to excessive RX EOL interrupts. */ OS_REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_HP, 0x1); OS_REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_LP, 0x1); /* * set receive buffer size. */ if (ahp->rx_buf_size) { OS_REG_WRITE(ah, AR_DATABUF, ahp->rx_buf_size); } } static inline void ar9300_init_bb(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t synth_delay; /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synth_delay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_CCK(chan)) { synth_delay = (4 * synth_delay) / 22; } else { synth_delay /= 10; } /* Activate the PHY (includes baseband activate + synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * There is an issue if the AP starts the calibration before * the base band timeout completes. This could result in the * rx_clear false triggering. As a workaround we add delay an * extra BASE_ACTIVATE_DELAY usecs to ensure this condition * does not happen. */ OS_DELAY(synth_delay + BASE_ACTIVATE_DELAY); } static inline void ar9300_init_interrupt_masks(struct ath_hal *ah, HAL_OPMODE opmode) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t msi_cfg = 0; u_int32_t sync_en_def = AR9300_INTR_SYNC_DEFAULT; /* * Setup interrupt handling. Note that ar9300_reset_tx_queue * manipulates the secondary IMR's as queues are enabled * and disabled. This is done with RMW ops to insure the * settings we make here are preserved. */ ahp->ah_mask_reg = AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_BCNMISC; if (ahp->ah_intr_mitigation_rx) { /* enable interrupt mitigation for rx */ ahp->ah_mask_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR | AR_IMR_RXOK_HP; msi_cfg |= AR_INTCFG_MSI_RXINTM | AR_INTCFG_MSI_RXMINTR; } else { ahp->ah_mask_reg |= AR_IMR_RXOK_LP | AR_IMR_RXOK_HP; msi_cfg |= AR_INTCFG_MSI_RXOK; } if (ahp->ah_intr_mitigation_tx) { /* enable interrupt mitigation for tx */ ahp->ah_mask_reg |= AR_IMR_TXINTM | AR_IMR_TXMINTR; msi_cfg |= AR_INTCFG_MSI_TXINTM | AR_INTCFG_MSI_TXMINTR; } else { ahp->ah_mask_reg |= AR_IMR_TXOK; msi_cfg |= AR_INTCFG_MSI_TXOK; } if (opmode == HAL_M_HOSTAP) { ahp->ah_mask_reg |= AR_IMR_MIB; } OS_REG_WRITE(ah, AR_IMR, ahp->ah_mask_reg); OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT); ahp->ah_mask2Reg = OS_REG_READ(ah, AR_IMR_S2); if (ah->ah_config.ath_hal_enable_msi) { /* Cache MSI register value */ ahp->ah_msi_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_MSI)); ahp->ah_msi_reg |= AR_PCIE_MSI_HW_DBI_WR_EN; if (AR_SREV_POSEIDON(ah)) { ahp->ah_msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64; } else { ahp->ah_msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR; } /* Program MSI configuration */ OS_REG_WRITE(ah, AR_INTCFG, msi_cfg); } /* * debug - enable to see all synchronous interrupts status */ /* Clear any pending sync cause interrupts */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE), 0xFFFFFFFF); /* Allow host interface sync interrupt sources to set cause bit */ if (AR_SREV_POSEIDON(ah)) { sync_en_def = AR9300_INTR_SYNC_DEF_NO_HOST1_PERR; } else if (AR_SREV_WASP(ah)) { sync_en_def = AR9340_INTR_SYNC_DEFAULT; } OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), sync_en_def); /* _Disable_ host interface sync interrupt when cause bits set */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_MASK), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_MASK), 0); } static inline void ar9300_init_qos(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa); /* XXX magic */ OS_REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210); /* XXX magic */ /* Turn on NOACK Support for QoS packets */ OS_REG_WRITE(ah, AR_QOS_NO_ACK, SM(2, AR_QOS_NO_ACK_TWO_BIT) | SM(5, AR_QOS_NO_ACK_BIT_OFF) | SM(0, AR_QOS_NO_ACK_BYTE_OFF)); /* * initialize TXOP for all TIDs */ OS_REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); OS_REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } static inline void ar9300_init_user_settings(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); /* Restore user-specified settings */ HALDEBUG(ah, HAL_DEBUG_RESET, "--AP %s ahp->ah_misc_mode 0x%x\n", __func__, ahp->ah_misc_mode); if (ahp->ah_misc_mode != 0) { OS_REG_WRITE(ah, AR_PCU_MISC, OS_REG_READ(ah, AR_PCU_MISC) | ahp->ah_misc_mode); } if (ahp->ah_get_plcp_hdr) { OS_REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_SEL_EVM); } if (ahp->ah_slot_time != (u_int) -1) { ar9300_set_slot_time(ah, ahp->ah_slot_time); } if (ahp->ah_ack_timeout != (u_int) -1) { ar9300_set_ack_timeout(ah, ahp->ah_ack_timeout); } if (AH_PRIVATE(ah)->ah_diagreg != 0) { OS_REG_SET_BIT(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); } if (ahp->ah_beacon_rssi_threshold != 0) { ar9300_set_hw_beacon_rssi_threshold(ah, ahp->ah_beacon_rssi_threshold); } //#ifdef ATH_SUPPORT_DFS if (ahp->ah_cac_quiet_enabled) { ar9300_cac_tx_quiet(ah, 1); } //#endif /* ATH_SUPPORT_DFS */ } int ar9300_get_spur_info(struct ath_hal * ah, int *enable, int len, u_int16_t *freq) { // struct ath_hal_private *ap = AH_PRIVATE(ah); int i, j; for (i = 0; i < len; i++) { freq[i] = 0; } *enable = ah->ah_config.ath_hal_spur_mode; for (i = 0, j = 0; i < AR_EEPROM_MODAL_SPURS; i++) { if (AH9300(ah)->ath_hal_spur_chans[i][0] != AR_NO_SPUR) { freq[j++] = AH9300(ah)->ath_hal_spur_chans[i][0]; HALDEBUG(ah, HAL_DEBUG_ANI, "1. get spur %d\n", AH9300(ah)->ath_hal_spur_chans[i][0]); } if (AH9300(ah)->ath_hal_spur_chans[i][1] != AR_NO_SPUR) { freq[j++] = AH9300(ah)->ath_hal_spur_chans[i][1]; HALDEBUG(ah, HAL_DEBUG_ANI, "2. get spur %d\n", AH9300(ah)->ath_hal_spur_chans[i][1]); } } return 0; } #define ATH_HAL_2GHZ_FREQ_MIN 20000 #define ATH_HAL_2GHZ_FREQ_MAX 29999 #define ATH_HAL_5GHZ_FREQ_MIN 50000 #define ATH_HAL_5GHZ_FREQ_MAX 59999 #if 0 int ar9300_set_spur_info(struct ath_hal * ah, int enable, int len, u_int16_t *freq) { struct ath_hal_private *ap = AH_PRIVATE(ah); int i, j, k; ap->ah_config.ath_hal_spur_mode = enable; if (ap->ah_config.ath_hal_spur_mode == SPUR_ENABLE_IOCTL) { for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { AH9300(ah)->ath_hal_spur_chans[i][0] = AR_NO_SPUR; AH9300(ah)->ath_hal_spur_chans[i][1] = AR_NO_SPUR; } for (i = 0, j = 0, k = 0; i < len; i++) { if (freq[i] > ATH_HAL_2GHZ_FREQ_MIN && freq[i] < ATH_HAL_2GHZ_FREQ_MAX) { /* 2GHz Spur */ if (j < AR_EEPROM_MODAL_SPURS) { AH9300(ah)->ath_hal_spur_chans[j++][1] = freq[i]; HALDEBUG(ah, HAL_DEBUG_ANI, "1 set spur %d\n", freq[i]); } } else if (freq[i] > ATH_HAL_5GHZ_FREQ_MIN && freq[i] < ATH_HAL_5GHZ_FREQ_MAX) { /* 5Ghz Spur */ if (k < AR_EEPROM_MODAL_SPURS) { AH9300(ah)->ath_hal_spur_chans[k++][0] = freq[i]; HALDEBUG(ah, HAL_DEBUG_ANI, "2 set spur %d\n", freq[i]); } } } } return 0; } #endif #define ar9300_check_op_mode(_opmode) \ ((_opmode == HAL_M_STA) || (_opmode == HAL_M_IBSS) ||\ (_opmode == HAL_M_HOSTAP) || (_opmode == HAL_M_MONITOR)) #ifndef ATH_NF_PER_CHAN /* * To fixed first reset noise floor value not correct issue * For ART need it to fixed low rate sens too low issue */ static int First_NFCal(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, int is_scan, struct ieee80211_channel *chan) { HAL_NFCAL_HIST_FULL *nfh; int i, j, k; int16_t nfarray[HAL_NUM_NF_READINGS] = {0}; int is_2g = 0; int nf_hist_len; int stats = 0; int16_t nf_buf[HAL_NUM_NF_READINGS]; #define IS(_c, _f) (((_c)->channel_flags & _f) || 0) if ((!is_scan) && chan->ic_freq == AH_PRIVATE(ah)->ah_curchan->ic_freq) { nfh = &AH_PRIVATE(ah)->nf_cal_hist; } else { nfh = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; } ar9300_start_nf_cal(ah); for (j = 0; j < 10000; j++) { if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0){ break; } OS_DELAY(10); } if (j < 10000) { is_2g = IEEE80211_IS_CHAN_2GHZ(chan); ar9300_upload_noise_floor(ah, is_2g, nfarray); if (is_scan) { /* * This channel's NF cal info is just a HAL_NFCAL_HIST_SMALL struct * rather than a HAL_NFCAL_HIST_FULL struct. * As long as we only use the first history element of nf_cal_buffer * (nf_cal_buffer[0][0:HAL_NUM_NF_READINGS-1]), we can use * HAL_NFCAL_HIST_SMALL and HAL_NFCAL_HIST_FULL interchangeably. */ nfh = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; } else { nfh = &AH_PRIVATE(ah)->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; } for (i = 0; i < HAL_NUM_NF_READINGS; i ++) { for (k = 0; k < HAL_NF_CAL_HIST_LEN_FULL; k++) { nfh->nf_cal_buffer[k][i] = nfarray[i]; } nfh->base.priv_nf[i] = ar9300_limit_nf_range(ah, ar9300_get_nf_hist_mid(ah, nfh, i, nf_hist_len)); } //ar9300StoreNewNf(ah, ichan, is_scan); /* * See if the NF value from the old channel should be * retained when switching to a new channel. * TBD: this may need to be changed, as it wipes out the * purpose of saving NF values for each channel. */ for (i = 0; i < HAL_NUM_NF_READINGS; i++) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { if (nfh->nf_cal_buffer[0][i] < AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_2GHZ) { ichan->nf_cal_hist.nf_cal_buffer[0][i] = AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; } } else { if (AR_SREV_AR9580(ah)) { if (nfh->nf_cal_buffer[0][i] < AR_PHY_CCA_NOM_VAL_PEACOCK_5GHZ) { ichan->nf_cal_hist.nf_cal_buffer[0][i] = AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; } } else { if (nfh->nf_cal_buffer[0][i] < AR_PHY_CCA_NOM_VAL_OSPREY_5GHZ) { ichan->nf_cal_hist.nf_cal_buffer[0][i] = AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; } } } } /* * Copy the channel's NF buffer, which may have been modified * just above here, to the full NF history buffer. */ ar9300_reset_nf_hist_buff(ah, ichan); ar9300_get_nf_hist_base(ah, ichan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); /* XXX TODO: handle failure from load_nf */ stats = 0; } else { stats = 1; } #undef IS return stats; } #endif /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * b_channel_change is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar9300_reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask, HAL_HT_EXTPROTSPACING extprotspacing, HAL_BOOL b_channel_change, - HAL_STATUS *status, int is_scan) + HAL_STATUS *status, HAL_RESET_TYPE reset_type, int is_scan) { #define FAIL(_code) do { ecode = _code; goto bad; } while (0) u_int32_t save_led_state; struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ap = AH_PRIVATE(ah); HAL_CHANNEL_INTERNAL *ichan; //const struct ieee80211_channel *curchan = ap->ah_curchan; #if ATH_SUPPORT_MCI HAL_BOOL save_full_sleep = ahp->ah_chip_full_sleep; #endif u_int32_t save_def_antenna; u_int32_t mac_sta_id1; HAL_STATUS ecode; int i, rx_chainmask; int nf_hist_buff_reset = 0; int16_t nf_buf[HAL_NUM_NF_READINGS]; #ifdef ATH_FORCE_PPM u_int32_t save_force_val, tmp_reg; #endif u_int8_t clk_25mhz = AH9300(ah)->clk_25mhz; HAL_BOOL stopped, cal_ret; HAL_BOOL apply_last_iqcorr = AH_FALSE; if (OS_REG_READ(ah, AR_IER) == AR_IER_ENABLE) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "** Reset called with WLAN " "interrupt enabled %08x **\n", ar9300_get_interrupts(ah)); } /* * Set the status to "ok" by default to cover the cases * where we return false without going to "bad" */ HALASSERT(status); *status = HAL_OK; if ((ah->ah_config.ath_hal_sta_update_tx_pwr_enable)) { AH9300(ah)->green_tx_status = HAL_RSSI_TX_POWER_NONE; } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah))) { ar9300_mci_2g5g_changed(ah, IEEE80211_IS_CHAN_2GHZ(chan)); } #endif ahp->ah_ext_prot_spacing = extprotspacing; ahp->ah_tx_chainmask = txchainmask & ap->ah_caps.halTxChainMask; ahp->ah_rx_chainmask = rxchainmask & ap->ah_caps.halRxChainMask; ahp->ah_tx_cal_chainmask = ap->ah_caps.halTxChainMask; ahp->ah_rx_cal_chainmask = ap->ah_caps.halRxChainMask; /* * Keep the previous optinal txchainmask value */ HALASSERT(ar9300_check_op_mode(opmode)); OS_MARK(ah, AH_MARK_RESET, b_channel_change); /* * Map public channel to private. */ ichan = ar9300_check_chan(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } ichan->paprd_table_write_done = 0; /* Clear PAPRD table write flag */ #if 0 chan->paprd_table_write_done = 0; /* Clear PAPRD table write flag */ #endif if (ar9300_get_power_mode(ah) != HAL_PM_FULL_SLEEP) { /* Need to stop RX DMA before reset otherwise chip might hang */ stopped = ar9300_set_rx_abort(ah, AH_TRUE); /* abort and disable PCU */ ar9300_set_rx_filter(ah, 0); stopped &= ar9300_stop_dma_receive(ah, 0); /* stop and disable RX DMA */ if (!stopped) { /* * During the transition from full sleep to reset, * recv DMA regs are not available to be read */ HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s[%d]: ar9300_stop_dma_receive failed\n", __func__, __LINE__); b_channel_change = AH_FALSE; } } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s[%d]: Chip is already in full sleep\n", __func__, __LINE__); } #if ATH_SUPPORT_MCI if ((AH_PRIVATE(ah)->ah_caps.halMciSupport) && (ahp->ah_mci_bt_state == MCI_BT_CAL_START)) { u_int32_t payload[4] = {0, 0, 0, 0}; HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Stop rx for BT cal.\n", __func__); ahp->ah_mci_bt_state = MCI_BT_CAL; /* * MCIFIX: disable mci interrupt here. This is to avoid SW_MSG_DONE or * RX_MSG bits to trigger MCI_INT and lead to mci_intr reentry. */ ar9300_mci_disable_interrupt(ah); HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_GRANT\n", __func__); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); /* Wait BT calibration to be completed for 25ms */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: BT is calibrating.\n", __func__); if (ar9300_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, 0, 25000)) { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Got BT_CAL_DONE.\n", __func__); } else { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: ### BT cal takes too long. Force bt_state to be bt_awake.\n", __func__); } ahp->ah_mci_bt_state = MCI_BT_AWAKE; /* MCIFIX: enable mci interrupt here */ ar9300_mci_enable_interrupt(ah); return AH_TRUE; } #endif /* Bring out of sleep mode */ if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { *status = HAL_INV_PMODE; return AH_FALSE; } /* Check the Rx mitigation config again, it might have changed * during attach in ath_vap_attach. */ if (ah->ah_config.ath_hal_intr_mitigation_rx != 0) { ahp->ah_intr_mitigation_rx = AH_TRUE; } else { ahp->ah_intr_mitigation_rx = AH_FALSE; } /* * XXX TODO FreeBSD: * * This is painful because we don't have a non-const channel pointer * at this stage. * * Make sure this gets fixed! */ #if 0 /* Get the value from the previous NF cal and update history buffer */ if (curchan && (ahp->ah_chip_full_sleep != AH_TRUE)) { if(ahp->ah_chip_reset_done){ ahp->ah_chip_reset_done = 0; } else { /* * is_scan controls updating NF for home channel or off channel. * Home -> Off, update home channel * Off -> Home, update off channel * Home -> Home, uppdate home channel */ if (ap->ah_curchan->channel != chan->channel) ar9300_store_new_nf(ah, curchan, !is_scan); else ar9300_store_new_nf(ah, curchan, is_scan); } } #endif /* * Account for the effect of being in either the 2 GHz or 5 GHz band * on the nominal, max allowable, and min allowable noise floor values. */ AH9300(ah)->nfp = IS_CHAN_2GHZ(ichan) ? &ahp->nf_2GHz : &ahp->nf_5GHz; /* * XXX FreeBSD For now, don't apply the last IQ correction. * * This should be done when scorpion is enabled on FreeBSD; just be * sure to fix this channel match code so it uses net80211 flags * instead. */ #if 0 if (AR_SREV_SCORPION(ah) && curchan && (chan->channel == curchan->channel) && ((chan->channel_flags & (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER)) == (curchan->channel_flags & (CHANNEL_ALL | CHANNEL_HALF | CHANNEL_QUARTER)))) { apply_last_iqcorr = AH_TRUE; } #endif apply_last_iqcorr = AH_FALSE; #ifndef ATH_NF_PER_CHAN /* * If there's only one full-size home-channel NF history buffer * rather than a full-size NF history buffer per channel, decide * whether to (re)initialize the home-channel NF buffer. * If this is just a channel change for a scan, or if the channel * is not being changed, don't mess up the home channel NF history * buffer with NF values from this scanned channel. If we're * changing the home channel to a new channel, reset the home-channel * NF history buffer with the most accurate NF known for the new channel. */ if (!is_scan && (!ap->ah_curchan || ap->ah_curchan->ic_freq != chan->ic_freq)) // || // ap->ah_curchan->channel_flags != chan->channel_flags)) { nf_hist_buff_reset = 1; ar9300_reset_nf_hist_buff(ah, ichan); } #endif /* * In case of * - offchan scan, or * - same channel and RX IQ Cal already available * disable RX IQ Cal. */ if (is_scan) { ahp->ah_skip_rx_iq_cal = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Skip RX IQ Cal due to scanning\n"); } else { #if 0 /* XXX FreeBSD: always just do the RX IQ cal */ /* XXX I think it's just going to speed things up; I don't think it's to avoid chan bugs */ if (ahp->ah_rx_cal_complete && ahp->ah_rx_cal_chan == ichan->channel && ahp->ah_rx_cal_chan_flag == chan->channel_flags) { ahp->ah_skip_rx_iq_cal = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Skip RX IQ Cal due to same channel with completed RX IQ Cal\n"); } else #endif ahp->ah_skip_rx_iq_cal = AH_FALSE; } /* FreeBSD: clear the channel survey data */ ath_hal_survey_clear(ah); /* * Fast channel change (Change synthesizer based on channel freq * without resetting chip) * Don't do it when * - Flag is not set * - Chip is just coming out of full sleep * - Channel to be set is same as current channel * - Channel flags are different, like when moving from 2GHz to 5GHz * channels * - Merlin: Switching in/out of fast clock enabled channels * (not currently coded, since fast clock is enabled * across the 5GHz band * and we already do a full reset when switching in/out * of 5GHz channels) */ #if 0 if (b_channel_change && (ahp->ah_chip_full_sleep != AH_TRUE) && (AH_PRIVATE(ah)->ah_curchan != AH_NULL) && ((chan->channel != AH_PRIVATE(ah)->ah_curchan->channel) && (((CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) & chan->channel_flags) == ((CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) & AH_PRIVATE(ah)->ah_curchan->channel_flags)))) { if (ar9300_channel_change(ah, chan, ichan, macmode)) { chan->channel_flags = ichan->channel_flags; chan->priv_flags = ichan->priv_flags; AH_PRIVATE(ah)->ah_curchan->ah_channel_time = 0; AH_PRIVATE(ah)->ah_curchan->ah_tsf_last = ar9300_get_tsf64(ah); /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical value. */ ar9300_get_nf_hist_base(ah, AH_PRIVATE(ah)->ah_curchan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); /* start NF calibration, without updating BB NF register*/ ar9300_start_nf_cal(ah); /* * If channel_change completed and DMA was stopped * successfully - skip the rest of reset */ if (AH9300(ah)->ah_dma_stuck != AH_TRUE) { ar9300_disable_pll_lock_detect(ah); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && ahp->ah_mci_ready) { ar9300_mci_2g5g_switch(ah, AH_TRUE); } #endif return HAL_OK; } } } #endif /* #if 0 */ #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { ar9300_mci_disable_interrupt(ah); if (ahp->ah_mci_ready && !save_full_sleep) { ar9300_mci_mute_bt(ah); OS_DELAY(20); OS_REG_WRITE(ah, AR_BTCOEX_CTRL, 0); } ahp->ah_mci_bt_state = MCI_BT_SLEEP; ahp->ah_mci_ready = AH_FALSE; } #endif AH9300(ah)->ah_dma_stuck = AH_FALSE; #ifdef ATH_FORCE_PPM /* Preserve force ppm state */ save_force_val = OS_REG_READ(ah, AR_PHY_TIMING2) & (AR_PHY_TIMING2_USE_FORCE | AR_PHY_TIMING2_FORCE_VAL); #endif /* * Preserve the antenna on a channel change */ save_def_antenna = OS_REG_READ(ah, AR_DEF_ANTENNA); if (0 == ahp->ah_smartantenna_enable ) { if (save_def_antenna == 0) { save_def_antenna = 1; } } /* Save hardware flag before chip reset clears the register */ mac_sta_id1 = OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; /* Save led state from pci config register */ save_led_state = OS_REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); /* Mark PHY inactive prior to reset, to be undone in ar9300_init_bb () */ ar9300_mark_phy_inactive(ah); - if (!ar9300_chip_reset(ah, chan)) { + if (!ar9300_chip_reset(ah, chan, reset_type)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Disable JTAG */ OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_JTAG_DISABLE); /* * Note that ar9300_init_chain_masks() is called from within * ar9300_process_ini() to ensure the swap bit is set before * the pdadc table is written. */ ecode = ar9300_process_ini(ah, chan, ichan, macmode); if (ecode != HAL_OK) { goto bad; } /* * Configuring WMAC PLL values for 25/40 MHz */ if(AR_SREV_WASP(ah) || AR_SREV_HONEYBEE(ah) || AR_SREV_SCORPION(ah) ) { if(clk_25mhz) { OS_REG_WRITE(ah, AR_RTC_DERIVED_RTC_CLK, (0x17c << 1)); // 32KHz sleep clk } else { OS_REG_WRITE(ah, AR_RTC_DERIVED_RTC_CLK, (0x261 << 1)); // 32KHz sleep clk } OS_DELAY(100); } ahp->ah_immunity_on = AH_FALSE; if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { ahp->tx_iq_cal_enable = OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL) ? 1 : 0; } ahp->tx_cl_cal_enable = (OS_REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) ? 1 : 0; /* For devices with full HW RIFS Rx support (Sowl/Howl/Merlin, etc), * restore register settings from prior to reset. */ if ((AH_PRIVATE(ah)->ah_curchan != AH_NULL) && (ar9300_get_capability(ah, HAL_CAP_LDPCWAR, 0, AH_NULL) == HAL_OK)) { /* Re-program RIFS Rx policy after reset */ ar9300_set_rifs_delay(ah, ahp->ah_rifs_enabled); } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { ar9300_mci_reset(ah, AH_FALSE, IS_CHAN_2GHZ(ichan), save_full_sleep); } #endif /* Initialize Management Frame Protection */ ar9300_init_mfp(ah); ahp->ah_immunity_vals[0] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M1_THRESH_LOW); ahp->ah_immunity_vals[1] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2_THRESH_LOW); ahp->ah_immunity_vals[2] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH); ahp->ah_immunity_vals[3] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH); ahp->ah_immunity_vals[4] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR); ahp->ah_immunity_vals[5] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW); /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan) || IEEE80211_IS_CHAN_HT(chan)) { ar9300_set_delta_slope(ah, chan); } ar9300_spur_mitigate(ah, chan); if (!ar9300_eeprom_set_board_values(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } #ifdef ATH_HAL_WAR_REG16284_APH128 /* temp work around, will be removed. */ if (AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, 0x16284, 0x1553e000); } #endif OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | mac_sta_id1 | AR_STA_ID1_RTS_USE_DEF | (ah->ah_config.ath_hal_6mb_ack ? AR_STA_ID1_ACKCTS_6MB : 0) | ahp->ah_sta_id1_defaults ); ar9300_set_operating_mode(ah, opmode); /* Set Venice BSSID mask according to current state */ OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssid_mask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssid_mask + 4)); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, save_def_antenna); #ifdef ATH_FORCE_PPM /* Restore force ppm state */ tmp_reg = OS_REG_READ(ah, AR_PHY_TIMING2) & ~(AR_PHY_TIMING2_USE_FORCE | AR_PHY_TIMING2_FORCE_VAL); OS_REG_WRITE(ah, AR_PHY_TIMING2, tmp_reg | save_force_val); #endif /* then our BSSID and assocID */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | ((ahp->ah_assoc_id & 0x3fff) << AR_BSS_ID1_AID_S)); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR); /* HW beacon processing */ /* * XXX what happens if I just leave filter_interval=0? * it stays disabled? */ OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_BCN_WEIGHT, INIT_RSSI_BEACON_WEIGHT); OS_REG_SET_BIT(ah, AR_HWBCNPROC1, AR_HWBCNPROC1_CRC_ENABLE | AR_HWBCNPROC1_EXCLUDE_TIM_ELM); if (ah->ah_config.ath_hal_beacon_filter_interval) { OS_REG_RMW_FIELD(ah, AR_HWBCNPROC2, AR_HWBCNPROC2_FILTER_INTERVAL, ah->ah_config.ath_hal_beacon_filter_interval); OS_REG_SET_BIT(ah, AR_HWBCNPROC2, AR_HWBCNPROC2_FILTER_INTERVAL_ENABLE); } /* * Set Channel now modifies bank 6 parameters for FOWL workaround * to force rf_pwd_icsyndiv bias current as function of synth * frequency.Thus must be called after ar9300_process_ini() to ensure * analog register cache is valid. */ if (!ahp->ah_rf_hal.set_channel(ah, chan)) { FAIL(HAL_EIO); } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Set 1:1 QCU to DCU mapping for all queues */ for (i = 0; i < AR_NUM_DCU; i++) { OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); } ahp->ah_intr_txqs = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++) { ar9300_reset_tx_queue(ah, i); } ar9300_init_interrupt_masks(ah, opmode); /* Reset ier reference count to disabled */ // OS_ATOMIC_SET(&ahp->ah_ier_ref_count, 1); if (ath_hal_isrfkillenabled(ah)) { ar9300_enable_rf_kill(ah); } /* must be called AFTER ini is processed */ ar9300_ani_init_defaults(ah, macmode); ar9300_init_qos(ah); ar9300_init_user_settings(ah); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ OS_MARK(ah, AH_MARK_RESET_DONE, 0); /* * disable seq number generation in hw */ OS_REG_WRITE(ah, AR_STA_ID1, OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); ar9300_set_dma(ah); /* * program OBS bus to see MAC interrupts */ #if ATH_SUPPORT_MCI if (!AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_OBS), 8); } #else OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_OBS), 8); #endif /* enabling AR_GTTM_IGNORE_IDLE in GTTM register so that GTT timer will not increment if the channel idle indicates the air is busy or NAV is still counting down */ OS_REG_WRITE(ah, AR_GTTM, AR_GTTM_IGNORE_IDLE); /* * GTT debug mode setting */ /* OS_REG_WRITE(ah, 0x64, 0x00320000); OS_REG_WRITE(ah, 0x68, 7); OS_REG_WRITE(ah, 0x4080, 0xC); */ /* * Disable general interrupt mitigation by setting MIRT = 0x0 * Rx and tx interrupt mitigation are conditionally enabled below. */ OS_REG_WRITE(ah, AR_MIRT, 0); if (ahp->ah_intr_mitigation_rx) { /* * Enable Interrupt Mitigation for Rx. * If no build-specific limits for the rx interrupt mitigation * timer have been specified, use conservative defaults. */ #ifndef AH_RIMT_VAL_LAST #define AH_RIMT_LAST_MICROSEC 500 #endif #ifndef AH_RIMT_VAL_FIRST #define AH_RIMT_FIRST_MICROSEC 2000 #endif #ifndef HOST_OFFLOAD OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, AH_RIMT_LAST_MICROSEC); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, AH_RIMT_FIRST_MICROSEC); #else /* lower mitigation level to reduce latency for offload arch. */ OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, (AH_RIMT_LAST_MICROSEC >> 2)); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, (AH_RIMT_FIRST_MICROSEC >> 2)); #endif } if (ahp->ah_intr_mitigation_tx) { /* * Enable Interrupt Mitigation for Tx. * If no build-specific limits for the tx interrupt mitigation * timer have been specified, use the values preferred for * the carrier group's products. */ #ifndef AH_TIMT_LAST #define AH_TIMT_LAST_MICROSEC 300 #endif #ifndef AH_TIMT_FIRST #define AH_TIMT_FIRST_MICROSEC 750 #endif OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, AH_TIMT_LAST_MICROSEC); OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, AH_TIMT_FIRST_MICROSEC); } rx_chainmask = ahp->ah_rx_chainmask; OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); ar9300_init_bb(ah, chan); /* BB Step 7: Calibration */ /* * Only kick off calibration not on offchan. * If coming back from offchan, restore prevous Cal results * since chip reset will clear existings. */ if (!ahp->ah_skip_rx_iq_cal) { int i; /* clear existing RX cal data */ for (i=0; iah_rx_cal_corr[i] = 0; ahp->ah_rx_cal_complete = AH_FALSE; // ahp->ah_rx_cal_chan = chan->channel; // ahp->ah_rx_cal_chan_flag = ichan->channel_flags; ahp->ah_rx_cal_chan = 0; ahp->ah_rx_cal_chan_flag = 0; /* XXX FreeBSD */ } ar9300_invalidate_saved_cals(ah, ichan); cal_ret = ar9300_init_cal(ah, chan, AH_FALSE, apply_last_iqcorr); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && ahp->ah_mci_ready) { if (IS_CHAN_2GHZ(ichan) && (ahp->ah_mci_bt_state == MCI_BT_SLEEP)) { if (ar9300_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) || ar9300_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) { /* * BT is sleeping. Check if BT wakes up duing WLAN * calibration. If BT wakes up during WLAN calibration, need * to go through all message exchanges again and recal. */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) ### %s: BT wakes up during WLAN calibration.\n", __func__); OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE); HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) send REMOTE_RESET\n"); ar9300_mci_remote_reset(ah, AH_TRUE); ar9300_mci_send_sys_waking(ah, AH_TRUE); OS_DELAY(1); if (IS_CHAN_2GHZ(ichan)) { ar9300_mci_send_lna_transfer(ah, AH_TRUE); } ahp->ah_mci_bt_state = MCI_BT_AWAKE; /* Redo calibration */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Re-calibrate.\n", __func__); ar9300_invalidate_saved_cals(ah, ichan); cal_ret = ar9300_init_cal(ah, chan, AH_FALSE, apply_last_iqcorr); } } ar9300_mci_enable_interrupt(ah); } #endif if (!cal_ret) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Init Cal Failed\n", __func__); FAIL(HAL_ESELFTEST); } ar9300_init_txbf(ah); #if 0 /* * WAR for owl 1.0 - restore chain mask for 2-chain cfgs after cal */ rx_chainmask = ahp->ah_rx_chainmask; if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); } #endif /* Restore previous led state */ OS_REG_WRITE(ah, AR_CFG_LED, save_led_state | AR_CFG_SCLK_32KHZ); #if ATH_BT_COEX if (ahp->ah_bt_coex_config_type != HAL_BT_COEX_CFG_NONE) { ar9300_init_bt_coex(ah); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && ahp->ah_mci_ready) { /* Check BT state again to make sure it's not changed. */ ar9300_mci_sync_bt_state(ah); ar9300_mci_2g5g_switch(ah, AH_TRUE); if ((ahp->ah_mci_bt_state == MCI_BT_AWAKE) && (ahp->ah_mci_query_bt == AH_TRUE)) { ahp->ah_mci_need_flush_btinfo = AH_TRUE; } } #endif } #endif /* Start TSF2 for generic timer 8-15. */ ar9300_start_tsf2(ah); /* MIMO Power save setting */ if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) == HAL_OK) { ar9300_set_sm_power_mode(ah, ahp->ah_sm_power_mode); } /* * For big endian systems turn on swapping for descriptors */ #if AH_BYTE_ORDER == AH_BIG_ENDIAN if (AR_SREV_HORNET(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah) || AR_SREV_HONEYBEE(ah)) { OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB | AR_CFG_SWRB, 0); } else { ar9300_init_cfg_reg(ah); } #endif if ( AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah) || AR_SREV_HONEYBEE(ah) ) { OS_REG_RMW(ah, AR_CFG_LED, AR_CFG_LED_ASSOC_CTL, AR_CFG_LED_ASSOC_CTL); } #if !(defined(ART_BUILD)) && defined(ATH_SUPPORT_LED) #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) #define ATH_GPIO_OUT_FUNCTION3 0xB8040038 #define ATH_GPIO_OE 0xB8040000 if ( AR_SREV_WASP(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff << 8))) | (0x33 << 8) ); REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) & (~(0x1 << 13) ))); } else { /* Disable 2G WLAN LED. During ath_open, reset function is called even before channel is set. So 2GHz is taken as default and it also blinks. Hence to avoid both from blinking, disable 2G led while in 5G mode */ REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) | (1 << 13) )); REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x33) ); REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) ))); } } else if (AR_SREV_SCORPION(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff << 8))) | (0x2F << 8) ); REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 13) )) | (0x1 << 12))); } else if (IS_CHAN_5GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x2F) ); REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) )) | (0x1 << 13))); } } else if (AR_SREV_HONEYBEE(ah)) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x32) ); REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) )))); } #undef REG_READ #undef REG_WRITE #endif /* XXX FreeBSD What's this? -adrian */ #if 0 chan->channel_flags = ichan->channel_flags; chan->priv_flags = ichan->priv_flags; #endif #if FIX_NOISE_FLOOR /* XXX FreeBSD is ichan appropariate? It was curchan.. */ ar9300_get_nf_hist_base(ah, ichan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); /* XXX TODO: handle NF load failure */ if (nf_hist_buff_reset == 1) { nf_hist_buff_reset = 0; #ifndef ATH_NF_PER_CHAN if (First_NFCal(ah, ichan, is_scan, chan)){ if (ahp->ah_skip_rx_iq_cal && !is_scan) { /* restore RX Cal result if existing */ ar9300_rx_iq_cal_restore(ah); ahp->ah_skip_rx_iq_cal = AH_FALSE; } } #endif /* ATH_NF_PER_CHAN */ } else{ ar9300_start_nf_cal(ah); } #endif #ifdef AH_SUPPORT_AR9300 /* BB Panic Watchdog */ if (ar9300_get_capability(ah, HAL_CAP_BB_PANIC_WATCHDOG, 0, AH_NULL) == HAL_OK) { ar9300_config_bb_panic_watchdog(ah); } #endif /* While receiving unsupported rate frame receive state machine * gets into a state 0xb and if phy_restart happens when rx * state machine is in 0xb state, BB would go hang, if we * see 0xb state after first bb panic, make sure that we * disable the phy_restart. * * There may be multiple panics, make sure that we always do * this if we see this panic at least once. This is required * because reset seems to be writing from INI file. */ if ((ar9300_get_capability(ah, HAL_CAP_PHYRESTART_CLR_WAR, 0, AH_NULL) == HAL_OK) && (((MS((AH9300(ah)->ah_bb_panic_last_status), AR_PHY_BB_WD_RX_OFDM_SM)) == 0xb) || AH9300(ah)->ah_phyrestart_disabled) ) { ar9300_disable_phy_restart(ah, 1); } ahp->ah_radar1 = MS(OS_REG_READ(ah, AR_PHY_RADAR_1), AR_PHY_RADAR_1_CF_BIN_THRESH); ahp->ah_dc_offset = MS(OS_REG_READ(ah, AR_PHY_TIMING2), AR_PHY_TIMING2_DC_OFFSET); ahp->ah_disable_cck = MS(OS_REG_READ(ah, AR_PHY_MODE), AR_PHY_MODE_DISABLE_CCK); if (AH9300(ah)->ah_enable_keysearch_always) { ar9300_enable_keysearch_always(ah, 1); } #if ATH_LOW_POWER_ENABLE #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val) #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) if (AR_SREV_OSPREY(ah)) { REG_WRITE(0xb4000080, REG_READ(0xb4000080) | 3); OS_REG_WRITE(ah, AR_RTC_RESET, 1); OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), AR_PCIE_PM_CTRL_ENA); OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_SPARE), 0xffffffff); } #undef REG_READ #undef REG_WRITE #endif /* ATH_LOW_POWER_ENABLE */ ar9300_disable_pll_lock_detect(ah); /* H/W Green TX */ ar9300_control_signals_for_green_tx_mode(ah); /* Smart Antenna, only for 5GHz on Scropion */ if (IEEE80211_IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan)) && AR_SREV_SCORPION(ah)) { ahp->ah_smartantenna_enable = 0; } ar9300_set_smart_antenna(ah, ahp->ah_smartantenna_enable); if (AR_SREV_APHRODITE(ah) && ahp->ah_lna_div_use_bt_ant_enable) OS_REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); if (ahp->ah_skip_rx_iq_cal && !is_scan) { /* restore RX Cal result if existing */ ar9300_rx_iq_cal_restore(ah); ahp->ah_skip_rx_iq_cal = AH_FALSE; } return AH_TRUE; bad: OS_MARK(ah, AH_MARK_RESET_DONE, ecode); *status = ecode; if (ahp->ah_skip_rx_iq_cal && !is_scan) { /* restore RX Cal result if existing */ ar9300_rx_iq_cal_restore(ah); ahp->ah_skip_rx_iq_cal = AH_FALSE; } return AH_FALSE; #undef FAIL } void ar9300_green_ap_ps_on_off( struct ath_hal *ah, u_int16_t on_off) { /* Set/reset the ps flag */ AH9300(ah)->green_ap_ps_on = !!on_off; } /* * This function returns 1, where it is possible to do * single-chain power save. */ u_int16_t ar9300_is_single_ant_power_save_possible(struct ath_hal *ah) { return AH_TRUE; } /* To avoid compilation warnings. Functions not used when EMULATION. */ /* * ar9300_find_mag_approx() */ static int32_t ar9300_find_mag_approx(struct ath_hal *ah, int32_t in_re, int32_t in_im) { int32_t abs_i = abs(in_re); int32_t abs_q = abs(in_im); int32_t max_abs, min_abs; if (abs_i > abs_q) { max_abs = abs_i; min_abs = abs_q; } else { max_abs = abs_q; min_abs = abs_i; } return (max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4)); } /* * ar9300_solve_iq_cal() * solve 4x4 linear equation used in loopback iq cal. */ static HAL_BOOL ar9300_solve_iq_cal( struct ath_hal *ah, int32_t sin_2phi_1, int32_t cos_2phi_1, int32_t sin_2phi_2, int32_t cos_2phi_2, int32_t mag_a0_d0, int32_t phs_a0_d0, int32_t mag_a1_d0, int32_t phs_a1_d0, int32_t solved_eq[]) { int32_t f1 = cos_2phi_1 - cos_2phi_2; int32_t f3 = sin_2phi_1 - sin_2phi_2; int32_t f2; int32_t mag_tx, phs_tx, mag_rx, phs_rx; const int32_t result_shift = 1 << 15; f2 = (((int64_t)f1 * (int64_t)f1) / result_shift) + (((int64_t)f3 * (int64_t)f3) / result_shift); if (0 == f2) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d).\n", __func__, __LINE__); return AH_FALSE; } /* magnitude mismatch, tx */ mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0); /* phase mismatch, tx */ phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0); mag_tx = (mag_tx / f2); phs_tx = (phs_tx / f2); /* magnitude mismatch, rx */ mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) / result_shift; /* phase mismatch, rx */ phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) / result_shift; solved_eq[0] = mag_tx; solved_eq[1] = phs_tx; solved_eq[2] = mag_rx; solved_eq[3] = phs_rx; return AH_TRUE; } /* * ar9300_calc_iq_corr() */ static HAL_BOOL ar9300_calc_iq_corr(struct ath_hal *ah, int32_t chain_idx, const int32_t iq_res[], int32_t iqc_coeff[]) { int32_t i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0; int32_t i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1; int32_t i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0; int32_t i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1; int32_t mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1; int32_t phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1; int32_t sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2; int32_t mag_tx, phs_tx, mag_rx, phs_rx; int32_t solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx; int32_t q_q_coff, q_i_coff; const int32_t res_scale = 1 << 15; const int32_t delpt_shift = 1 << 8; int32_t mag1, mag2; i2_m_q2_a0_d0 = iq_res[0] & 0xfff; i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff; iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8); if (i2_m_q2_a0_d0 > 0x800) { i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1); } if (iq_corr_a0_d0 > 0x800) { iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1); } i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff; i2_p_q2_a0_d1 = (iq_res[2] & 0xfff); iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff; if (i2_m_q2_a0_d1 > 0x800) { i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); } if (iq_corr_a0_d1 > 0x800) { iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); } i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8); i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff; iq_corr_a1_d0 = iq_res[4] & 0xfff; if (i2_m_q2_a1_d0 > 0x800) { i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1); } if (iq_corr_a1_d0 > 0x800) { iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1); } i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff; i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8); iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff; if (i2_m_q2_a1_d1 > 0x800) { i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1); } if (iq_corr_a1_d1 > 0x800) { iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1); } if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) || (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d):\na0_d0=%d\na0_d1=%d\na2_d0=%d\na1_d1=%d\n", __func__, __LINE__, i2_p_q2_a0_d0, i2_p_q2_a0_d1, i2_p_q2_a1_d0, i2_p_q2_a1_d1); return AH_FALSE; } if ((i2_p_q2_a0_d0 <= 1024) || (i2_p_q2_a0_d0 > 2047) || (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) || (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) || (i2_p_q2_a0_d0 <= iq_corr_a0_d0) || (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) || (i2_p_q2_a0_d1 <= iq_corr_a0_d1) || (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) || (i2_p_q2_a1_d0 <= iq_corr_a1_d0) || (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) || (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) { return AH_FALSE; } mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1; phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1; mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0; phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0; mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1; phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1; /* without analog phase shift */ sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT); /* without analog phase shift */ cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT); /* with analog phase shift */ sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT); /* with analog phase shift */ cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT); /* force sin^2 + cos^2 = 1; */ /* find magnitude by approximation */ mag1 = ar9300_find_mag_approx(ah, cos_2phi_1, sin_2phi_1); mag2 = ar9300_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); if ((mag1 == 0) || (mag2 == 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d): mag1=%d, mag2=%d\n", __func__, __LINE__, mag1, mag2); return AH_FALSE; } /* normalization sin and cos by mag */ sin_2phi_1 = (sin_2phi_1 * res_scale / mag1); cos_2phi_1 = (cos_2phi_1 * res_scale / mag1); sin_2phi_2 = (sin_2phi_2 * res_scale / mag2); cos_2phi_2 = (cos_2phi_2 * res_scale / mag2); /* calculate IQ mismatch */ if (AH_FALSE == ar9300_solve_iq_cal(ah, sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2, mag_a0_d0, phs_a0_d0, mag_a1_d0, phs_a1_d0, solved_eq)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Call to ar9300_solve_iq_cal failed.\n", __func__); return AH_FALSE; } mag_tx = solved_eq[0]; phs_tx = solved_eq[1]; mag_rx = solved_eq[2]; phs_rx = solved_eq[3]; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: chain %d: mag mismatch=%d phase mismatch=%d\n", __func__, chain_idx, mag_tx / res_scale, phs_tx / res_scale); if (res_scale == mag_tx) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d): mag_tx=%d, res_scale=%d\n", __func__, __LINE__, mag_tx, res_scale); return AH_FALSE; } /* calculate and quantize Tx IQ correction factor */ mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx); phs_corr_tx = -phs_tx; q_q_coff = (mag_corr_tx * 128 / res_scale); q_i_coff = (phs_corr_tx * 256 / res_scale); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: tx chain %d: mag corr=%d phase corr=%d\n", __func__, chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) { q_i_coff = -63; } if (q_i_coff > 63) { q_i_coff = 63; } if (q_q_coff < -63) { q_q_coff = -63; } if (q_q_coff > 63) { q_q_coff = 63; } iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: tx chain %d: iq corr coeff=%x\n", __func__, chain_idx, iqc_coeff[0]); if (-mag_rx == res_scale) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d): mag_rx=%d, res_scale=%d\n", __func__, __LINE__, mag_rx, res_scale); return AH_FALSE; } /* calculate and quantize Rx IQ correction factors */ mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx); phs_corr_rx = -phs_rx; q_q_coff = (mag_corr_rx * 128 / res_scale); q_i_coff = (phs_corr_rx * 256 / res_scale); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: rx chain %d: mag corr=%d phase corr=%d\n", __func__, chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) { q_i_coff = -63; } if (q_i_coff > 63) { q_i_coff = 63; } if (q_q_coff < -63) { q_q_coff = -63; } if (q_q_coff > 63) { q_q_coff = 63; } iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: rx chain %d: iq corr coeff=%x\n", __func__, chain_idx, iqc_coeff[1]); return AH_TRUE; } #define MAX_MAG_DELTA 11 //maximum magnitude mismatch delta across gains #define MAX_PHS_DELTA 10 //maximum phase mismatch delta across gains #define ABS(x) ((x) >= 0 ? (x) : (-(x))) u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, }; static void ar9300_tx_iq_cal_outlier_detection(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, u_int32_t num_chains, struct coeff_t *coeff, HAL_BOOL is_cal_reusable) { int nmeasurement, ch_idx, im; int32_t magnitude, phase; int32_t magnitude_max, phase_max; int32_t magnitude_min, phase_min; int32_t magnitude_max_idx, phase_max_idx; int32_t magnitude_min_idx, phase_min_idx; int32_t magnitude_avg, phase_avg; int32_t outlier_mag_idx = 0; int32_t outlier_phs_idx = 0; if (AR_SREV_POSEIDON(ah)) { HALASSERT(num_chains == 0x1); tx_corr_coeff[0][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[2][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[3][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[4][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[5][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[6][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; tx_corr_coeff[7][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; } for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { nmeasurement = OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_CALIBRATED_GAINS_0); if (nmeasurement > MAX_MEASUREMENT) { nmeasurement = MAX_MEASUREMENT; } if (!AR_SREV_SCORPION(ah)) { /* * reset max/min variable to min/max values so that * we always start with 1st calibrated gain value */ magnitude_max = -64; phase_max = -64; magnitude_min = 63; phase_min = 63; magnitude_avg = 0; phase_avg = 0; magnitude_max_idx = 0; magnitude_min_idx = 0; phase_max_idx = 0; phase_min_idx = 0; /* detect outlier only if nmeasurement > 1 */ if (nmeasurement > 1) { /* printf("----------- start outlier detection -----------\n"); */ /* * find max/min and phase/mag mismatch across all calibrated gains */ for (im = 0; im < nmeasurement; im++) { magnitude = coeff->mag_coeff[ch_idx][im][0]; phase = coeff->phs_coeff[ch_idx][im][0]; magnitude_avg = magnitude_avg + magnitude; phase_avg = phase_avg + phase; if (magnitude > magnitude_max) { magnitude_max = magnitude; magnitude_max_idx = im; } if (magnitude < magnitude_min) { magnitude_min = magnitude; magnitude_min_idx = im; } if (phase > phase_max) { phase_max = phase; phase_max_idx = im; } if (phase < phase_min) { phase_min = phase; phase_min_idx = im; } } /* find average (exclude max abs value) */ for (im = 0; im < nmeasurement; im++) { magnitude = coeff->mag_coeff[ch_idx][im][0]; phase = coeff->phs_coeff[ch_idx][im][0]; if ((ABS(magnitude) < ABS(magnitude_max)) || (ABS(magnitude) < ABS(magnitude_min))) { magnitude_avg = magnitude_avg + magnitude; } if ((ABS(phase) < ABS(phase_max)) || (ABS(phase) < ABS(phase_min))) { phase_avg = phase_avg + phase; } } magnitude_avg = magnitude_avg / (nmeasurement - 1); phase_avg = phase_avg / (nmeasurement - 1); /* detect magnitude outlier */ if (ABS(magnitude_max - magnitude_min) > MAX_MAG_DELTA) { if (ABS(magnitude_max - magnitude_avg) > ABS(magnitude_min - magnitude_avg)) { /* max is outlier, force to avg */ outlier_mag_idx = magnitude_max_idx; } else { /* min is outlier, force to avg */ outlier_mag_idx = magnitude_min_idx; } coeff->mag_coeff[ch_idx][outlier_mag_idx][0] = magnitude_avg; coeff->phs_coeff[ch_idx][outlier_mag_idx][0] = phase_avg; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "[ch%d][outlier mag gain%d]:: " "mag_avg = %d (/128), phase_avg = %d (/256)\n", ch_idx, outlier_mag_idx, magnitude_avg, phase_avg); } /* detect phase outlier */ if (ABS(phase_max - phase_min) > MAX_PHS_DELTA) { if (ABS(phase_max-phase_avg) > ABS(phase_min - phase_avg)) { /* max is outlier, force to avg */ outlier_phs_idx = phase_max_idx; } else{ /* min is outlier, force to avg */ outlier_phs_idx = phase_min_idx; } coeff->mag_coeff[ch_idx][outlier_phs_idx][0] = magnitude_avg; coeff->phs_coeff[ch_idx][outlier_phs_idx][0] = phase_avg; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "[ch%d][outlier phs gain%d]:: " "mag_avg = %d (/128), phase_avg = %d (/256)\n", ch_idx, outlier_phs_idx, magnitude_avg, phase_avg); } } } /*printf("------------ after outlier detection -------------\n");*/ for (im = 0; im < nmeasurement; im++) { magnitude = coeff->mag_coeff[ch_idx][im][0]; phase = coeff->phs_coeff[ch_idx][im][0]; #if 0 printf("[ch%d][gain%d]:: mag = %d (/128), phase = %d (/256)\n", ch_idx, im, magnitude, phase); #endif coeff->iqc_coeff[0] = (phase & 0x7f) | ((magnitude & 0x7f) << 7); if ((im % 2) == 0) { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, coeff->iqc_coeff[0]); } else { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, coeff->iqc_coeff[0]); } #if ATH_SUPPORT_CAL_REUSE ichan->tx_corr_coeff[im][ch_idx] = coeff->iqc_coeff[0]; #endif } #if ATH_SUPPORT_CAL_REUSE ichan->num_measures[ch_idx] = nmeasurement; #endif } OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); #if ATH_SUPPORT_CAL_REUSE if (is_cal_reusable) { ichan->one_time_txiqcal_done = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TXIQCAL saved - %d\n", ichan->channel); } #endif } #if ATH_SUPPORT_CAL_REUSE static void ar9300_tx_iq_cal_apply(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { struct ath_hal_9300 *ahp = AH9300(ah); int nmeasurement, ch_idx, im; u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, }; if (AR_SREV_POSEIDON(ah)) { HALASSERT(ahp->ah_tx_cal_chainmask == 0x1); tx_corr_coeff[0][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[2][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[3][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[4][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[5][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[6][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; tx_corr_coeff[7][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; } for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { continue; } nmeasurement = ichan->num_measures[ch_idx]; for (im = 0; im < nmeasurement; im++) { if ((im % 2) == 0) { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, ichan->tx_corr_coeff[im][ch_idx]); } else { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, ichan->tx_corr_coeff[im][ch_idx]); } } } OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); } #endif /* * ar9300_tx_iq_cal_hw_run is only needed for osprey/wasp/hornet * It is not needed for jupiter/poseidon. */ HAL_BOOL ar9300_tx_iq_cal_hw_run(struct ath_hal *ah) { int is_tx_gain_forced; is_tx_gain_forced = OS_REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE); if (is_tx_gain_forced) { /*printf("Tx gain can not be forced during tx I/Q cal!\n");*/ OS_REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE, 0); } /* enable tx IQ cal */ OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START(ah), AR_PHY_TX_IQCAL_START_DO_CAL, AR_PHY_TX_IQCAL_START_DO_CAL); if (!ath_hal_wait(ah, AR_PHY_TX_IQCAL_START(ah), AR_PHY_TX_IQCAL_START_DO_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Tx IQ Cal is never completed.\n", __func__); return AH_FALSE; } return AH_TRUE; } static void ar9300_tx_iq_cal_post_proc(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, int iqcal_idx, int max_iqcal,HAL_BOOL is_cal_reusable, HAL_BOOL apply_last_corr) { int nmeasurement=0, im, ix, iy, temp; struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t txiqcal_status[AR9300_MAX_CHAINS] = { AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_TX_IQCAL_STATUS_B1, AR_PHY_TX_IQCAL_STATUS_B2, }; const u_int32_t chan_info_tab[] = { AR_PHY_CHAN_INFO_TAB_0, AR_PHY_CHAN_INFO_TAB_1, AR_PHY_CHAN_INFO_TAB_2, }; int32_t iq_res[6]; int32_t ch_idx, j; u_int32_t num_chains = 0; static struct coeff_t coeff; txiqcal_status[0] = AR_PHY_TX_IQCAL_STATUS_B0(ah); for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if (ahp->ah_tx_chainmask & (1 << ch_idx)) { num_chains++; } } if (apply_last_corr) { if (coeff.last_cal == AH_TRUE) { int32_t magnitude, phase; int ch_idx, im; u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, }; for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { for (im = 0; im < coeff.last_nmeasurement; im++) { magnitude = coeff.mag_coeff[ch_idx][im][0]; phase = coeff.phs_coeff[ch_idx][im][0]; #if 0 printf("[ch%d][gain%d]:: mag = %d (/128), phase = %d (/256)\n", ch_idx, im, magnitude, phase); #endif coeff.iqc_coeff[0] = (phase & 0x7f) | ((magnitude & 0x7f) << 7); if ((im % 2) == 0) { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, coeff.iqc_coeff[0]); } else { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, coeff.iqc_coeff[0]); } } } OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); } return; } for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { nmeasurement = OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_CALIBRATED_GAINS_0); if (nmeasurement > MAX_MEASUREMENT) { nmeasurement = MAX_MEASUREMENT; } for (im = 0; im < nmeasurement; im++) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Doing Tx IQ Cal for chain %d.\n", __func__, ch_idx); if (OS_REG_READ(ah, txiqcal_status[ch_idx]) & AR_PHY_TX_IQCAL_STATUS_FAILED) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Tx IQ Cal failed for chain %d.\n", __func__, ch_idx); goto TX_IQ_CAL_FAILED_; } for (j = 0; j < 3; j++) { u_int32_t idx = 2 * j; /* 3 registers for each calibration result */ u_int32_t offset = 4 * (3 * im + j); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_TAB_S2_READ, 0); /* 32 bits */ iq_res[idx] = OS_REG_READ(ah, chan_info_tab[ch_idx] + offset); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_TAB_S2_READ, 1); /* 16 bits */ iq_res[idx + 1] = 0xffff & OS_REG_READ(ah, chan_info_tab[ch_idx] + offset); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: IQ RES[%d]=0x%x IQ_RES[%d]=0x%x\n", __func__, idx, iq_res[idx], idx + 1, iq_res[idx + 1]); } if (AH_FALSE == ar9300_calc_iq_corr( ah, ch_idx, iq_res, coeff.iqc_coeff)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Failed in calculation of IQ correction.\n", __func__); goto TX_IQ_CAL_FAILED_; } coeff.phs_coeff[ch_idx][im][iqcal_idx-1] = coeff.iqc_coeff[0] & 0x7f; coeff.mag_coeff[ch_idx][im][iqcal_idx-1] = (coeff.iqc_coeff[0] >> 7) & 0x7f; if (coeff.mag_coeff[ch_idx][im][iqcal_idx-1] > 63) { coeff.mag_coeff[ch_idx][im][iqcal_idx-1] -= 128; } if (coeff.phs_coeff[ch_idx][im][iqcal_idx-1] > 63) { coeff.phs_coeff[ch_idx][im][iqcal_idx-1] -= 128; } #if 0 ath_hal_printf(ah, "IQCAL::[ch%d][gain%d]:: mag = %d phase = %d \n", ch_idx, im, coeff.mag_coeff[ch_idx][im][iqcal_idx-1], coeff.phs_coeff[ch_idx][im][iqcal_idx-1]); #endif } } //last iteration; calculate mag and phs if (iqcal_idx == max_iqcal) { if (max_iqcal>1) { for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { for (im = 0; im < nmeasurement; im++) { //sort mag and phs for( ix=0;ixah_phyrestart_disabled = 1; } else { val |= AR_PHY_RESTART_ENA; AH9300(ah)->ah_phyrestart_disabled = 0; } OS_REG_WRITE(ah, AR_PHY_RESTART, val); val = OS_REG_READ(ah, AR_PHY_RESTART); } HAL_BOOL ar9300_interference_is_present(struct ath_hal *ah) { int i; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); const struct ieee80211_channel *chan = ahpriv->ah_curchan; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (ichan == NULL) { ath_hal_printf(ah, "%s: called with ichan=NULL\n", __func__); return AH_FALSE; } /* This function is called after a stuck beacon, if EACS is enabled. * If CW interference is severe, then HW goes into a loop of continuous * stuck beacons and resets. On reset the NF cal history is cleared. * So the median value of the history cannot be used - * hence check if any value (Chain 0/Primary Channel) * is outside the bounds. */ HAL_NFCAL_HIST_FULL *h = AH_HOME_CHAN_NFCAL_HIST(ah, ichan); for (i = 0; i < HAL_NF_CAL_HIST_LEN_FULL; i++) { if (h->nf_cal_buffer[i][0] > AH9300(ah)->nfp->nominal + AH9300(ah)->nf_cw_int_delta) { return AH_TRUE; } } return AH_FALSE; } #if ATH_SUPPORT_CRDC void ar9300_crdc_rx_notify(struct ath_hal *ah, struct ath_rx_status *rxs) { struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int rssi_index; if ((!AR_SREV_WASP(ah)) || (!ahpriv->ah_config.ath_hal_crdc_enable)) { return; } if (rxs->rs_isaggr && rxs->rs_moreaggr) { return; } if ((rxs->rs_rssi_ctl0 >= HAL_RSSI_BAD) || (rxs->rs_rssi_ctl1 >= HAL_RSSI_BAD)) { return; } rssi_index = ah->ah_crdc_rssi_ptr % HAL_MAX_CRDC_RSSI_SAMPLE; ah->ah_crdc_rssi_sample[0][rssi_index] = rxs->rs_rssi_ctl0; ah->ah_crdc_rssi_sample[1][rssi_index] = rxs->rs_rssi_ctl1; ah->ah_crdc_rssi_ptr++; } static int ar9300_crdc_avg_rssi(struct ath_hal *ah, int chain) { int crdc_rssi_sum = 0; int crdc_rssi_ptr = ah->ah_crdc_rssi_ptr, i; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int crdc_window = ahpriv->ah_config.ath_hal_crdc_window; if (crdc_window > HAL_MAX_CRDC_RSSI_SAMPLE) { crdc_window = HAL_MAX_CRDC_RSSI_SAMPLE; } for (i = 1; i <= crdc_window; i++) { crdc_rssi_sum += ah->ah_crdc_rssi_sample[chain] [(crdc_rssi_ptr - i) % HAL_MAX_CRDC_RSSI_SAMPLE]; } return crdc_rssi_sum / crdc_window; } static void ar9300_crdc_activate(struct ath_hal *ah, int rssi_diff, int enable) { int val, orig_val; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int crdc_numerator = ahpriv->ah_config.ath_hal_crdc_numerator; int crdc_denominator = ahpriv->ah_config.ath_hal_crdc_denominator; int c = (rssi_diff * crdc_numerator) / crdc_denominator; val = orig_val = OS_REG_READ(ah, AR_PHY_MULTICHAIN_CTRL); val &= 0xffffff00; if (enable) { val |= 0x1; val |= ((c << 1) & 0xff); } OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_CTRL, val); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "diff: %02d comp: %02d reg: %08x %08x\n", rssi_diff, c, orig_val, val); } void ar9300_chain_rssi_diff_compensation(struct ath_hal *ah) { struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int crdc_window = ahpriv->ah_config.ath_hal_crdc_window; int crdc_rssi_ptr = ah->ah_crdc_rssi_ptr; int crdc_rssi_thresh = ahpriv->ah_config.ath_hal_crdc_rssithresh; int crdc_diff_thresh = ahpriv->ah_config.ath_hal_crdc_diffthresh; int avg_rssi[2], avg_rssi_diff; if ((!AR_SREV_WASP(ah)) || (!ahpriv->ah_config.ath_hal_crdc_enable)) { if (ah->ah_crdc_rssi_ptr) { ar9300_crdc_activate(ah, 0, 0); ah->ah_crdc_rssi_ptr = 0; } return; } if (crdc_window > HAL_MAX_CRDC_RSSI_SAMPLE) { crdc_window = HAL_MAX_CRDC_RSSI_SAMPLE; } if (crdc_rssi_ptr < crdc_window) { return; } avg_rssi[0] = ar9300_crdc_avg_rssi(ah, 0); avg_rssi[1] = ar9300_crdc_avg_rssi(ah, 1); avg_rssi_diff = avg_rssi[1] - avg_rssi[0]; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "crdc: avg: %02d %02d ", avg_rssi[0], avg_rssi[1]); if ((avg_rssi[0] < crdc_rssi_thresh) && (avg_rssi[1] < crdc_rssi_thresh)) { ar9300_crdc_activate(ah, 0, 0); } else { if (ABS(avg_rssi_diff) >= crdc_diff_thresh) { ar9300_crdc_activate(ah, avg_rssi_diff, 1); } else { ar9300_crdc_activate(ah, 0, 1); } } } #endif #if ATH_ANT_DIV_COMB HAL_BOOL ar9300_ant_ctrl_set_lna_div_use_bt_ant(struct ath_hal *ah, HAL_BOOL enable, const struct ieee80211_channel *chan) { u_int32_t value; u_int32_t regval; struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pcap = &ahpriv->ah_caps; HALDEBUG(ah, HAL_DEBUG_RESET | HAL_DEBUG_BT_COEX, "%s: called; enable=%d\n", __func__, enable); if (AR_SREV_POSEIDON(ah)) { // Make sure this scheme is only used for WB225(Astra) ahp->ah_lna_div_use_bt_ant_enable = enable; ichan = ar9300_check_chan(ah, chan); if ( ichan == AH_NULL ) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } if ( enable == TRUE ) { pcap->halAntDivCombSupport = TRUE; } else { pcap->halAntDivCombSupport = pcap->halAntDivCombSupportOrg; } #define AR_SWITCH_TABLE_COM2_ALL (0xffffff) #define AR_SWITCH_TABLE_COM2_ALL_S (0) value = ar9300_ant_ctrl_common2_get(ah, IS_CHAN_2GHZ(ichan)); if ( enable == TRUE ) { value &= ~AR_SWITCH_TABLE_COM2_ALL; value |= ah->ah_config.ath_hal_ant_ctrl_comm2g_switch_enable; } HALDEBUG(ah, HAL_DEBUG_RESET, "%s: com2=0x%08x\n", __func__, value); OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value); value = ar9300_eeprom_get(ahp, EEP_ANTDIV_control); /* main_lnaconf, alt_lnaconf, main_tb, alt_tb */ regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= (~ANT_DIV_CONTROL_ALL); /* clear bit 25~30 */ regval |= (value & 0x3f) << ANT_DIV_CONTROL_ALL_S; /* enable_lnadiv */ regval &= (~MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__MASK); regval |= ((value >> 6) & 0x1) << MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__SHIFT; if ( enable == TRUE ) { regval |= ANT_DIV_ENABLE; } OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); /* enable fast_div */ regval = OS_REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__MASK); regval |= ((value >> 7) & 0x1) << BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__SHIFT; if ( enable == TRUE ) { regval |= FAST_DIV_ENABLE; } OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); if ( AR_SREV_POSEIDON_11_OR_LATER(ah) ) { if (pcap->halAntDivCombSupport) { /* If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning */ regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); /* clear bit 25~30 main_lnaconf, alt_lnaconf, main_tb, alt_tb */ regval &= (~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK)); regval |= (HAL_ANT_DIV_COMB_LNA1 << MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); regval |= (HAL_ANT_DIV_COMB_LNA2 << MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } } return AH_TRUE; } else if (AR_SREV_APHRODITE(ah)) { ahp->ah_lna_div_use_bt_ant_enable = enable; if (enable) { OS_REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, ANT_DIV_ENABLE); OS_REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, (1 << MULTICHAIN_GAIN_CTRL__ENABLE_ANT_SW_RX_PROT__SHIFT)); OS_REG_SET_BIT(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); OS_REG_SET_BIT(ah, AR_PHY_RESTART, RESTART__ENABLE_ANT_FAST_DIV_M2FLAG__MASK); OS_REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); } else { OS_REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, ANT_DIV_ENABLE); OS_REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, (1 << MULTICHAIN_GAIN_CTRL__ENABLE_ANT_SW_RX_PROT__SHIFT)); OS_REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); OS_REG_CLR_BIT(ah, AR_PHY_RESTART, RESTART__ENABLE_ANT_FAST_DIV_M2FLAG__MASK); OS_REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= (~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK)); regval |= (HAL_ANT_DIV_COMB_LNA1 << MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); regval |= (HAL_ANT_DIV_COMB_LNA2 << MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } return AH_TRUE; } return AH_TRUE; } #endif /* ATH_ANT_DIV_COMB */ Index: head/sys/dev/ath/ath_hal/ar5416/ar5416.h =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416.h (revision 361485) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416.h (revision 361486) @@ -1,422 +1,423 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _ATH_AR5416_H_ #define _ATH_AR5416_H_ #include "ar5212/ar5212.h" #include "ar5416_cal.h" #include "ah_eeprom_v14.h" /* for CAL_TARGET_POWER_* */ #define AR5416_MAGIC 0x20065416 typedef struct { uint16_t synth_center; uint16_t ctl_center; uint16_t ext_center; } CHAN_CENTERS; typedef enum Ar5416_Rates { rate6mb, rate9mb, rate12mb, rate18mb, rate24mb, rate36mb, rate48mb, rate54mb, rate1l, rate2l, rate2s, rate5_5l, rate5_5s, rate11l, rate11s, rateXr, rateHt20_0, rateHt20_1, rateHt20_2, rateHt20_3, rateHt20_4, rateHt20_5, rateHt20_6, rateHt20_7, rateHt40_0, rateHt40_1, rateHt40_2, rateHt40_3, rateHt40_4, rateHt40_5, rateHt40_6, rateHt40_7, rateDupCck, rateDupOfdm, rateExtCck, rateExtOfdm, Ar5416RateSize } AR5416_RATES; #define AR5416_DEFAULT_RXCHAINMASK 7 #define AR5416_DEFAULT_TXCHAINMASK 1 #define AR5416_MAX_RATE_POWER 63 #define AR5416_KEYTABLE_SIZE 128 #define AR5416_CCA_MAX_GOOD_VALUE -85 #define AR5416_CCA_MAX_HIGH_VALUE -62 #define AR5416_CCA_MIN_BAD_VALUE -140 #define AR9285_CCA_MAX_GOOD_VALUE -118 #define AR5416_SPUR_RSSI_THRESH 40 struct ar5416NfLimits { int16_t max; int16_t min; int16_t nominal; }; struct ath_hal_5416 { struct ath_hal_5212 ah_5212; /* NB: RF data setup at attach */ HAL_INI_ARRAY ah_ini_bb_rfgain; HAL_INI_ARRAY ah_ini_bank0; HAL_INI_ARRAY ah_ini_bank1; HAL_INI_ARRAY ah_ini_bank2; HAL_INI_ARRAY ah_ini_bank3; HAL_INI_ARRAY ah_ini_bank6; HAL_INI_ARRAY ah_ini_bank7; HAL_INI_ARRAY ah_ini_addac; HAL_INI_ARRAY ah_ini_pcieserdes; void (*ah_writeIni)(struct ath_hal *, const struct ieee80211_channel *); void (*ah_spurMitigate)(struct ath_hal *, const struct ieee80211_channel *); /* calibration ops */ HAL_BOOL (*ah_cal_initcal)(struct ath_hal *, const struct ieee80211_channel *); void (*ah_cal_pacal)(struct ath_hal *, HAL_BOOL is_reset); /* optional open-loop tx power control related methods */ void (*ah_olcInit)(struct ath_hal *); void (*ah_olcTempCompensation)(struct ath_hal *); /* tx power control */ HAL_BOOL (*ah_setPowerCalTable) (struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset); /* baseband operations */ void (*ah_initPLL) (struct ath_hal *ah, const struct ieee80211_channel *chan); /* bluetooth coexistence operations */ void (*ah_btCoexSetDiversity)(struct ath_hal *ah); u_int ah_globaltxtimeout; /* global tx timeout */ u_int ah_gpioMask; int ah_hangs; /* h/w hangs state */ uint8_t ah_keytype[AR5416_KEYTABLE_SIZE]; /* * Primary/Extension Channel Tx, Rx, Rx Clear State */ uint32_t ah_cycleCount; uint32_t ah_ctlBusy; uint32_t ah_extBusy; uint32_t ah_rxBusy; uint32_t ah_txBusy; uint32_t ah_rx_chainmask; uint32_t ah_tx_chainmask; HAL_ANI_CMD ah_ani_function; struct ar5416PerCal ah_cal; /* periodic calibration state */ struct ar5416NfLimits nf_2g; struct ar5416NfLimits nf_5g; /* * TX power configuration related structures */ int initPDADC; int ah_ht40PowerIncForPdadc; int16_t ah_ratesArray[Ar5416RateSize]; int ah_need_an_top2_fixup; /* merlin or later chips that may need this workaround */ /* * Bluetooth coexistence static setup according to the registry */ HAL_BT_MODULE ah_btModule; /* Bluetooth module identifier */ uint8_t ah_btCoexConfigType; /* BT coex configuration */ uint8_t ah_btActiveGpioSelect; /* GPIO pin for BT_ACTIVE */ uint8_t ah_btPriorityGpioSelect; /* GPIO pin for BT_PRIORITY */ uint8_t ah_wlanActiveGpioSelect; /* GPIO pin for WLAN_ACTIVE */ uint8_t ah_btActivePolarity; /* Polarity of BT_ACTIVE */ HAL_BOOL ah_btCoexSingleAnt; /* Single or dual antenna configuration */ uint8_t ah_btWlanIsolation; /* Isolation between BT and WLAN in dB */ /* * Bluetooth coexistence runtime settings */ HAL_BOOL ah_btCoexEnabled; /* If Bluetooth coexistence is enabled */ uint32_t ah_btCoexMode; /* Register setting for AR_BT_COEX_MODE */ uint32_t ah_btCoexBTWeight; /* Register setting for AR_BT_COEX_WEIGHT */ uint32_t ah_btCoexWLANWeight; /* Register setting for AR_BT_COEX_WEIGHT */ uint32_t ah_btCoexMode2; /* Register setting for AR_BT_COEX_MODE2 */ uint32_t ah_btCoexFlag; /* Special tuning flags for BT coex */ }; #define AH5416(_ah) ((struct ath_hal_5416 *)(_ah)) #define IS_5416_PCI(ah) ((AH_PRIVATE(ah)->ah_macVersion) == AR_SREV_VERSION_OWL_PCI) #define IS_5416_PCIE(ah) ((AH_PRIVATE(ah)->ah_macVersion) == AR_SREV_VERSION_OWL_PCIE) #undef IS_PCIE #define IS_PCIE(ah) (IS_5416_PCIE(ah)) extern HAL_BOOL ar2133RfAttach(struct ath_hal *, HAL_STATUS *); struct ath_hal; extern uint32_t ar5416GetRadioRev(struct ath_hal *ah); extern void ar5416InitState(struct ath_hal_5416 *, uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status); extern void ar5416Detach(struct ath_hal *ah); extern void ar5416AttachPCIE(struct ath_hal *ah); extern HAL_BOOL ar5416FillCapabilityInfo(struct ath_hal *ah); extern void ar5416AniAttach(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *, HAL_BOOL ena); extern void ar5416AniDetach(struct ath_hal *); extern HAL_BOOL ar5416AniControl(struct ath_hal *, HAL_ANI_CMD cmd, int param); extern HAL_BOOL ar5416AniSetParams(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *); extern void ar5416ProcessMibIntr(struct ath_hal *, const HAL_NODE_STATS *); extern void ar5416RxMonitor(struct ath_hal *, const HAL_NODE_STATS *, const struct ieee80211_channel *); extern void ar5416AniPoll(struct ath_hal *, const struct ieee80211_channel *); extern void ar5416AniReset(struct ath_hal *, const struct ieee80211_channel *, HAL_OPMODE, int); extern void ar5416SetBeaconTimers(struct ath_hal *, const HAL_BEACON_TIMERS *); extern void ar5416BeaconInit(struct ath_hal *ah, uint32_t next_beacon, uint32_t beacon_period); extern void ar5416ResetStaBeaconTimers(struct ath_hal *ah); extern void ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *); extern uint64_t ar5416GetNextTBTT(struct ath_hal *); /* ar5416_btcoex.c */ extern void ar5416SetBTCoexInfo(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo); extern void ar5416BTCoexConfig(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf); extern void ar5416BTCoexAntennaDiversity(struct ath_hal *ah); extern void ar5416BTCoexSetQcuThresh(struct ath_hal *ah, int qnum); extern void ar5416BTCoexSetWeights(struct ath_hal *ah, uint32_t stompType); extern void ar5416BTCoexSetupBmissThresh(struct ath_hal *ah, uint32_t thresh); extern void ar5416BTCoexSetParameter(struct ath_hal *ah, uint32_t type, uint32_t value); extern void ar5416BTCoexDisable(struct ath_hal *ah); extern int ar5416BTCoexEnable(struct ath_hal *ah); extern void ar5416InitBTCoex(struct ath_hal *ah); extern HAL_BOOL ar5416EepromRead(struct ath_hal *, u_int off, uint16_t *data); extern HAL_BOOL ar5416EepromWrite(struct ath_hal *, u_int off, uint16_t data); extern HAL_BOOL ar5416IsInterruptPending(struct ath_hal *ah); extern HAL_BOOL ar5416GetPendingInterrupts(struct ath_hal *, HAL_INT *masked); extern HAL_INT ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints); extern HAL_BOOL ar5416GpioCfgOutput(struct ath_hal *, uint32_t gpio, HAL_GPIO_MUX_TYPE); extern HAL_BOOL ar5416GpioCfgInput(struct ath_hal *, uint32_t gpio); extern HAL_BOOL ar5416GpioSet(struct ath_hal *, uint32_t gpio, uint32_t val); extern uint32_t ar5416GpioGet(struct ath_hal *ah, uint32_t gpio); extern void ar5416GpioSetIntr(struct ath_hal *ah, u_int, uint32_t ilevel); extern u_int ar5416GetWirelessModes(struct ath_hal *ah); extern void ar5416SetLedState(struct ath_hal *ah, HAL_LED_STATE state); extern uint64_t ar5416GetTsf64(struct ath_hal *ah); extern void ar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64); extern void ar5416ResetTsf(struct ath_hal *ah); extern uint32_t ar5416GetCurRssi(struct ath_hal *ah); extern HAL_BOOL ar5416SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); extern HAL_BOOL ar5416SetDecompMask(struct ath_hal *, uint16_t, int); extern void ar5416SetCoverageClass(struct ath_hal *, uint8_t, int); extern HAL_BOOL ar5416GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample); extern void ar5416SetChainMasks(struct ath_hal *ah, uint32_t, uint32_t); extern uint32_t ar5416Get11nExtBusy(struct ath_hal *ah); extern void ar5416Set11nMac2040(struct ath_hal *ah, HAL_HT_MACMODE mode); extern HAL_HT_RXCLEAR ar5416Get11nRxClear(struct ath_hal *ah); extern void ar5416Set11nRxClear(struct ath_hal *ah, HAL_HT_RXCLEAR rxclear); extern HAL_STATUS ar5416SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag); extern HAL_STATUS ar5416GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result); extern HAL_BOOL ar5416SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t val, HAL_STATUS *status); extern HAL_BOOL ar5416GetDiagState(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); extern HAL_BOOL ar5416SetRifsDelay(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL enable); extern void ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar5416GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern void ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event); extern HAL_BOOL ar5416IsFastClockEnabled(struct ath_hal *ah); /* ar9280_spectral.c */ extern void ar5416ConfigureSpectralScan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); extern void ar5416GetSpectralParams(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); extern HAL_BOOL ar5416IsSpectralActive(struct ath_hal *ah); extern HAL_BOOL ar5416IsSpectralEnabled(struct ath_hal *ah); extern void ar5416StartSpectralScan(struct ath_hal *ah); extern void ar5416StopSpectralScan(struct ath_hal *ah); extern uint32_t ar5416GetSpectralConfig(struct ath_hal *ah); extern void ar5416RestoreSpectralConfig(struct ath_hal *ah, uint32_t restoreval); extern HAL_BOOL ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip); extern HAL_POWER_MODE ar5416GetPowerMode(struct ath_hal *ah); extern HAL_BOOL ar5416GetPowerStatus(struct ath_hal *ah); extern HAL_BOOL ar5416ResetKeyCacheEntry(struct ath_hal *ah, uint16_t entry); extern HAL_BOOL ar5416SetKeyCacheEntry(struct ath_hal *ah, uint16_t entry, const HAL_KEYVAL *k, const uint8_t *mac, int xorKey); extern uint32_t ar5416GetRxFilter(struct ath_hal *ah); extern void ar5416SetRxFilter(struct ath_hal *ah, uint32_t bits); extern HAL_BOOL ar5416StopDmaReceive(struct ath_hal *ah); extern void ar5416StartPcuReceive(struct ath_hal *ah, HAL_BOOL); extern void ar5416StopPcuReceive(struct ath_hal *ah); extern HAL_BOOL ar5416SetupRxDesc(struct ath_hal *, struct ath_desc *, uint32_t size, u_int flags); extern HAL_STATUS ar5416ProcRxDesc(struct ath_hal *ah, struct ath_desc *, uint32_t, struct ath_desc *, uint64_t, struct ath_rx_status *); extern HAL_BOOL ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_RESET_TYPE, HAL_STATUS *status); extern HAL_BOOL ar5416PhyDisable(struct ath_hal *ah); extern HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah); extern HAL_BOOL ar5416Disable(struct ath_hal *ah); extern HAL_BOOL ar5416ChipReset(struct ath_hal *ah, - const struct ieee80211_channel *); + const struct ieee80211_channel *, + HAL_RESET_TYPE); extern int ar5416GetRegChainOffset(struct ath_hal *ah, int i); extern HAL_BOOL ar5416SetBoardValues(struct ath_hal *, const struct ieee80211_channel *); extern HAL_BOOL ar5416SetResetReg(struct ath_hal *, uint32_t type); extern HAL_BOOL ar5416SetTxPowerLimit(struct ath_hal *ah, uint32_t limit); extern HAL_BOOL ar5416SetTransmitPower(struct ath_hal *, const struct ieee80211_channel *, uint16_t *); extern HAL_BOOL ar5416GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar5416GetChannelCenters(struct ath_hal *, const struct ieee80211_channel *chan, CHAN_CENTERS *centers); extern void ar5416SetRatesArrayFromTargetPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *ratesArray, const CAL_TARGET_POWER_LEG *targetPowerCck, const CAL_TARGET_POWER_LEG *targetPowerCckExt, const CAL_TARGET_POWER_LEG *targetPowerOfdm, const CAL_TARGET_POWER_LEG *targetPowerOfdmExt, const CAL_TARGET_POWER_HT *targetPowerHt20, const CAL_TARGET_POWER_HT *targetPowerHt40); extern void ar5416GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_HT *powInfo, uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, uint16_t numRates, HAL_BOOL isHt40Target); extern void ar5416GetTargetPowersLeg(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_LEG *powInfo, uint16_t numChannels, CAL_TARGET_POWER_LEG *pNewPower, uint16_t numRates, HAL_BOOL isExtTarget); extern void ar5416InitChainMasks(struct ath_hal *ah); extern void ar5416RestoreChainMask(struct ath_hal *ah); extern void ar5416EepromSetAddac(struct ath_hal *ah, const struct ieee80211_channel *chan); extern uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz); extern void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan); /* TX power setup related routines in ar5416_reset.c */ extern void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, uint8_t * bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, uint16_t numXpdGains); extern void ar5416SetGainBoundariesClosedLoop(struct ath_hal *ah, int i, uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]); extern uint16_t ar5416GetXpdGainValues(struct ath_hal *ah, uint16_t xpdMask, uint16_t xpdGainValues[]); extern void ar5416WriteDetectorGainBiases(struct ath_hal *ah, uint16_t numXpdGain, uint16_t xpdGainValues[]); extern void ar5416WritePdadcValues(struct ath_hal *ah, int i, uint8_t pdadcValues[]); extern HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset); extern void ar5416WriteTxPowerRateRegisters(struct ath_hal *ah, const struct ieee80211_channel *chan, const int16_t ratesArray[]); extern HAL_BOOL ar5416StopTxDma(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, u_int txRate0, u_int txTries0, u_int keyIx, u_int antMode, u_int flags, u_int rtsctsRate, u_int rtsctsDuration, u_int compicvLen, u_int compivLen, u_int comp); extern HAL_BOOL ar5416SetupXTxDesc(struct ath_hal *, struct ath_desc *, u_int txRate1, u_int txRetries1, u_int txRate2, u_int txRetries2, u_int txRate3, u_int txRetries3); extern HAL_BOOL ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0); extern HAL_STATUS ar5416ProcTxDesc(struct ath_hal *ah, struct ath_desc *, struct ath_tx_status *); extern HAL_BOOL ar5416GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries); extern HAL_BOOL ar5416ResetTxQueue(struct ath_hal *ah, u_int q); extern int ar5416SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, HAL_CIPHER cipher, uint8_t delims, HAL_BOOL firstSeg, HAL_BOOL lastSeg, HAL_BOOL lastAggr); extern HAL_BOOL ar5416SetupFirstTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0, u_int antMode, u_int rtsctsRate, u_int rtsctsDuration); extern HAL_BOOL ar5416SetupLastTxDesc(struct ath_hal *ah, struct ath_desc *ds, const struct ath_desc *ds0); extern HAL_BOOL ar5416SetGlobalTxTimeout(struct ath_hal *ah, u_int tu); extern u_int ar5416GetGlobalTxTimeout(struct ath_hal *ah); extern void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[], u_int nseries, u_int flags); extern void ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int numDelims); extern void ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims); extern void ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds); extern void ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds); extern void ar5416Set11nVirtualMoreFrag(struct ath_hal *ah, struct ath_desc *ds, u_int vmf); extern void ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds, u_int burstDuration); extern const HAL_RATE_TABLE *ar5416GetRateTable(struct ath_hal *, u_int mode); #endif /* _ATH_AR5416_H_ */ Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c (revision 361486) @@ -1,1076 +1,1076 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v14.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar5416/ar5416.ini" static void ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off); static void ar5416DisablePCIE(struct ath_hal *ah); static void ar5416WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416SpurMitigate(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416AniSetup(struct ath_hal *ah) { static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 7, .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; /* NB: disable ANI noise immmunity for reliable RIFS rx */ AH5416(ah)->ah_ani_function &= ~(1 << HAL_ANI_NOISE_IMMUNITY_LEVEL); ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } /* * AR5416 doesn't do OLC or temperature compensation. */ static void ar5416olcInit(struct ath_hal *ah) { } static void ar5416olcTempCompensation(struct ath_hal *ah) { } /* * Attach for an AR5416 part. */ void ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status) { struct ath_hal_5212 *ahp; struct ath_hal *ah; ahp = &ahp5416->ah_5212; ar5212InitState(ahp, devid, sc, st, sh, status); ah = &ahp->ah_priv.h; /* override 5212 methods for our needs */ ah->ah_magic = AR5416_MAGIC; ah->ah_getRateTable = ar5416GetRateTable; ah->ah_detach = ar5416Detach; /* Reset functions */ ah->ah_reset = ar5416Reset; ah->ah_phyDisable = ar5416PhyDisable; ah->ah_disable = ar5416Disable; ah->ah_configPCIE = ar5416ConfigPCIE; ah->ah_disablePCIE = ar5416DisablePCIE; ah->ah_perCalibration = ar5416PerCalibration; ah->ah_perCalibrationN = ar5416PerCalibrationN; ah->ah_resetCalValid = ar5416ResetCalValid; ah->ah_setTxPowerLimit = ar5416SetTxPowerLimit; ah->ah_setTxPower = ar5416SetTransmitPower; ah->ah_setBoardValues = ar5416SetBoardValues; /* Transmit functions */ ah->ah_stopTxDma = ar5416StopTxDma; ah->ah_setupTxDesc = ar5416SetupTxDesc; ah->ah_setupXTxDesc = ar5416SetupXTxDesc; ah->ah_fillTxDesc = ar5416FillTxDesc; ah->ah_procTxDesc = ar5416ProcTxDesc; ah->ah_getTxCompletionRates = ar5416GetTxCompletionRates; ah->ah_setupTxQueue = ar5416SetupTxQueue; ah->ah_resetTxQueue = ar5416ResetTxQueue; /* Receive Functions */ ah->ah_getRxFilter = ar5416GetRxFilter; ah->ah_setRxFilter = ar5416SetRxFilter; ah->ah_stopDmaReceive = ar5416StopDmaReceive; ah->ah_startPcuReceive = ar5416StartPcuReceive; ah->ah_stopPcuReceive = ar5416StopPcuReceive; ah->ah_setupRxDesc = ar5416SetupRxDesc; ah->ah_procRxDesc = ar5416ProcRxDesc; ah->ah_rxMonitor = ar5416RxMonitor; ah->ah_aniPoll = ar5416AniPoll; ah->ah_procMibEvent = ar5416ProcessMibIntr; /* Misc Functions */ ah->ah_getCapability = ar5416GetCapability; ah->ah_setCapability = ar5416SetCapability; ah->ah_getDiagState = ar5416GetDiagState; ah->ah_setLedState = ar5416SetLedState; ah->ah_gpioCfgOutput = ar5416GpioCfgOutput; ah->ah_gpioCfgInput = ar5416GpioCfgInput; ah->ah_gpioGet = ar5416GpioGet; ah->ah_gpioSet = ar5416GpioSet; ah->ah_gpioSetIntr = ar5416GpioSetIntr; ah->ah_getTsf64 = ar5416GetTsf64; ah->ah_setTsf64 = ar5416SetTsf64; ah->ah_resetTsf = ar5416ResetTsf; ah->ah_getRfGain = ar5416GetRfgain; ah->ah_setAntennaSwitch = ar5416SetAntennaSwitch; ah->ah_setDecompMask = ar5416SetDecompMask; ah->ah_setCoverageClass = ar5416SetCoverageClass; ah->ah_setQuiet = ar5416SetQuiet; ah->ah_getMibCycleCounts = ar5416GetMibCycleCounts; ah->ah_setChainMasks = ar5416SetChainMasks; ah->ah_resetKeyCacheEntry = ar5416ResetKeyCacheEntry; ah->ah_setKeyCacheEntry = ar5416SetKeyCacheEntry; /* DFS Functions */ ah->ah_enableDfs = ar5416EnableDfs; ah->ah_getDfsThresh = ar5416GetDfsThresh; ah->ah_getDfsDefaultThresh = ar5416GetDfsDefaultThresh; ah->ah_procRadarEvent = ar5416ProcessRadarEvent; ah->ah_isFastClockEnabled = ar5416IsFastClockEnabled; /* Spectral Scan Functions */ ah->ah_spectralConfigure = ar5416ConfigureSpectralScan; ah->ah_spectralGetConfig = ar5416GetSpectralParams; ah->ah_spectralStart = ar5416StartSpectralScan; ah->ah_spectralStop = ar5416StopSpectralScan; ah->ah_spectralIsEnabled = ar5416IsSpectralEnabled; ah->ah_spectralIsActive = ar5416IsSpectralActive; /* Power Management Functions */ ah->ah_setPowerMode = ar5416SetPowerMode; /* Beacon Management Functions */ ah->ah_setBeaconTimers = ar5416SetBeaconTimers; ah->ah_beaconInit = ar5416BeaconInit; ah->ah_setStationBeaconTimers = ar5416SetStaBeaconTimers; ah->ah_resetStationBeaconTimers = ar5416ResetStaBeaconTimers; ah->ah_getNextTBTT = ar5416GetNextTBTT; /* 802.11n Functions */ ah->ah_chainTxDesc = ar5416ChainTxDesc; ah->ah_setupFirstTxDesc = ar5416SetupFirstTxDesc; ah->ah_setupLastTxDesc = ar5416SetupLastTxDesc; ah->ah_set11nRateScenario = ar5416Set11nRateScenario; ah->ah_set11nAggrFirst = ar5416Set11nAggrFirst; ah->ah_set11nAggrMiddle = ar5416Set11nAggrMiddle; ah->ah_set11nAggrLast = ar5416Set11nAggrLast; ah->ah_clr11nAggr = ar5416Clr11nAggr; ah->ah_set11nBurstDuration = ar5416Set11nBurstDuration; ah->ah_get11nExtBusy = ar5416Get11nExtBusy; ah->ah_set11nMac2040 = ar5416Set11nMac2040; ah->ah_get11nRxClear = ar5416Get11nRxClear; ah->ah_set11nRxClear = ar5416Set11nRxClear; ah->ah_set11nVirtMoreFrag = ar5416Set11nVirtualMoreFrag; /* Interrupt functions */ ah->ah_isInterruptPending = ar5416IsInterruptPending; ah->ah_getPendingInterrupts = ar5416GetPendingInterrupts; ah->ah_setInterrupts = ar5416SetInterrupts; /* Bluetooth Coexistence functions */ ah->ah_btCoexSetInfo = ar5416SetBTCoexInfo; ah->ah_btCoexSetConfig = ar5416BTCoexConfig; ah->ah_btCoexSetQcuThresh = ar5416BTCoexSetQcuThresh; ah->ah_btCoexSetWeights = ar5416BTCoexSetWeights; ah->ah_btCoexSetBmissThresh = ar5416BTCoexSetupBmissThresh; ah->ah_btCoexSetParameter = ar5416BTCoexSetParameter; ah->ah_btCoexDisable = ar5416BTCoexDisable; ah->ah_btCoexEnable = ar5416BTCoexEnable; AH5416(ah)->ah_btCoexSetDiversity = ar5416BTCoexAntennaDiversity; ahp->ah_priv.ah_getWirelessModes= ar5416GetWirelessModes; ahp->ah_priv.ah_eepromRead = ar5416EepromRead; #ifdef AH_SUPPORT_WRITE_EEPROM ahp->ah_priv.ah_eepromWrite = ar5416EepromWrite; #endif ahp->ah_priv.ah_getChipPowerLimits = ar5416GetChipPowerLimits; /* Internal ops */ AH5416(ah)->ah_writeIni = ar5416WriteIni; AH5416(ah)->ah_spurMitigate = ar5416SpurMitigate; /* Internal baseband ops */ AH5416(ah)->ah_initPLL = ar5416InitPLL; /* Internal calibration ops */ AH5416(ah)->ah_cal_initcal = ar5416InitCalHardware; /* Internal TX power control related operations */ AH5416(ah)->ah_olcInit = ar5416olcInit; AH5416(ah)->ah_olcTempCompensation = ar5416olcTempCompensation; AH5416(ah)->ah_setPowerCalTable = ar5416SetPowerCalTable; /* * Start by setting all Owl devices to 2x2 */ AH5416(ah)->ah_rx_chainmask = AR5416_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR5416_DEFAULT_TXCHAINMASK; /* Enable all ANI functions to begin with */ AH5416(ah)->ah_ani_function = 0xffffffff; /* Set overridable ANI methods */ AH5212(ah)->ah_aniControl = ar5416AniControl; /* * Default FIFO Trigger levels * * These define how filled the TX FIFO needs to be before * the baseband begins to be given some data. * * To be paranoid, we ensure that the TX trigger level always * has at least enough space for two TX DMA to occur. * The TX DMA size is currently hard-coded to AR_TXCFG_DMASZ_128B. * That means we need to leave at least 256 bytes available in * the TX DMA FIFO. */ #define AR_FTRIG_512B 0x00000080 // 5 bits total /* * AR9285/AR9271 have half the size TX FIFO compared to * other devices */ if (AR_SREV_KITE(ah) || AR_SREV_9271(ah)) { AH5212(ah)->ah_txTrigLev = (AR_FTRIG_256B >> AR_FTRIG_S); AH5212(ah)->ah_maxTxTrigLev = ((2048 / 64) - 1); } else { AH5212(ah)->ah_txTrigLev = (AR_FTRIG_512B >> AR_FTRIG_S); AH5212(ah)->ah_maxTxTrigLev = ((4096 / 64) - 1); } #undef AR_FTRIG_512B /* And now leave some headspace - 256 bytes */ AH5212(ah)->ah_maxTxTrigLev -= 4; } uint32_t ar5416GetRadioRev(struct ath_hal *ah) { uint32_t val; int i; /* Read Radio Chip Rev Extract */ OS_REG_WRITE(ah, AR_PHY(0x36), 0x00007058); for (i = 0; i < 8; i++) OS_REG_WRITE(ah, AR_PHY(0x20), 0x00010000); val = (OS_REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); return ath_hal_reverseBits(val, 8); } /* * Attach for an AR5416 part. */ static struct ath_hal * ar5416Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416) + /* extra space for Owl 2.1/2.2 WAR */ sizeof(ar5416Addac) ); if (ahp5416 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ar5416InitState(ahp5416, devid, sc, st, sh, status); ahp = &ahp5416->ah_5212; ah = &ahp->ah_priv.h; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID; AH_PRIVATE(ah)->ah_macVersion = val >> AR_SREV_ID_S; AH_PRIVATE(ah)->ah_macRev = val & AR_SREV_REVISION; AH_PRIVATE(ah)->ah_ispcie = (devid == AR5416_DEVID_PCIE); /* setup common ini data; rf backends handle remainder */ HAL_INI_INIT(&ahp->ah_ini_modes, ar5416Modes, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar5416Common, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bb_rfgain, ar5416BB_RfGain, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank0, ar5416Bank0, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank1, ar5416Bank1, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank2, ar5416Bank2, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank3, ar5416Bank3, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar5416Bank6, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank7, ar5416Bank7, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar5416Addac, 2); if (! IS_5416V2_2(ah)) { /* Owl 2.1/2.0 */ ath_hal_printf(ah, "[ath] Enabling CLKDRV workaround for AR5416 < v2.2\n"); struct ini { uint32_t *data; /* NB: !const */ int rows, cols; }; /* override CLKDRV value */ OS_MEMCPY(&AH5416(ah)[1], ar5416Addac, sizeof(ar5416Addac)); AH5416(ah)->ah_ini_addac.data = (uint32_t *) &AH5416(ah)[1]; HAL_INI_VAL((struct ini *)&AH5416(ah)->ah_ini_addac, 31, 1) = 0; } HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar5416PciePhy, 2); ar5416AttachPCIE(ah); ecode = ath_hal_v14EepromAttach(ah); if (ecode != HAL_OK) goto bad; - if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD5122_SREV_MAJOR: /* Fowl: 5G/2x2 */ case AR_RAD2122_SREV_MAJOR: /* Fowl: 2+5G/2x2 */ case AR_RAD2133_SREV_MAJOR: /* Fowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Fowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { /* * When RF_Silen is used the analog chip is reset. * So when the system boots with radio switch off * the RF chip rev reads back as zero and we need * to use the mac+phy revs to set the radio rev. */ AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } /* NB: silently accept anything in release code per Atheros */ #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } /* * Got everything we need now to setup the capabilities. */ if (!ar5416FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); AH_PRIVATE(ah)->ah_currentRDext = ath_hal_eepromGet(ah, AR_EEP_REGDMN_1, AH_NULL); /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); rfStatus = ar2133RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } ar5416AniSetup(ah); /* Anti Noise Immunity */ AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ; AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ; AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_5416_2GHZ; AH5416(ah)->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ; AH5416(ah)->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ; AH5416(ah)->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_5416_5GHZ; ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ahp) ar5416Detach((struct ath_hal *) ahp); if (status) *status = ecode; return AH_NULL; } void ar5416Detach(struct ath_hal *ah) { HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s:\n", __func__); HALASSERT(ah != AH_NULL); HALASSERT(ah->ah_magic == AR5416_MAGIC); /* Make sure that chip is awake before writing to it */ if (! ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: failed to wake up chip\n", __func__); ar5416AniDetach(ah); ar5212RfDetach(ah); ah->ah_disable(ah); ar5416SetPowerMode(ah, HAL_PM_FULL_SLEEP, AH_TRUE); ath_hal_eepromDetach(ah); ath_hal_free(ah); } void ar5416AttachPCIE(struct ath_hal *ah) { if (AH_PRIVATE(ah)->ah_ispcie) ath_hal_configPCIE(ah, AH_FALSE, AH_FALSE); else ath_hal_disablePCIE(ah); } static void ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { /* This is only applicable for AR5418 (AR5416 PCIe) */ if (! AH_PRIVATE(ah)->ah_ispcie) return; if (! restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); } if (power_off) { /* Power-off */ /* clear bit 19 to disable L1 */ OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); } else { /* Power-on */ /* Set default WAR values for Owl */ OS_REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); /* set bit 19 to allow forcing of pcie core into L1 state */ OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); } } /* * Disable PCIe PHY if PCIe isn't used. */ static void ar5416DisablePCIE(struct ath_hal *ah) { /* PCIe? Don't */ if (AH_PRIVATE(ah)->ah_ispcie) return; /* .. Only applicable for AR5416v2 or later */ if (! (AR_SREV_OWL(ah) && AR_SREV_OWL_20_OR_LATER(ah))) return; OS_REG_WRITE_BUFFER_ENABLE(ah); /* * Disable the PCIe PHY. */ OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007); /* Load the new settings */ OS_REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); OS_REG_WRITE_BUFFER_FLUSH(ah); OS_REG_WRITE_BUFFER_DISABLE(ah); } static void ar5416WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { u_int modesIndex, freqIndex; int regWrites = 0; /* Setup the indices for the next set of register array writes */ /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { freqIndex = 2; if (IEEE80211_IS_CHAN_HT40(chan)) modesIndex = 3; else if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else modesIndex = 4; } else { freqIndex = 1; if (IEEE80211_IS_CHAN_HT40(chan) || IEEE80211_IS_CHAN_TURBO(chan)) modesIndex = 2; else modesIndex = 1; } /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* * Write addac shifts */ OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); /* NB: only required for Sowl */ if (AR_SREV_SOWL(ah)) ar5416EepromSetAddac(ah, chan); regWrites = ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_addac, 1, regWrites); OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, modesIndex, regWrites); regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites); /* XXX updated regWrites? */ AH5212(ah)->ah_rfHal->writeRegs(ah, modesIndex, freqIndex, regWrites); } /* * Convert to baseband spur frequency given input channel frequency * and compute register settings below. */ static void ar5416SpurMitigate(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); static const int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 }; static const int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 }; static const int inc[4] = { 0, 100, 0, 0 }; int bb_spur = AR_NO_SPUR; int bin, cur_bin; int spur_freq_sd; int spur_delta_phase; int denominator; int upper, lower, cur_vit_mask; int tmp, new; int i; int8_t mask_m[123]; int8_t mask_p[123]; int8_t mask_amt; int tmp_mask; int cur_bb_spur; HAL_BOOL is2GHz = IEEE80211_IS_CHAN_2GHZ(chan); OS_MEMZERO(mask_m, sizeof(mask_m)); OS_MEMZERO(mask_p, sizeof(mask_p)); /* * Need to verify range +/- 9.5 for static ht20, otherwise spur * is out-of-band and can be ignored. */ /* XXX ath9k changes */ for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ath_hal_getSpurChan(ah, i, is2GHz); if (AR_NO_SPUR == cur_bb_spur) break; cur_bb_spur = cur_bb_spur - (freq * 10); if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) { bb_spur = cur_bb_spur; break; } } if (AR_NO_SPUR == bb_spur) return; bin = bb_spur * 32; tmp = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0)); new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); OS_REG_WRITE_BUFFER_ENABLE(ah); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0), new); new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | AR_PHY_SPUR_REG_ENABLE_MASK_PPM | AR_PHY_SPUR_REG_MASK_RATE_SELECT | AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | SM(AR5416_SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); OS_REG_WRITE(ah, AR_PHY_SPUR_REG, new); /* * Should offset bb_spur by +/- 10 MHz for dynamic 2040 MHz * config, no offset for HT20. * spur_delta_phase = bb_spur/40 * 2**21 for static ht20, * /80 for dyn2040. */ spur_delta_phase = ((bb_spur * 524288) / 100) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; /* * in 11A mode the denominator of spur_freq_sd should be 40 and * it should be 44 in 11G */ denominator = IEEE80211_IS_CHAN_2GHZ(chan) ? 440 : 400; spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff; new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); OS_REG_WRITE(ah, AR_PHY_TIMING11, new); /* * ============================================ * pilot mask 1 [31:0] = +6..-26, no 0 bin * pilot mask 2 [19:0] = +26..+7 * * channel mask 1 [31:0] = +6..-26, no 0 bin * channel mask 2 [19:0] = +26..+7 */ //cur_bin = -26; cur_bin = -6000; upper = bin + 100; lower = bin - 100; for (i = 0; i < 4; i++) { int pilot_mask = 0; int chan_mask = 0; int bp = 0; for (bp = 0; bp < 30; bp++) { if ((cur_bin > lower) && (cur_bin < upper)) { pilot_mask = pilot_mask | 0x1 << bp; chan_mask = chan_mask | 0x1 << bp; } cur_bin += 100; } cur_bin += inc[i]; OS_REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); OS_REG_WRITE(ah, chan_mask_reg[i], chan_mask); } /* ================================================= * viterbi mask 1 based on channel magnitude * four levels 0-3 * - mask (-27 to 27) (reg 64,0x9900 to 67,0x990c) * [1 2 2 1] for -9.6 or [1 2 1] for +16 * - enable_mask_ppm, all bins move with freq * * - mask_select, 8 bits for rates (reg 67,0x990c) * - mask_rate_cntl, 8 bits for rates (reg 67,0x990c) * choose which mask to use mask or mask2 */ /* * viterbi mask 2 2nd set for per data rate puncturing * four levels 0-3 * - mask_select, 8 bits for rates (reg 67) * - mask (-27 to 27) (reg 98,0x9988 to 101,0x9994) * [1 2 2 1] for -9.6 or [1 2 1] for +16 */ cur_vit_mask = 6100; upper = bin + 120; lower = bin - 120; for (i = 0; i < 123; i++) { if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { if ((abs(cur_vit_mask - bin)) < 75) { mask_amt = 1; } else { mask_amt = 0; } if (cur_vit_mask < 0) { mask_m[abs(cur_vit_mask / 100)] = mask_amt; } else { mask_p[cur_vit_mask / 100] = mask_amt; } } cur_vit_mask -= 100; } tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) | (mask_m[48] << 26) | (mask_m[49] << 24) | (mask_m[50] << 22) | (mask_m[51] << 20) | (mask_m[52] << 18) | (mask_m[53] << 16) | (mask_m[54] << 14) | (mask_m[55] << 12) | (mask_m[56] << 10) | (mask_m[57] << 8) | (mask_m[58] << 6) | (mask_m[59] << 4) | (mask_m[60] << 2) | (mask_m[61] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); OS_REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); tmp_mask = (mask_m[31] << 28) | (mask_m[32] << 26) | (mask_m[33] << 24) | (mask_m[34] << 22) | (mask_m[35] << 20) | (mask_m[36] << 18) | (mask_m[37] << 16) | (mask_m[48] << 14) | (mask_m[39] << 12) | (mask_m[40] << 10) | (mask_m[41] << 8) | (mask_m[42] << 6) | (mask_m[43] << 4) | (mask_m[44] << 2) | (mask_m[45] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) | (mask_m[18] << 26) | (mask_m[18] << 24) | (mask_m[20] << 22) | (mask_m[20] << 20) | (mask_m[22] << 18) | (mask_m[22] << 16) | (mask_m[24] << 14) | (mask_m[24] << 12) | (mask_m[25] << 10) | (mask_m[26] << 8) | (mask_m[27] << 6) | (mask_m[28] << 4) | (mask_m[29] << 2) | (mask_m[30] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); tmp_mask = (mask_m[ 0] << 30) | (mask_m[ 1] << 28) | (mask_m[ 2] << 26) | (mask_m[ 3] << 24) | (mask_m[ 4] << 22) | (mask_m[ 5] << 20) | (mask_m[ 6] << 18) | (mask_m[ 7] << 16) | (mask_m[ 8] << 14) | (mask_m[ 9] << 12) | (mask_m[10] << 10) | (mask_m[11] << 8) | (mask_m[12] << 6) | (mask_m[13] << 4) | (mask_m[14] << 2) | (mask_m[15] << 0); OS_REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); tmp_mask = (mask_p[15] << 28) | (mask_p[14] << 26) | (mask_p[13] << 24) | (mask_p[12] << 22) | (mask_p[11] << 20) | (mask_p[10] << 18) | (mask_p[ 9] << 16) | (mask_p[ 8] << 14) | (mask_p[ 7] << 12) | (mask_p[ 6] << 10) | (mask_p[ 5] << 8) | (mask_p[ 4] << 6) | (mask_p[ 3] << 4) | (mask_p[ 2] << 2) | (mask_p[ 1] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); tmp_mask = (mask_p[30] << 28) | (mask_p[29] << 26) | (mask_p[28] << 24) | (mask_p[27] << 22) | (mask_p[26] << 20) | (mask_p[25] << 18) | (mask_p[24] << 16) | (mask_p[23] << 14) | (mask_p[22] << 12) | (mask_p[21] << 10) | (mask_p[20] << 8) | (mask_p[19] << 6) | (mask_p[18] << 4) | (mask_p[17] << 2) | (mask_p[16] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); tmp_mask = (mask_p[45] << 28) | (mask_p[44] << 26) | (mask_p[43] << 24) | (mask_p[42] << 22) | (mask_p[41] << 20) | (mask_p[40] << 18) | (mask_p[39] << 16) | (mask_p[38] << 14) | (mask_p[37] << 12) | (mask_p[36] << 10) | (mask_p[35] << 8) | (mask_p[34] << 6) | (mask_p[33] << 4) | (mask_p[32] << 2) | (mask_p[31] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) | (mask_p[59] << 26) | (mask_p[58] << 24) | (mask_p[57] << 22) | (mask_p[56] << 20) | (mask_p[55] << 18) | (mask_p[54] << 16) | (mask_p[53] << 14) | (mask_p[52] << 12) | (mask_p[51] << 10) | (mask_p[50] << 8) | (mask_p[49] << 6) | (mask_p[48] << 4) | (mask_p[47] << 2) | (mask_p[46] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); OS_REG_WRITE_BUFFER_FLUSH(ah); OS_REG_WRITE_BUFFER_DISABLE(ah); } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ HAL_BOOL ar5416FillCapabilityInfo(struct ath_hal *ah) { struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; uint16_t val; /* Construct wireless mode from EEPROM */ pCap->halWirelessModes = 0; if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) { pCap->halWirelessModes |= HAL_MODE_11A | HAL_MODE_11NA_HT20 | HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS ; } if (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE)) { pCap->halWirelessModes |= HAL_MODE_11G | HAL_MODE_11NG_HT20 | HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS ; pCap->halWirelessModes |= HAL_MODE_11A | HAL_MODE_11NA_HT20 | HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS ; } pCap->halLow2GhzChan = 2312; pCap->halHigh2GhzChan = 2732; pCap->halLow5GhzChan = 4915; pCap->halHigh5GhzChan = 6100; pCap->halCipherCkipSupport = AH_FALSE; pCap->halCipherTkipSupport = AH_TRUE; pCap->halCipherAesCcmSupport = ath_hal_eepromGetFlag(ah, AR_EEP_AES); pCap->halMicCkipSupport = AH_FALSE; pCap->halMicTkipSupport = AH_TRUE; pCap->halMicAesCcmSupport = ath_hal_eepromGetFlag(ah, AR_EEP_AES); /* * Starting with Griffin TX+RX mic keys can be combined * in one key cache slot. */ pCap->halTkipMicTxRxKeySupport = AH_TRUE; pCap->halChanSpreadSupport = AH_TRUE; pCap->halSleepAfterBeaconBroken = AH_TRUE; pCap->halCompressSupport = AH_FALSE; pCap->halBurstSupport = AH_TRUE; pCap->halFastFramesSupport = AH_TRUE; pCap->halChapTuningSupport = AH_TRUE; pCap->halTurboPrimeSupport = AH_TRUE; pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ pCap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ pCap->halNumTxMaps = 1; /* Single TX ptr per descr */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; /* Works on AR5416 and later */ pCap->halTsfAddSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_FALSE; /* Broken in Owl */ pCap->halSpectralScanSupport = AH_FALSE; /* AR9280 and later */ if (ath_hal_eepromGet(ah, AR_EEP_MAXQCU, &val) == HAL_OK) pCap->halTotalQueues = val; else pCap->halTotalQueues = HAL_NUM_TX_QUEUES; if (ath_hal_eepromGet(ah, AR_EEP_KCENTRIES, &val) == HAL_OK) pCap->halKeyCacheSize = val; else pCap->halKeyCacheSize = AR5416_KEYTABLE_SIZE; /* XXX Which chips? */ pCap->halChanHalfRate = AH_TRUE; pCap->halChanQuarterRate = AH_TRUE; pCap->halTxTstampPrecision = 32; pCap->halRxTstampPrecision = 32; pCap->halHwPhyCounterSupport = AH_TRUE; pCap->halIntrMask = HAL_INT_COMMON | HAL_INT_RX | HAL_INT_TX | HAL_INT_FATAL | HAL_INT_BNR | HAL_INT_BMISC | HAL_INT_DTIMSYNC | HAL_INT_TSFOOR | HAL_INT_CST | HAL_INT_GTT ; pCap->halFastCCSupport = AH_TRUE; pCap->halNumGpioPins = 14; pCap->halWowSupport = AH_FALSE; pCap->halWowMatchPatternExact = AH_FALSE; pCap->halBtCoexSupport = AH_FALSE; /* XXX need support */ pCap->halAutoSleepSupport = AH_FALSE; pCap->hal4kbSplitTransSupport = AH_TRUE; /* Disable this so Block-ACK works correctly */ pCap->halHasRxSelfLinkedTail = AH_FALSE; #if 0 /* XXX not yet */ pCap->halNumAntCfg2GHz = ar5416GetNumAntConfig(ahp, HAL_FREQ_BAND_2GHZ); pCap->halNumAntCfg5GHz = ar5416GetNumAntConfig(ahp, HAL_FREQ_BAND_5GHZ); #endif pCap->halHTSupport = AH_TRUE; pCap->halTxChainMask = ath_hal_eepromGet(ah, AR_EEP_TXMASK, AH_NULL); /* XXX CB71 uses GPIO 0 to indicate 3 rx chains */ pCap->halRxChainMask = ath_hal_eepromGet(ah, AR_EEP_RXMASK, AH_NULL); /* AR5416 may have 3 antennas but is a 2x2 stream device */ pCap->halTxStreams = 2; pCap->halRxStreams = 2; /* * If the TX or RX chainmask has less than 2 chains active, * mark it as a 1-stream device for the relevant stream. */ if (owl_get_ntxchains(pCap->halTxChainMask) == 1) pCap->halTxStreams = 1; /* XXX Eww */ if (owl_get_ntxchains(pCap->halRxChainMask) == 1) pCap->halRxStreams = 1; pCap->halRtsAggrLimit = 8*1024; /* Owl 2.0 limit */ pCap->halMbssidAggrSupport = AH_FALSE; /* Broken on Owl */ pCap->halForcePpmSupport = AH_TRUE; pCap->halEnhancedPmSupport = AH_TRUE; pCap->halBssidMatchSupport = AH_TRUE; pCap->halGTTSupport = AH_TRUE; pCap->halCSTSupport = AH_TRUE; pCap->halEnhancedDfsSupport = AH_FALSE; /* * BB Read WAR: this is only for AR5008/AR9001 NICs * It is also set individually in the AR91xx attach functions. */ if (AR_SREV_OWL(ah)) pCap->halHasBBReadWar = AH_TRUE; if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) && ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) { /* NB: enabled by default */ ahpriv->ah_rfkillEnabled = AH_TRUE; pCap->halRfSilentSupport = AH_TRUE; } /* * The MAC will mark frames as RXed if there's a descriptor * to write them to. So if it hits a self-linked final descriptor, * it'll keep ACKing frames even though they're being silently * dropped. Thus, this particular feature of the driver can't * be used for 802.11n devices. */ ahpriv->ah_rxornIsFatal = AH_FALSE; /* * If it's a PCI NIC, ask the HAL OS layer to serialise * register access, or SMP machines may cause the hardware * to hang. This is applicable to AR5416 and AR9220; I'm not * sure about AR9160 or AR9227. */ if (! AH_PRIVATE(ah)->ah_ispcie) pCap->halSerialiseRegWar = 1; /* * AR5416 and later NICs support MYBEACON filtering. */ pCap->halRxDoMyBeacon = AH_TRUE; return AH_TRUE; } static const char* ar5416Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID) { if (devid == AR5416_DEVID_PCI) return "Atheros 5416"; if (devid == AR5416_DEVID_PCIE) return "Atheros 5418"; } return AH_NULL; } AH_CHIP(AR5416, ar5416Probe, ar5416Attach); Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c (revision 361486) @@ -1,2896 +1,2906 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v14.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) #define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* 100 usec */ #define PLL_SETTLE_DELAY 300 /* 300 usec */ #define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */ static void ar5416InitDMA(struct ath_hal *ah); static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *); static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode); static void ar5416InitQoS(struct ath_hal *ah); static void ar5416InitUserSettings(struct ath_hal *ah); static void ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *); #if 0 static HAL_BOOL ar5416ChannelChange(struct ath_hal *, const struct ieee80211_channel *); #endif static void ar5416SetDeltaSlope(struct ath_hal *, const struct ieee80211_channel *); static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah); static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type); static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit); static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416MarkPhyInactive(struct ath_hal *ah); static void ar5416SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan); /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * bChannelChange is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_RESET_TYPE resetType, HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan; uint32_t saveDefAntenna, saveLedState; uint32_t macStaId1; uint16_t rfXpdGain[2]; HAL_STATUS ecode; uint32_t powerVal, rssiThrReg; uint32_t ackTpcPow, ctsTpcPow, chirpTpcPow; int i; uint64_t tsf = 0; OS_MARK(ah, AH_MARK_RESET, bChannelChange); /* Bring out of sleep mode */ if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip did not wakeup\n", __func__); FAIL(HAL_EIO); } /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) FAIL(HAL_EINVAL); switch (opmode) { case HAL_M_STA: case HAL_M_IBSS: case HAL_M_HOSTAP: case HAL_M_MONITOR: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n", __func__, opmode); FAIL(HAL_EINVAL); break; } HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); /* Blank the channel survey statistics */ ath_hal_survey_clear(ah); /* XXX Turn on fast channel change for 5416 */ /* * Preserve the bmiss rssi threshold and count threshold * across resets */ rssiThrReg = OS_REG_READ(ah, AR_RSSI_THR); /* If reg is zero, first time thru set to default val */ if (rssiThrReg == 0) rssiThrReg = INIT_RSSI_THR; /* * Preserve the antenna on a channel change */ saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA); /* * Don't do this for the AR9285 - it breaks RX for single * antenna designs when diversity is disabled. * * I'm not sure what this was working around; it may be * something to do with the AR5416. Certainly this register * isn't supposed to be used by the MIMO chips for anything * except for defining the default antenna when an external * phase array / smart antenna is connected. * * See PR: kern/179269 . */ if ((! AR_SREV_KITE(ah)) && saveDefAntenna == 0) /* XXX magic constants */ saveDefAntenna = 1; /* Save hardware flag before chip reset clears the register */ macStaId1 = OS_REG_READ(ah, AR_STA_ID1) & (AR_STA_ID1_BASE_RATE_11B | AR_STA_ID1_USE_DEFANT); /* Save led state from pci config register */ saveLedState = OS_REG_READ(ah, AR_MAC_LED) & (AR_MAC_LED_ASSOC | AR_MAC_LED_MODE | AR_MAC_LED_BLINK_THRESH_SEL | AR_MAC_LED_BLINK_SLOW); /* For chips on which the RTC reset is done, save TSF before it gets cleared */ if (AR_SREV_HOWL(ah) || (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) || + (resetType == HAL_RESET_FORCE_COLD) || + (resetType == HAL_RESET_BBPANIC) || (ah->ah_config.ah_force_full_reset)) tsf = ar5416GetTsf64(ah); /* Mark PHY as inactive; marked active in ar5416InitBB() */ ar5416MarkPhyInactive(ah); - if (!ar5416ChipReset(ah, chan)) { + if (!ar5416ChipReset(ah, chan, resetType)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } /* Restore TSF */ if (tsf) ar5416SetTsf64(ah, tsf); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); if (AR_SREV_MERLIN_10_OR_LATER(ah)) OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); AH5416(ah)->ah_writeIni(ah, chan); if(AR_SREV_KIWI_13_OR_LATER(ah) ) { /* Enable ASYNC FIFO */ OS_REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL); OS_REG_SET_BIT(ah, AR_PHY_MODE, AR_PHY_MODE_ASYNCFIFO); OS_REG_CLR_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); OS_REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); } /* Override ini values (that can be overriden in this fashion) */ ar5416OverrideIni(ah, chan); /* Setup 11n MAC/Phy mode registers */ ar5416Set11nRegs(ah, chan); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* * Some AR91xx SoC devices frequently fail to accept TSF writes * right after the chip reset. When that happens, write a new * value after the initvals have been applied, with an offset * based on measured time difference */ if (AR_SREV_HOWL(ah) && (ar5416GetTsf64(ah) < tsf)) { tsf += 1500; ar5416SetTsf64(ah, tsf); } HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_DAG_CTRLCCK=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_DAG_CTRLCCK)); HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_ADC_CTL=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_ADC_CTL)); /* * This routine swaps the analog chains - it should be done * before any radio register twiddling is done. */ ar5416InitChainMasks(ah); /* Setup the open-loop power calibration if required */ if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { AH5416(ah)->ah_olcInit(ah); AH5416(ah)->ah_olcTempCompensation(ah); } /* Setup the transmit power values. */ if (!ah->ah_setTxPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); FAIL(HAL_EIO); } /* Write the analog registers */ if (!ahp->ah_rfHal->setRfRegs(ah, chan, IEEE80211_IS_CHAN_2GHZ(chan) ? 2: 1, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5212SetRfRegs failed\n", __func__); FAIL(HAL_EIO); } /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan)|| IEEE80211_IS_CHAN_HT(chan)) ar5416SetDeltaSlope(ah, chan); AH5416(ah)->ah_spurMitigate(ah, chan); /* Setup board specific options for EEPROM version 3 */ if (!ah->ah_setBoardValues(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | macStaId1 | AR_STA_ID1_RTS_USE_DEF | ahp->ah_staId1Defaults ); ar5212SetOperatingMode(ah, opmode); /* Set Venice BSSID mask according to current state */ OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4)); /* Restore previous led state */ if (AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_MAC_LED, AR_MAC_LED_ASSOC_ACTIVE | AR_CFG_SCLK_32KHZ); else OS_REG_WRITE(ah, AR_MAC_LED, OS_REG_READ(ah, AR_MAC_LED) | saveLedState); /* Start TSF2 for generic timer 8-15 */ #ifdef NOTYET if (AR_SREV_KIWI(ah)) ar5416StartTsf2(ah); #endif /* * Enable Bluetooth Coexistence if it's enabled. */ if (AH5416(ah)->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE) ar5416InitBTCoex(ah); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); /* then our BSSID and associate id */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, rssiThrReg); if (!ar5212SetChannel(ah, chan)) FAIL(HAL_EIO); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Set 1:1 QCU to DCU mapping for all queues */ for (i = 0; i < AR_NUM_DCU; i++) OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); ahp->ah_intrTxqs = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++) ah->ah_resetTxQueue(ah, i); ar5416InitIMR(ah, opmode); ar5416SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); ar5416InitQoS(ah); /* This may override the AR_DIAG_SW register */ ar5416InitUserSettings(ah); /* XXX this won't work for AR9287! */ if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { ar5416SetIFSTiming(ah, chan); #if 0 /* * AR5413? * Force window_length for 1/2 and 1/4 rate channels, * the ini file sets this to zero otherwise. */ OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_WINLEN, 3); } #endif } if (AR_SREV_KIWI_13_OR_LATER(ah)) { /* * Enable ASYNC FIFO * * If Async FIFO is enabled, the following counters change * as MAC now runs at 117 Mhz instead of 88/44MHz when * async FIFO is disabled. * * Overwrite the delay/timeouts initialized in ProcessIni() * above. */ OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, AR_D_GBL_IFS_SLOT_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, AR_D_GBL_IFS_EIFS_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_TIME_OUT, AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_USEC, AR_USEC_ASYNC_FIFO_DUR); OS_REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER, AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768); OS_REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN, AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL); } if (AR_SREV_KIWI_13_OR_LATER(ah)) { /* Enable AGGWEP to accelerate encryption engine */ OS_REG_SET_BIT(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_ENABLE_AGGWEP); } /* * disable seq number generation in hw */ OS_REG_WRITE(ah, AR_STA_ID1, OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); ar5416InitDMA(ah); /* * program OBS bus to see MAC interrupts */ OS_REG_WRITE(ah, AR_OBS, 8); /* * Disable the "general" TX/RX mitigation timers. */ OS_REG_WRITE(ah, AR_MIRT, 0); #ifdef AH_AR5416_INTERRUPT_MITIGATION /* * This initialises the RX interrupt mitigation timers. * * The mitigation timers begin at idle and are triggered * upon the RXOK of a single frame (or sub-frame, for A-MPDU.) * Then, the RX mitigation interrupt will fire: * * + 250uS after the last RX'ed frame, or * + 700uS after the first RX'ed frame * * Thus, the LAST field dictates the extra latency * induced by the RX mitigation method and the FIRST * field dictates how long to delay before firing an * RX mitigation interrupt. * * Please note this only seems to be for RXOK frames; * not CRC or PHY error frames. * */ OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 250); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 700); #endif ar5416InitBB(ah, chan); /* Setup compression registers */ ar5212SetCompRegs(ah); /* XXX not needed? */ /* * 5416 baseband will check the per rate power table * and select the lower of the two */ ackTpcPow = 63; ctsTpcPow = 63; chirpTpcPow = 63; powerVal = SM(ackTpcPow, AR_TPC_ACK) | SM(ctsTpcPow, AR_TPC_CTS) | SM(chirpTpcPow, AR_TPC_CHIRP); OS_REG_WRITE(ah, AR_TPC, powerVal); if (!ar5416InitCal(ah, chan)) FAIL(HAL_ESELFTEST); ar5416RestoreChainMask(ah); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ if (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; if (AR_SREV_HOWL(ah)) { /* * Enable the MBSSID block-ack fix for HOWL. * This feature is only supported on Howl 1.4, but it is safe to * set bit 22 of STA_ID1 on other Howl revisions (1.1, 1.2, 1.3), * since bit 22 is unused in those Howl revisions. */ unsigned int reg; reg = (OS_REG_READ(ah, AR_STA_ID1) | (1<<22)); OS_REG_WRITE(ah,AR_STA_ID1, reg); ath_hal_printf(ah, "MBSSID Set bit 22 of AR_STA_ID 0x%x\n", reg); } HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__); OS_MARK(ah, AH_MARK_RESET_DONE, 0); return AH_TRUE; bad: OS_MARK(ah, AH_MARK_RESET_DONE, ecode); if (status != AH_NULL) *status = ecode; return AH_FALSE; #undef FAIL #undef N } #if 0 /* * This channel change evaluates whether the selected hardware can * perform a synthesizer-only channel change (no reset). If the * TX is not stopped, or the RFBus cannot be granted in the given * time, the function returns false as a reset is necessary */ HAL_BOOL ar5416ChannelChange(struct ath_hal *ah, const structu ieee80211_channel *chan) { uint32_t ulCount; uint32_t data, synthDelay, qnum; uint16_t rfXpdGain[4]; struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan; /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); /* TX must be stopped or RF Bus grant will not work */ for (qnum = 0; qnum < AH_PRIVATE(ah)->ah_caps.halTotalQueues; qnum++) { if (ar5212NumTxPending(ah, qnum)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: frames pending on queue %d\n", __func__, qnum); return AH_FALSE; } } /* * Kill last Baseband Rx Frame - Request analog bus grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_REQUEST); if (!ath_hal_wait(ah, AR_PHY_RFBUS_GNT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: could not kill baseband rx\n", __func__); return AH_FALSE; } ar5416Set11nRegs(ah, chan); /* NB: setup 5416-specific regs */ /* Change the synth */ if (!ar5212SetChannel(ah, chan)) return AH_FALSE; /* Setup the transmit power values. */ if (!ah->ah_setTxPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); return AH_FALSE; } /* * Wait for the frequency synth to settle (synth goes on * via PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ data = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IS_CHAN_CCK(ichan)) { synthDelay = (4 * data) / 22; } else { synthDelay = data / 10; } OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); /* Release the RFBus Grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(ichan)|| IEEE80211_IS_CHAN_HT(chan)) { HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3); ar5212SetSpurMitigation(ah, chan); ar5416SetDeltaSlope(ah, chan); } /* XXX spur mitigation for Melin */ if (!IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; ichan->channel_time = 0; ichan->tsf_last = ar5416GetTsf64(ah); ar5212TxEnable(ah, AH_TRUE); return AH_TRUE; } #endif static void ar5416InitDMA(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); /* * set AHB_MODE not to do cacheline prefetches */ OS_REG_SET_BIT(ah, AR_AHB_MODE, AR_AHB_PREFETCH_RD_EN); /* * let mac dma reads be in 128 byte chunks */ OS_REG_WRITE(ah, AR_TXCFG, (OS_REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK) | AR_TXCFG_DMASZ_128B); /* * let mac dma writes be in 128 byte chunks */ /* * XXX If you change this, you must change the headroom * assigned in ah_maxTxTrigLev - see ar5416InitState(). */ OS_REG_WRITE(ah, AR_RXCFG, (OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK) | AR_RXCFG_DMASZ_128B); /* restore TX trigger level */ OS_REG_WRITE(ah, AR_TXCFG, (OS_REG_READ(ah, AR_TXCFG) &~ AR_FTRIG) | SM(ahp->ah_txTrigLev, AR_FTRIG)); /* * Setup receive FIFO threshold to hold off TX activities */ OS_REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); /* * reduce the number of usable entries in PCU TXBUF to avoid * wrap around. */ if (AR_SREV_KITE(ah)) /* * For AR9285 the number of Fifos are reduced to half. * So set the usable tx buf size also to half to * avoid data/delimiter underruns */ OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE); else OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t synthDelay; /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synthDelay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_CCK(chan)) { synthDelay = (4 * synthDelay) / 22; } else { synthDelay /= 10; } /* Turn on PLL on 5416 */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s %s channel\n", __func__, IEEE80211_IS_CHAN_5GHZ(chan) ? "5GHz" : "2GHz"); /* Activate the PHY (includes baseband activate and synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * If the AP starts the calibration before the base band timeout * completes we could get rx_clear false triggering. Add an * extra BASE_ACTIVATE_DELAY usecs to ensure this condition * does not happen. */ if (IEEE80211_IS_CHAN_HALF(chan)) { OS_DELAY((synthDelay << 1) + BASE_ACTIVATE_DELAY); } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { OS_DELAY((synthDelay << 2) + BASE_ACTIVATE_DELAY); } else { OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); } } static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode) { struct ath_hal_5212 *ahp = AH5212(ah); /* * Setup interrupt handling. Note that ar5212ResetTxQueue * manipulates the secondary IMR's as queues are enabled * and disabled. This is done with RMW ops to insure the * settings we make here are preserved. */ ahp->ah_maskReg = AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_BCNMISC; #ifdef AH_AR5416_INTERRUPT_MITIGATION ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; #else ahp->ah_maskReg |= AR_IMR_RXOK; #endif ahp->ah_maskReg |= AR_IMR_TXOK; if (opmode == HAL_M_HOSTAP) ahp->ah_maskReg |= AR_IMR_MIB; OS_REG_WRITE(ah, AR_IMR, ahp->ah_maskReg); #ifdef ADRIAN_NOTYET /* This is straight from ath9k */ if (! AR_SREV_HOWL(ah)) { OS_REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT); OS_REG_WRITE(ah, AR_INTR_SYNC_MASK, 0); } #endif /* Enable bus errors that are OR'd to set the HIUERR bit */ #if 0 OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT | AR_IMR_S2_CST); #endif } static void ar5416InitQoS(struct ath_hal *ah) { /* QoS support */ OS_REG_WRITE(ah, AR_QOS_CONTROL, 0x100aa); /* XXX magic */ OS_REG_WRITE(ah, AR_QOS_SELECT, 0x3210); /* XXX magic */ /* Turn on NOACK Support for QoS packets */ OS_REG_WRITE(ah, AR_NOACK, SM(2, AR_NOACK_2BIT_VALUE) | SM(5, AR_NOACK_BIT_OFFSET) | SM(0, AR_NOACK_BYTE_OFFSET)); /* * initialize TXOP for all TIDs */ OS_REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); OS_REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } static void ar5416InitUserSettings(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); /* Restore user-specified settings */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); if (ahp->ah_sifstime != (u_int) -1) ar5212SetSifsTime(ah, ahp->ah_sifstime); if (ahp->ah_slottime != (u_int) -1) ar5212SetSlotTime(ah, ahp->ah_slottime); if (ahp->ah_acktimeout != (u_int) -1) ar5212SetAckTimeout(ah, ahp->ah_acktimeout); if (ahp->ah_ctstimeout != (u_int) -1) ar5212SetCTSTimeout(ah, ahp->ah_ctstimeout); if (AH_PRIVATE(ah)->ah_diagreg != 0) OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); if (AH5416(ah)->ah_globaltxtimeout != (u_int) -1) ar5416SetGlobalTxTimeout(ah, AH5416(ah)->ah_globaltxtimeout); } static void ar5416SetRfMode(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t rfMode; if (chan == AH_NULL) return; /* treat channel B as channel G , no B mode suport in owl */ rfMode = IEEE80211_IS_CHAN_CCK(chan) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; if (AR_SREV_MERLIN_20(ah) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* phy mode bits for 5GHz channels require Fast Clock */ rfMode |= AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE; } else if (!AR_SREV_MERLIN_10_OR_LATER(ah)) { rfMode |= IEEE80211_IS_CHAN_5GHZ(chan) ? AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ; } OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); } /* * Places the hardware into reset and then pulls it out of reset */ HAL_BOOL -ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) +ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan, + HAL_RESET_TYPE resetType) { OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Warm reset is optimistic for open-loop TX power control. */ if (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) return AH_FALSE; } else if (ah->ah_config.ah_force_full_reset) { + if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) + return AH_FALSE; + } else if ((resetType == HAL_RESET_FORCE_COLD) || + (resetType == HAL_RESET_BBPANIC)) { + HALDEBUG(ah, HAL_DEBUG_RESET, + "%s: full reset; resetType=%d\n", + __func__, resetType); if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) return AH_FALSE; } else { if (!ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; } /* Bring out of sleep mode (AGAIN) */ if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; #ifdef notyet ahp->ah_chipFullSleep = AH_FALSE; #endif AH5416(ah)->ah_initPLL(ah, chan); /* * Perform warm reset before the mode/PLL/turbo registers * are changed in order to deactivate the radio. Mode changes * with an active radio can result in corrupted shifts to the * radio device. */ ar5416SetRfMode(ah, chan); return AH_TRUE; } /* * Delta slope coefficient computation. * Required for OFDM operation. */ static void ar5416GetDeltaSlopeValues(struct ath_hal *ah, uint32_t coef_scaled, uint32_t *coef_mantissa, uint32_t *coef_exponent) { #define COEF_SCALE_S 24 uint32_t coef_exp, coef_man; /* * ALGO -> coef_exp = 14-floor(log2(coef)); * floor(log2(x)) is the highest set bit position */ for (coef_exp = 31; coef_exp > 0; coef_exp--) if ((coef_scaled >> coef_exp) & 0x1) break; /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ HALASSERT(coef_exp); coef_exp = 14 - (coef_exp - COEF_SCALE_S); /* * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); * The coefficient is already shifted up for scaling */ coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); *coef_exponent = coef_exp - 16; #undef COEF_SCALE_S } void ar5416SetDeltaSlope(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define INIT_CLOCKMHZSCALED 0x64000000 uint32_t coef_scaled, ds_coef_exp, ds_coef_man; uint32_t clockMhzScaled; CHAN_CENTERS centers; /* half and quarter rate can divide the scaled clock by 2 or 4 respectively */ /* scale for selected channel bandwidth */ clockMhzScaled = INIT_CLOCKMHZSCALED; if (IEEE80211_IS_CHAN_TURBO(chan)) clockMhzScaled <<= 1; else if (IEEE80211_IS_CHAN_HALF(chan)) clockMhzScaled >>= 1; else if (IEEE80211_IS_CHAN_QUARTER(chan)) clockMhzScaled >>= 2; /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ ar5416GetChannelCenters(ah, chan, ¢ers); coef_scaled = clockMhzScaled / centers.synth_center; ar5416GetDeltaSlopeValues(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); /* * For Short GI, * scaled coeff is 9/10 that of normal coeff */ coef_scaled = (9 * coef_scaled)/10; ar5416GetDeltaSlopeValues(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); /* for short gi */ OS_REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_EXP, ds_coef_exp); #undef INIT_CLOCKMHZSCALED } /* * Set a limit on the overall output power. Used for dynamic * transmit power control and the like. * * NB: limit is in units of 0.5 dbM. */ HAL_BOOL ar5416SetTxPowerLimit(struct ath_hal *ah, uint32_t limit) { uint16_t dummyXpdGains[2]; AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); return ah->ah_setTxPower(ah, AH_PRIVATE(ah)->ah_curchan, dummyXpdGains); } HAL_BOOL ar5416GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); int16_t minPower, maxPower; /* * Get Pier table max and min powers. */ if (ahp->ah_rfHal->getChannelMaxMinPower(ah, chan, &maxPower, &minPower)) { /* NB: rf code returns 1/4 dBm units, convert */ chan->ic_maxpower = maxPower / 2; chan->ic_minpower = minPower / 2; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no min/max power for %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); chan->ic_maxpower = AR5416_MAX_RATE_POWER; chan->ic_minpower = 0; } HALDEBUG(ah, HAL_DEBUG_RESET, "Chan %d: MaxPow = %d MinPow = %d\n", chan->ic_freq, chan->ic_maxpower, chan->ic_minpower); return AH_TRUE; } /************************************************************** * ar5416WriteTxPowerRateRegisters * * Write the TX power rate registers from the raw values given * in ratesArray[]. * * The CCK and HT40 rate registers are only written if needed. * HT20 and 11g/11a OFDM rate registers are always written. * * The values written are raw values which should be written * to the registers - so it's up to the caller to pre-adjust * them (eg CCK power offset value, or Merlin TX power offset, * etc.) */ void ar5416WriteTxPowerRateRegisters(struct ath_hal *ah, const struct ieee80211_channel *chan, const int16_t ratesArray[]) { #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) /* Write the OFDM power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, POW_SM(ratesArray[rate18mb], 24) | POW_SM(ratesArray[rate12mb], 16) | POW_SM(ratesArray[rate9mb], 8) | POW_SM(ratesArray[rate6mb], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, POW_SM(ratesArray[rate54mb], 24) | POW_SM(ratesArray[rate48mb], 16) | POW_SM(ratesArray[rate36mb], 8) | POW_SM(ratesArray[rate24mb], 0) ); if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Write the CCK power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, POW_SM(ratesArray[rate2s], 24) | POW_SM(ratesArray[rate2l], 16) | POW_SM(ratesArray[rateXr], 8) /* XR target power */ | POW_SM(ratesArray[rate1l], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, POW_SM(ratesArray[rate11s], 24) | POW_SM(ratesArray[rate11l], 16) | POW_SM(ratesArray[rate5_5s], 8) | POW_SM(ratesArray[rate5_5l], 0) ); HALDEBUG(ah, HAL_DEBUG_RESET, "%s AR_PHY_POWER_TX_RATE3=0x%x AR_PHY_POWER_TX_RATE4=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_POWER_TX_RATE3), OS_REG_READ(ah,AR_PHY_POWER_TX_RATE4)); } /* Write the HT20 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, POW_SM(ratesArray[rateHt20_3], 24) | POW_SM(ratesArray[rateHt20_2], 16) | POW_SM(ratesArray[rateHt20_1], 8) | POW_SM(ratesArray[rateHt20_0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, POW_SM(ratesArray[rateHt20_7], 24) | POW_SM(ratesArray[rateHt20_6], 16) | POW_SM(ratesArray[rateHt20_5], 8) | POW_SM(ratesArray[rateHt20_4], 0) ); if (IEEE80211_IS_CHAN_HT40(chan)) { /* Write the HT40 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, POW_SM(ratesArray[rateHt40_3], 24) | POW_SM(ratesArray[rateHt40_2], 16) | POW_SM(ratesArray[rateHt40_1], 8) | POW_SM(ratesArray[rateHt40_0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, POW_SM(ratesArray[rateHt40_7], 24) | POW_SM(ratesArray[rateHt40_6], 16) | POW_SM(ratesArray[rateHt40_5], 8) | POW_SM(ratesArray[rateHt40_4], 0) ); /* Write the Dup/Ext 40 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, POW_SM(ratesArray[rateExtOfdm], 24) | POW_SM(ratesArray[rateExtCck], 16) | POW_SM(ratesArray[rateDupOfdm], 8) | POW_SM(ratesArray[rateDupCck], 0) ); } /* * Set max power to 30 dBm and, optionally, * enable TPC in tx descriptors. */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER | (AH5212(ah)->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0)); #undef POW_SM } /************************************************************** * ar5416SetTransmitPower * * Set the transmit power in the baseband for the given * operating channel and mode. */ HAL_BOOL ar5416SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define N(a) (sizeof (a) / sizeof (a[0])) #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) MODAL_EEP_HEADER *pModal; struct ath_hal_5212 *ahp = AH5212(ah); int16_t txPowerIndexOffset = 0; int i; uint16_t cfgCtl; uint16_t powerLimit; uint16_t twiceAntennaReduction; uint16_t twiceMaxRegulatoryPower; int16_t maxPower; HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar5416eeprom *pEepData = &ee->ee_base; HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); /* * Default to 2, is overridden based on the EEPROM version / value. */ AH5416(ah)->ah_ht40PowerIncForPdadc = 2; /* Setup info for the actual eeprom */ OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray)); cfgCtl = ath_hal_getctl(ah, chan); powerLimit = chan->ic_maxregpower * 2; twiceAntennaReduction = chan->ic_maxantgain; twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); pModal = &pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]; HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", __func__,chan->ic_freq, cfgCtl ); if (IS_EEP_MINOR_V2(ah)) { AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } if (!ar5416SetPowerPerRateTable(ah, pEepData, chan, &AH5416(ah)->ah_ratesArray[0], cfgCtl, twiceAntennaReduction, twiceMaxRegulatoryPower, powerLimit)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set tx power per rate table\n", __func__); return AH_FALSE; } if (!AH5416(ah)->ah_setPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", __func__); return AH_FALSE; } maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb], AH5416(ah)->ah_ratesArray[rateHt20_0]); if (IEEE80211_IS_CHAN_2GHZ(chan)) { maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]); } if (IEEE80211_IS_CHAN_HT40(chan)) { maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]); } ahp->ah_tx6PowerInHalfDbm = maxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; ahp->ah_txPowerIndexOffset = txPowerIndexOffset; /* * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table (0 offset if rates EEPROM not loaded) */ for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { AH5416(ah)->ah_ratesArray[i] = (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]); if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; } #ifdef AH_EEPROM_DUMP /* * Dump the rate array whilst it represents the intended dBm*2 * values versus what's being adjusted before being programmed * in. Keep this in mind if you code up this function and enable * this debugging; the values won't necessarily be what's being * programmed into the hardware. */ ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray); #endif /* * Merlin and later have a power offset, so subtract * pwr_table_offset * 2 from each value. The default * power offset is -5 dBm - ie, a register value of 0 * equates to a TX power of -5 dBm. */ if (AR_SREV_MERLIN_20_OR_LATER(ah)) { int8_t pwr_table_offset; (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); /* Underflow power gets clamped at raw value 0 */ /* Overflow power gets camped at AR5416_MAX_RATE_POWER */ for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { /* * + pwr_table_offset is in dBm * + ratesArray is in 1/2 dBm */ AH5416(ah)->ah_ratesArray[i] -= (pwr_table_offset * 2); if (AH5416(ah)->ah_ratesArray[i] < 0) AH5416(ah)->ah_ratesArray[i] = 0; else if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; } } /* * Adjust rates for OLC where needed * * The following CCK rates need adjusting when doing 2.4ghz * CCK transmission. * * + rate2s, rate2l, rate1l, rate11s, rate11l, rate5_5s, rate5_5l * + rateExtCck, rateDupCck * * They're adjusted here regardless. The hardware then gets * programmed as needed. 5GHz operation doesn't program in CCK * rates for legacy mode but they seem to be initialised for * HT40 regardless of channel type. */ if (AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { int adj[] = { rate2s, rate2l, rate1l, rate11s, rate11l, rate5_5s, rate5_5l, rateExtCck, rateDupCck }; int cck_ofdm_delta = 2; int i; for (i = 0; i < N(adj); i++) { AH5416(ah)->ah_ratesArray[adj[i]] -= cck_ofdm_delta; if (AH5416(ah)->ah_ratesArray[adj[i]] < 0) AH5416(ah)->ah_ratesArray[adj[i]] = 0; } } /* * Adjust the HT40 power to meet the correct target TX power * for 40MHz mode, based on TX power curves that are established * for 20MHz mode. * * XXX handle overflow/too high power level? */ if (IEEE80211_IS_CHAN_HT40(chan)) { AH5416(ah)->ah_ratesArray[rateHt40_0] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_1] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_2] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_3] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_4] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_5] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_6] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_7] += AH5416(ah)->ah_ht40PowerIncForPdadc; } /* Write the TX power rate registers */ ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray); /* Write the Power subtraction for dynamic chain changing, for per-packet powertx */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, POW_SM(pModal->pwrDecreaseFor3Chain, 6) | POW_SM(pModal->pwrDecreaseFor2Chain, 0) ); return AH_TRUE; #undef POW_SM #undef N } /* * Exported call to check for a recent gain reading and return * the current state of the thermal calibration gain engine. */ HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah) { return (HAL_RFGAIN_INACTIVE); } /* * Places all of hardware into reset */ HAL_BOOL ar5416Disable(struct ath_hal *ah) { if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; if (! ar5416SetResetReg(ah, HAL_RESET_COLD)) return AH_FALSE; AH5416(ah)->ah_initPLL(ah, AH_NULL); return (AH_TRUE); } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar5416PhyDisable(struct ath_hal *ah) { if (! ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; AH5416(ah)->ah_initPLL(ah, AH_NULL); return (AH_TRUE); } /* * Write the given reset bit mask into the reset register */ HAL_BOOL ar5416SetResetReg(struct ath_hal *ah, uint32_t type) { /* * Set force wake */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); switch (type) { case HAL_RESET_POWER_ON: return ar5416SetResetPowerOn(ah); case HAL_RESET_WARM: case HAL_RESET_COLD: return ar5416SetReset(ah, type); default: HALASSERT(AH_FALSE); return AH_FALSE; } } static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah) { /* Power On Reset (Hard Reset) */ /* * Set force wake * * If the MAC was running, previously calling * reset will wake up the MAC but it may go back to sleep * before we can start polling. * Set force wake stops that * This must be called before initiating a hard reset. */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* * PowerOn reset can be used in open loop power control or failure recovery. * If we do RTC reset while DMA is still running, hardware may corrupt memory. * Therefore, we need to reset AHB first to stop DMA. */ if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, AR_RC_AHB); /* * RTC reset and clear */ OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(20); if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, 0); OS_REG_WRITE(ah, AR_RTC_RESET, 1); /* * Poll till RTC is ON */ if (!ath_hal_wait(ah, AR_RTC_STATUS, AR_RTC_PM_STATUS_M, AR_RTC_STATUS_ON)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RTC not waking up\n", __func__); return AH_FALSE; } return ar5416SetReset(ah, HAL_RESET_COLD); } static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type) { uint32_t tmpReg, mask; uint32_t rst_flags; #ifdef AH_SUPPORT_AR9130 /* Because of the AR9130 specific registers */ if (AR_SREV_HOWL(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "[ath] HOWL: Fiddling with derived clk!\n"); uint32_t val = OS_REG_READ(ah, AR_RTC_DERIVED_CLK); val &= ~AR_RTC_DERIVED_CLK_PERIOD; val |= SM(1, AR_RTC_DERIVED_CLK_PERIOD); OS_REG_WRITE(ah, AR_RTC_DERIVED_CLK, val); (void) OS_REG_READ(ah, AR_RTC_DERIVED_CLK); } #endif /* AH_SUPPORT_AR9130 */ /* * Force wake */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); #ifdef AH_SUPPORT_AR9130 if (AR_SREV_HOWL(ah)) { rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD | AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET; } else { #endif /* AH_SUPPORT_AR9130 */ /* * Reset AHB * * (In case the last interrupt source was a bus timeout.) * XXX TODO: this is not the way to do it! It should be recorded * XXX by the interrupt handler and passed _into_ the * XXX reset path routine so this occurs. */ tmpReg = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); if (tmpReg & (AR_INTR_SYNC_LOCAL_TIMEOUT|AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); OS_REG_WRITE(ah, AR_RC, AR_RC_AHB|AR_RC_HOSTIF); } else { OS_REG_WRITE(ah, AR_RC, AR_RC_AHB); } rst_flags = AR_RTC_RC_MAC_WARM; if (type == HAL_RESET_COLD) rst_flags |= AR_RTC_RC_MAC_COLD; #ifdef AH_SUPPORT_AR9130 } #endif /* AH_SUPPORT_AR9130 */ OS_REG_WRITE(ah, AR_RTC_RC, rst_flags); if (AR_SREV_HOWL(ah)) OS_DELAY(10000); else OS_DELAY(100); /* * Clear resets and force wakeup */ OS_REG_WRITE(ah, AR_RTC_RC, 0); if (!ath_hal_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RTC stuck in MAC reset\n", __func__); return AH_FALSE; } /* Clear AHB reset */ if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, 0); if (AR_SREV_HOWL(ah)) OS_DELAY(50); if (AR_SREV_HOWL(ah)) { uint32_t mask; mask = OS_REG_READ(ah, AR_CFG); if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { HALDEBUG(ah, HAL_DEBUG_RESET, "CFG Byte Swap Set 0x%x\n", mask); } else { mask = INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; OS_REG_WRITE(ah, AR_CFG, mask); HALDEBUG(ah, HAL_DEBUG_RESET, "Setting CFG 0x%x\n", OS_REG_READ(ah, AR_CFG)); } } else { if (type == HAL_RESET_COLD) { if (isBigEndian()) { /* * Set CFG, little-endian for descriptor accesses. */ mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; #ifndef AH_NEED_DESC_SWAP mask |= AR_CFG_SWTD; #endif HALDEBUG(ah, HAL_DEBUG_RESET, "%s Applying descriptor swap\n", __func__); OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } } return AH_TRUE; } void ar5416InitChainMasks(struct ath_hal *ah) { int rx_chainmask = AH5416(ah)->ah_rx_chainmask; /* Flip this for this chainmask regardless of chip */ if (rx_chainmask == 0x5) OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); /* * Workaround for OWL 1.0 calibration failure; enable multi-chain; * then set true mask after calibration. */ if (IS_5416V1(ah) && (rx_chainmask == 0x5 || rx_chainmask == 0x3)) { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); } else { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, AH5416(ah)->ah_rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, AH5416(ah)->ah_rx_chainmask); } OS_REG_WRITE(ah, AR_SELFGEN_MASK, AH5416(ah)->ah_tx_chainmask); if (AH5416(ah)->ah_tx_chainmask == 0x5) OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); if (AR_SREV_HOWL(ah)) { OS_REG_WRITE(ah, AR_PHY_ANALOG_SWAP, OS_REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001); } } /* * Work-around for Owl 1.0 calibration failure. * * ar5416InitChainMasks sets the RX chainmask to 0x7 if it's Owl 1.0 * due to init calibration failures. ar5416RestoreChainMask restores * these registers to the correct setting. */ void ar5416RestoreChainMask(struct ath_hal *ah) { int rx_chainmask = AH5416(ah)->ah_rx_chainmask; if (IS_5416V1(ah) && (rx_chainmask == 0x5 || rx_chainmask == 0x3)) { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); } } void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2; if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_PLL_CLKSEL); if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0xa, AR_RTC_PLL_DIV); else pll |= SM(0xb, AR_RTC_PLL_DIV); } else pll |= SM(0xb, AR_RTC_PLL_DIV); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); /* TODO: * For multi-band owl, switch between bands by reiniting the PLL. */ OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_SLEEP_DERIVED_CLK); } static void ar5416SetDefGainValues(struct ath_hal *ah, const MODAL_EEP_HEADER *pModal, const struct ar5416eeprom *eep, uint8_t txRxAttenLocal, int regChainOffset, int i) { if (IS_EEP_MINOR_V3(ah)) { txRxAttenLocal = pModal->txRxAttenCh[i]; if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, pModal->xatten2Margin[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[i]); } else { OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_BSW_MARGIN, pModal->bswMargin[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_BSW_ATTEN, pModal->bswAtten[i]); } } if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); } else { OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_RXTX_MARGIN, pModal->rxTxMarginCh[i]); } } /* * Get the register chain offset for the given chain. * * Take into account the register chain swapping with AR5416 v2.0. * * XXX make sure that the reg chain swapping is only done for * XXX AR5416 v2.0 or greater, and not later chips? */ int ar5416GetRegChainOffset(struct ath_hal *ah, int i) { int regChainOffset; if (AR_SREV_5416_V20_OR_LATER(ah) && (AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && (i != 0)) { /* Regs are swapped from chain 2 to 1 for 5416 2_0 with * only chains 0 and 2 populated */ regChainOffset = (i == 1) ? 0x2000 : 0x1000; } else { regChainOffset = i * 0x1000; } return regChainOffset; } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ HAL_BOOL ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { const HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; const struct ar5416eeprom *eep = &ee->ee_base; const MODAL_EEP_HEADER *pModal; int i, regChainOffset; uint8_t txRxAttenLocal; /* workaround for eeprom versions <= 14.2 */ HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); pModal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]; /* NB: workaround for eeprom versions <= 14.2 */ txRxAttenLocal = IEEE80211_IS_CHAN_2GHZ(chan) ? 23 : 44; OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (AR_SREV_MERLIN(ah)) { if (i >= 2) break; } regChainOffset = ar5416GetRegChainOffset(ah, i); OS_REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4 + regChainOffset, (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4 + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); /* * Large signal upgrade, * If 14.3 or later EEPROM, use * txRxAttenLocal = pModal->txRxAttenCh[i] * else txRxAttenLocal is fixed value above. */ if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) ar5416SetDefGainValues(ah, pModal, eep, txRxAttenLocal, regChainOffset, i); } if (AR_SREV_MERLIN_10_OR_LATER(ah)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_OB, pModal->ob); OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_DB, pModal->db); OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH1, AR_AN_RF2G1_CH1_OB, pModal->ob_ch1); OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH1, AR_AN_RF2G1_CH1_DB, pModal->db_ch1); } else { OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH0, AR_AN_RF5G1_CH0_OB5, pModal->ob); OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH0, AR_AN_RF5G1_CH0_DB5, pModal->db); OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH1, AR_AN_RF5G1_CH1_OB5, pModal->ob_ch1); OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH1, AR_AN_RF5G1_CH1_DB5, pModal->db_ch1); } OS_A_REG_RMW_FIELD(ah, AR_AN_TOP2, AR_AN_TOP2_XPABIAS_LVL, pModal->xpaBiasLvl); OS_A_REG_RMW_FIELD(ah, AR_AN_TOP2, AR_AN_TOP2_LOCALBIAS, !!(pModal->flagBits & AR5416_EEP_FLAG_LOCALBIAS)); OS_A_REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG, !!(pModal->flagBits & AR5416_EEP_FLAG_FORCEXPAON)); } OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); if (! AR_SREV_MERLIN_10_OR_LATER(ah)) OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pModal->pgaDesiredSize); OS_REG_WRITE(ah, AR_PHY_RF_CTL4, SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, pModal->thresh62); } else { OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62, pModal->thresh62); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CCA_THRESH62, pModal->thresh62); } /* Minor Version Specific application */ if (IS_EEP_MINOR_V2(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_DATA_START, pModal->txFrameToDataStart); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_PA_ON, pModal->txFrameToPaOn); } if (IS_EEP_MINOR_V3(ah) && IEEE80211_IS_CHAN_HT40(chan)) /* Overwrite switch settling with HT40 value */ OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); if (AR_SREV_MERLIN_20_OR_LATER(ah) && EEP_MINOR(ah) >= AR5416_EEP_MINOR_VER_19) OS_REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, pModal->miscBits); if (AR_SREV_MERLIN_20(ah) && EEP_MINOR(ah) >= AR5416_EEP_MINOR_VER_20) { if (IEEE80211_IS_CHAN_2GHZ(chan)) OS_A_REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); else if (eep->baseEepHeader.dacHiPwrMode_5G) OS_A_REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); else OS_A_REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); OS_DELAY(100); OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, pModal->miscBits >> 2); OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, AR_PHY_TX_DESIRED_SCALE_CCK, eep->baseEepHeader.desiredScaleCCK); } return (AH_TRUE); } /* * Helper functions common for AP/CB/XB */ /* * Set the target power array "ratesArray" from the * given set of target powers. * * This is used by the various chipset/EEPROM TX power * setup routines. */ void ar5416SetRatesArrayFromTargetPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *ratesArray, const CAL_TARGET_POWER_LEG *targetPowerCck, const CAL_TARGET_POWER_LEG *targetPowerCckExt, const CAL_TARGET_POWER_LEG *targetPowerOfdm, const CAL_TARGET_POWER_LEG *targetPowerOfdmExt, const CAL_TARGET_POWER_HT *targetPowerHt20, const CAL_TARGET_POWER_HT *targetPowerHt40) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; /* Blank the rates array, to be consistent */ for (i = 0; i < Ar5416RateSize; i++) ratesArray[i] = 0; /* Set rates Array from collected data */ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm->tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm->tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm->tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm->tPow2x[3]; ratesArray[rateXr] = targetPowerOfdm->tPow2x[0]; for (i = 0; i < N(targetPowerHt20->tPow2x); i++) { ratesArray[rateHt20_0 + i] = targetPowerHt20->tPow2x[i]; } if (IEEE80211_IS_CHAN_2GHZ(chan)) { ratesArray[rate1l] = targetPowerCck->tPow2x[0]; ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck->tPow2x[1]; ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck->tPow2x[2]; ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck->tPow2x[3]; } if (IEEE80211_IS_CHAN_HT40(chan)) { for (i = 0; i < N(targetPowerHt40->tPow2x); i++) { ratesArray[rateHt40_0 + i] = targetPowerHt40->tPow2x[i]; } ratesArray[rateDupOfdm] = targetPowerHt40->tPow2x[0]; ratesArray[rateDupCck] = targetPowerHt40->tPow2x[0]; ratesArray[rateExtOfdm] = targetPowerOfdmExt->tPow2x[0]; if (IEEE80211_IS_CHAN_2GHZ(chan)) { ratesArray[rateExtCck] = targetPowerCckExt->tPow2x[0]; } } #undef N } /* * ar5416SetPowerPerRateTable * * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit) { #define N(a) (sizeof(a)/sizeof(a[0])) /* Local defines to distinguish between extension and control CTL's */ #define EXT_ADDITIVE (0x8000) #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; int16_t twiceLargestAntenna; CAL_CTL_DATA *rep; CAL_TARGET_POWER_LEG targetPowerOfdm, targetPowerCck = {0, {0, 0, 0, 0}}; CAL_TARGET_POWER_LEG targetPowerOfdmExt = {0, {0, 0, 0, 0}}, targetPowerCckExt = {0, {0, 0, 0, 0}}; CAL_TARGET_POWER_HT targetPowerHt20, targetPowerHt40 = {0, {0, 0, 0, 0}}; int16_t scaledPower, minCtlPower; #define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ static const uint16_t ctlModesFor11a[] = { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; static const uint16_t ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; const uint16_t *pCtlMode; uint16_t numCtlModes, ctlMode, freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); /* Compute TxPower reduction due to Antenna Gain */ twiceLargestAntenna = AH_MAX(AH_MAX( pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[0], pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]), pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); #if 0 /* Turn it back on if we need to calculate per chain antenna gain reduction */ /* Use only if the expected gain > 6dbi */ /* Chain 0 is always used */ twiceLargestAntenna = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[0]; /* Look at antenna gains of Chains 1 and 2 if the TX mask is set */ if (ahp->ah_tx_chainmask & 0x2) twiceLargestAntenna = AH_MAX(twiceLargestAntenna, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]); if (ahp->ah_tx_chainmask & 0x4) twiceLargestAntenna = AH_MAX(twiceLargestAntenna, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); #endif twiceLargestAntenna = (int16_t)AH_MIN((AntennaReduction) - twiceLargestAntenna, 0); /* XXX setup for 5212 use (really used?) */ ath_hal_eepromSet(ah, IEEE80211_IS_CHAN_2GHZ(chan) ? AR_EEP_ANTGAINMAX_2 : AR_EEP_ANTGAINMAX_5, twiceLargestAntenna); /* * scaledPower is the minimum of the user input power level and * the regulatory allowed power level */ scaledPower = AH_MIN(powerLimit, twiceMaxRegulatoryPower + twiceLargestAntenna); /* Reduce scaled Power by number of chains active to get to per chain tx power level */ /* TODO: better value than these? */ switch (owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask)) { case 1: break; case 2: scaledPower -= pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pwrDecreaseFor2Chain; break; case 3: scaledPower -= pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pwrDecreaseFor3Chain; break; default: return AH_FALSE; /* Unsupported number of chains */ } scaledPower = AH_MAX(0, scaledPower); /* Get target powers from EEPROM - our baseline for TX Power */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Setup for CTL modes */ numCtlModes = N(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; /* CTL_11B, CTL_11G, CTL_2GHT20 */ pCtlMode = ctlModesFor11g; ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, AH_FALSE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT20, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); if (IEEE80211_IS_CHAN_HT40(chan)) { numCtlModes = N(ctlModesFor11g); /* All 2G CTL's */ ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT40, AR5416_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); /* Get target powers for extension channels */ ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, AH_TRUE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); } } else { /* Setup for CTL modes */ numCtlModes = N(ctlModesFor11a) - SUB_NUM_CTL_MODES_AT_5G_40; /* CTL_11A, CTL_5GHT20 */ pCtlMode = ctlModesFor11a; ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower5GHT20, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); if (IEEE80211_IS_CHAN_HT40(chan)) { numCtlModes = N(ctlModesFor11a); /* All 5G CTL's */ ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower5GHT40, AR5416_NUM_5G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); } } /* * For MIMO, need to apply regulatory caps individually across dynamically * running modes: CCK, OFDM, HT20, HT40 * * The outer loop walks through each possible applicable runtime mode. * The inner loop walks through each ctlIndex entry in EEPROM. * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. * */ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { HAL_BOOL isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || (pCtlMode[ctlMode] == CTL_2GHT40); if (isHt40CtlMode) { freq = centers.ctl_center; } else if (pCtlMode[ctlMode] & EXT_ADDITIVE) { freq = centers.ext_center; } else { freq = centers.ctl_center; } /* walk through each CTL index stored in EEPROM */ for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { uint16_t twiceMinEdgePower; /* compare test group from regulatory channel list with test mode from pCtlMode list */ if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { rep = &(pEepData->ctlData[i]); twiceMinEdgePower = ar5416GetMaxEdgePower(freq, rep->ctlEdges[owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask) - 1], IEEE80211_IS_CHAN_2GHZ(chan)); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); } else { /* specific */ twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = (uint8_t)AH_MIN(twiceMaxEdgePower, scaledPower); /* Apply ctl mode to correct target power set */ switch(pCtlMode[ctlMode]) { case CTL_11B: for (i = 0; i < N(targetPowerCck.tPow2x); i++) { targetPowerCck.tPow2x[i] = (uint8_t)AH_MIN(targetPowerCck.tPow2x[i], minCtlPower); } break; case CTL_11A: case CTL_11G: for (i = 0; i < N(targetPowerOfdm.tPow2x); i++) { targetPowerOfdm.tPow2x[i] = (uint8_t)AH_MIN(targetPowerOfdm.tPow2x[i], minCtlPower); } break; case CTL_5GHT20: case CTL_2GHT20: for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { targetPowerHt20.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt20.tPow2x[i], minCtlPower); } break; case CTL_11B_EXT: targetPowerCckExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerCckExt.tPow2x[0], minCtlPower); break; case CTL_11A_EXT: case CTL_11G_EXT: targetPowerOfdmExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerOfdmExt.tPow2x[0], minCtlPower); break; case CTL_5GHT40: case CTL_2GHT40: for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { targetPowerHt40.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt40.tPow2x[i], minCtlPower); } break; default: return AH_FALSE; break; } } /* end ctl mode checking */ /* Set rates Array from collected data */ ar5416SetRatesArrayFromTargetPower(ah, chan, ratesArray, &targetPowerCck, &targetPowerCckExt, &targetPowerOfdm, &targetPowerOfdmExt, &targetPowerHt20, &targetPowerHt40); return AH_TRUE; #undef EXT_ADDITIVE #undef CTL_11A_EXT #undef CTL_11G_EXT #undef CTL_11B_EXT #undef SUB_NUM_CTL_MODES_AT_5G_40 #undef SUB_NUM_CTL_MODES_AT_2G_40 #undef N } /************************************************************************** * fbin2freq * * Get channel value from binary representation held in eeprom * RETURNS: the frequency in MHz */ static uint16_t fbin2freq(uint8_t fbin, HAL_BOOL is2GHz) { /* * Reserved value 0xFF provides an empty definition both as * an fbin and as a frequency - do not convert */ if (fbin == AR5416_BCHAN_UNUSED) { return fbin; } return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); } /* * ar5416GetMaxEdgePower * * Find the maximum conformance test limit for the given channel and CTL info */ uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz) { uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; /* Get the edge power */ for (i = 0; (i < AR5416_NUM_BAND_EDGES) && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED) ; i++) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ if (freq == fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { twiceMaxEdgePower = MS(pRdEdgesPower[i].tPowerFlag, CAL_CTL_EDGES_POWER); break; } else if ((i > 0) && (freq < fbin2freq(pRdEdgesPower[i].bChannel, is2GHz))) { if (fbin2freq(pRdEdgesPower[i - 1].bChannel, is2GHz) < freq && (pRdEdgesPower[i - 1].tPowerFlag & CAL_CTL_EDGES_FLAG) != 0) { twiceMaxEdgePower = MS(pRdEdgesPower[i - 1].tPowerFlag, CAL_CTL_EDGES_POWER); } /* Leave loop - no more affecting edges possible in this monotonic increasing list */ break; } } HALASSERT(twiceMaxEdgePower > 0); return twiceMaxEdgePower; } /************************************************************** * ar5416GetTargetPowers * * Return the rates of target power for the given target power table * channel, and number of channels */ void ar5416GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_HT *powInfo, uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, uint16_t numRates, HAL_BOOL isHt40Target) { uint16_t clo, chi; int i; int matchIndex = -1, lowIndex = -1; uint16_t freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); freq = isHt40Target ? centers.synth_center : centers.ctl_center; /* Copy the target powers into the temp channel list */ if (freq <= fbin2freq(powInfo[0].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if ((freq < fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) && (freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) { HALASSERT(freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))); matchIndex = i - 1; } } if (matchIndex != -1) { OS_MEMCPY(pNewPower, &powInfo[matchIndex], sizeof(*pNewPower)); } else { HALASSERT(lowIndex != -1); /* * Get the lower and upper channels, target powers, * and interpolate between them. */ clo = fbin2freq(powInfo[lowIndex].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); chi = fbin2freq(powInfo[lowIndex + 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (uint8_t)ath_ee_interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } /************************************************************** * ar5416GetTargetPowersLeg * * Return the four rates of target power for the given target power table * channel, and number of channels */ void ar5416GetTargetPowersLeg(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_LEG *powInfo, uint16_t numChannels, CAL_TARGET_POWER_LEG *pNewPower, uint16_t numRates, HAL_BOOL isExtTarget) { uint16_t clo, chi; int i; int matchIndex = -1, lowIndex = -1; uint16_t freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); freq = (isExtTarget) ? centers.ext_center :centers.ctl_center; /* Copy the target powers into the temp channel list */ if (freq <= fbin2freq(powInfo[0].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if ((freq < fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) && (freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) { HALASSERT(freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))); matchIndex = i - 1; } } if (matchIndex != -1) { OS_MEMCPY(pNewPower, &powInfo[matchIndex], sizeof(*pNewPower)); } else { HALASSERT(lowIndex != -1); /* * Get the lower and upper channels, target powers, * and interpolate between them. */ clo = fbin2freq(powInfo[lowIndex].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); chi = fbin2freq(powInfo[lowIndex + 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (uint8_t)ath_ee_interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } /* * Set the gain boundaries for the given radio chain. * * The gain boundaries tell the hardware at what point in the * PDADC array to "switch over" from one PD gain setting * to another. There's also a gain overlap between two * PDADC array gain curves where there's valid PD values * for 2 gain settings. * * The hardware uses the gain overlap and gain boundaries * to determine which gain curve to use for the given * target TX power. */ void ar5416SetGainBoundariesClosedLoop(struct ath_hal *ah, int i, uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) { int regChainOffset; regChainOffset = ar5416GetRegChainOffset(ah, i); HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: gainOverlap_t2: %d," " gainBoundaries: %d, %d, %d, %d\n", __func__, i, pdGainOverlap_t2, gainBoundaries[0], gainBoundaries[1], gainBoundaries[2], gainBoundaries[3]); OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); } /* * Get the gain values and the number of gain levels given * in xpdMask. * * The EEPROM xpdMask determines which power detector gain * levels were used during calibration. Each of these mask * bits maps to a fixed gain level in hardware. */ uint16_t ar5416GetXpdGainValues(struct ath_hal *ah, uint16_t xpdMask, uint16_t xpdGainValues[]) { int i; uint16_t numXpdGain = 0; for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { if (numXpdGain >= AR5416_NUM_PD_GAINS) { HALASSERT(0); break; } xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); numXpdGain++; } } return numXpdGain; } /* * Write the detector gain and biases. * * There are four power detector gain levels. The xpdMask in the EEPROM * determines which power detector gain levels have TX power calibration * data associated with them. This function writes the number of * PD gain levels and their values into the hardware. * * This is valid for all TX chains - the calibration data itself however * will likely differ per-chain. */ void ar5416WriteDetectorGainBiases(struct ath_hal *ah, uint16_t numXpdGain, uint16_t xpdGainValues[]) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: numXpdGain: %d," " xpdGainValues: %d, %d, %d\n", __func__, numXpdGain, xpdGainValues[0], xpdGainValues[1], xpdGainValues[2]); OS_REG_WRITE(ah, AR_PHY_TPCRG1, (OS_REG_READ(ah, AR_PHY_TPCRG1) & ~(AR_PHY_TPCRG1_NUM_PD_GAIN | AR_PHY_TPCRG1_PD_GAIN_1 | AR_PHY_TPCRG1_PD_GAIN_2 | AR_PHY_TPCRG1_PD_GAIN_3)) | SM(numXpdGain - 1, AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(xpdGainValues[0], AR_PHY_TPCRG1_PD_GAIN_1 ) | SM(xpdGainValues[1], AR_PHY_TPCRG1_PD_GAIN_2) | SM(xpdGainValues[2], AR_PHY_TPCRG1_PD_GAIN_3)); } /* * Write the PDADC array to the given radio chain i. * * The 32 PDADC registers are written without any care about * their contents - so if various chips treat values as "special", * this routine will not care. */ void ar5416WritePdadcValues(struct ath_hal *ah, int i, uint8_t pdadcValues[]) { int regOffset, regChainOffset; int j; int reg32; regChainOffset = ar5416GetRegChainOffset(ah, i); regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = ((pdadcValues[4*j + 0] & 0xFF) << 0) | ((pdadcValues[4*j + 1] & 0xFF) << 8) | ((pdadcValues[4*j + 2] & 0xFF) << 16) | ((pdadcValues[4*j + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regOffset, reg32); HALDEBUG(ah, HAL_DEBUG_EEPROM, "PDADC: Chain %d |" " PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d" " Value %3d | PDADC %3d Value %3d |\n", i, 4*j, pdadcValues[4*j], 4*j+1, pdadcValues[4*j + 1], 4*j+2, pdadcValues[4*j + 2], 4*j+3, pdadcValues[4*j + 3]); regOffset += 4; } } /************************************************************** * ar5416SetPowerCalTable * * Pull the PDADC piers from cal data and interpolate them across the given * points as well as from the nearest pier(s) to get a power detector * linear voltage to power level table. */ HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) { CAL_DATA_PER_FREQ *pRawDataset; uint8_t *pCalBChans = AH_NULL; uint16_t pdGainOverlap_t2; static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; uint16_t numPiers, i; int16_t tMinCalPower; uint16_t numXpdGain, xpdMask; uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; uint32_t regChainOffset; OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; if (IS_EEP_MINOR_V2(ah)) { pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; } else { pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } if (IEEE80211_IS_CHAN_2GHZ(chan)) { pCalBChans = pEepData->calFreqPier2G; numPiers = AR5416_NUM_2G_CAL_PIERS; } else { pCalBChans = pEepData->calFreqPier5G; numPiers = AR5416_NUM_5G_CAL_PIERS; } /* Calculate the value of xpdgains from the xpdGain Mask */ numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); /* Write the detector gain biases and their number */ ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); for (i = 0; i < AR5416_MAX_CHAINS; i++) { regChainOffset = ar5416GetRegChainOffset(ah, i); if (pEepData->baseEepHeader.txMask & (1 << i)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { pRawDataset = pEepData->calPierData2G[i]; } else { pRawDataset = pEepData->calPierData5G[i]; } /* Fetch the gain boundaries and the PDADC values */ ar5416GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, &tMinCalPower, gainBoundaries, pdadcValues, numXpdGain); if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { ar5416SetGainBoundariesClosedLoop(ah, i, pdGainOverlap_t2, gainBoundaries); } /* Write the power values into the baseband power table */ ar5416WritePdadcValues(ah, i, pdadcValues); } } *pTxPowerIndexOffset = 0; return AH_TRUE; } /************************************************************** * ar5416GetGainBoundariesAndPdadcs * * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar5416SetPowerCalTable only. */ void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, uint8_t * bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, uint16_t numXpdGains) { int i, j, k; int16_t ss; /* potentially -ve index for taking care of pdGainOverlap */ uint16_t idxL, idxR, numPiers; /* Pier indexes */ /* filled out Vpd table for all pdGains (chanL) */ static uint8_t vpdTableL[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ static uint8_t vpdTableR[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ static uint8_t vpdTableI[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; uint8_t *pVpdL, *pVpdR, *pPwrL, *pPwrR; uint8_t minPwrT4[AR5416_NUM_PD_GAINS]; uint8_t maxPwrT4[AR5416_NUM_PD_GAINS]; int16_t vpdStep; int16_t tmpVal; uint16_t sizeCurrVpdTable, maxIndex, tgtIndex; HAL_BOOL match; int16_t minDelta = 0; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); /* Trim numPiers for the number of populated channel Piers */ for (numPiers = 0; numPiers < availPiers; numPiers++) { if (bChans[numPiers] == AR5416_BCHAN_UNUSED) { break; } } /* Find pier indexes around the current channel */ match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), bChans, numPiers, &idxL, &idxR); if (match) { /* Directly fill both vpd tables from the matching index */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pRawDataSet[idxL].pwrPdg[i], pRawDataSet[idxL].vpdPdg[i], AR5416_PD_GAIN_ICEPTS, vpdTableI[i]); } } else { for (i = 0; i < numXpdGains; i++) { pVpdL = pRawDataSet[idxL].vpdPdg[i]; pPwrL = pRawDataSet[idxL].pwrPdg[i]; pVpdR = pRawDataSet[idxR].vpdPdg[i]; pPwrR = pRawDataSet[idxR].pwrPdg[i]; /* Start Vpd interpolation from the max of the minimum powers */ minPwrT4[i] = AH_MAX(pPwrL[0], pPwrR[0]); /* End Vpd interpolation from the min of the max powers */ maxPwrT4[i] = AH_MIN(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); HALASSERT(maxPwrT4[i] > minPwrT4[i]); /* Fill pier Vpds */ ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrL, pVpdL, AR5416_PD_GAIN_ICEPTS, vpdTableL[i]); ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrR, pVpdR, AR5416_PD_GAIN_ICEPTS, vpdTableR[i]); /* Interpolate the final vpd */ for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { vpdTableI[i][j] = (uint8_t)(ath_ee_interpolate((uint16_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), bChans[idxL], bChans[idxR], vpdTableL[i][j], vpdTableR[i][j])); } } } *pMinCalPower = (int16_t)(minPwrT4[0] / 2); k = 0; /* index for the final table */ for (i = 0; i < numXpdGains; i++) { if (i == (numXpdGains - 1)) { pPdGainBoundaries[i] = (uint16_t)(maxPwrT4[i] / 2); } else { pPdGainBoundaries[i] = (uint16_t)((maxPwrT4[i] + minPwrT4[i+1]) / 4); } pPdGainBoundaries[i] = (uint16_t)AH_MIN(AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]); /* NB: only applies to owl 1.0 */ if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah) ) { /* * fix the gain delta, but get a delta that can be applied to min to * keep the upper power values accurate, don't think max needs to * be adjusted because should not be at that area of the table? */ minDelta = pPdGainBoundaries[0] - 23; pPdGainBoundaries[0] = 23; } else { minDelta = 0; } /* Find starting index for this pdGain */ if (i == 0) { if (AR_SREV_MERLIN_10_OR_LATER(ah)) ss = (int16_t)(0 - (minPwrT4[i] / 2)); else ss = 0; /* for the first pdGain, start from index 0 */ } else { /* need overlap entries extrapolated below. */ ss = (int16_t)((pPdGainBoundaries[i-1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); } vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); pPDADCValues[k++] = (uint8_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 +1); tgtIndex = (uint8_t)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { pPDADCValues[k++] = vpdTableI[i][ss++]; } vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - vpdTableI[i][sizeCurrVpdTable - 2]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex >= maxIndex) { /* need to extrapolate above */ while ((ss <= tgtIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex +1) * vpdStep)); pPDADCValues[k++] = (uint8_t)((tmpVal > 255) ? 255 : tmpVal); ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ /* Fill out pdGainBoundaries - only up to 2 allowed here, but hardware allows up to 4 */ while (i < AR5416_PD_GAINS_IN_MASK) { pPdGainBoundaries[i] = pPdGainBoundaries[i-1]; i++; } while (k < AR5416_NUM_PDADC_VALUES) { pPDADCValues[k] = pPDADCValues[k-1]; k++; } return; } /* * The linux ath9k driver and (from what I've been told) the reference * Atheros driver enables the 11n PHY by default whether or not it's * configured. */ static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t phymode; uint32_t enableDacFifo = 0; HAL_HT_MACMODE macmode; /* MAC - 20/40 mode */ if (AR_SREV_KITE_10_OR_LATER(ah)) enableDacFifo = (OS_REG_READ(ah, AR_PHY_TURBO) & AR_PHY_FC_ENABLE_DAC_FIFO); /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH | enableDacFifo; /* Configure baseband for dynamic 20/40 operation */ if (IEEE80211_IS_CHAN_HT40(chan)) { phymode |= AR_PHY_FC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ if (IEEE80211_IS_CHAN_HT40U(chan)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; #if 0 /* Configure 20/25 spacing */ if (ht->ht_extprotspacing == HAL_HT_EXTPROTSPACING_25) phymode |= AR_PHY_FC_DYN2040_EXT_CH; #endif macmode = HAL_HT_MACMODE_2040; } else macmode = HAL_HT_MACMODE_20; OS_REG_WRITE(ah, AR_PHY_TURBO, phymode); /* Configure MAC for 20/40 operation */ ar5416Set11nMac2040(ah, macmode); /* global transmit timeout (25 TUs default)*/ /* XXX - put this elsewhere??? */ OS_REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S) ; /* carrier sense timeout */ OS_REG_SET_BIT(ah, AR_GTTM, AR_GTTM_CST_USEC); OS_REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); } void ar5416GetChannelCenters(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers) { uint16_t freq = ath_hal_gethwchannel(ah, chan); centers->ctl_center = freq; centers->synth_center = freq; /* * In 20/40 phy mode, the center frequency is * "between" the control and extension channels. */ if (IEEE80211_IS_CHAN_HT40U(chan)) { centers->synth_center += HT40_CHANNEL_CENTER_SHIFT; centers->ext_center = centers->synth_center + HT40_CHANNEL_CENTER_SHIFT; } else if (IEEE80211_IS_CHAN_HT40D(chan)) { centers->synth_center -= HT40_CHANNEL_CENTER_SHIFT; centers->ext_center = centers->synth_center - HT40_CHANNEL_CENTER_SHIFT; } else { centers->ext_center = freq; } } /* * Override the INI vals being programmed. */ static void ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t val; /* * Set the RX_ABORT and RX_DIS and clear if off only after * RXE is set for MAC. This prevents frames with corrupted * descriptor status. */ OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); if (AR_SREV_MERLIN_10_OR_LATER(ah)) { val = OS_REG_READ(ah, AR_PCU_MISC_MODE2); val &= (~AR_PCU_MISC_MODE2_ADHOC_MCAST_KEYID_ENABLE); if (!AR_SREV_9271(ah)) val &= ~AR_PCU_MISC_MODE2_HWWAR1; if (AR_SREV_KIWI_10_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, val); } /* * Disable RIFS search on some chips to avoid baseband * hang issues. */ if (AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) (void) ar5416SetRifsDelay(ah, chan, AH_FALSE); if (!AR_SREV_5416_V20_OR_LATER(ah) || AR_SREV_MERLIN(ah)) return; /* * Disable BB clock gating * Necessary to avoid issues on AR5416 2.0 */ OS_REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); } struct ini { uint32_t *data; /* NB: !const */ int rows, cols; }; /* * Override XPA bias level based on operating frequency. * This is a v14 EEPROM specific thing for the AR9160. */ void ar5416EepromSetAddac(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt]) MODAL_EEP_HEADER *pModal; HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar5416eeprom *eep = &ee->ee_base; uint8_t biaslevel; if (! AR_SREV_SOWL(ah)) return; if (EEP_MINOR(ah) < AR5416_EEP_MINOR_VER_7) return; pModal = &(eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]); if (pModal->xpaBiasLvl != 0xff) biaslevel = pModal->xpaBiasLvl; else { uint16_t resetFreqBin, freqBin, freqCount = 0; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); resetFreqBin = FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)); freqBin = XPA_LVL_FREQ(0) & 0xff; biaslevel = (uint8_t) (XPA_LVL_FREQ(0) >> 14); freqCount++; while (freqCount < 3) { if (XPA_LVL_FREQ(freqCount) == 0x0) break; freqBin = XPA_LVL_FREQ(freqCount) & 0xff; if (resetFreqBin >= freqBin) biaslevel = (uint8_t)(XPA_LVL_FREQ(freqCount) >> 14); else break; freqCount++; } } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: overriding XPA bias level = %d\n", __func__, biaslevel); /* * This is a dirty workaround for the const initval data, * which will upset multiple AR9160's on the same board. * * The HAL should likely just have a private copy of the addac * data per instance. */ if (IEEE80211_IS_CHAN_2GHZ(chan)) HAL_INI_VAL((struct ini *) &AH5416(ah)->ah_ini_addac, 7, 1) = (HAL_INI_VAL(&AH5416(ah)->ah_ini_addac, 7, 1) & (~0x18)) | biaslevel << 3; else HAL_INI_VAL((struct ini *) &AH5416(ah)->ah_ini_addac, 6, 1) = (HAL_INI_VAL(&AH5416(ah)->ah_ini_addac, 6, 1) & (~0xc0)) | biaslevel << 6; #undef XPA_LVL_FREQ } static void ar5416MarkPhyInactive(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); } #define AR5416_IFS_SLOT_FULL_RATE_40 0x168 /* 9 us half, 40 MHz core clock (9*40) */ #define AR5416_IFS_SLOT_HALF_RATE_40 0x104 /* 13 us half, 20 MHz core clock (13*20) */ #define AR5416_IFS_SLOT_QUARTER_RATE_40 0xD2 /* 21 us quarter, 10 MHz core clock (21*10) */ #define AR5416_IFS_EIFS_FULL_RATE_40 0xE60 /* (74 + (2 * 9)) * 40MHz core clock */ #define AR5416_IFS_EIFS_HALF_RATE_40 0xDAC /* (149 + (2 * 13)) * 20MHz core clock */ #define AR5416_IFS_EIFS_QUARTER_RATE_40 0xD48 /* (298 + (2 * 21)) * 10MHz core clock */ #define AR5416_IFS_SLOT_FULL_RATE_44 0x18c /* 9 us half, 44 MHz core clock (9*44) */ #define AR5416_IFS_SLOT_HALF_RATE_44 0x11e /* 13 us half, 22 MHz core clock (13*22) */ #define AR5416_IFS_SLOT_QUARTER_RATE_44 0xe7 /* 21 us quarter, 11 MHz core clock (21*11) */ #define AR5416_IFS_EIFS_FULL_RATE_44 0xfd0 /* (74 + (2 * 9)) * 44MHz core clock */ #define AR5416_IFS_EIFS_HALF_RATE_44 0xf0a /* (149 + (2 * 13)) * 22MHz core clock */ #define AR5416_IFS_EIFS_QUARTER_RATE_44 0xe9c /* (298 + (2 * 21)) * 11MHz core clock */ #define AR5416_INIT_USEC_40 40 #define AR5416_HALF_RATE_USEC_40 19 /* ((40 / 2) - 1 ) */ #define AR5416_QUARTER_RATE_USEC_40 9 /* ((40 / 4) - 1 ) */ #define AR5416_INIT_USEC_44 44 #define AR5416_HALF_RATE_USEC_44 21 /* ((44 / 2) - 1 ) */ #define AR5416_QUARTER_RATE_USEC_44 10 /* ((44 / 4) - 1 ) */ /* XXX What should these be for 40/44MHz clocks (and half/quarter) ? */ #define AR5416_RX_NON_FULL_RATE_LATENCY 63 #define AR5416_TX_HALF_RATE_LATENCY 108 #define AR5416_TX_QUARTER_RATE_LATENCY 216 /* * Adjust various register settings based on half/quarter rate clock setting. * This includes: * * + USEC, TX/RX latency, * + IFS params: slot, eifs, misc etc. * * TODO: * * + Verify which other registers need to be tweaked; * + Verify the behaviour of this for 5GHz fast and non-fast clock mode; * + This just plain won't work for long distance links - the coverage class * code isn't aware of the slot/ifs/ACK/RTS timeout values that need to * change; * + Verify whether the 32KHz USEC value needs to be kept for the 802.11n * series chips? * + Calculate/derive values for 2GHz, 5GHz, 5GHz fast clock */ static void ar5416SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec; int clk_44 = 0; HALASSERT(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)); /* 2GHz and 5GHz fast clock - 44MHz; else 40MHz */ if (IEEE80211_IS_CHAN_2GHZ(chan)) clk_44 = 1; else if (IEEE80211_IS_CHAN_5GHZ(chan) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) clk_44 = 1; /* XXX does this need save/restoring for the 11n chips? */ /* * XXX TODO: should mask out the txlat/rxlat/usec values? */ refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32; /* * XXX This really should calculate things, not use * hard coded values! Ew. */ if (IEEE80211_IS_CHAN_HALF(chan)) { if (clk_44) { slot = AR5416_IFS_SLOT_HALF_RATE_44; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_HALF_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_HALF_RATE_USEC_44; eifs = AR5416_IFS_EIFS_HALF_RATE_44; init_usec = AR5416_INIT_USEC_44 >> 1; } else { slot = AR5416_IFS_SLOT_HALF_RATE_40; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_HALF_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_HALF_RATE_USEC_40; eifs = AR5416_IFS_EIFS_HALF_RATE_40; init_usec = AR5416_INIT_USEC_40 >> 1; } } else { /* quarter rate */ if (clk_44) { slot = AR5416_IFS_SLOT_QUARTER_RATE_44; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_QUARTER_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_QUARTER_RATE_USEC_44; eifs = AR5416_IFS_EIFS_QUARTER_RATE_44; init_usec = AR5416_INIT_USEC_44 >> 2; } else { slot = AR5416_IFS_SLOT_QUARTER_RATE_40; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_QUARTER_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_QUARTER_RATE_USEC_40; eifs = AR5416_IFS_EIFS_QUARTER_RATE_40; init_usec = AR5416_INIT_USEC_40 >> 2; } } /* XXX verify these! */ OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat)); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec); } Index: head/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c (revision 361486) @@ -1,325 +1,325 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd * Copyright (c) 2008 Sam Leffler, Errno Consulting * Copyright (c) 2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9001/ar9130reg.h" #include "ar9001/ar9130_phy.h" #include "ar9001/ar9130_eeprom.h" #include "ar9001/ar9130.ini" static const HAL_PERCAL_DATA ar9130_iq_cal = { /* multi sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, .calNumSamples = MAX_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416IQCalCollect, .calPostProc = ar5416IQCalibration }; static const HAL_PERCAL_DATA ar9130_adc_gain_cal = { /* multi sample */ .calName = "ADC Gain", .calType = ADC_GAIN_CAL, .calNumSamples = MAX_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcGainCalCollect, .calPostProc = ar5416AdcGainCalibration }; static const HAL_PERCAL_DATA ar9130_adc_dc_cal = { /* multi sample */ .calName = "ADC DC", .calType = ADC_DC_CAL, .calNumSamples = MAX_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static const HAL_PERCAL_DATA ar9130_adc_init_dc_cal = { .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = INIT_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static HAL_BOOL ar9130FillCapabilityInfo(struct ath_hal *ah); /* * Attach for an AR9130 part. */ static struct ath_hal * ar9130Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416)); if (ahp5416 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ar5416InitState(ahp5416, devid, sc, st, sh, status); ahp = &ahp5416->ah_5212; ah = &ahp->ah_priv.h; /* XXX override with 9100 specific state */ AH5416(ah)->ah_initPLL = ar9130InitPLL; /* XXX should force chainmasks to 0x7, as per ath9k calibration bugs */ /* override 5416 methods for our needs */ AH5416(ah)->ah_cal.iqCalData.calData = &ar9130_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9130_adc_gain_cal; AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9130_adc_dc_cal; AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9130_adc_init_dc_cal; AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; /* * This was hard-set because the initial ath9k port of this * code kept their runtime conditional register #defines. * AR_SREV and the RTC registers have shifted for Howl; * they detected this and changed the values at runtime. * The current port doesn't yet do this; it may do at a * later stage, so this is set early so any routines which * manipulate the registers have ah_macVersion set to base * the above decision upon. */ AH_PRIVATE((ah))->ah_macVersion = AR_XSREV_VERSION_HOWL; /* * Use the "local" EEPROM data given to us by the higher layers. * This is a private copy out of system flash. * By this stage the SoC SPI flash may have disabled the memory- * mapping and rely purely on port-based SPI IO. */ AH_PRIVATE((ah))->ah_eepromRead = ath_hal_EepromDataRead; AH_PRIVATE((ah))->ah_eepromWrite = NULL; ah->ah_eepromdata = eepromdata; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV_CHIP_HOWL) & AR_SREV_CHIP_HOWL_ID; /* XXX are these values even valid for the mac/radio revision? -adrian */ HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); AH_PRIVATE(ah)->ah_ispcie = 0; /* setup common ini data; rf backends handle remainder */ HAL_INI_INIT(&ahp->ah_ini_modes, ar5416Modes_9100, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar5416Common_9100, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bb_rfgain, ar5416BB_RfGain_9100, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank0, ar5416Bank0_9100, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank1, ar5416Bank1_9100, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank2, ar5416Bank2_9100, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank3, ar5416Bank3_9100, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar5416Bank6TPC_9100, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank7, ar5416Bank7_9100, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar5416Addac_9100, 2); ecode = ath_hal_v14EepromAttach(ah); if (ecode != HAL_OK) goto bad; - if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } rfStatus = ar2133RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } /* * Got everything we need now to setup the capabilities. */ if (!ar9130FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); AH_PRIVATE(ah)->ah_currentRDext = ath_hal_eepromGet(ah, AR_EEP_REGDMN_1, AH_NULL); /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode); /* XXX no ANI for AR9130 */ AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ; AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ; AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_5416_2GHZ; AH5416(ah)->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ; AH5416(ah)->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ; AH5416(ah)->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_5416_5GHZ; ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ahp) ar5416Detach((struct ath_hal *) ahp); if (status) *status = ecode; return AH_NULL; } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ static HAL_BOOL ar9130FillCapabilityInfo(struct ath_hal *ah) { HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: begin\n", __func__); if (!ar5416FillCapabilityInfo(ah)) return AH_FALSE; HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: fill'ed; now setting\n", __func__); pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; pCap->halUseCombinedRadarRssi = AH_TRUE; pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ /* * MBSSID aggregation is broken in Howl v1.1, v1.2, v1.3 * and works fine in v1.4. * XXX todo, enable it for v1.4. */ pCap->halMbssidAggrSupport = AH_FALSE; pCap->hal4AddrAggrSupport = AH_TRUE; /* BB Read WAR */ pCap->halHasBBReadWar = AH_TRUE; /* * Implement the PLL/config changes needed for half/quarter * rates before re-enabling them here. */ pCap->halChanHalfRate = AH_FALSE; pCap->halChanQuarterRate = AH_FALSE; return AH_TRUE; } static const char* ar9130Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID && devid == AR5416_AR9130_DEVID) return "Atheros 9130"; return AH_NULL; } AH_CHIP(AR9130, ar9130Probe, ar9130Attach); Index: head/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c (revision 361486) @@ -1,343 +1,343 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2008 Sam Leffler, Errno Consulting * Copyright (c) 2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9001/ar9160.ini" static const HAL_PERCAL_DATA ar9160_iq_cal = { /* multi sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, .calNumSamples = MAX_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416IQCalCollect, .calPostProc = ar5416IQCalibration }; static const HAL_PERCAL_DATA ar9160_adc_gain_cal = { /* multi sample */ .calName = "ADC Gain", .calType = ADC_GAIN_CAL, .calNumSamples = MAX_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcGainCalCollect, .calPostProc = ar5416AdcGainCalibration }; static const HAL_PERCAL_DATA ar9160_adc_dc_cal = { /* multi sample */ .calName = "ADC DC", .calType = ADC_DC_CAL, .calNumSamples = MAX_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static const HAL_PERCAL_DATA ar9160_adc_init_dc_cal = { .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = INIT_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static HAL_BOOL ar9160FillCapabilityInfo(struct ath_hal *ah); static void ar9160AniSetup(struct ath_hal *ah) { static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 7, .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; /* NB: disable ANI noise immmunity for reliable RIFS rx */ AH5416(ah)->ah_ani_function &= ~(1 << HAL_ANI_NOISE_IMMUNITY_LEVEL); ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } static void ar9160InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV); if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL); if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0x50, AR_RTC_SOWL_PLL_DIV); else pll |= SM(0x58, AR_RTC_SOWL_PLL_DIV); } else pll |= SM(0x58, AR_RTC_SOWL_PLL_DIV); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_SLEEP_DERIVED_CLK); } /* * Attach for an AR9160 part. */ static struct ath_hal * ar9160Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416)); if (ahp5416 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ar5416InitState(ahp5416, devid, sc, st, sh, status); ahp = &ahp5416->ah_5212; ah = &ahp->ah_priv.h; /* XXX override with 9160 specific state */ /* override 5416 methods for our needs */ AH5416(ah)->ah_initPLL = ar9160InitPLL; AH5416(ah)->ah_cal.iqCalData.calData = &ar9160_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9160_adc_gain_cal; AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9160_adc_dc_cal; AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9160_adc_init_dc_cal; AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); /* NB: include chip type to differentiate from pre-Sowl versions */ AH_PRIVATE(ah)->ah_macVersion = (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; /* setup common ini data; rf backends handle remainder */ HAL_INI_INIT(&ahp->ah_ini_modes, ar9160Modes, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9160Common, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bb_rfgain, ar9160BB_RfGain, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank0, ar9160Bank0, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank1, ar9160Bank1, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank2, ar9160Bank2, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank3, ar9160Bank3, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar9160Bank6TPC, 3); HAL_INI_INIT(&AH5416(ah)->ah_ini_bank7, ar9160Bank7, 2); if (AR_SREV_SOWL_11(ah)) HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar9160Addac_1_1, 2); else HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar9160Addac, 2); ecode = ath_hal_v14EepromAttach(ah); if (ecode != HAL_OK) goto bad; HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9160PciePhy, 2); ar5416AttachPCIE(ah); - if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } rfStatus = ar2133RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } /* * Got everything we need now to setup the capabilities. */ if (!ar9160FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); AH_PRIVATE(ah)->ah_currentRDext = ath_hal_eepromGet(ah, AR_EEP_REGDMN_1, AH_NULL); /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); ar9160AniSetup(ah); /* Anti Noise Immunity */ /* This just uses the AR5416 NF values */ AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ; AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ; AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_5416_2GHZ; AH5416(ah)->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ; AH5416(ah)->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ; AH5416(ah)->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_5416_5GHZ; ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ahp) ar5416Detach((struct ath_hal *) ahp); if (status) *status = ecode; return AH_NULL; } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ static HAL_BOOL ar9160FillCapabilityInfo(struct ath_hal *ah) { HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (!ar5416FillCapabilityInfo(ah)) return AH_FALSE; pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; pCap->halUseCombinedRadarRssi = AH_TRUE; pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ pCap->halMbssidAggrSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_TRUE; /* BB Read WAR */ pCap->halHasBBReadWar = AH_TRUE; /* AR9160 is a 2x2 stream device */ pCap->halTxStreams = 2; pCap->halRxStreams = 2; return AH_TRUE; } static const char* ar9160Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID && devid == AR9160_DEVID_PCI) return "Atheros 9160"; return AH_NULL; } AH_CHIP(AR9160, ar9160Probe, ar9160Attach); Index: head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c (revision 361486) @@ -1,978 +1,978 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting * Copyright (c) 2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v14.h" /* XXX for tx/rx gain */ #include "ar9002/ar9280.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9002/ar9280v1.ini" #include "ar9002/ar9280v2.ini" #include "ar9002/ar9280_olc.h" static const HAL_PERCAL_DATA ar9280_iq_cal = { /* single sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MAX_LOG_COUNT, .calCollect = ar5416IQCalCollect, .calPostProc = ar5416IQCalibration }; static const HAL_PERCAL_DATA ar9280_adc_gain_cal = { /* single sample */ .calName = "ADC Gain", .calType = ADC_GAIN_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MAX_LOG_COUNT, .calCollect = ar5416AdcGainCalCollect, .calPostProc = ar5416AdcGainCalibration }; static const HAL_PERCAL_DATA ar9280_adc_dc_cal = { /* single sample */ .calName = "ADC DC", .calType = ADC_DC_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MAX_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = INIT_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off); static void ar9280DisablePCIE(struct ath_hal *ah); static HAL_BOOL ar9280FillCapabilityInfo(struct ath_hal *ah); static void ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar9280AniSetup(struct ath_hal *ah) { /* * These are the parameters from the AR5416 ANI code; * they likely need quite a bit of adjustment for the * AR9280. */ static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 7, .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; /* NB: disable ANI noise immmunity for reliable RIFS rx */ AH5416(ah)->ah_ani_function &= ~(1 << HAL_ANI_NOISE_IMMUNITY_LEVEL); /* NB: ANI is not enabled yet */ ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } void ar9280InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV); if (AR_SREV_MERLIN_20(ah) && chan != AH_NULL && IEEE80211_IS_CHAN_5GHZ(chan)) { /* * PLL WAR for Merlin 2.0/2.1 * When doing fast clock, set PLL to 0x142c * Else, set PLL to 0x2850 to prevent reset-to-reset variation */ pll = IS_5GHZ_FAST_CLOCK_EN(ah, chan) ? 0x142c : 0x2850; if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL); } else if (AR_SREV_MERLIN_10_OR_LATER(ah)) { pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV); if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL); if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0x28, AR_RTC_SOWL_PLL_DIV); else pll |= SM(0x2c, AR_RTC_SOWL_PLL_DIV); } else pll |= SM(0x2c, AR_RTC_SOWL_PLL_DIV); } OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_SLEEP_DERIVED_CLK); } /* XXX shouldn't be here! */ #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) /* * Attach for an AR9280 part. */ static struct ath_hal * ar9280Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9280 *ahp9280; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; int8_t pwr_table_offset; uint8_t pwr; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9280 = ath_hal_malloc(sizeof (struct ath_hal_9280)); if (ahp9280 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ahp = AH5212(ahp9280); ah = &ahp->ah_priv.h; ar5416InitState(AH5416(ah), devid, sc, st, sh, status); /* * Use the "local" EEPROM data given to us by the higher layers. * This is a private copy out of system flash. The Linux ath9k * commit for the initial AR9130 support mentions MMIO flash * access is "unreliable." -adrian */ if (eepromdata != AH_NULL) { AH_PRIVATE((ah))->ah_eepromRead = ath_hal_EepromDataRead; AH_PRIVATE((ah))->ah_eepromWrite = NULL; ah->ah_eepromdata = eepromdata; } /* XXX override with 9280 specific state */ /* override 5416 methods for our needs */ AH5416(ah)->ah_initPLL = ar9280InitPLL; ah->ah_setAntennaSwitch = ar9280SetAntennaSwitch; ah->ah_configPCIE = ar9280ConfigPCIE; ah->ah_disablePCIE = ar9280DisablePCIE; AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9280_adc_dc_cal; AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9280_adc_init_dc_cal; AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; AH5416(ah)->ah_writeIni = ar9280WriteIni; AH5416(ah)->ah_olcInit = ar9280olcInit; AH5416(ah)->ah_olcTempCompensation = ar9280olcTemperatureCompensation; AH5416(ah)->ah_setPowerCalTable = ar9280SetPowerCalTable; AH5416(ah)->ah_rx_chainmask = AR9280_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR9280_DEFAULT_TXCHAINMASK; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); /* NB: include chip type to differentiate from pre-Sowl versions */ AH_PRIVATE(ah)->ah_macVersion = (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; /* setup common ini data; rf backends handle remainder */ if (AR_SREV_MERLIN_20_OR_LATER(ah)) { HAL_INI_INIT(&ahp->ah_ini_modes, ar9280Modes_v2, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9280Common_v2, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9280PciePhy_clkreq_always_on_L1_v2, 2); HAL_INI_INIT(&ahp9280->ah_ini_xmodes, ar9280Modes_fast_clock_v2, 3); } else { HAL_INI_INIT(&ahp->ah_ini_modes, ar9280Modes_v1, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9280Common_v1, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9280PciePhy_v1, 2); } ar5416AttachPCIE(ah); ecode = ath_hal_v14EepromAttach(ah); if (ecode != HAL_OK) goto bad; - if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } rfStatus = ar9280RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } /* Enable fixup for AR_AN_TOP2 if necessary */ /* * The v14 EEPROM layer returns HAL_EIO if PWDCLKIND isn't supported * by the EEPROM version. * * ath9k checks the EEPROM minor version is >= 0x0a here, instead of * the abstracted EEPROM access layer. */ ecode = ath_hal_eepromGet(ah, AR_EEP_PWDCLKIND, &pwr); if (AR_SREV_MERLIN_20_OR_LATER(ah) && ecode == HAL_OK && pwr == 0) { printf("[ath] enabling AN_TOP2_FIXUP\n"); AH5416(ah)->ah_need_an_top2_fixup = 1; } /* * Check whether the power table offset isn't the default. * This can occur with eeprom minor V21 or greater on Merlin. */ (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); if (pwr_table_offset != AR5416_PWR_TABLE_OFFSET_DB) ath_hal_printf(ah, "[ath]: default pwr offset: %d dBm != EEPROM pwr offset: %d dBm; curves will be adjusted.\n", AR5416_PWR_TABLE_OFFSET_DB, (int) pwr_table_offset); /* XXX check for >= minor ver 17 */ if (AR_SREV_MERLIN_20(ah)) { /* setup rxgain table */ switch (ath_hal_eepromGet(ah, AR_EEP_RXGAIN_TYPE, AH_NULL)) { case AR5416_EEP_RXGAIN_13dB_BACKOFF: HAL_INI_INIT(&ahp9280->ah_ini_rxgain, ar9280Modes_backoff_13db_rxgain_v2, 6); break; case AR5416_EEP_RXGAIN_23dB_BACKOFF: HAL_INI_INIT(&ahp9280->ah_ini_rxgain, ar9280Modes_backoff_23db_rxgain_v2, 6); break; case AR5416_EEP_RXGAIN_ORIG: HAL_INI_INIT(&ahp9280->ah_ini_rxgain, ar9280Modes_original_rxgain_v2, 6); break; default: HALASSERT(AH_FALSE); goto bad; /* XXX ? try to continue */ } } /* XXX check for >= minor ver 19 */ if (AR_SREV_MERLIN_20(ah)) { /* setp txgain table */ switch (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL)) { case AR5416_EEP_TXGAIN_HIGH_POWER: HAL_INI_INIT(&ahp9280->ah_ini_txgain, ar9280Modes_high_power_tx_gain_v2, 6); break; case AR5416_EEP_TXGAIN_ORIG: HAL_INI_INIT(&ahp9280->ah_ini_txgain, ar9280Modes_original_tx_gain_v2, 6); break; default: HALASSERT(AH_FALSE); goto bad; /* XXX ? try to continue */ } } /* * Got everything we need now to setup the capabilities. */ if (!ar9280FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); AH_PRIVATE(ah)->ah_currentRDext = ath_hal_eepromGet(ah, AR_EEP_REGDMN_1, AH_NULL); /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); ar9280AniSetup(ah); /* Anti Noise Immunity */ /* Setup noise floor min/max/nominal values */ AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9280_2GHZ; AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9280_2GHZ; AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9280_2GHZ; AH5416(ah)->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9280_5GHZ; AH5416(ah)->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9280_5GHZ; AH5416(ah)->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9280_5GHZ; ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ah != AH_NULL) ah->ah_detach(ah); if (status) *status = ecode; return AH_NULL; } static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { uint32_t val; if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); } /* * Set PCIe workaround bits * * NOTE: * * In Merlin and Kite, bit 14 in WA register (disable L1) should only * be set when device enters D3 and be cleared when device comes back * to D0. */ if (power_off) { /* Power-off */ OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); val = OS_REG_READ(ah, AR_WA); /* * Disable bit 6 and 7 before entering D3 to prevent * system hang. */ val &= ~(AR_WA_BIT6 | AR_WA_BIT7); /* * XXX Not sure, is specified in the reference HAL. */ val |= AR_WA_BIT22; /* * See above: set AR_WA_D3_L1_DISABLE when entering D3 state. * * XXX The reference HAL does it this way - it only sets * AR_WA_D3_L1_DISABLE if it's set in AR9280_WA_DEFAULT, * which it (currently) isn't. So the following statement * is currently a NOP. */ if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE) val |= AR_WA_D3_L1_DISABLE; OS_REG_WRITE(ah, AR_WA, val); } else { /* Power-on */ val = AR9280_WA_DEFAULT; /* * See note above: make sure L1_DISABLE is not set. */ val &= (~AR_WA_D3_L1_DISABLE); OS_REG_WRITE(ah, AR_WA, val); /* set bit 19 to allow forcing of pcie core into L1 state */ OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); } } static void ar9280DisablePCIE(struct ath_hal *ah) { } static void ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { u_int modesIndex, freqIndex; int regWrites = 0; int i; const HAL_INI_ARRAY *ia; /* Setup the indices for the next set of register array writes */ /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { freqIndex = 2; if (IEEE80211_IS_CHAN_HT40(chan)) modesIndex = 3; else if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else modesIndex = 4; } else { freqIndex = 1; if (IEEE80211_IS_CHAN_HT40(chan) || IEEE80211_IS_CHAN_TURBO(chan)) modesIndex = 2; else modesIndex = 1; } /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); /* * This is unwound because at the moment, there's a requirement * for Merlin (and later, perhaps) to have a specific bit fixed * in the AR_AN_TOP2 register before writing it. */ ia = &AH5212(ah)->ah_ini_modes; #if 0 regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, modesIndex, regWrites); #endif HALASSERT(modesIndex < ia->cols); for (i = 0; i < ia->rows; i++) { uint32_t reg = HAL_INI_VAL(ia, i, 0); uint32_t val = HAL_INI_VAL(ia, i, modesIndex); if (reg == AR_AN_TOP2 && AH5416(ah)->ah_need_an_top2_fixup) val &= ~AR_AN_TOP2_PWDCLKIND; OS_REG_WRITE(ah, reg, val); /* Analog shift register delay seems needed for Merlin - PR kern/154220 */ if (reg >= 0x7800 && reg < 0x7900) OS_DELAY(100); DMA_YIELD(regWrites); } if (AR_SREV_MERLIN_20_OR_LATER(ah)) { regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_rxgain, modesIndex, regWrites); regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_txgain, modesIndex, regWrites); } /* XXX Merlin 100us delay for shift registers */ regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites); if (AR_SREV_MERLIN_20(ah) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* 5GHz channels w/ Fast Clock use different modal values */ regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_xmodes, modesIndex, regWrites); } } #define AR_BASE_FREQ_2GHZ 2300 #define AR_BASE_FREQ_5GHZ 4900 #define AR_SPUR_FEEQ_BOUND_HT40 19 #define AR_SPUR_FEEQ_BOUND_HT20 10 void ar9280SpurMitigate(struct ath_hal *ah, const struct ieee80211_channel *chan) { static const int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 }; static const int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 }; static int inc[4] = { 0, 100, 0, 0 }; int bb_spur = AR_NO_SPUR; int freq; int bin, cur_bin; int bb_spur_off, spur_subchannel_sd; int spur_freq_sd; int spur_delta_phase; int denominator; int upper, lower, cur_vit_mask; int tmp, newVal; int i; CHAN_CENTERS centers; int8_t mask_m[123]; int8_t mask_p[123]; int8_t mask_amt; int tmp_mask; int cur_bb_spur; HAL_BOOL is2GHz = IEEE80211_IS_CHAN_2GHZ(chan); OS_MEMZERO(&mask_m, sizeof(int8_t) * 123); OS_MEMZERO(&mask_p, sizeof(int8_t) * 123); ar5416GetChannelCenters(ah, chan, ¢ers); freq = centers.synth_center; /* * Need to verify range +/- 9.38 for static ht20 and +/- 18.75 for ht40, * otherwise spur is out-of-band and can be ignored. */ for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ath_hal_getSpurChan(ah, i, is2GHz); /* Get actual spur freq in MHz from EEPROM read value */ if (is2GHz) { cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ; } else { cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ; } if (AR_NO_SPUR == cur_bb_spur) break; cur_bb_spur = cur_bb_spur - freq; if (IEEE80211_IS_CHAN_HT40(chan)) { if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) && (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) { bb_spur = cur_bb_spur; break; } } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) && (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) { bb_spur = cur_bb_spur; break; } } if (AR_NO_SPUR == bb_spur) { #if 1 /* * MRC CCK can interfere with beacon detection and cause deaf/mute. * Disable MRC CCK for now. */ OS_REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); #else /* Enable MRC CCK if no spur is found in this channel. */ OS_REG_SET_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); #endif return; } else { /* * For Merlin, spur can break CCK MRC algorithm. Disable CCK MRC if spur * is found in this channel. */ OS_REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); } bin = bb_spur * 320; tmp = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0)); newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0), newVal); newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | AR_PHY_SPUR_REG_ENABLE_MASK_PPM | AR_PHY_SPUR_REG_MASK_RATE_SELECT | AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | SM(AR5416_SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); OS_REG_WRITE(ah, AR_PHY_SPUR_REG, newVal); /* Pick control or extn channel to cancel the spur */ if (IEEE80211_IS_CHAN_HT40(chan)) { if (bb_spur < 0) { spur_subchannel_sd = 1; bb_spur_off = bb_spur + 10; } else { spur_subchannel_sd = 0; bb_spur_off = bb_spur - 10; } } else { spur_subchannel_sd = 0; bb_spur_off = bb_spur; } /* * spur_delta_phase = bb_spur/40 * 2**21 for static ht20, * /80 for dyn2040. */ if (IEEE80211_IS_CHAN_HT40(chan)) spur_delta_phase = ((bb_spur * 262144) / 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; else spur_delta_phase = ((bb_spur * 524288) / 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; /* * in 11A mode the denominator of spur_freq_sd should be 40 and * it should be 44 in 11G */ denominator = IEEE80211_IS_CHAN_2GHZ(chan) ? 44 : 40; spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff; newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); OS_REG_WRITE(ah, AR_PHY_TIMING11, newVal); /* Choose to cancel between control and extension channels */ newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S; OS_REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal); /* * ============================================ * Set Pilot and Channel Masks * * pilot mask 1 [31:0] = +6..-26, no 0 bin * pilot mask 2 [19:0] = +26..+7 * * channel mask 1 [31:0] = +6..-26, no 0 bin * channel mask 2 [19:0] = +26..+7 */ cur_bin = -6000; upper = bin + 100; lower = bin - 100; for (i = 0; i < 4; i++) { int pilot_mask = 0; int chan_mask = 0; int bp = 0; for (bp = 0; bp < 30; bp++) { if ((cur_bin > lower) && (cur_bin < upper)) { pilot_mask = pilot_mask | 0x1 << bp; chan_mask = chan_mask | 0x1 << bp; } cur_bin += 100; } cur_bin += inc[i]; OS_REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); OS_REG_WRITE(ah, chan_mask_reg[i], chan_mask); } /* ================================================= * viterbi mask 1 based on channel magnitude * four levels 0-3 * - mask (-27 to 27) (reg 64,0x9900 to 67,0x990c) * [1 2 2 1] for -9.6 or [1 2 1] for +16 * - enable_mask_ppm, all bins move with freq * * - mask_select, 8 bits for rates (reg 67,0x990c) * - mask_rate_cntl, 8 bits for rates (reg 67,0x990c) * choose which mask to use mask or mask2 */ /* * viterbi mask 2 2nd set for per data rate puncturing * four levels 0-3 * - mask_select, 8 bits for rates (reg 67) * - mask (-27 to 27) (reg 98,0x9988 to 101,0x9994) * [1 2 2 1] for -9.6 or [1 2 1] for +16 */ cur_vit_mask = 6100; upper = bin + 120; lower = bin - 120; for (i = 0; i < 123; i++) { if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { if ((abs(cur_vit_mask - bin)) < 75) { mask_amt = 1; } else { mask_amt = 0; } if (cur_vit_mask < 0) { mask_m[abs(cur_vit_mask / 100)] = mask_amt; } else { mask_p[cur_vit_mask / 100] = mask_amt; } } cur_vit_mask -= 100; } tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) | (mask_m[48] << 26) | (mask_m[49] << 24) | (mask_m[50] << 22) | (mask_m[51] << 20) | (mask_m[52] << 18) | (mask_m[53] << 16) | (mask_m[54] << 14) | (mask_m[55] << 12) | (mask_m[56] << 10) | (mask_m[57] << 8) | (mask_m[58] << 6) | (mask_m[59] << 4) | (mask_m[60] << 2) | (mask_m[61] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); OS_REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); tmp_mask = (mask_m[31] << 28) | (mask_m[32] << 26) | (mask_m[33] << 24) | (mask_m[34] << 22) | (mask_m[35] << 20) | (mask_m[36] << 18) | (mask_m[37] << 16) | (mask_m[48] << 14) | (mask_m[39] << 12) | (mask_m[40] << 10) | (mask_m[41] << 8) | (mask_m[42] << 6) | (mask_m[43] << 4) | (mask_m[44] << 2) | (mask_m[45] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) | (mask_m[18] << 26) | (mask_m[18] << 24) | (mask_m[20] << 22) | (mask_m[20] << 20) | (mask_m[22] << 18) | (mask_m[22] << 16) | (mask_m[24] << 14) | (mask_m[24] << 12) | (mask_m[25] << 10) | (mask_m[26] << 8) | (mask_m[27] << 6) | (mask_m[28] << 4) | (mask_m[29] << 2) | (mask_m[30] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); tmp_mask = (mask_m[ 0] << 30) | (mask_m[ 1] << 28) | (mask_m[ 2] << 26) | (mask_m[ 3] << 24) | (mask_m[ 4] << 22) | (mask_m[ 5] << 20) | (mask_m[ 6] << 18) | (mask_m[ 7] << 16) | (mask_m[ 8] << 14) | (mask_m[ 9] << 12) | (mask_m[10] << 10) | (mask_m[11] << 8) | (mask_m[12] << 6) | (mask_m[13] << 4) | (mask_m[14] << 2) | (mask_m[15] << 0); OS_REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); tmp_mask = (mask_p[15] << 28) | (mask_p[14] << 26) | (mask_p[13] << 24) | (mask_p[12] << 22) | (mask_p[11] << 20) | (mask_p[10] << 18) | (mask_p[ 9] << 16) | (mask_p[ 8] << 14) | (mask_p[ 7] << 12) | (mask_p[ 6] << 10) | (mask_p[ 5] << 8) | (mask_p[ 4] << 6) | (mask_p[ 3] << 4) | (mask_p[ 2] << 2) | (mask_p[ 1] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); tmp_mask = (mask_p[30] << 28) | (mask_p[29] << 26) | (mask_p[28] << 24) | (mask_p[27] << 22) | (mask_p[26] << 20) | (mask_p[25] << 18) | (mask_p[24] << 16) | (mask_p[23] << 14) | (mask_p[22] << 12) | (mask_p[21] << 10) | (mask_p[20] << 8) | (mask_p[19] << 6) | (mask_p[18] << 4) | (mask_p[17] << 2) | (mask_p[16] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); tmp_mask = (mask_p[45] << 28) | (mask_p[44] << 26) | (mask_p[43] << 24) | (mask_p[42] << 22) | (mask_p[41] << 20) | (mask_p[40] << 18) | (mask_p[39] << 16) | (mask_p[38] << 14) | (mask_p[37] << 12) | (mask_p[36] << 10) | (mask_p[35] << 8) | (mask_p[34] << 6) | (mask_p[33] << 4) | (mask_p[32] << 2) | (mask_p[31] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) | (mask_p[59] << 26) | (mask_p[58] << 24) | (mask_p[57] << 22) | (mask_p[56] << 20) | (mask_p[55] << 18) | (mask_p[54] << 16) | (mask_p[53] << 14) | (mask_p[52] << 12) | (mask_p[51] << 10) | (mask_p[50] << 8) | (mask_p[49] << 6) | (mask_p[48] << 4) | (mask_p[47] << 2) | (mask_p[46] << 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); OS_REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ static HAL_BOOL ar9280FillCapabilityInfo(struct ath_hal *ah) { HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (!ar5416FillCapabilityInfo(ah)) return AH_FALSE; pCap->halNumGpioPins = 10; pCap->halWowSupport = AH_TRUE; pCap->halWowMatchPatternExact = AH_TRUE; #if 0 pCap->halWowMatchPatternDword = AH_TRUE; #endif pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; pCap->halUseCombinedRadarRssi = AH_TRUE; #if 0 /* XXX bluetooth */ pCap->halBtCoexSupport = AH_TRUE; #endif pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ pCap->hal4kbSplitTransSupport = AH_FALSE; /* Disable this so Block-ACK works correctly */ pCap->halHasRxSelfLinkedTail = AH_FALSE; pCap->halMbssidAggrSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_TRUE; pCap->halSpectralScanSupport = AH_TRUE; if (AR_SREV_MERLIN_20(ah)) { pCap->halPSPollBroken = AH_FALSE; /* * This just enables the support; it doesn't * state 5ghz fast clock will always be used. */ pCap->halSupportsFastClock5GHz = AH_TRUE; } pCap->halRxStbcSupport = 1; pCap->halTxStbcSupport = 1; pCap->halEnhancedDfsSupport = AH_TRUE; return AH_TRUE; } /* * This has been disabled - having the HAL flip chainmasks on/off * when attempting to implement 11n disrupts things. For now, just * leave this flipped off and worry about implementing TX diversity * for legacy and MCS0-7 when 11n is fully functioning. */ HAL_BOOL ar9280SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) { #define ANTENNA0_CHAINMASK 0x1 #define ANTENNA1_CHAINMASK 0x2 #if 0 struct ath_hal_5416 *ahp = AH5416(ah); /* Antenna selection is done by setting the tx/rx chainmasks approp. */ switch (settings) { case HAL_ANT_FIXED_A: /* Enable first antenna only */ ahp->ah_tx_chainmask = ANTENNA0_CHAINMASK; ahp->ah_rx_chainmask = ANTENNA0_CHAINMASK; break; case HAL_ANT_FIXED_B: /* Enable second antenna only, after checking capability */ if (AH_PRIVATE(ah)->ah_caps.halTxChainMask > ANTENNA1_CHAINMASK) ahp->ah_tx_chainmask = ANTENNA1_CHAINMASK; ahp->ah_rx_chainmask = ANTENNA1_CHAINMASK; break; case HAL_ANT_VARIABLE: /* Restore original chainmask settings */ /* XXX */ ahp->ah_tx_chainmask = AR9280_DEFAULT_TXCHAINMASK; ahp->ah_rx_chainmask = AR9280_DEFAULT_RXCHAINMASK; break; } HALDEBUG(ah, HAL_DEBUG_ANY, "%s: settings=%d, tx/rx chainmask=%d/%d\n", __func__, settings, ahp->ah_tx_chainmask, ahp->ah_rx_chainmask); #endif return AH_TRUE; #undef ANTENNA0_CHAINMASK #undef ANTENNA1_CHAINMASK } static const char* ar9280Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID) { if (devid == AR9280_DEVID_PCI) return "Atheros 9220"; if (devid == AR9280_DEVID_PCIE) return "Atheros 9280"; } return AH_NULL; } AH_CHIP(AR9280, ar9280Probe, ar9280Attach); Index: head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c (revision 361486) @@ -1,583 +1,583 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting * Copyright (c) 2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v4k.h" /* XXX for tx/rx gain */ #include "ar9002/ar9280.h" #include "ar9002/ar9285.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9002/ar9285.ini" #include "ar9002/ar9285v2.ini" #include "ar9002/ar9280v2.ini" /* XXX ini for tx/rx gain */ #include "ar9002/ar9285_cal.h" #include "ar9002/ar9285_phy.h" #include "ar9002/ar9285_diversity.h" static const HAL_PERCAL_DATA ar9280_iq_cal = { /* single sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MAX_LOG_COUNT, .calCollect = ar5416IQCalCollect, .calPostProc = ar5416IQCalibration }; static const HAL_PERCAL_DATA ar9280_adc_gain_cal = { /* single sample */ .calName = "ADC Gain", .calType = ADC_GAIN_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcGainCalCollect, .calPostProc = ar5416AdcGainCalibration }; static const HAL_PERCAL_DATA ar9280_adc_dc_cal = { /* single sample */ .calName = "ADC DC", .calType = ADC_DC_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = INIT_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off); static void ar9285DisablePCIE(struct ath_hal *ah); static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah); static void ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar9285AniSetup(struct ath_hal *ah) { /* * These are the parameters from the AR5416 ANI code; * they likely need quite a bit of adjustment for the * AR9285. */ static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 7, .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; /* NB: disable ANI noise immmunity for reliable RIFS rx */ AH5416(ah)->ah_ani_function &= ~(1 << HAL_ANI_NOISE_IMMUNITY_LEVEL); ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } static const char * ar9285_lna_conf[] = { "LNA1-LNA2", "LNA2", "LNA1", "LNA1+LNA2", }; static void ar9285_eeprom_print_diversity_settings(struct ath_hal *ah) { const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader; ath_hal_printf(ah, "[ath] AR9285 Main LNA config: %s\n", ar9285_lna_conf[(pModal->antdiv_ctl2 >> 2) & 0x3]); ath_hal_printf(ah, "[ath] AR9285 Alt LNA config: %s\n", ar9285_lna_conf[pModal->antdiv_ctl2 & 0x3]); ath_hal_printf(ah, "[ath] LNA diversity %s, Diversity %s\n", ((pModal->antdiv_ctl1 & 0x1) ? "enabled" : "disabled"), ((pModal->antdiv_ctl1 & 0x8) ? "enabled" : "disabled")); } /* * Attach for an AR9285 part. */ static struct ath_hal * ar9285Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9285 *ahp9285; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9285 = ath_hal_malloc(sizeof (struct ath_hal_9285)); if (ahp9285 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ahp = AH5212(ahp9285); ah = &ahp->ah_priv.h; ar5416InitState(AH5416(ah), devid, sc, st, sh, status); /* * Use the "local" EEPROM data given to us by the higher layers. * This is a private copy out of system flash. The Linux ath9k * commit for the initial AR9130 support mentions MMIO flash * access is "unreliable." -adrian */ if (eepromdata != AH_NULL) { AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead; AH_PRIVATE(ah)->ah_eepromWrite = NULL; ah->ah_eepromdata = eepromdata; } /* override with 9285 specific state */ AH5416(ah)->ah_initPLL = ar9280InitPLL; AH5416(ah)->ah_btCoexSetDiversity = ar9285BTCoexAntennaDiversity; ah->ah_setAntennaSwitch = ar9285SetAntennaSwitch; ah->ah_configPCIE = ar9285ConfigPCIE; ah->ah_disablePCIE = ar9285DisablePCIE; ah->ah_setTxPower = ar9285SetTransmitPower; ah->ah_setBoardValues = ar9285SetBoardValues; ah->ah_btCoexSetParameter = ar9285BTCoexSetParameter; ah->ah_divLnaConfGet = ar9285_antdiv_comb_conf_get; ah->ah_divLnaConfSet = ar9285_antdiv_comb_conf_set; AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9280_adc_dc_cal; AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9280_adc_init_dc_cal; AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; AH5416(ah)->ah_writeIni = ar9285WriteIni; AH5416(ah)->ah_rx_chainmask = AR9285_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR9285_DEFAULT_TXCHAINMASK; ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD >> 1; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); /* NB: include chip type to differentiate from pre-Sowl versions */ AH_PRIVATE(ah)->ah_macVersion = (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; /* setup common ini data; rf backends handle remainder */ if (AR_SREV_KITE_12_OR_LATER(ah)) { HAL_INI_INIT(&ahp->ah_ini_modes, ar9285Modes_v2, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9285Common_v2, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9285PciePhy_clkreq_always_on_L1_v2, 2); } else { HAL_INI_INIT(&ahp->ah_ini_modes, ar9285Modes, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9285Common, 2); HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9285PciePhy_clkreq_always_on_L1, 2); } ar5416AttachPCIE(ah); /* Attach methods that require MAC version/revision info */ if (AR_SREV_KITE_12_OR_LATER(ah)) AH5416(ah)->ah_cal_initcal = ar9285InitCalHardware; if (AR_SREV_KITE_11_OR_LATER(ah)) AH5416(ah)->ah_cal_pacal = ar9002_hw_pa_cal; ecode = ath_hal_v4kEepromAttach(ah); if (ecode != HAL_OK) goto bad; - if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } rfStatus = ar9285RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } HAL_INI_INIT(&ahp9285->ah_ini_rxgain, ar9280Modes_original_rxgain_v2, 6); if (AR_SREV_9285E_20(ah)) ath_hal_printf(ah, "[ath] AR9285E_20 detected; using XE TX gain tables\n"); /* setup txgain table */ switch (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL)) { case AR5416_EEP_TXGAIN_HIGH_POWER: if (AR_SREV_9285E_20(ah)) HAL_INI_INIT(&ahp9285->ah_ini_txgain, ar9285Modes_XE2_0_high_power, 6); else HAL_INI_INIT(&ahp9285->ah_ini_txgain, ar9285Modes_high_power_tx_gain_v2, 6); break; case AR5416_EEP_TXGAIN_ORIG: if (AR_SREV_9285E_20(ah)) HAL_INI_INIT(&ahp9285->ah_ini_txgain, ar9285Modes_XE2_0_normal_power, 6); else HAL_INI_INIT(&ahp9285->ah_ini_txgain, ar9285Modes_original_tx_gain_v2, 6); break; default: HALASSERT(AH_FALSE); goto bad; /* XXX ? try to continue */ } /* * Got everything we need now to setup the capabilities. */ if (!ar9285FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } /* * Print out the EEPROM antenna configuration mapping. * Some devices have a hard-coded LNA configuration profile; * others enable diversity. */ ar9285_eeprom_print_diversity_settings(ah); /* Print out whether the EEPROM settings enable AR9285 diversity */ if (ar9285_check_div_comb(ah)) { ath_hal_printf(ah, "[ath] Enabling diversity for Kite\n"); } /* Disable 11n for the AR2427 */ if (devid == AR2427_DEVID_PCIE) AH_PRIVATE(ah)->ah_caps.halHTSupport = AH_FALSE; ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); /* * For Kite and later chipsets, the following bits are not * programmed in EEPROM and so are set as enabled always. */ AH_PRIVATE(ah)->ah_currentRDext = AR9285_RDEXT_DEFAULT; /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); ar9285AniSetup(ah); /* Anti Noise Immunity */ /* Setup noise floor min/max/nominal values */ AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ; AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ; AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9285_2GHZ; /* XXX no 5ghz values? */ ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ah != AH_NULL) ah->ah_detach(ah); if (status) *status = ecode; return AH_NULL; } static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { uint32_t val; /* * This workaround needs some integration work with the HAL * config parameters and the if_ath_pci.c glue. * Specifically, read the value of the PCI register 0x70c * (4 byte PCI config space register) and store it in ath_hal_war70c. * Then if it's non-zero, the below WAR would override register * 0x570c upon suspend/resume. */ #if 0 if (AR_SREV_9285E_20(ah)) { val = AH_PRIVATE(ah)->ah_config.ath_hal_war70c; if (val) { val &= 0xffff00ff; val |= 0x6f00; OS_REG_WRITE(ah, 0x570c, val); } } #endif if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); } /* * Set PCIe workaround bits * * NOTE: * * In Merlin and Kite, bit 14 in WA register (disable L1) should only * be set when device enters D3 and be cleared when device comes back * to D0. */ if (power_off) { /* Power-off */ OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); val = OS_REG_READ(ah, AR_WA); /* * Disable bit 6 and 7 before entering D3 to prevent * system hang. */ val &= ~(AR_WA_BIT6 | AR_WA_BIT7); /* * See above: set AR_WA_D3_L1_DISABLE when entering D3 state. * * XXX The reference HAL does it this way - it only sets * AR_WA_D3_L1_DISABLE if it's set in AR9280_WA_DEFAULT, * which it (currently) isn't. So the following statement * is currently a NOP. */ if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE) val |= AR_WA_D3_L1_DISABLE; if (AR_SREV_9285E_20(ah)) val |= AR_WA_BIT23; OS_REG_WRITE(ah, AR_WA, val); } else { /* Power-on */ val = AR9285_WA_DEFAULT; /* * See note above: make sure L1_DISABLE is not set. */ val &= (~AR_WA_D3_L1_DISABLE); /* Software workaroud for ASPM system hang. */ val |= (AR_WA_BIT6 | AR_WA_BIT7); if (AR_SREV_9285E_20(ah)) val |= AR_WA_BIT23; OS_REG_WRITE(ah, AR_WA, val); /* set bit 19 to allow forcing of pcie core into L1 state */ OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); } } static void ar9285DisablePCIE(struct ath_hal *ah) { } static void ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { u_int modesIndex, freqIndex; int regWrites = 0; /* Setup the indices for the next set of register array writes */ /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ freqIndex = 2; if (IEEE80211_IS_CHAN_HT40(chan)) modesIndex = 3; else if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else modesIndex = 4; /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, modesIndex, regWrites); if (AR_SREV_KITE_12_OR_LATER(ah)) { regWrites = ath_hal_ini_write(ah, &AH9285(ah)->ah_ini_txgain, modesIndex, regWrites); } regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites); } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah) { HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (!ar5416FillCapabilityInfo(ah)) return AH_FALSE; pCap->halNumGpioPins = 12; pCap->halWowSupport = AH_TRUE; pCap->halWowMatchPatternExact = AH_TRUE; #if 0 pCap->halWowMatchPatternDword = AH_TRUE; #endif /* AR9285 has 2 antennas but is a 1x1 stream device */ pCap->halTxStreams = 1; pCap->halRxStreams = 1; if (ar9285_check_div_comb(ah)) pCap->halAntDivCombSupport = AH_TRUE; pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; pCap->halUseCombinedRadarRssi = AH_TRUE; #if 1 /* XXX bluetooth */ pCap->halBtCoexSupport = AH_TRUE; #endif pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ pCap->hal4kbSplitTransSupport = AH_FALSE; /* Disable this so Block-ACK works correctly */ pCap->halHasRxSelfLinkedTail = AH_FALSE; pCap->halMbssidAggrSupport = AH_TRUE; pCap->hal4AddrAggrSupport = AH_TRUE; pCap->halSpectralScanSupport = AH_TRUE; pCap->halRxUsingLnaMixing = AH_TRUE; if (AR_SREV_KITE_12_OR_LATER(ah)) pCap->halPSPollBroken = AH_FALSE; /* Only RX STBC supported */ pCap->halRxStbcSupport = 1; pCap->halTxStbcSupport = 0; return AH_TRUE; } static const char* ar9285Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID && devid == AR9285_DEVID_PCIE) return "Atheros 9285"; if (vendorid == ATHEROS_VENDOR_ID && (devid == AR2427_DEVID_PCIE)) return "Atheros 2427"; return AH_NULL; } AH_CHIP(AR9285, ar9285Probe, ar9285Attach); Index: head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c =================================================================== --- head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c (revision 361485) +++ head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c (revision 361486) @@ -1,496 +1,496 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting * Copyright (c) 2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v14.h" /* XXX for tx/rx gain */ #include "ah_eeprom_9287.h" #include "ar9002/ar9280.h" #include "ar9002/ar9287.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" #include "ar9002/ar9287_cal.h" #include "ar9002/ar9287_reset.h" #include "ar9002/ar9287_olc.h" #include "ar9002/ar9287.ini" static const HAL_PERCAL_DATA ar9287_iq_cal = { /* single sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MAX_LOG_COUNT, .calCollect = ar5416IQCalCollect, .calPostProc = ar5416IQCalibration }; static const HAL_PERCAL_DATA ar9287_adc_gain_cal = { /* single sample */ .calName = "ADC Gain", .calType = ADC_GAIN_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcGainCalCollect, .calPostProc = ar5416AdcGainCalibration }; static const HAL_PERCAL_DATA ar9287_adc_dc_cal = { /* single sample */ .calName = "ADC DC", .calType = ADC_DC_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = PER_MIN_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static const HAL_PERCAL_DATA ar9287_adc_init_dc_cal = { .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, .calNumSamples = MIN_CAL_SAMPLES, .calCountMax = INIT_LOG_COUNT, .calCollect = ar5416AdcDcCalCollect, .calPostProc = ar5416AdcDcCalibration }; static void ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off); static void ar9287DisablePCIE(struct ath_hal *ah); static HAL_BOOL ar9287FillCapabilityInfo(struct ath_hal *ah); static void ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar9287AniSetup(struct ath_hal *ah) { /* * These are the parameters from the AR5416 ANI code; * they likely need quite a bit of adjustment for the * AR9287. */ static const struct ar5212AniParams aniparams = { .maxNoiseImmunityLevel = 4, /* levels 0..4 */ .totalSizeDesired = { -55, -55, -55, -55, -62 }, .coarseHigh = { -14, -14, -14, -14, -12 }, .coarseLow = { -64, -64, -64, -64, -70 }, .firpwr = { -78, -78, -78, -78, -80 }, .maxSpurImmunityLevel = 7, .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, .maxFirstepLevel = 2, /* levels 0..2 */ .firstep = { 0, 4, 8 }, .ofdmTrigHigh = 500, .ofdmTrigLow = 200, .cckTrigHigh = 200, .cckTrigLow = 100, .rssiThrHigh = 40, .rssiThrLow = 7, .period = 100, }; /* NB: disable ANI noise immmunity for reliable RIFS rx */ AH5416(ah)->ah_ani_function &= ~ HAL_ANI_NOISE_IMMUNITY_LEVEL; /* NB: ANI is not enabled yet */ ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); } /* * Attach for an AR9287 part. */ static struct ath_hal * ar9287Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9287 *ahp9287; struct ath_hal_5212 *ahp; struct ath_hal *ah; uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; int8_t pwr_table_offset; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); /* NB: memory is returned zero'd */ ahp9287 = ath_hal_malloc(sizeof (struct ath_hal_9287)); if (ahp9287 == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_ANY, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ahp = AH5212(ahp9287); ah = &ahp->ah_priv.h; ar5416InitState(AH5416(ah), devid, sc, st, sh, status); if (eepromdata != AH_NULL) { AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead; AH_PRIVATE(ah)->ah_eepromWrite = NULL; ah->ah_eepromdata = eepromdata; } /* XXX override with 9280 specific state */ /* override 5416 methods for our needs */ AH5416(ah)->ah_initPLL = ar9280InitPLL; ah->ah_setAntennaSwitch = ar9287SetAntennaSwitch; ah->ah_configPCIE = ar9287ConfigPCIE; ah->ah_disablePCIE = ar9287DisablePCIE; AH5416(ah)->ah_cal.iqCalData.calData = &ar9287_iq_cal; AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9287_adc_gain_cal; AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9287_adc_dc_cal; AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9287_adc_init_dc_cal; /* Better performance without ADC Gain Calibration */ AH5416(ah)->ah_cal.suppCals = ADC_DC_CAL | IQ_MISMATCH_CAL; AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; AH5416(ah)->ah_writeIni = ar9287WriteIni; ah->ah_setTxPower = ar9287SetTransmitPower; ah->ah_setBoardValues = ar9287SetBoardValues; AH5416(ah)->ah_olcInit = ar9287olcInit; AH5416(ah)->ah_olcTempCompensation = ar9287olcTemperatureCompensation; //AH5416(ah)->ah_setPowerCalTable = ar9287SetPowerCalTable; AH5416(ah)->ah_cal_initcal = ar9287InitCalHardware; AH5416(ah)->ah_cal_pacal = ar9287PACal; /* XXX NF calibration */ /* XXX Ini override? (IFS vars - since the kiwi mac clock is faster?) */ /* XXX what else is kiwi-specific in the radio/calibration pathway? */ AH5416(ah)->ah_rx_chainmask = AR9287_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR9287_DEFAULT_TXCHAINMASK; if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* Read Revisions from Chips before taking out of reset */ val = OS_REG_READ(ah, AR_SREV); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); /* NB: include chip type to differentiate from pre-Sowl versions */ AH_PRIVATE(ah)->ah_macVersion = (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; /* Don't support Kiwi < 1.2; those are pre-release chips */ if (! AR_SREV_KIWI_12_OR_LATER(ah)) { ath_hal_printf(ah, "[ath]: Kiwi < 1.2 is not supported\n"); ecode = HAL_EIO; goto bad; } /* setup common ini data; rf backends handle remainder */ HAL_INI_INIT(&ahp->ah_ini_modes, ar9287Modes_9287_1_1, 6); HAL_INI_INIT(&ahp->ah_ini_common, ar9287Common_9287_1_1, 2); /* If pcie_clock_req */ HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9287PciePhy_clkreq_always_on_L1_9287_1_1, 2); /* XXX WoW ini values */ /* Else */ #if 0 HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, ar9287PciePhy_clkreq_off_L1_9287_1_1, 2); #endif /* Initialise Japan arrays */ HAL_INI_INIT(&ahp9287->ah_ini_cckFirNormal, ar9287Common_normal_cck_fir_coeff_9287_1_1, 2); HAL_INI_INIT(&ahp9287->ah_ini_cckFirJapan2484, ar9287Common_japan_2484_cck_fir_coeff_9287_1_1, 2); ar5416AttachPCIE(ah); ecode = ath_hal_9287EepromAttach(ah); if (ecode != HAL_OK) goto bad; - if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); ecode = HAL_EIO; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); if (!ar5212ChipTest(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", __func__); ecode = HAL_ESELFTEST; goto bad; } /* * Set correct Baseband to analog shift * setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Read Radio Chip Rev Extract */ AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ break; default: if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { AH_PRIVATE(ah)->ah_analog5GhzRev = AR_RAD5133_SREV_MAJOR; break; } #ifdef AH_DEBUG HALDEBUG(ah, HAL_DEBUG_ANY, "%s: 5G Radio Chip Rev 0x%02X is not supported by " "this driver\n", __func__, AH_PRIVATE(ah)->ah_analog5GhzRev); ecode = HAL_ENOTSUPP; goto bad; #endif } rfStatus = ar9287RfAttach(ah, &ecode); if (!rfStatus) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", __func__, ecode); goto bad; } /* * We only implement open-loop TX power control * for the AR9287 in this codebase. */ if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { ath_hal_printf(ah, "[ath] AR9287 w/ closed-loop TX power control" " isn't supported.\n"); ecode = HAL_ENOTSUPP; goto bad; } /* * Check whether the power table offset isn't the default. * This can occur with eeprom minor V21 or greater on Merlin. */ (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); if (pwr_table_offset != AR5416_PWR_TABLE_OFFSET_DB) ath_hal_printf(ah, "[ath]: default pwr offset: %d dBm != EEPROM pwr offset: %d dBm; curves will be adjusted.\n", AR5416_PWR_TABLE_OFFSET_DB, (int) pwr_table_offset); /* setup rxgain table */ HAL_INI_INIT(&ahp9287->ah_ini_rxgain, ar9287Modes_rx_gain_9287_1_1, 6); /* setup txgain table */ HAL_INI_INIT(&ahp9287->ah_ini_txgain, ar9287Modes_tx_gain_9287_1_1, 6); /* * Got everything we need now to setup the capabilities. */ if (!ar9287FillCapabilityInfo(ah)) { ecode = HAL_EEREAD; goto bad; } ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error getting mac address from EEPROM\n", __func__); goto bad; } /* XXX How about the serial number ? */ /* Read Reg Domain */ AH_PRIVATE(ah)->ah_currentRD = ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); AH_PRIVATE(ah)->ah_currentRDext = AR9287_RDEXT_DEFAULT; /* * ah_miscMode is populated by ar5416FillCapabilityInfo() * starting from griffin. Set here to make sure that * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is * placed into hardware. */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); ar9287AniSetup(ah); /* Anti Noise Immunity */ /* Setup noise floor min/max/nominal values */ AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ; AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ; AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9287_2GHZ; AH5416(ah)->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9287_5GHZ; AH5416(ah)->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9287_5GHZ; AH5416(ah)->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9287_5GHZ; ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); return ah; bad: if (ah != AH_NULL) ah->ah_detach(ah); if (status) *status = ecode; return AH_NULL; } static void ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) { if (AH_PRIVATE(ah)->ah_ispcie && !restore) { ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); OS_DELAY(1000); OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); /* Yes, Kiwi uses the Kite PCIe PHY WA */ OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); } } static void ar9287DisablePCIE(struct ath_hal *ah) { /* XXX TODO */ } static void ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { u_int modesIndex, freqIndex; int regWrites = 0; /* Setup the indices for the next set of register array writes */ /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { freqIndex = 2; if (IEEE80211_IS_CHAN_HT40(chan)) modesIndex = 3; else if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else modesIndex = 4; } else { freqIndex = 1; if (IEEE80211_IS_CHAN_HT40(chan) || IEEE80211_IS_CHAN_TURBO(chan)) modesIndex = 2; else modesIndex = 1; } /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, modesIndex, regWrites); regWrites = ath_hal_ini_write(ah, &AH9287(ah)->ah_ini_rxgain, modesIndex, regWrites); regWrites = ath_hal_ini_write(ah, &AH9287(ah)->ah_ini_txgain, modesIndex, regWrites); regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites); } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ static HAL_BOOL ar9287FillCapabilityInfo(struct ath_hal *ah) { HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; if (!ar5416FillCapabilityInfo(ah)) return AH_FALSE; pCap->halNumGpioPins = 10; pCap->halWowSupport = AH_TRUE; pCap->halWowMatchPatternExact = AH_TRUE; #if 0 pCap->halWowMatchPatternDword = AH_TRUE; #endif pCap->halCSTSupport = AH_TRUE; pCap->halRifsRxSupport = AH_TRUE; pCap->halRifsTxSupport = AH_TRUE; pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ pCap->halExtChanDfsSupport = AH_TRUE; pCap->halUseCombinedRadarRssi = AH_TRUE; #if 0 /* XXX bluetooth */ pCap->halBtCoexSupport = AH_TRUE; #endif pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ pCap->hal4kbSplitTransSupport = AH_FALSE; /* Disable this so Block-ACK works correctly */ pCap->halHasRxSelfLinkedTail = AH_FALSE; pCap->halPSPollBroken = AH_FALSE; pCap->halSpectralScanSupport = AH_TRUE; /* Hardware supports (at least) single-stream STBC TX/RX */ pCap->halRxStbcSupport = 1; pCap->halTxStbcSupport = 1; /* Hardware supports short-GI w/ 20MHz */ pCap->halHTSGI20Support = 1; pCap->halEnhancedDfsSupport = AH_TRUE; return AH_TRUE; } /* * This has been disabled - having the HAL flip chainmasks on/off * when attempting to implement 11n disrupts things. For now, just * leave this flipped off and worry about implementing TX diversity * for legacy and MCS0-15 when 11n is fully functioning. */ HAL_BOOL ar9287SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) { return AH_TRUE; } static const char* ar9287Probe(uint16_t vendorid, uint16_t devid) { if (vendorid == ATHEROS_VENDOR_ID) { if (devid == AR9287_DEVID_PCI) return "Atheros 9227"; if (devid == AR9287_DEVID_PCIE) return "Atheros 9287"; } return AH_NULL; } AH_CHIP(AR9287, ar9287Probe, ar9287Attach); Index: head/sys/dev/ath/if_ath.c =================================================================== --- head/sys/dev/ath/if_ath.c (revision 361485) +++ head/sys/dev/ath/if_ath.c (revision 361486) @@ -1,7073 +1,7078 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" /* * This is needed for register operations which are performed * by the driver - eg, calls to ath_hal_gettsf32(). * * It's also required for any AH_DEBUG checks in here, eg the * module dependencies. */ #include "opt_ah.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for mp_ncpus */ #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATH_TX99_DIAG #include #endif #ifdef ATH_DEBUG_ALQ #include #endif /* * Only enable this if you're working on PS-POLL support. */ #define ATH_SW_PSQ /* * ATH_BCBUF determines the number of vap's that can transmit * beacons and also (currently) the number of vap's that can * have unique mac addresses/bssid. When staggering beacons * 4 is probably a good max as otherwise the beacons become * very closely spaced and there is limited time for cab q traffic * to go out. You can burst beacons instead but that is not good * for stations in power save and at some point you really want * another radio (and channel). * * The limit on the number of mac addresses is tied to our use of * the U/L bit and tracking addresses in a byte; it would be * worthwhile to allow more for applications like proxy sta. */ CTASSERT(ATH_BCBUF <= 8); static struct ieee80211vap *ath_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void ath_vap_delete(struct ieee80211vap *); static int ath_init(struct ath_softc *); static void ath_stop(struct ath_softc *); static int ath_reset_vap(struct ieee80211vap *, u_long); static int ath_transmit(struct ieee80211com *, struct mbuf *); static int ath_media_change(struct ifnet *); static void ath_watchdog(void *); static void ath_parent(struct ieee80211com *); static void ath_fatal_proc(void *, int); static void ath_bmiss_vap(struct ieee80211vap *); static void ath_bmiss_proc(void *, int); static void ath_key_update_begin(struct ieee80211vap *); static void ath_key_update_end(struct ieee80211vap *); static void ath_update_mcast_hw(struct ath_softc *); static void ath_update_mcast(struct ieee80211com *); static void ath_update_promisc(struct ieee80211com *); static void ath_updateslot(struct ieee80211com *); static void ath_bstuck_proc(void *, int); static void ath_reset_proc(void *, int); static int ath_desc_alloc(struct ath_softc *); static void ath_desc_free(struct ath_softc *); static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); static void ath_node_cleanup(struct ieee80211_node *); static void ath_node_free(struct ieee80211_node *); static void ath_node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); static int ath_tx_setup(struct ath_softc *, int, int); static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *); static void ath_tx_cleanup(struct ath_softc *); static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched); static void ath_tx_proc_q0(void *, int); static void ath_tx_proc_q0123(void *, int); static void ath_tx_proc(void *, int); static void ath_txq_sched_tasklet(void *, int); static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); static void ath_scan_start(struct ieee80211com *); static void ath_scan_end(struct ieee80211com *); static void ath_set_channel(struct ieee80211com *); #ifdef ATH_ENABLE_11N static void ath_update_chw(struct ieee80211com *); #endif /* ATH_ENABLE_11N */ static int ath_set_quiet_ie(struct ieee80211_node *, uint8_t *); static void ath_calibrate(void *); static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ath_setup_stationkey(struct ieee80211_node *); static void ath_newassoc(struct ieee80211_node *, int); static int ath_setregdomain(struct ieee80211com *, struct ieee80211_regdomain *, int, struct ieee80211_channel []); static void ath_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel []); static int ath_getchannels(struct ath_softc *); static int ath_rate_setup(struct ath_softc *, u_int mode); static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); static void ath_announce(struct ath_softc *); static void ath_dfs_tasklet(void *, int); static void ath_node_powersave(struct ieee80211_node *, int); static int ath_node_set_tim(struct ieee80211_node *, int); static void ath_node_recv_pspoll(struct ieee80211_node *, struct mbuf *); #ifdef IEEE80211_SUPPORT_TDMA #include #endif SYSCTL_DECL(_hw_ath); /* XXX validate sysctl values */ static int ath_longcalinterval = 30; /* long cals every 30 secs */ SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval, 0, "long chip calibration interval (secs)"); static int ath_shortcalinterval = 100; /* short cals every 100 ms */ SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval, 0, "short chip calibration interval (msecs)"); static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */ SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval, 0, "reset chip calibration results (secs)"); static int ath_anicalinterval = 100; /* ANI calibration - 100 msec */ SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 0, "ANI calibration (msecs)"); int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RWTUN, &ath_rxbuf, 0, "rx buffers allocated"); int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RWTUN, &ath_txbuf, 0, "tx buffers allocated"); int ath_txbuf_mgmt = ATH_MGMT_TXBUF; /* # mgmt tx buffers to allocate */ SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RWTUN, &ath_txbuf_mgmt, 0, "tx (mgmt) buffers allocated"); int ath_bstuck_threshold = 4; /* max missed beacons */ SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, 0, "max missed beacon xmits before chip reset"); MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); void ath_legacy_attach_comp_func(struct ath_softc *sc) { /* * Special case certain configurations. Note the * CAB queue is handled by these specially so don't * include them when checking the txq setup mask. */ switch (sc->sc_txqsetup &~ (1<sc_cabq->axq_qnum)) { case 0x01: TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); break; case 0x0f: TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); break; default: TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); break; } } /* * Set the target power mode. * * If this is called during a point in time where * the hardware is being programmed elsewhere, it will * simply store it away and update it when all current * uses of the hardware are completed. * * If the chip is going into network sleep or power off, then * we will wait until all uses of the chip are done before * going into network sleep or power off. * * If the chip is being programmed full-awake, then immediately * program it full-awake so we can actually stay awake rather than * the chip potentially going to sleep underneath us. */ void _ath_power_setpower(struct ath_softc *sc, int power_state, int selfgen, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d, target=%d, cur=%d\n", __func__, file, line, power_state, sc->sc_powersave_refcnt, sc->sc_target_powerstate, sc->sc_cur_powerstate); sc->sc_target_powerstate = power_state; /* * Don't program the chip into network sleep if the chip * is being programmed elsewhere. * * However, if the chip is being programmed /awake/, force * the chip awake so we stay awake. */ if ((sc->sc_powersave_refcnt == 0 || power_state == HAL_PM_AWAKE) && power_state != sc->sc_cur_powerstate) { sc->sc_cur_powerstate = power_state; ath_hal_setpower(sc->sc_ah, power_state); /* * If the NIC is force-awake, then set the * self-gen frame state appropriately. * * If the nic is in network sleep or full-sleep, * we let the above call leave the self-gen * state as "sleep". */ if (selfgen && sc->sc_cur_powerstate == HAL_PM_AWAKE && sc->sc_target_selfgen_state != HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, sc->sc_target_selfgen_state); } } } /* * Set the current self-generated frames state. * * This is separate from the target power mode. The chip may be * awake but the desired state is "sleep", so frames sent to the * destination has PWRMGT=1 in the 802.11 header. The NIC also * needs to know to set PWRMGT=1 in self-generated frames. */ void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", __func__, file, line, power_state, sc->sc_target_selfgen_state); sc->sc_target_selfgen_state = power_state; /* * If the NIC is force-awake, then set the power state. * Network-state and full-sleep will already transition it to * mark self-gen frames as sleeping - and we can't * guarantee the NIC is awake to program the self-gen frame * setting anyway. */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, power_state); } } /* * Set the hardware power mode and take a reference. * * This doesn't update the target power mode in the driver; * it just updates the hardware power state. * * XXX it should only ever force the hardware awake; it should * never be called to set it asleep. */ void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", __func__, file, line, power_state, sc->sc_powersave_refcnt); sc->sc_powersave_refcnt++; /* * Only do the power state change if we're not programming * it elsewhere. */ if (power_state != sc->sc_cur_powerstate) { ath_hal_setpower(sc->sc_ah, power_state); sc->sc_cur_powerstate = power_state; /* * Adjust the self-gen powerstate if appropriate. */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE && sc->sc_target_selfgen_state != HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, sc->sc_target_selfgen_state); } } } /* * Restore the power save mode to what it once was. * * This will decrement the reference counter and once it hits * zero, it'll restore the powersave state. */ void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line) { ATH_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) refcnt=%d, target state=%d\n", __func__, file, line, sc->sc_powersave_refcnt, sc->sc_target_powerstate); if (sc->sc_powersave_refcnt == 0) device_printf(sc->sc_dev, "%s: refcnt=0?\n", __func__); else sc->sc_powersave_refcnt--; if (sc->sc_powersave_refcnt == 0 && sc->sc_target_powerstate != sc->sc_cur_powerstate) { sc->sc_cur_powerstate = sc->sc_target_powerstate; ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate); } /* * Adjust the self-gen powerstate if appropriate. */ if (sc->sc_cur_powerstate == HAL_PM_AWAKE && sc->sc_target_selfgen_state != HAL_PM_AWAKE) { ath_hal_setselfgenpower(sc->sc_ah, sc->sc_target_selfgen_state); } } /* * Configure the initial HAL configuration values based on bus * specific parameters. * * Some PCI IDs and other information may need tweaking. * * XXX TODO: ath9k and the Atheros HAL only program comm2g_switch_enable * if BT antenna diversity isn't enabled. * * So, let's also figure out how to enable BT diversity for AR9485. */ static void ath_setup_hal_config(struct ath_softc *sc, HAL_OPS_CONFIG *ah_config) { /* XXX TODO: only for PCI devices? */ if (sc->sc_pci_devinfo & (ATH_PCI_CUS198 | ATH_PCI_CUS230)) { ah_config->ath_hal_ext_lna_ctl_gpio = 0x200; /* bit 9 */ ah_config->ath_hal_ext_atten_margin_cfg = AH_TRUE; ah_config->ath_hal_min_gainidx = AH_TRUE; ah_config->ath_hal_ant_ctrl_comm2g_switch_enable = 0x000bbb88; /* XXX low_rssi_thresh */ /* XXX fast_div_bias */ device_printf(sc->sc_dev, "configuring for %s\n", (sc->sc_pci_devinfo & ATH_PCI_CUS198) ? "CUS198" : "CUS230"); } if (sc->sc_pci_devinfo & ATH_PCI_CUS217) device_printf(sc->sc_dev, "CUS217 card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_CUS252) device_printf(sc->sc_dev, "CUS252 card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) device_printf(sc->sc_dev, "WB335 1-ANT card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) device_printf(sc->sc_dev, "WB335 2-ANT card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV) device_printf(sc->sc_dev, "Bluetooth Antenna Diversity card detected\n"); if (sc->sc_pci_devinfo & ATH_PCI_KILLER) device_printf(sc->sc_dev, "Killer Wireless card detected\n"); #if 0 /* * Some WB335 cards do not support antenna diversity. Since * we use a hardcoded value for AR9565 instead of using the * EEPROM/OTP data, remove the combining feature from * the HW capabilities bitmap. */ if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { if (!(sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV)) pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB; } if (sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV) { pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; device_printf(sc->sc_dev, "Set BT/WLAN RX diversity capability\n"); } #endif if (sc->sc_pci_devinfo & ATH_PCI_D3_L1_WAR) { ah_config->ath_hal_pcie_waen = 0x0040473b; device_printf(sc->sc_dev, "Enable WAR for ASPM D3/L1\n"); } #if 0 if (sc->sc_pci_devinfo & ATH9K_PCI_NO_PLL_PWRSAVE) { ah->config.no_pll_pwrsave = true; device_printf(sc->sc_dev, "Disable PLL PowerSave\n"); } #endif } /* * Attempt to fetch the MAC address from the kernel environment. * * Returns 0, macaddr in macaddr if successful; -1 otherwise. */ static int ath_fetch_mac_kenv(struct ath_softc *sc, uint8_t *macaddr) { char devid_str[32]; int local_mac = 0; char *local_macstr; /* * Fetch from the kenv rather than using hints. * * Hints would be nice but the transition to dynamic * hints/kenv doesn't happen early enough for this * to work reliably (eg on anything embedded.) */ snprintf(devid_str, 32, "hint.%s.%d.macaddr", device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); if ((local_macstr = kern_getenv(devid_str)) != NULL) { uint32_t tmpmac[ETHER_ADDR_LEN]; int count; int i; /* Have a MAC address; should use it */ device_printf(sc->sc_dev, "Overriding MAC address from environment: '%s'\n", local_macstr); /* Extract out the MAC address */ count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", &tmpmac[0], &tmpmac[1], &tmpmac[2], &tmpmac[3], &tmpmac[4], &tmpmac[5]); if (count == 6) { /* Valid! */ local_mac = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) macaddr[i] = tmpmac[i]; } /* Done! */ freeenv(local_macstr); local_macstr = NULL; } if (local_mac) return (0); return (-1); } #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) int ath_attach(u_int16_t devid, struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = NULL; HAL_STATUS status; int error = 0, i; u_int wmodes; int rx_chainmask, tx_chainmask; HAL_OPS_CONFIG ah_config; DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(sc->sc_dev); /* * Configure the initial configuration data. * * This is stuff that may be needed early during attach * rather than done via configuration calls later. */ bzero(&ah_config, sizeof(ah_config)); ath_setup_hal_config(sc, &ah_config); ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, sc->sc_eepromdata, &ah_config, &status); if (ah == NULL) { device_printf(sc->sc_dev, "unable to attach hardware; HAL status %u\n", status); error = ENXIO; goto bad; } sc->sc_ah = ah; sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ #ifdef ATH_DEBUG sc->sc_debug = ath_debug; #endif /* * Force the chip awake during setup, just to keep * the HAL/driver power tracking happy. * * There are some methods (eg ath_hal_setmac()) * that poke the hardware. */ ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_AWAKE, 1); ATH_UNLOCK(sc); /* * Setup the DMA/EDMA functions based on the current * hardware support. * * This is required before the descriptors are allocated. */ if (ath_hal_hasedma(sc->sc_ah)) { sc->sc_isedma = 1; ath_recv_setup_edma(sc); ath_xmit_setup_edma(sc); } else { ath_recv_setup_legacy(sc); ath_xmit_setup_legacy(sc); } if (ath_hal_hasmybeacon(sc->sc_ah)) { sc->sc_do_mybeacon = 1; } /* * Check if the MAC has multi-rate retry support. * We do this by trying to setup a fake extended * descriptor. MAC's that don't have support will * return false w/o doing anything. MAC's that do * support it will return true w/o doing anything. */ sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); /* * Check if the device has hardware counters for PHY * errors. If so we need to enable the MIB interrupt * so we can act on stat triggers. */ if (ath_hal_hwphycounters(ah)) sc->sc_needmib = 1; /* * Get the hardware key cache size. */ sc->sc_keymax = ath_hal_keycachesize(ah); if (sc->sc_keymax > ATH_KEYMAX) { device_printf(sc->sc_dev, "Warning, using only %u of %u key cache slots\n", ATH_KEYMAX, sc->sc_keymax); sc->sc_keymax = ATH_KEYMAX; } /* * Reset the key cache since some parts do not * reset the contents on initial power up. */ for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); /* * Collect the default channel list. */ error = ath_getchannels(sc); if (error != 0) goto bad; /* * Setup rate tables for all potential media types. */ ath_rate_setup(sc, IEEE80211_MODE_11A); ath_rate_setup(sc, IEEE80211_MODE_11B); ath_rate_setup(sc, IEEE80211_MODE_11G); ath_rate_setup(sc, IEEE80211_MODE_TURBO_A); ath_rate_setup(sc, IEEE80211_MODE_TURBO_G); ath_rate_setup(sc, IEEE80211_MODE_STURBO_A); ath_rate_setup(sc, IEEE80211_MODE_11NA); ath_rate_setup(sc, IEEE80211_MODE_11NG); ath_rate_setup(sc, IEEE80211_MODE_HALF); ath_rate_setup(sc, IEEE80211_MODE_QUARTER); /* NB: setup here so ath_rate_update is happy */ ath_setcurmode(sc, IEEE80211_MODE_11A); /* * Allocate TX descriptors and populate the lists. */ error = ath_desc_alloc(sc); if (error != 0) { device_printf(sc->sc_dev, "failed to allocate TX descriptors: %d\n", error); goto bad; } error = ath_txdma_setup(sc); if (error != 0) { device_printf(sc->sc_dev, "failed to allocate TX descriptors: %d\n", error); goto bad; } /* * Allocate RX descriptors and populate the lists. */ error = ath_rxdma_setup(sc); if (error != 0) { device_printf(sc->sc_dev, "failed to allocate RX descriptors: %d\n", error); goto bad; } callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0); ATH_TXBUF_LOCK_INIT(sc); sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->sc_dev)); TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc); TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); TASK_INIT(&sc->sc_resettask,0, ath_reset_proc, sc); TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc); TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc); /* * Allocate hardware transmit queues: one queue for * beacon frames and one data queue for each QoS * priority. Note that the hal handles resetting * these queues at the needed time. * * XXX PS-Poll */ sc->sc_bhalq = ath_beaconq_setup(sc); if (sc->sc_bhalq == (u_int) -1) { device_printf(sc->sc_dev, "unable to setup a beacon xmit queue!\n"); error = EIO; goto bad2; } sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0); if (sc->sc_cabq == NULL) { device_printf(sc->sc_dev, "unable to setup CAB xmit queue!\n"); error = EIO; goto bad2; } /* NB: insure BK queue is the lowest priority h/w queue */ if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) { device_printf(sc->sc_dev, "unable to setup xmit queue for %s traffic!\n", ieee80211_wme_acnames[WME_AC_BK]); error = EIO; goto bad2; } if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) || !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { /* * Not enough hardware tx queues to properly do WME; * just punt and assign them all to the same h/w queue. * We could do a better job of this if, for example, * we allocate queues when we switch from station to * AP mode. */ if (sc->sc_ac2q[WME_AC_VI] != NULL) ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]); if (sc->sc_ac2q[WME_AC_BE] != NULL) ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]); sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; } /* * Attach the TX completion function. * * The non-EDMA chips may have some special case optimisations; * this method gives everyone a chance to attach cleanly. */ sc->sc_tx.xmit_attach_comp_func(sc); /* * Setup rate control. Some rate control modules * call back to change the anntena state so expose * the necessary entry points. * XXX maybe belongs in struct ath_ratectrl? */ sc->sc_setdefantenna = ath_setdefantenna; sc->sc_rc = ath_rate_attach(sc); if (sc->sc_rc == NULL) { error = EIO; goto bad2; } /* Attach DFS module */ if (! ath_dfs_attach(sc)) { device_printf(sc->sc_dev, "%s: unable to attach DFS\n", __func__); error = EIO; goto bad2; } /* Attach spectral module */ if (ath_spectral_attach(sc) < 0) { device_printf(sc->sc_dev, "%s: unable to attach spectral\n", __func__); error = EIO; goto bad2; } /* Attach bluetooth coexistence module */ if (ath_btcoex_attach(sc) < 0) { device_printf(sc->sc_dev, "%s: unable to attach bluetooth coexistence\n", __func__); error = EIO; goto bad2; } /* Attach LNA diversity module */ if (ath_lna_div_attach(sc) < 0) { device_printf(sc->sc_dev, "%s: unable to attach LNA diversity\n", __func__); error = EIO; goto bad2; } /* Start DFS processing tasklet */ TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc); /* Configure LED state */ sc->sc_blinking = 0; sc->sc_ledstate = 1; sc->sc_ledon = 0; /* low true */ sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ callout_init(&sc->sc_ledtimer, 1); /* * Don't setup hardware-based blinking. * * Although some NICs may have this configured in the * default reset register values, the user may wish * to alter which pins have which function. * * The reference driver attaches the MAC network LED to GPIO1 and * the MAC power LED to GPIO2. However, the DWA-552 cardbus * NIC has these reversed. */ sc->sc_hardled = (1 == 0); sc->sc_led_net_pin = -1; sc->sc_led_pwr_pin = -1; /* * Auto-enable soft led processing for IBM cards and for * 5211 minipci cards. Users can also manually enable/disable * support with a sysctl. */ sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); ath_led_config(sc); ath_hal_setledstate(ah, HAL_LED_INIT); /* XXX not right but it's not used anywhere important */ ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ | IEEE80211_C_HOSTAP /* hostap mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_AHDEMO /* adhoc demo mode */ | IEEE80211_C_WDS /* 4-address traffic works */ | IEEE80211_C_MBSS /* mesh point link mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ #ifndef ATH_ENABLE_11N | IEEE80211_C_BGSCAN /* capable of bg scanning */ #endif | IEEE80211_C_TXFRAG /* handle tx frags */ #ifdef ATH_ENABLE_DFS | IEEE80211_C_DFS /* Enable radar detection */ #endif | IEEE80211_C_PMGT /* Station side power mgmt */ | IEEE80211_C_SWSLEEP ; /* * Query the hal to figure out h/w crypto support. */ if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB; if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM; if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP; if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP; /* * Check if h/w does the MIC and/or whether the * separate key cache entries are required to * handle both tx+rx MIC keys. */ if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; /* * If the h/w supports storing tx+rx MIC keys * in one cache slot automatically enable use. */ if (ath_hal_hastkipsplit(ah) || !ath_hal_settkipsplit(ah, AH_FALSE)) sc->sc_splitmic = 1; /* * If the h/w can do TKIP MIC together with WME then * we use it; otherwise we force the MIC to be done * in software by the net80211 layer. */ if (ath_hal_haswmetkipmic(ah)) sc->sc_wmetkipmic = 1; } sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); /* * Check for multicast key search support. */ if (ath_hal_hasmcastkeysearch(sc->sc_ah) && !ath_hal_getmcastkeysearch(sc->sc_ah)) { ath_hal_setmcastkeysearch(sc->sc_ah, 1); } sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); /* * Mark key cache slots associated with global keys * as in use. If we knew TKIP was not to be used we * could leave the +32, +64, and +32+64 slots free. */ for (i = 0; i < IEEE80211_WEP_NKID; i++) { setbit(sc->sc_keymap, i); setbit(sc->sc_keymap, i+64); if (sc->sc_splitmic) { setbit(sc->sc_keymap, i+32); setbit(sc->sc_keymap, i+32+64); } } /* * TPC support can be done either with a global cap or * per-packet support. The latter is not available on * all parts. We're a bit pedantic here as all parts * support a global cap. */ if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah)) ic->ic_caps |= IEEE80211_C_TXPMGT; /* * Mark WME capability only if we have sufficient * hardware queues to do proper priority scheduling. */ if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK]) ic->ic_caps |= IEEE80211_C_WME; /* * Check for misc other capabilities. */ if (ath_hal_hasbursting(ah)) ic->ic_caps |= IEEE80211_C_BURST; sc->sc_hasbmask = ath_hal_hasbssidmask(ah); sc->sc_hasbmatch = ath_hal_hasbssidmatch(ah); sc->sc_hastsfadd = ath_hal_hastsfadjust(ah); sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah); /* XXX TODO: just make this a "store tx/rx timestamp length" operation */ if (ath_hal_get_rx_tsf_prec(ah, &i)) { if (i == 32) { sc->sc_rxtsf32 = 1; } if (bootverbose) device_printf(sc->sc_dev, "RX timestamp: %d bits\n", i); } if (ath_hal_get_tx_tsf_prec(ah, &i)) { if (bootverbose) device_printf(sc->sc_dev, "TX timestamp: %d bits\n", i); } sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah); sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah); sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah); /* * Some WB335 cards do not support antenna diversity. Since * we use a hardcoded value for AR9565 instead of using the * EEPROM/OTP data, remove the combining feature from * the HW capabilities bitmap. */ /* * XXX TODO: check reference driver and ath9k for what to do * here for WB335. I think we have to actually disable the * LNA div processing in the HAL and instead use the hard * coded values; and then use BT diversity. * * .. but also need to setup MCI too for WB335.. */ #if 0 if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { device_printf(sc->sc_dev, "%s: WB335: disabling LNA mixer diversity\n", __func__); sc->sc_dolnadiv = 0; } #endif if (ath_hal_hasfastframes(ah)) ic->ic_caps |= IEEE80211_C_FF; wmodes = ath_hal_getwirelessmodes(ah); if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) ic->ic_caps |= IEEE80211_C_TURBOP; #ifdef IEEE80211_SUPPORT_TDMA if (ath_hal_macversion(ah) > 0x78) { ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ ic->ic_tdma_update = ath_tdma_update; } #endif /* * TODO: enforce that at least this many frames are available * in the txbuf list before allowing data frames (raw or * otherwise) to be transmitted. */ sc->sc_txq_data_minfree = 10; /* * Shorten this to 64 packets, or 1/4 ath_txbuf, whichever * is smaller. * * Anything bigger can potentially see the cabq consume * almost all buffers, starving everything else, only to * see most fail to transmit in the given beacon interval. */ sc->sc_txq_mcastq_maxdepth = MIN(64, ath_txbuf / 4); /* * How deep can the node software TX queue get whilst it's asleep. */ sc->sc_txq_node_psq_maxdepth = 16; /* * Default the maximum queue to 1/4'th the TX buffers, or * 64, whichever is smaller. */ sc->sc_txq_node_maxdepth = MIN(64, ath_txbuf / 4); /* Enable CABQ by default */ sc->sc_cabq_enable = 1; /* * Allow the TX and RX chainmasks to be overridden by * environment variables and/or device.hints. * * This must be done early - before the hardware is * calibrated or before the 802.11n stream calculation * is done. */ if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "rx_chainmask", &rx_chainmask) == 0) { device_printf(sc->sc_dev, "Setting RX chainmask to 0x%x\n", rx_chainmask); (void) ath_hal_setrxchainmask(sc->sc_ah, rx_chainmask); } if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "tx_chainmask", &tx_chainmask) == 0) { device_printf(sc->sc_dev, "Setting TX chainmask to 0x%x\n", tx_chainmask); (void) ath_hal_settxchainmask(sc->sc_ah, tx_chainmask); } /* * Query the TX/RX chainmask configuration. * * This is only relevant for 11n devices. */ ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask); ath_hal_gettxchainmask(ah, &sc->sc_txchainmask); /* * Disable MRR with protected frames by default. * Only 802.11n series NICs can handle this. */ sc->sc_mrrprot = 0; /* XXX should be a capability */ /* * Query the enterprise mode information the HAL. */ if (ath_hal_getcapability(ah, HAL_CAP_ENTERPRISE_MODE, 0, &sc->sc_ent_cfg) == HAL_OK) sc->sc_use_ent = 1; #ifdef ATH_ENABLE_11N /* * Query HT capabilities */ if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK && (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) { uint32_t rxs, txs; uint32_t ldpc; device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); sc->sc_mrrprot = 1; /* XXX should be a capability */ ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ /* * Enable short-GI for HT20 only if the hardware * advertises support. * Notably, anything earlier than the AR9287 doesn't. */ if ((ath_hal_getcapability(ah, HAL_CAP_HT20_SGI, 0, NULL) == HAL_OK) && (wmodes & HAL_MODE_HT20)) { device_printf(sc->sc_dev, "[HT] enabling short-GI in 20MHz mode\n"); ic->ic_htcaps |= IEEE80211_HTCAP_SHORTGI20; } if (wmodes & HAL_MODE_HT40) ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 | IEEE80211_HTCAP_SHORTGI40; /* * TX/RX streams need to be taken into account when * negotiating which MCS rates it'll receive and * what MCS rates are available for TX. */ (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &txs); (void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &rxs); ic->ic_txstream = txs; ic->ic_rxstream = rxs; /* * Setup TX and RX STBC based on what the HAL allows and * the currently configured chainmask set. * Ie - don't enable STBC TX if only one chain is enabled. * STBC RX is fine on a single RX chain; it just won't * provide any real benefit. */ if (ath_hal_getcapability(ah, HAL_CAP_RX_STBC, 0, NULL) == HAL_OK) { sc->sc_rx_stbc = 1; device_printf(sc->sc_dev, "[HT] 1 stream STBC receive enabled\n"); ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_1STREAM; } if (txs > 1 && ath_hal_getcapability(ah, HAL_CAP_TX_STBC, 0, NULL) == HAL_OK) { sc->sc_tx_stbc = 1; device_printf(sc->sc_dev, "[HT] 1 stream STBC transmit enabled\n"); ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; } (void) ath_hal_getcapability(ah, HAL_CAP_RTS_AGGR_LIMIT, 1, &sc->sc_rts_aggr_limit); if (sc->sc_rts_aggr_limit != (64 * 1024)) device_printf(sc->sc_dev, "[HT] RTS aggregates limited to %d KiB\n", sc->sc_rts_aggr_limit / 1024); /* * LDPC */ if ((ath_hal_getcapability(ah, HAL_CAP_LDPC, 0, &ldpc)) == HAL_OK && (ldpc == 1)) { sc->sc_has_ldpc = 1; device_printf(sc->sc_dev, "[HT] LDPC transmit/receive enabled\n"); ic->ic_htcaps |= IEEE80211_HTCAP_LDPC | IEEE80211_HTC_TXLDPC; } device_printf(sc->sc_dev, "[HT] %d RX streams; %d TX streams\n", rxs, txs); } #endif /* * Initial aggregation settings. */ sc->sc_hwq_limit_aggr = ATH_AGGR_MIN_QDEPTH; sc->sc_hwq_limit_nonaggr = ATH_NONAGGR_MIN_QDEPTH; sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW; sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH; sc->sc_aggr_limit = ATH_AGGR_MAXSIZE; sc->sc_delim_min_pad = 0; /* * Check if the hardware requires PCI register serialisation. * Some of the Owl based MACs require this. */ if (mp_ncpus > 1 && ath_hal_getcapability(ah, HAL_CAP_SERIALISE_WAR, 0, NULL) == HAL_OK) { sc->sc_ah->ah_config.ah_serialise_reg_war = 1; device_printf(sc->sc_dev, "Enabling register serialisation\n"); } /* * Initialise the deferred completed RX buffer list. */ TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]); TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]); /* * Indicate we need the 802.11 header padded to a * 32-bit boundary for 4-address and QoS frames. */ ic->ic_flags |= IEEE80211_F_DATAPAD; /* * Query the hal about antenna support. */ sc->sc_defant = ath_hal_getdefantenna(ah); /* * Not all chips have the VEOL support we want to * use with IBSS beacons; check here for it. */ sc->sc_hasveol = ath_hal_hasveol(ah); /* get mac address from kenv first, then hardware */ if (ath_fetch_mac_kenv(sc, ic->ic_macaddr) == 0) { /* Tell the HAL now about the new MAC */ ath_hal_setmac(ah, ic->ic_macaddr); } else { ath_hal_getmac(ah, ic->ic_macaddr); } if (sc->sc_hasbmask) ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); /* NB: used to size node table key mapping array */ ic->ic_max_keyix = sc->sc_keymax; /* call MI attach routine. */ ieee80211_ifattach(ic); ic->ic_setregdomain = ath_setregdomain; ic->ic_getradiocaps = ath_getradiocaps; sc->sc_opmode = HAL_M_STA; /* override default methods */ ic->ic_ioctl = ath_ioctl; ic->ic_parent = ath_parent; ic->ic_transmit = ath_transmit; ic->ic_newassoc = ath_newassoc; ic->ic_updateslot = ath_updateslot; ic->ic_wme.wme_update = ath_wme_update; ic->ic_vap_create = ath_vap_create; ic->ic_vap_delete = ath_vap_delete; ic->ic_raw_xmit = ath_raw_xmit; ic->ic_update_mcast = ath_update_mcast; ic->ic_update_promisc = ath_update_promisc; ic->ic_node_alloc = ath_node_alloc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = ath_node_free; sc->sc_node_cleanup = ic->ic_node_cleanup; ic->ic_node_cleanup = ath_node_cleanup; ic->ic_node_getsignal = ath_node_getsignal; ic->ic_scan_start = ath_scan_start; ic->ic_scan_end = ath_scan_end; ic->ic_set_channel = ath_set_channel; #ifdef ATH_ENABLE_11N /* 802.11n specific - but just override anyway */ sc->sc_addba_request = ic->ic_addba_request; sc->sc_addba_response = ic->ic_addba_response; sc->sc_addba_stop = ic->ic_addba_stop; sc->sc_bar_response = ic->ic_bar_response; sc->sc_addba_response_timeout = ic->ic_addba_response_timeout; ic->ic_addba_request = ath_addba_request; ic->ic_addba_response = ath_addba_response; ic->ic_addba_response_timeout = ath_addba_response_timeout; ic->ic_addba_stop = ath_addba_stop; ic->ic_bar_response = ath_bar_response; ic->ic_update_chw = ath_update_chw; #endif /* ATH_ENABLE_11N */ ic->ic_set_quiet = ath_set_quiet_ie; #ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT /* * There's one vendor bitmap entry in the RX radiotap * header; make sure that's taken into account. */ ieee80211_radiotap_attachv(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 0, ATH_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 1, ATH_RX_RADIOTAP_PRESENT); #else /* * No vendor bitmap/extensions are present. */ ieee80211_radiotap_attach(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), ATH_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), ATH_RX_RADIOTAP_PRESENT); #endif /* ATH_ENABLE_RADIOTAP_VENDOR_EXT */ /* * Setup the ALQ logging if required */ #ifdef ATH_DEBUG_ALQ if_ath_alq_init(&sc->sc_alq, device_get_nameunit(sc->sc_dev)); if_ath_alq_setcfg(&sc->sc_alq, sc->sc_ah->ah_macVersion, sc->sc_ah->ah_macRev, sc->sc_ah->ah_phyRev, sc->sc_ah->ah_magic); #endif /* * Setup dynamic sysctl's now that country code and * regdomain are available from the hal. */ ath_sysctlattach(sc); ath_sysctl_stats_attach(sc); ath_sysctl_hal_attach(sc); if (bootverbose) ieee80211_announce(ic); ath_announce(sc); /* * Put it to sleep for now. */ ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_FULL_SLEEP, 1); ATH_UNLOCK(sc); return 0; bad2: ath_tx_cleanup(sc); ath_desc_free(sc); ath_txdma_teardown(sc); ath_rxdma_teardown(sc); bad: if (ah) ath_hal_detach(ah); sc->sc_invalid = 1; return error; } int ath_detach(struct ath_softc *sc) { /* * NB: the order of these is important: * o stop the chip so no more interrupts will fire * o call the 802.11 layer before detaching the hal to * insure callbacks into the driver to delete global * key cache entries can be handled * o free the taskqueue which drains any pending tasks * o reclaim the tx queue data structures after calling * the 802.11 layer as we'll get called back to reclaim * node state and potentially want to use them * o to cleanup the tx queues the hal is called, so detach * it last * Other than that, it's straightforward... */ /* * XXX Wake the hardware up first. ath_stop() will still * wake it up first, but I'd rather do it here just to * ensure it's awake. */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE, 1); /* * Stop things cleanly. */ ath_stop(sc); ATH_UNLOCK(sc); ieee80211_ifdetach(&sc->sc_ic); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->detach(sc->sc_tx99); #endif ath_rate_detach(sc->sc_rc); #ifdef ATH_DEBUG_ALQ if_ath_alq_tidyup(&sc->sc_alq); #endif ath_lna_div_detach(sc); ath_btcoex_detach(sc); ath_spectral_detach(sc); ath_dfs_detach(sc); ath_desc_free(sc); ath_txdma_teardown(sc); ath_rxdma_teardown(sc); ath_tx_cleanup(sc); ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ return 0; } /* * MAC address handling for multiple BSS on the same radio. * The first vap uses the MAC address from the EEPROM. For * subsequent vap's we set the U/L bit (bit 1) in the MAC * address and use the next six bits as an index. */ static void assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone) { int i; if (clone && sc->sc_hasbmask) { /* NB: we only do this if h/w supports multiple bssid */ for (i = 0; i < 8; i++) if ((sc->sc_bssidmask & (1<sc_bssidmask |= 1<sc_hwbssidmask[0] &= ~mac[0]; if (i == 0) sc->sc_nbssid0++; } static void reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN]) { int i = mac[0] >> 2; uint8_t mask; if (i != 0 || --sc->sc_nbssid0 == 0) { sc->sc_bssidmask &= ~(1<sc_bssidmask & (1<sc_hwbssidmask[0] |= mask; } } /* * Assign a beacon xmit slot. We try to space out * assignments so when beacons are staggered the * traffic coming out of the cab q has maximal time * to go out before the next beacon is scheduled. */ static int assign_bslot(struct ath_softc *sc) { u_int slot, free; free = 0; for (slot = 0; slot < ATH_BCBUF; slot++) if (sc->sc_bslot[slot] == NULL) { if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL && sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL) return slot; free = slot; /* NB: keep looking for a double slot */ } return free; } static struct ieee80211vap * ath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac0[IEEE80211_ADDR_LEN]) { struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp; struct ieee80211vap *vap; uint8_t mac[IEEE80211_ADDR_LEN]; int needbeacon, error; enum ieee80211_opmode ic_opmode; avp = malloc(sizeof(struct ath_vap), M_80211_VAP, M_WAITOK | M_ZERO); needbeacon = 0; IEEE80211_ADDR_COPY(mac, mac0); ATH_LOCK(sc); ic_opmode = opmode; /* default to opmode of new vap */ switch (opmode) { case IEEE80211_M_STA: if (sc->sc_nstavaps != 0) { /* XXX only 1 for now */ device_printf(sc->sc_dev, "only 1 sta vap supported\n"); goto bad; } if (sc->sc_nvaps) { /* * With multiple vaps we must fall back * to s/w beacon miss handling. */ flags |= IEEE80211_CLONE_NOBEACONS; } if (flags & IEEE80211_CLONE_NOBEACONS) { /* * Station mode w/o beacons are implemented w/ AP mode. */ ic_opmode = IEEE80211_M_HOSTAP; } break; case IEEE80211_M_IBSS: if (sc->sc_nvaps != 0) { /* XXX only 1 for now */ device_printf(sc->sc_dev, "only 1 ibss vap supported\n"); goto bad; } needbeacon = 1; break; case IEEE80211_M_AHDEMO: #ifdef IEEE80211_SUPPORT_TDMA if (flags & IEEE80211_CLONE_TDMA) { if (sc->sc_nvaps != 0) { device_printf(sc->sc_dev, "only 1 tdma vap supported\n"); goto bad; } needbeacon = 1; flags |= IEEE80211_CLONE_NOBEACONS; } /* fall thru... */ #endif case IEEE80211_M_MONITOR: if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { /* * Adopt existing mode. Adding a monitor or ahdemo * vap to an existing configuration is of dubious * value but should be ok. */ /* XXX not right for monitor mode */ ic_opmode = ic->ic_opmode; } break; case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: needbeacon = 1; break; case IEEE80211_M_WDS: if (sc->sc_nvaps != 0 && ic->ic_opmode == IEEE80211_M_STA) { device_printf(sc->sc_dev, "wds not supported in sta mode\n"); goto bad; } /* * Silently remove any request for a unique * bssid; WDS vap's always share the local * mac address. */ flags &= ~IEEE80211_CLONE_BSSID; if (sc->sc_nvaps == 0) ic_opmode = IEEE80211_M_HOSTAP; else ic_opmode = ic->ic_opmode; break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); goto bad; } /* * Check that a beacon buffer is available; the code below assumes it. */ if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) { device_printf(sc->sc_dev, "no beacon buffer available\n"); goto bad; } /* STA, AHDEMO? */ if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS || opmode == IEEE80211_M_STA) { assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID); ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); } vap = &avp->av_vap; /* XXX can't hold mutex across if_alloc */ ATH_UNLOCK(sc); error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); ATH_LOCK(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: error %d creating vap\n", __func__, error); goto bad2; } /* h/w crypto support */ vap->iv_key_alloc = ath_key_alloc; vap->iv_key_delete = ath_key_delete; vap->iv_key_set = ath_key_set; vap->iv_key_update_begin = ath_key_update_begin; vap->iv_key_update_end = ath_key_update_end; /* override various methods */ avp->av_recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = ath_recv_mgmt; vap->iv_reset = ath_reset_vap; vap->iv_update_beacon = ath_beacon_update; avp->av_newstate = vap->iv_newstate; vap->iv_newstate = ath_newstate; avp->av_bmiss = vap->iv_bmiss; vap->iv_bmiss = ath_bmiss_vap; avp->av_node_ps = vap->iv_node_ps; vap->iv_node_ps = ath_node_powersave; avp->av_set_tim = vap->iv_set_tim; vap->iv_set_tim = ath_node_set_tim; avp->av_recv_pspoll = vap->iv_recv_pspoll; vap->iv_recv_pspoll = ath_node_recv_pspoll; /* Set default parameters */ /* * Anything earlier than some AR9300 series MACs don't * support a smaller MPDU density. */ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8; /* * All NICs can handle the maximum size, however * AR5416 based MACs can only TX aggregates w/ RTS * protection when the total aggregate size is <= 8k. * However, for now that's enforced by the TX path. */ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; vap->iv_ampdu_limit = IEEE80211_HTCAP_MAXRXAMPDU_64K; avp->av_bslot = -1; if (needbeacon) { /* * Allocate beacon state and setup the q for buffered * multicast frames. We know a beacon buffer is * available because we checked above. */ avp->av_bcbuf = TAILQ_FIRST(&sc->sc_bbuf); TAILQ_REMOVE(&sc->sc_bbuf, avp->av_bcbuf, bf_list); if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) { /* * Assign the vap to a beacon xmit slot. As above * this cannot fail to find a free one. */ avp->av_bslot = assign_bslot(sc); KASSERT(sc->sc_bslot[avp->av_bslot] == NULL, ("beacon slot %u not empty", avp->av_bslot)); sc->sc_bslot[avp->av_bslot] = vap; sc->sc_nbcnvaps++; } if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) { /* * Multple vaps are to transmit beacons and we * have h/w support for TSF adjusting; enable * use of staggered beacons. */ sc->sc_stagbeacons = 1; } ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ); } ic->ic_opmode = ic_opmode; if (opmode != IEEE80211_M_WDS) { sc->sc_nvaps++; if (opmode == IEEE80211_M_STA) sc->sc_nstavaps++; if (opmode == IEEE80211_M_MBSS) sc->sc_nmeshvaps++; } switch (ic_opmode) { case IEEE80211_M_IBSS: sc->sc_opmode = HAL_M_IBSS; break; case IEEE80211_M_STA: sc->sc_opmode = HAL_M_STA; break; case IEEE80211_M_AHDEMO: #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) { sc->sc_tdma = 1; /* NB: disable tsf adjust */ sc->sc_stagbeacons = 0; } /* * NB: adhoc demo mode is a pseudo mode; to the hal it's * just ap mode. */ /* fall thru... */ #endif case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: sc->sc_opmode = HAL_M_HOSTAP; break; case IEEE80211_M_MONITOR: sc->sc_opmode = HAL_M_MONITOR; break; default: /* XXX should not happen */ break; } if (sc->sc_hastsfadd) { /* * Configure whether or not TSF adjust should be done. */ ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); } if (flags & IEEE80211_CLONE_NOBEACONS) { /* * Enable s/w beacon miss handling. */ sc->sc_swbmiss = 1; } ATH_UNLOCK(sc); /* complete setup */ ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status, mac); return vap; bad2: reclaim_address(sc, mac); ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask); bad: free(avp, M_80211_VAP); ATH_UNLOCK(sc); return NULL; } static void ath_vap_delete(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); if (sc->sc_running) { /* * Quiesce the hardware while we remove the vap. In * particular we need to reclaim all references to * the vap state by any frames pending on the tx queues. */ ath_hal_intrset(ah, 0); /* disable interrupts */ /* XXX Do all frames from all vaps/nodes need draining here? */ ath_stoprecv(sc, 1); /* stop recv side */ ath_rx_flush(sc); ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */ } /* .. leave the hardware awake for now. */ ieee80211_vap_detach(vap); /* * XXX Danger Will Robinson! Danger! * * Because ieee80211_vap_detach() can queue a frame (the station * diassociate message?) after we've drained the TXQ and * flushed the software TXQ, we will end up with a frame queued * to a node whose vap is about to be freed. * * To work around this, flush the hardware/software again. * This may be racy - the ath task may be running and the packet * may be being scheduled between sw->hw txq. Tsk. * * TODO: figure out why a new node gets allocated somewhere around * here (after the ath_tx_swq() call; and after an ath_stop() * call!) */ ath_draintxq(sc, ATH_RESET_DEFAULT); ATH_LOCK(sc); /* * Reclaim beacon state. Note this must be done before * the vap instance is reclaimed as we may have a reference * to it in the buffer for the beacon frame. */ if (avp->av_bcbuf != NULL) { if (avp->av_bslot != -1) { sc->sc_bslot[avp->av_bslot] = NULL; sc->sc_nbcnvaps--; } ath_beacon_return(sc, avp->av_bcbuf); avp->av_bcbuf = NULL; if (sc->sc_nbcnvaps == 0) { sc->sc_stagbeacons = 0; if (sc->sc_hastsfadd) ath_hal_settsfadjust(sc->sc_ah, 0); } /* * Reclaim any pending mcast frames for the vap. */ ath_tx_draintxq(sc, &avp->av_mcastq); } /* * Update bookkeeping. */ if (vap->iv_opmode == IEEE80211_M_STA) { sc->sc_nstavaps--; if (sc->sc_nstavaps == 0 && sc->sc_swbmiss) sc->sc_swbmiss = 0; } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_STA || vap->iv_opmode == IEEE80211_M_MBSS) { reclaim_address(sc, vap->iv_myaddr); ath_hal_setbssidmask(ah, sc->sc_hwbssidmask); if (vap->iv_opmode == IEEE80211_M_MBSS) sc->sc_nmeshvaps--; } if (vap->iv_opmode != IEEE80211_M_WDS) sc->sc_nvaps--; #ifdef IEEE80211_SUPPORT_TDMA /* TDMA operation ceases when the last vap is destroyed */ if (sc->sc_tdma && sc->sc_nvaps == 0) { sc->sc_tdma = 0; sc->sc_swbmiss = 0; } #endif free(avp, M_80211_VAP); if (sc->sc_running) { /* * Restart rx+tx machines if still running (RUNNING will * be reset if we just destroyed the last vap). */ if (ath_startrecv(sc) != 0) device_printf(sc->sc_dev, "%s: unable to restart recv logic\n", __func__); if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } ath_hal_intrset(ah, sc->sc_imask); } /* Ok, let the hardware asleep. */ ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } void ath_suspend(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; sc->sc_resume_up = ic->ic_nrunning != 0; ieee80211_suspend_all(ic); /* * NB: don't worry about putting the chip in low power * mode; pci will power off our socket on suspend and * CardBus detaches the device. * * XXX TODO: well, that's great, except for non-cardbus * devices! */ /* * XXX This doesn't wait until all pending taskqueue * items and parallel transmit/receive/other threads * are running! */ ath_hal_intrset(sc->sc_ah, 0); taskqueue_block(sc->sc_tq); ATH_LOCK(sc); callout_stop(&sc->sc_cal_ch); ATH_UNLOCK(sc); /* * XXX ensure sc_invalid is 1 */ /* Disable the PCIe PHY, complete with workarounds */ ath_hal_enablepcie(sc->sc_ah, 1, 1); } /* * Reset the key cache since some parts do not reset the * contents on resume. First we clear all entries, then * re-load keys that the 802.11 layer assumes are setup * in h/w. */ static void ath_reset_keycache(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; int i; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ieee80211_crypto_reload_keys(ic); } /* * Fetch the current chainmask configuration based on the current * operating channel and options. */ static void ath_update_chainmasks(struct ath_softc *sc, struct ieee80211_channel *chan) { /* * Set TX chainmask to the currently configured chainmask; * the TX chainmask depends upon the current operating mode. */ sc->sc_cur_rxchainmask = sc->sc_rxchainmask; if (IEEE80211_IS_CHAN_HT(chan)) { sc->sc_cur_txchainmask = sc->sc_txchainmask; } else { sc->sc_cur_txchainmask = 1; } DPRINTF(sc, ATH_DEBUG_RESET, "%s: TX chainmask is now 0x%x, RX is now 0x%x\n", __func__, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); } void ath_resume(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; ath_hal_enablepcie(ah, 0, 0); /* * Must reset the chip before we reload the * keycache as we were powered down on suspend. */ ath_update_chainmasks(sc, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); /* Ensure we set the current power state to on */ ATH_LOCK(sc); ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE, 1); ATH_UNLOCK(sc); ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, AH_FALSE, HAL_RESET_NORMAL, &status); ath_reset_keycache(sc); ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, ic->ic_curchan); /* * Let bluetooth coexistence at in case it's needed for this channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips that * support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); /* Restore the LED configuration */ ath_led_config(sc); ath_hal_setledstate(ah, HAL_LED_INIT); if (sc->sc_resume_up) ieee80211_resume_all(ic); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); /* XXX beacons ? */ } void ath_shutdown(struct ath_softc *sc) { ATH_LOCK(sc); ath_stop(sc); ATH_UNLOCK(sc); /* NB: no point powering down chip as we're about to reboot */ } /* * Interrupt handler. Most of the actual processing is deferred. */ void ath_intr(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; HAL_INT status = 0; uint32_t txqs; /* * If we're inside a reset path, just print a warning and * clear the ISR. The reset routine will finish it for us. */ ATH_PCU_LOCK(sc); if (sc->sc_inreset_cnt) { HAL_INT status; ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: in reset, ignoring: status=0x%x\n", __func__, status); ATH_PCU_UNLOCK(sc); return; } if (sc->sc_invalid) { /* * The hardware is not ready/present, don't touch anything. * Note this can happen early on if the IRQ is shared. */ DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__); ATH_PCU_UNLOCK(sc); return; } if (!ath_hal_intrpend(ah)) { /* shared irq, not for us */ ATH_PCU_UNLOCK(sc); return; } ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); if (sc->sc_ic.ic_nrunning == 0 && sc->sc_running == 0) { HAL_INT status; DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_nrunning %d sc_running %d\n", __func__, sc->sc_ic.ic_nrunning, sc->sc_running); ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } /* * Figure out the reason(s) for the interrupt. Note * that the hal returns a pseudo-ISR that may include * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ ath_hal_getisr(ah, &status); /* NB: clears ISR too */ DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, "ath_intr: mask=0x%.8x", status); #ifdef ATH_DEBUG_ALQ if_ath_alq_post_intr(&sc->sc_alq, status, ah->ah_intrstate, ah->ah_syncstate); #endif /* ATH_DEBUG_ALQ */ #ifdef ATH_KTR_INTR_DEBUG ATH_KTR(sc, ATH_KTR_INTERRUPTS, 5, "ath_intr: ISR=0x%.8x, ISR_S0=0x%.8x, ISR_S1=0x%.8x, ISR_S2=0x%.8x, ISR_S5=0x%.8x", ah->ah_intrstate[0], ah->ah_intrstate[1], ah->ah_intrstate[2], ah->ah_intrstate[3], ah->ah_intrstate[6]); #endif /* Squirrel away SYNC interrupt debugging */ if (ah->ah_syncstate != 0) { int i; for (i = 0; i < 32; i++) if (ah->ah_syncstate & (1 << i)) sc->sc_intr_stats.sync_intr[i]++; } status &= sc->sc_imask; /* discard unasked for bits */ /* Short-circuit un-handled interrupts */ if (status == 0x0) { ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } /* * Take a note that we're inside the interrupt handler, so * the reset routines know to wait. */ sc->sc_intr_cnt++; ATH_PCU_UNLOCK(sc); /* * Handle the interrupt. We won't run concurrent with the reset * or channel change routines as they'll wait for sc_intr_cnt * to be 0 before continuing. */ if (status & HAL_INT_FATAL) { sc->sc_stats.ast_hardware++; ath_hal_intrset(ah, 0); /* disable intr's until reset */ taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask); } else { if (status & HAL_INT_SWBA) { /* * Software beacon alert--time to send a beacon. * Handle beacon transmission directly; deferring * this is too slow to meet timing constraints * under load. */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { if (sc->sc_tdmaswba == 0) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); ath_tdma_beacon_send(sc, vap); sc->sc_tdmaswba = vap->iv_tdma->tdma_bintval; } else sc->sc_tdmaswba--; } else #endif { ath_beacon_proc(sc, 0); #ifdef IEEE80211_SUPPORT_SUPERG /* * Schedule the rx taskq in case there's no * traffic so any frames held on the staging * queue are aged and potentially flushed. */ sc->sc_rx.recv_sched(sc, 1); #endif } } if (status & HAL_INT_RXEOL) { int imask; ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXEOL"); if (! sc->sc_isedma) { ATH_PCU_LOCK(sc); /* * NB: the hardware should re-read the link when * RXE bit is written, but it doesn't work at * least on older hardware revs. */ sc->sc_stats.ast_rxeol++; /* * Disable RXEOL/RXORN - prevent an interrupt * storm until the PCU logic can be reset. * In case the interface is reset some other * way before "sc_kickpcu" is called, don't * modify sc_imask - that way if it is reset * by a call to ath_reset() somehow, the * interrupt mask will be correctly reprogrammed. */ imask = sc->sc_imask; imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); ath_hal_intrset(ah, imask); /* * Only blank sc_rxlink if we've not yet kicked * the PCU. * * This isn't entirely correct - the correct solution * would be to have a PCU lock and engage that for * the duration of the PCU fiddling; which would include * running the RX process. Otherwise we could end up * messing up the RX descriptor chain and making the * RX desc list much shorter. */ if (! sc->sc_kickpcu) sc->sc_rxlink = NULL; sc->sc_kickpcu = 1; ATH_PCU_UNLOCK(sc); } /* * Enqueue an RX proc to handle whatever * is in the RX queue. * This will then kick the PCU if required. */ sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TXURN) { sc->sc_stats.ast_txurn++; /* bump tx trigger level */ ath_hal_updatetxtriglevel(ah, AH_TRUE); } /* * Handle both the legacy and RX EDMA interrupt bits. * Note that HAL_INT_RXLP is also HAL_INT_RXDESC. */ if (status & (HAL_INT_RX | HAL_INT_RXHP | HAL_INT_RXLP)) { sc->sc_stats.ast_rx_intr++; sc->sc_rx.recv_sched(sc, 1); } if (status & HAL_INT_TX) { sc->sc_stats.ast_tx_intr++; /* * Grab all the currently set bits in the HAL txq bitmap * and blank them. This is the only place we should be * doing this. */ if (! sc->sc_isedma) { ATH_PCU_LOCK(sc); txqs = 0xffffffff; ath_hal_gettxintrtxqs(sc->sc_ah, &txqs); ATH_KTR(sc, ATH_KTR_INTERRUPTS, 3, "ath_intr: TX; txqs=0x%08x, txq_active was 0x%08x, now 0x%08x", txqs, sc->sc_txq_active, sc->sc_txq_active | txqs); sc->sc_txq_active |= txqs; ATH_PCU_UNLOCK(sc); } taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask); } if (status & HAL_INT_BMISS) { sc->sc_stats.ast_bmiss++; taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask); } if (status & HAL_INT_GTT) sc->sc_stats.ast_tx_timeout++; if (status & HAL_INT_CST) sc->sc_stats.ast_tx_cst++; if (status & HAL_INT_MIB) { sc->sc_stats.ast_mib++; ATH_PCU_LOCK(sc); /* * Disable interrupts until we service the MIB * interrupt; otherwise it will continue to fire. */ ath_hal_intrset(ah, 0); /* * Let the hal handle the event. We assume it will * clear whatever condition caused the interrupt. */ ath_hal_mibevent(ah, &sc->sc_halstats); /* * Don't reset the interrupt if we've just * kicked the PCU, or we may get a nested * RXEOL before the rxproc has had a chance * to run. */ if (sc->sc_kickpcu == 0) ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); } if (status & HAL_INT_RXORN) { /* NB: hal marks HAL_INT_FATAL when RXORN is fatal */ ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN"); sc->sc_stats.ast_rxorn++; } if (status & HAL_INT_TSFOOR) { /* out of range beacon - wake the chip up, * but don't modify self-gen frame config */ device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__); sc->sc_syncbeacon = 1; ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_AWAKE, 0); ATH_UNLOCK(sc); } if (status & HAL_INT_MCI) { ath_btcoex_mci_intr(sc); } } ATH_PCU_LOCK(sc); sc->sc_intr_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } static void ath_fatal_proc(void *arg, int pending) { struct ath_softc *sc = arg; u_int32_t *state; u_int32_t len; void *sp; if (sc->sc_invalid) return; device_printf(sc->sc_dev, "hardware error; resetting\n"); /* * Fatal errors are unrecoverable. Typically these * are caused by DMA errors. Collect h/w state from * the hal so we can diagnose what's going on. */ if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) { KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len)); state = sp; device_printf(sc->sc_dev, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n", state[0], state[1] , state[2], state[3], state[4], state[5]); } - ath_reset(sc, ATH_RESET_NOLOSS); + ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD); } static void ath_bmiss_vap(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; /* * Workaround phantom bmiss interrupts by sanity-checking * the time of our last rx'd frame. If it is within the * beacon miss interval then ignore the interrupt. If it's * truly a bmiss we'll get another interrupt soon and that'll * be dispatched up for processing. Note this applies only * for h/w beacon miss events. */ /* * XXX TODO: Just read the TSF during the interrupt path; * that way we don't have to wake up again just to read it * again. */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { u_int64_t lastrx = sc->sc_lastrx; u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); /* XXX should take a locked ref to iv_bss */ u_int bmisstimeout = vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024; DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n", __func__, (unsigned long long) tsf, (unsigned long long)(tsf - lastrx), (unsigned long long) lastrx, bmisstimeout); if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } } /* * Keep the hardware awake if it's asleep (and leave self-gen * frame config alone) until the next beacon, so we can resync * against the next beacon. * * This handles three common beacon miss cases in STA powersave mode - * (a) the beacon TBTT isnt a multiple of bintval; * (b) the beacon was missed; and * (c) the beacons are being delayed because the AP is busy and * isn't reliably able to meet its TBTT. */ ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_AWAKE, 0); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_BEACON, "%s: forced awake; force syncbeacon=1\n", __func__); /* * Attempt to force a beacon resync. */ sc->sc_syncbeacon = 1; ATH_VAP(vap)->av_bmiss(vap); } /* XXX this needs a force wakeup! */ int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) { uint32_t rsize; void *sp; if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, &mask, sizeof(mask), &sp, &rsize)) return 0; KASSERT(rsize == sizeof(uint32_t), ("resultsize %u", rsize)); *hangs = *(uint32_t *)sp; return 1; } static void ath_bmiss_proc(void *arg, int pending) { struct ath_softc *sc = arg; uint32_t hangs; DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ath_beacon_miss(sc); /* * Do a reset upon any becaon miss event. * * It may be a non-recognised RX clear hang which needs a reset * to clear. */ if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { - ath_reset(sc, ATH_RESET_NOLOSS); + ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_BBPANIC); device_printf(sc->sc_dev, "bb hang detected (0x%x), resetting\n", hangs); } else { - ath_reset(sc, ATH_RESET_NOLOSS); + ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD); ieee80211_beacon_miss(&sc->sc_ic); } /* Force a beacon resync, in case they've drifted */ sc->sc_syncbeacon = 1; ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } /* * Handle TKIP MIC setup to deal hardware that doesn't do MIC * calcs together with WME. If necessary disable the crypto * hardware and mark the 802.11 state so keys will be setup * with the MIC work done in software. */ static void ath_settkipmic(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) { if (ic->ic_flags & IEEE80211_F_WME) { ath_hal_settkipmic(sc->sc_ah, AH_FALSE); ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC; } else { ath_hal_settkipmic(sc->sc_ah, AH_TRUE); ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC; } } } static void ath_vap_clear_quiet_ie(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap; struct ath_vap *avp; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { avp = ATH_VAP(vap); /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); } } static int ath_init(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; ATH_LOCK_ASSERT(sc); /* * Force the sleep state awake. */ ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE, 1); /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ ath_stop(sc); /* * The basic interface to setting the hardware in a good * state is ``reset''. On return the hardware is known to * be powered up and with interrupts disabled. This must * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ ath_settkipmic(sc); ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, HAL_RESET_NORMAL, &status)) { device_printf(sc->sc_dev, "unable to reset hardware; hal status %u\n", status); return (ENODEV); } ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Clear quiet IE state for each VAP */ ath_vap_clear_quiet_ie(sc); ath_chan_change(sc, ic->ic_curchan); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, ic->ic_curchan); /* * Let bluetooth coexistence at in case it's needed for this channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips that * support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); /* * Likewise this is set during reset so update * state cached in the driver. */ sc->sc_diversity = ath_hal_getdiversity(ah); sc->sc_lastlongcal = ticks; sc->sc_resetcal = 1; sc->sc_lastcalreset = 0; sc->sc_lastani = ticks; sc->sc_lastshortcal = ticks; sc->sc_doresetcal = AH_FALSE; /* * Beacon timers were cleared here; give ath_newstate() * a hint that the beacon timers should be poked when * things transition to the RUN state. */ sc->sc_beacons = 0; /* * Setup the hardware after reset: the key cache * is filled as needed and the receive engine is * set going. Frame transmit is handled entirely * in the frame output path; there's nothing to do * here except setup the interrupt mask. */ if (ath_startrecv(sc) != 0) { device_printf(sc->sc_dev, "unable to start recv logic\n"); ath_power_restore_power_state(sc); return (ENODEV); } /* * Enable interrupts. */ sc->sc_imask = HAL_INT_RX | HAL_INT_TX | HAL_INT_RXORN | HAL_INT_TXURN | HAL_INT_FATAL | HAL_INT_GLOBAL; /* * Enable RX EDMA bits. Note these overlap with * HAL_INT_RX and HAL_INT_RXDESC respectively. */ if (sc->sc_isedma) sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP); /* * If we're an EDMA NIC, we don't care about RXEOL. * Writing a new descriptor in will simply restart * RX DMA. */ if (! sc->sc_isedma) sc->sc_imask |= HAL_INT_RXEOL; /* * Enable MCI interrupt for MCI devices. */ if (sc->sc_btcoex_mci) sc->sc_imask |= HAL_INT_MCI; /* * Enable MIB interrupts when there are hardware phy counters. * Note we only do this (at the moment) for station mode. */ if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_MIB; /* * XXX add capability for this. * * If we're in STA mode (and maybe IBSS?) then register for * TSFOOR interrupts. */ if (ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_TSFOOR; /* Enable global TX timeout and carrier sense timeout if available */ if (ath_hal_gtxto_supported(ah)) sc->sc_imask |= HAL_INT_GTT; DPRINTF(sc, ATH_DEBUG_RESET, "%s: imask=0x%x\n", __func__, sc->sc_imask); sc->sc_running = 1; callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); ath_hal_intrset(ah, sc->sc_imask); ath_power_restore_power_state(sc); return (0); } static void ath_stop(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; ATH_LOCK_ASSERT(sc); /* * Wake the hardware up before fiddling with it. */ ath_power_set_power_state(sc, HAL_PM_AWAKE); if (sc->sc_running) { /* * Shutdown the hardware and driver: * reset 802.11 state machine * turn off timers * disable interrupts * turn off the radio * clear transmit machinery * clear receive machinery * drain and release tx queues * reclaim beacon resources * power down hardware * * Note that some of this work is not possible if the * hardware is gone (invalid). */ #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->stop(sc->sc_tx99); #endif callout_stop(&sc->sc_wd_ch); sc->sc_wd_timer = 0; sc->sc_running = 0; if (!sc->sc_invalid) { if (sc->sc_softled) { callout_stop(&sc->sc_ledtimer); ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); sc->sc_blinking = 0; } ath_hal_intrset(ah, 0); } /* XXX we should stop RX regardless of whether it's valid */ if (!sc->sc_invalid) { ath_stoprecv(sc, 1); ath_hal_phydisable(ah); } else sc->sc_rxlink = NULL; ath_draintxq(sc, ATH_RESET_DEFAULT); ath_beacon_free(sc); /* XXX not needed */ } /* And now, restore the current power state */ ath_power_restore_power_state(sc); } /* * Wait until all pending TX/RX has completed. * * This waits until all existing transmit, receive and interrupts * have completed. It's assumed that the caller has first * grabbed the reset lock so it doesn't try to do overlapping * chip resets. */ #define MAX_TXRX_ITERATIONS 100 static void ath_txrx_stop_locked(struct ath_softc *sc) { int i = MAX_TXRX_ITERATIONS; ATH_UNLOCK_ASSERT(sc); ATH_PCU_LOCK_ASSERT(sc); /* * Sleep until all the pending operations have completed. * * The caller must ensure that reset has been incremented * or the pending operations may continue being queued. */ while (sc->sc_rxproc_cnt || sc->sc_txproc_cnt || sc->sc_txstart_cnt || sc->sc_intr_cnt) { if (i <= 0) break; msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", msecs_to_ticks(10)); i--; } if (i <= 0) device_printf(sc->sc_dev, "%s: didn't finish after %d iterations\n", __func__, MAX_TXRX_ITERATIONS); } #undef MAX_TXRX_ITERATIONS #if 0 static void ath_txrx_stop(struct ath_softc *sc) { ATH_UNLOCK_ASSERT(sc); ATH_PCU_UNLOCK_ASSERT(sc); ATH_PCU_LOCK(sc); ath_txrx_stop_locked(sc); ATH_PCU_UNLOCK(sc); } #endif static void ath_txrx_start(struct ath_softc *sc) { taskqueue_unblock(sc->sc_tq); } /* * Grab the reset lock, and wait around until no one else * is trying to do anything with it. * * This is totally horrible but we can't hold this lock for * long enough to do TX/RX or we end up with net80211/ip stack * LORs and eventual deadlock. * * "dowait" signals whether to spin, waiting for the reset * lock count to reach 0. This should (for now) only be used * during the reset path, as the rest of the code may not * be locking-reentrant enough to behave correctly. * * Another, cleaner way should be found to serialise all of * these operations. */ #define MAX_RESET_ITERATIONS 25 static int ath_reset_grablock(struct ath_softc *sc, int dowait) { int w = 0; int i = MAX_RESET_ITERATIONS; ATH_PCU_LOCK_ASSERT(sc); do { if (sc->sc_inreset_cnt == 0) { w = 1; break; } if (dowait == 0) { w = 0; break; } ATH_PCU_UNLOCK(sc); /* * 1 tick is likely not enough time for long calibrations * to complete. So we should wait quite a while. */ pause("ath_reset_grablock", msecs_to_ticks(100)); i--; ATH_PCU_LOCK(sc); } while (i > 0); /* * We always increment the refcounter, regardless * of whether we succeeded to get it in an exclusive * way. */ sc->sc_inreset_cnt++; if (i <= 0) device_printf(sc->sc_dev, "%s: didn't finish after %d iterations\n", __func__, MAX_RESET_ITERATIONS); if (w == 0) device_printf(sc->sc_dev, "%s: warning, recursive reset path!\n", __func__); return w; } #undef MAX_RESET_ITERATIONS /* * Reset the hardware w/o losing operational state. This is * basically a more efficient way of doing ath_stop, ath_init, * followed by state transitions to the current 802.11 * operational state. Used to recover from various errors and * to reset or reload hardware state. */ int -ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type) +ath_reset(struct ath_softc *sc, ATH_RESET_TYPE reset_type, + HAL_RESET_TYPE ah_reset_type) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; int i; DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); /* Ensure ATH_LOCK isn't held; ath_rx_proc can't be locked */ ATH_PCU_UNLOCK_ASSERT(sc); ATH_UNLOCK_ASSERT(sc); /* Try to (stop any further TX/RX from occurring */ taskqueue_block(sc->sc_tq); /* * Wake the hardware up. */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); /* * Grab the reset lock before TX/RX is stopped. * * This is needed to ensure that when the TX/RX actually does finish, * no further TX/RX/reset runs in parallel with this. */ if (ath_reset_grablock(sc, 1) == 0) { device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", __func__); } /* disable interrupts */ ath_hal_intrset(ah, 0); /* * Now, ensure that any in progress TX/RX completes before we * continue. */ ath_txrx_stop_locked(sc); ATH_PCU_UNLOCK(sc); /* * Regardless of whether we're doing a no-loss flush or * not, stop the PCU and handle what's in the RX queue. * That way frames aren't dropped which shouldn't be. */ ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS)); ath_rx_flush(sc); /* * Should now wait for pending TX/RX to complete * and block future ones from occurring. This needs to be * done before the TX queue is drained. */ ath_draintxq(sc, reset_type); /* stop xmit side */ ath_settkipmic(sc); /* configure TKIP MIC handling */ /* NB: indicate channel change so we do a full reset */ ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, - HAL_RESET_NORMAL, &status)) + ah_reset_type, &status)) device_printf(sc->sc_dev, "%s: unable to reset hardware; hal status %u\n", __func__, status); sc->sc_diversity = ath_hal_getdiversity(ah); ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Quiet time handling - ensure we resync */ ath_vap_clear_quiet_ie(sc); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, ic->ic_curchan); /* * Let bluetooth coexistence at in case it's needed for this channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips that * support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); if (ath_startrecv(sc) != 0) /* restart recv */ device_printf(sc->sc_dev, "%s: unable to start recv logic\n", __func__); /* * We may be doing a reset in response to an ioctl * that changes the channel so update any state that * might change as a result. */ ath_chan_change(sc, ic->ic_curchan); if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } /* * Release the reset lock and re-enable interrupts here. * If an interrupt was being processed in ath_intr(), * it would disable interrupts at this point. So we have * to atomically enable interrupts and decrement the * reset counter - this way ath_intr() doesn't end up * disabling interrupts without a corresponding enable * in the rest or channel change path. * * Grab the TX reference in case we need to transmit. * That way a parallel transmit doesn't. */ ATH_PCU_LOCK(sc); sc->sc_inreset_cnt--; sc->sc_txstart_cnt++; /* XXX only do this if sc_inreset_cnt == 0? */ ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); /* * TX and RX can be started here. If it were started with * sc_inreset_cnt > 0, the TX and RX path would abort. * Thus if this is a nested call through the reset or * channel change code, TX completion will occur but * RX completion and ath_start / ath_tx_start will not * run. */ /* Restart TX/RX as needed */ ath_txrx_start(sc); /* XXX TODO: we need to hold the tx refcount here! */ /* Restart TX completion and pending TX */ if (reset_type == ATH_RESET_NOLOSS) { for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { ATH_TXQ_LOCK(&sc->sc_txq[i]); ath_txq_restart_dma(sc, &sc->sc_txq[i]); ATH_TXQ_UNLOCK(&sc->sc_txq[i]); ATH_TX_LOCK(sc); ath_txq_sched(sc, &sc->sc_txq[i]); ATH_TX_UNLOCK(sc); } } } ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); /* Handle any frames in the TX queue */ /* * XXX should this be done by the caller, rather than * ath_reset() ? */ ath_tx_kick(sc); /* restart xmit */ return 0; } static int ath_reset_vap(struct ieee80211vap *vap, u_long cmd) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; switch (cmd) { case IEEE80211_IOC_TXPOWER: /* * If per-packet TPC is enabled, then we have nothing * to do; otherwise we need to force the global limit. * All this can happen directly; no need to reset. */ if (!ath_hal_gettpc(ah)) ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); return 0; } /* XXX? Full or NOLOSS? */ - return ath_reset(sc, ATH_RESET_FULL); + return ath_reset(sc, ATH_RESET_FULL, HAL_RESET_NORMAL); } struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK_ASSERT(sc); if (btype == ATH_BUFTYPE_MGMT) bf = TAILQ_FIRST(&sc->sc_txbuf_mgmt); else bf = TAILQ_FIRST(&sc->sc_txbuf); if (bf == NULL) { sc->sc_stats.ast_tx_getnobuf++; } else { if (bf->bf_flags & ATH_BUF_BUSY) { sc->sc_stats.ast_tx_getbusybuf++; bf = NULL; } } if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) { if (btype == ATH_BUFTYPE_MGMT) TAILQ_REMOVE(&sc->sc_txbuf_mgmt, bf, bf_list); else { TAILQ_REMOVE(&sc->sc_txbuf, bf, bf_list); sc->sc_txbuf_cnt--; /* * This shuldn't happen; however just to be * safe print a warning and fudge the txbuf * count. */ if (sc->sc_txbuf_cnt < 0) { device_printf(sc->sc_dev, "%s: sc_txbuf_cnt < 0?\n", __func__); sc->sc_txbuf_cnt = 0; } } } else bf = NULL; if (bf == NULL) { /* XXX should check which list, mgmt or otherwise */ DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, TAILQ_FIRST(&sc->sc_txbuf) == NULL ? "out of xmit buffers" : "xmit buffer busy"); return NULL; } /* XXX TODO: should do this at buffer list initialisation */ /* XXX (then, ensure the buffer has the right flag set) */ bf->bf_flags = 0; if (btype == ATH_BUFTYPE_MGMT) bf->bf_flags |= ATH_BUF_MGMT; else bf->bf_flags &= (~ATH_BUF_MGMT); /* Valid bf here; clear some basic fields */ bf->bf_next = NULL; /* XXX just to be sure */ bf->bf_last = NULL; /* XXX again, just to be sure */ bf->bf_comp = NULL; /* XXX again, just to be sure */ bzero(&bf->bf_state, sizeof(bf->bf_state)); /* * Track the descriptor ID only if doing EDMA */ if (sc->sc_isedma) { bf->bf_descid = sc->sc_txbuf_descid; sc->sc_txbuf_descid++; } return bf; } /* * When retrying a software frame, buffers marked ATH_BUF_BUSY * can't be thrown back on the queue as they could still be * in use by the hardware. * * This duplicates the buffer, or returns NULL. * * The descriptor is also copied but the link pointers and * the DMA segments aren't copied; this frame should thus * be again passed through the descriptor setup/chain routines * so the link is correct. * * The caller must free the buffer using ath_freebuf(). */ struct ath_buf * ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf) { struct ath_buf *tbf; tbf = ath_getbuf(sc, (bf->bf_flags & ATH_BUF_MGMT) ? ATH_BUFTYPE_MGMT : ATH_BUFTYPE_NORMAL); if (tbf == NULL) return NULL; /* XXX failure? Why? */ /* Copy basics */ tbf->bf_next = NULL; tbf->bf_nseg = bf->bf_nseg; tbf->bf_flags = bf->bf_flags & ATH_BUF_FLAGS_CLONE; tbf->bf_status = bf->bf_status; tbf->bf_m = bf->bf_m; tbf->bf_node = bf->bf_node; KASSERT((bf->bf_node != NULL), ("%s: bf_node=NULL!", __func__)); /* will be setup by the chain/setup function */ tbf->bf_lastds = NULL; /* for now, last == self */ tbf->bf_last = tbf; tbf->bf_comp = bf->bf_comp; /* NOTE: DMA segments will be setup by the setup/chain functions */ /* The caller has to re-init the descriptor + links */ /* * Free the DMA mapping here, before we NULL the mbuf. * We must only call bus_dmamap_unload() once per mbuf chain * or behaviour is undefined. */ if (bf->bf_m != NULL) { /* * XXX is this POSTWRITE call required? */ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); } bf->bf_m = NULL; bf->bf_node = NULL; /* Copy state */ memcpy(&tbf->bf_state, &bf->bf_state, sizeof(bf->bf_state)); return tbf; } struct ath_buf * ath_getbuf(struct ath_softc *sc, ath_buf_type_t btype) { struct ath_buf *bf; ATH_TXBUF_LOCK(sc); bf = _ath_getbuf_locked(sc, btype); /* * If a mgmt buffer was requested but we're out of those, * try requesting a normal one. */ if (bf == NULL && btype == ATH_BUFTYPE_MGMT) bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL); ATH_TXBUF_UNLOCK(sc); if (bf == NULL) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); sc->sc_stats.ast_tx_qstop++; } return bf; } /* * Transmit a single frame. * * net80211 will free the node reference if the transmit * fails, so don't free the node reference here. */ static int ath_transmit(struct ieee80211com *ic, struct mbuf *m) { struct ath_softc *sc = ic->ic_softc; struct ieee80211_node *ni; struct mbuf *next; struct ath_buf *bf; ath_bufhead frags; int retval = 0; /* * Tell the reset path that we're currently transmitting. */ ATH_PCU_LOCK(sc); if (sc->sc_inreset_cnt > 0) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: sc_inreset_cnt > 0; bailing\n", __func__); ATH_PCU_UNLOCK(sc); sc->sc_stats.ast_tx_qstop++; ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: OACTIVE, finish"); return (ENOBUFS); /* XXX should be EINVAL or? */ } sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); /* Wake the hardware up already */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); /* * Grab the TX lock - it's ok to do this here; we haven't * yet started transmitting. */ ATH_TX_LOCK(sc); /* * Node reference, if there's one. */ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; /* * Enforce how deep a node queue can get. * * XXX it would be nicer if we kept an mbuf queue per * node and only whacked them into ath_bufs when we * are ready to schedule some traffic from them. * .. that may come later. * * XXX we should also track the per-node hardware queue * depth so it is easy to limit the _SUM_ of the swq and * hwq frames. Since we only schedule two HWQ frames * at a time, this should be OK for now. */ if ((!(m->m_flags & M_EAPOL)) && (ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_maxdepth)) { sc->sc_stats.ast_tx_nodeq_overflow++; retval = ENOBUFS; goto finish; } /* * Check how many TX buffers are available. * * If this is for non-EAPOL traffic, just leave some * space free in order for buffer cloning and raw * frame transmission to occur. * * If it's for EAPOL traffic, ignore this for now. * Management traffic will be sent via the raw transmit * method which bypasses this check. * * This is needed to ensure that EAPOL frames during * (re) keying have a chance to go out. * * See kern/138379 for more information. */ if ((!(m->m_flags & M_EAPOL)) && (sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree)) { sc->sc_stats.ast_tx_nobuf++; retval = ENOBUFS; goto finish; } /* * Grab a TX buffer and associated resources. * * If it's an EAPOL frame, allocate a MGMT ath_buf. * That way even with temporary buffer exhaustion due to * the data path doesn't leave us without the ability * to transmit management frames. * * Otherwise allocate a normal buffer. */ if (m->m_flags & M_EAPOL) bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT); else bf = ath_getbuf(sc, ATH_BUFTYPE_NORMAL); if (bf == NULL) { /* * If we failed to allocate a buffer, fail. * * We shouldn't fail normally, due to the check * above. */ sc->sc_stats.ast_tx_nobuf++; retval = ENOBUFS; goto finish; } /* * At this point we have a buffer; so we need to free it * if we hit any error conditions. */ /* * Check for fragmentation. If this frame * has been broken up verify we have enough * buffers to send all the fragments so all * go out or none... */ TAILQ_INIT(&frags); if ((m->m_flags & M_FRAG) && !ath_txfrag_setup(sc, &frags, m, ni)) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of txfrag buffers\n", __func__); sc->sc_stats.ast_tx_nofrag++; if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); /* * XXXGL: is mbuf valid after ath_txfrag_setup? If yes, * we shouldn't free it but return back. */ ieee80211_free_mbuf(m); m = NULL; goto bad; } /* * At this point if we have any TX fragments, then we will * have bumped the node reference once for each of those. */ /* * XXX Is there anything actually _enforcing_ that the * fragments are being transmitted in one hit, rather than * being interleaved with other transmissions on that * hardware queue? * * The ATH TX output lock is the only thing serialising this * right now. */ /* * Calculate the "next fragment" length field in ath_buf * in order to let the transmit path know enough about * what to next write to the hardware. */ if (m->m_flags & M_FRAG) { struct ath_buf *fbf = bf; struct ath_buf *n_fbf = NULL; struct mbuf *fm = m->m_nextpkt; /* * We need to walk the list of fragments and set * the next size to the following buffer. * However, the first buffer isn't in the frag * list, so we have to do some gymnastics here. */ TAILQ_FOREACH(n_fbf, &frags, bf_list) { fbf->bf_nextfraglen = fm->m_pkthdr.len; fbf = n_fbf; fm = fm->m_nextpkt; } } nextfrag: /* * Pass the frame to the h/w for transmission. * Fragmented frames have each frag chained together * with m_nextpkt. We know there are sufficient ath_buf's * to send all the frags because of work done by * ath_txfrag_setup. We leave m_nextpkt set while * calling ath_tx_start so it can use it to extend the * the tx duration to cover the subsequent frag and * so it can reclaim all the mbufs in case of an error; * ath_tx_start clears m_nextpkt once it commits to * handing the frame to the hardware. * * Note: if this fails, then the mbufs are freed but * not the node reference. * * So, we now have to free the node reference ourselves here * and return OK up to the stack. */ next = m->m_nextpkt; if (ath_tx_start(sc, ni, bf, m)) { bad: if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); reclaim: bf->bf_m = NULL; bf->bf_node = NULL; ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); /* * Free the rest of the node references and * buffers for the fragment list. */ ath_txfrag_cleanup(sc, &frags, ni); ATH_TXBUF_UNLOCK(sc); /* * XXX: And free the node/return OK; ath_tx_start() may have * modified the buffer. We currently have no way to * signify that the mbuf was freed but there was an error. */ ieee80211_free_node(ni); retval = 0; goto finish; } /* * Check here if the node is in power save state. */ ath_tx_update_tim(sc, ni, 1); if (next != NULL) { /* * Beware of state changing between frags. * XXX check sta power-save state? */ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { DPRINTF(sc, ATH_DEBUG_XMIT, "%s: flush fragmented packet, state %s\n", __func__, ieee80211_state_name[ni->ni_vap->iv_state]); /* XXX dmamap */ ieee80211_free_mbuf(next); goto reclaim; } m = next; bf = TAILQ_FIRST(&frags); KASSERT(bf != NULL, ("no buf for txfrag")); TAILQ_REMOVE(&frags, bf, bf_list); goto nextfrag; } /* * Bump watchdog timer. */ sc->sc_wd_timer = 5; finish: ATH_TX_UNLOCK(sc); /* * Finished transmitting! */ ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); /* Sleep the hardware if required */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); return (retval); } static int ath_media_change(struct ifnet *ifp) { int error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } /* * Block/unblock tx+rx processing while a key change is done. * We assume the caller serializes key management operations * so we only need to worry about synchronization with other * uses that originate in the driver. */ static void ath_key_update_begin(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_block(sc->sc_tq); } static void ath_key_update_end(struct ieee80211vap *vap) { struct ath_softc *sc = vap->iv_ic->ic_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_unblock(sc->sc_tq); } static void ath_update_promisc(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; u_int32_t rfilt; /* configure rx filter */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(sc->sc_ah, rfilt); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); } static u_int ath_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { uint32_t val, *mfilt = arg; char *dl; uint8_t pos; /* calculate XOR of eight 6bit values */ dl = LLADDR(sdl); val = le32dec(dl + 0); pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; val = le32dec(dl + 3); pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); return (1); } /* * Driver-internal mcast update call. * * Assumes the hardware is already awake. */ static void ath_update_mcast_hw(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; u_int32_t mfilt[2]; /* calculate and install multicast filter */ if (ic->ic_allmulti == 0) { struct ieee80211vap *vap; /* * Merge multicast addresses to form the hardware filter. */ mfilt[0] = mfilt[1] = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if_foreach_llmaddr(vap->iv_ifp, ath_hash_maddr, &mfilt); } else mfilt[0] = mfilt[1] = ~0; ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } /* * Called from the net80211 layer - force the hardware * awake before operating. */ static void ath_update_mcast(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ath_update_mcast_hw(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } void ath_mode_init(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; /* XXX power state? */ /* configure rx filter */ rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(ah, rfilt); /* configure operational mode */ ath_hal_setopmode(ah); /* handle any link-level address change */ ath_hal_setmac(ah, ic->ic_macaddr); /* calculate and install multicast filter */ ath_update_mcast_hw(sc); } /* * Set the slot time based on the current setting. */ void ath_setslottime(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; u_int usec; if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) usec = 13; else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) usec = 21; else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { /* honor short/long slot time only in 11g */ /* XXX shouldn't honor on pure g or turbo g channel */ if (ic->ic_flags & IEEE80211_F_SHSLOT) usec = HAL_SLOT_TIME_9; else usec = HAL_SLOT_TIME_20; } else usec = HAL_SLOT_TIME_9; DPRINTF(sc, ATH_DEBUG_RESET, "%s: chan %u MHz flags 0x%x %s slot, %u usec\n", __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); /* Wake up the hardware first before updating the slot time */ ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); ath_power_restore_power_state(sc); sc->sc_updateslot = OK; ATH_UNLOCK(sc); } /* * Callback from the 802.11 layer to update the * slot time based on the current setting. */ static void ath_updateslot(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; /* * When not coordinating the BSS, change the hardware * immediately. For other operation we defer the change * until beacon updates have propagated to the stations. * * XXX sc_updateslot isn't changed behind a lock? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) sc->sc_updateslot = UPDATE; else ath_setslottime(sc); } /* * Append the contents of src to dst; both queues * are assumed to be locked. */ void ath_txqmove(struct ath_txq *dst, struct ath_txq *src) { ATH_TXQ_LOCK_ASSERT(src); ATH_TXQ_LOCK_ASSERT(dst); TAILQ_CONCAT(&dst->axq_q, &src->axq_q, bf_list); dst->axq_link = src->axq_link; src->axq_link = NULL; dst->axq_depth += src->axq_depth; dst->axq_aggr_depth += src->axq_aggr_depth; src->axq_depth = 0; src->axq_aggr_depth = 0; } /* * Reset the hardware, with no loss. * * This can't be used for a general case reset. */ static void ath_reset_proc(void *arg, int pending) { struct ath_softc *sc = arg; #if 0 device_printf(sc->sc_dev, "%s: resetting\n", __func__); #endif - ath_reset(sc, ATH_RESET_NOLOSS); + ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD); } /* * Reset the hardware after detecting beacons have stopped. */ static void ath_bstuck_proc(void *arg, int pending) { struct ath_softc *sc = arg; uint32_t hangs = 0; if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) device_printf(sc->sc_dev, "bb hang detected (0x%x)\n", hangs); #ifdef ATH_DEBUG_ALQ if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_STUCK_BEACON)) if_ath_alq_post(&sc->sc_alq, ATH_ALQ_STUCK_BEACON, 0, NULL); #endif device_printf(sc->sc_dev, "stuck beacon; resetting (bmiss count %u)\n", sc->sc_bmisscount); sc->sc_stats.ast_bstuck++; /* * This assumes that there's no simultaneous channel mode change * occurring. */ - ath_reset(sc, ATH_RESET_NOLOSS); + ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD); } static int ath_desc_alloc(struct ath_softc *sc) { int error; error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, "tx", sc->sc_tx_desclen, ath_txbuf, ATH_MAX_SCATTER); if (error != 0) { return error; } sc->sc_txbuf_cnt = ath_txbuf; error = ath_descdma_setup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt, "tx_mgmt", sc->sc_tx_desclen, ath_txbuf_mgmt, ATH_TXDESC); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); return error; } /* * XXX mark txbuf_mgmt frames with ATH_BUF_MGMT, so the * flag doesn't have to be set in ath_getbuf_locked(). */ error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, "beacon", sc->sc_tx_desclen, ATH_BCBUF, 1); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt); return error; } return 0; } static void ath_desc_free(struct ath_softc *sc) { if (sc->sc_bdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); if (sc->sc_txdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); if (sc->sc_txdma_mgmt.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt); } static struct ieee80211_node * ath_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; struct ath_node *an; an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO); if (an == NULL) { /* XXX stat+msg */ return NULL; } ath_rate_node_init(sc, an); /* Setup the mutex - there's no associd yet so set the name to NULL */ snprintf(an->an_name, sizeof(an->an_name), "%s: node %p", device_get_nameunit(sc->sc_dev), an); mtx_init(&an->an_mtx, an->an_name, NULL, MTX_DEF); /* XXX setup ath_tid */ ath_tx_tid_init(sc, an); an->an_node_stats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; an->an_node_stats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; an->an_node_stats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an); return &an->an_node; } static void ath_node_cleanup(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, ni->ni_macaddr, ":", ATH_NODE(ni)); /* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */ ath_tx_node_flush(sc, ATH_NODE(ni)); ath_rate_node_cleanup(sc, ATH_NODE(ni)); sc->sc_node_cleanup(ni); } static void ath_node_free(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, ni->ni_macaddr, ":", ATH_NODE(ni)); mtx_destroy(&ATH_NODE(ni)->an_mtx); sc->sc_node_free(ni); } static void ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) { struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; *rssi = ic->ic_node_getrssi(ni); if (ni->ni_chan != IEEE80211_CHAN_ANYC) *noise = ath_hal_getchannoise(ah, ni->ni_chan); else *noise = -95; /* nominally correct */ } /* * Set the default antenna. */ void ath_setdefantenna(struct ath_softc *sc, u_int antenna) { struct ath_hal *ah = sc->sc_ah; /* XXX block beacon interrupts */ ath_hal_setdefantenna(ah, antenna); if (sc->sc_defant != antenna) sc->sc_stats.ast_ant_defswitch++; sc->sc_defant = antenna; sc->sc_rxotherant = 0; } static void ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) { txq->axq_qnum = qnum; txq->axq_ac = 0; txq->axq_depth = 0; txq->axq_aggr_depth = 0; txq->axq_intrcnt = 0; txq->axq_link = NULL; txq->axq_softc = sc; TAILQ_INIT(&txq->axq_q); TAILQ_INIT(&txq->axq_tidq); TAILQ_INIT(&txq->fifo.axq_q); ATH_TXQ_LOCK_INIT(sc, txq); } /* * Setup a h/w transmit queue. */ static struct ath_txq * ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hal *ah = sc->sc_ah; HAL_TXQ_INFO qi; int qnum; memset(&qi, 0, sizeof(qi)); qi.tqi_subtype = subtype; qi.tqi_aifs = HAL_TXQ_USEDEFAULT; qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; /* * Enable interrupts only for EOL and DESC conditions. * We mark tx descriptors to receive a DESC interrupt * when a tx queue gets deep; otherwise waiting for the * EOL to reap descriptors. Note that this is done to * reduce interrupt load and this only defers reaping * descriptors, never transmitting frames. Aside from * reducing interrupts this also permits more concurrency. * The only potential downside is if the tx queue backs * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. */ if (sc->sc_isedma) qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXOKINT_ENABLE; else qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; qnum = ath_hal_setuptxqueue(ah, qtype, &qi); if (qnum == -1) { /* * NB: don't print a message, this happens * normally on parts with too few tx queues */ return NULL; } if (qnum >= nitems(sc->sc_txq)) { device_printf(sc->sc_dev, "hal qnum %u out of range, max %zu!\n", qnum, nitems(sc->sc_txq)); ath_hal_releasetxqueue(ah, qnum); return NULL; } if (!ATH_TXQ_SETUP(sc, qnum)) { ath_txq_init(sc, &sc->sc_txq[qnum], qnum); sc->sc_txqsetup |= 1<sc_txq[qnum]; } /* * Setup a hardware data transmit queue for the specified * access control. The hal may not support all requested * queues in which case it will return a reference to a * previously setup queue. We record the mapping from ac's * to h/w queues for use by ath_tx_start and also track * the set of h/w queues being used to optimize work in the * transmit interrupt handler and related routines. */ static int ath_tx_setup(struct ath_softc *sc, int ac, int haltype) { struct ath_txq *txq; if (ac >= nitems(sc->sc_ac2q)) { device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n", ac, nitems(sc->sc_ac2q)); return 0; } txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype); if (txq != NULL) { txq->axq_ac = ac; sc->sc_ac2q[ac] = txq; return 1; } else return 0; } /* * Update WME parameters for a transmit queue. */ static int ath_txq_update(struct ath_softc *sc, int ac) { #define ATH_EXPONENT_TO_VALUE(v) ((1<sc_ic; struct ath_txq *txq = sc->sc_ac2q[ac]; struct chanAccParams chp; struct wmeParams *wmep; struct ath_hal *ah = sc->sc_ah; HAL_TXQ_INFO qi; ieee80211_wme_ic_getparams(ic, &chp); wmep = &chp.cap_wmeParams[ac]; ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) { /* * AIFS is zero so there's no pre-transmit wait. The * burst time defines the slot duration and is configured * through net80211. The QCU is setup to not do post-xmit * back off, lockout all lower-priority QCU's, and fire * off the DMA beacon alert timer which is setup based * on the slot configuration. */ qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE | HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_DBA_GATED | HAL_TXQ_BACKOFF_DISABLE | HAL_TXQ_ARB_LOCKOUT_GLOBAL ; qi.tqi_aifs = 0; /* XXX +dbaprep? */ qi.tqi_readyTime = sc->sc_tdmaslotlen; qi.tqi_burstTime = qi.tqi_readyTime; } else { #endif /* * XXX shouldn't this just use the default flags * used in the previous queue setup? */ qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE | HAL_TXQ_TXEOLINT_ENABLE ; qi.tqi_aifs = wmep->wmep_aifsn; qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); qi.tqi_readyTime = 0; qi.tqi_burstTime = IEEE80211_TXOP_TO_US(wmep->wmep_txopLimit); #ifdef IEEE80211_SUPPORT_TDMA } #endif DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", __func__, txq->axq_qnum, qi.tqi_qflags, qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { device_printf(sc->sc_dev, "unable to update hardware queue " "parameters for %s traffic!\n", ieee80211_wme_acnames[ac]); return 0; } else { ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */ return 1; } #undef ATH_EXPONENT_TO_VALUE } /* * Callback from the 802.11 layer to update WME parameters. */ int ath_wme_update(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; return !ath_txq_update(sc, WME_AC_BE) || !ath_txq_update(sc, WME_AC_BK) || !ath_txq_update(sc, WME_AC_VI) || !ath_txq_update(sc, WME_AC_VO) ? EIO : 0; } /* * Reclaim resources for a setup queue. */ static void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) { ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); sc->sc_txqsetup &= ~(1<axq_qnum); ATH_TXQ_LOCK_DESTROY(txq); } /* * Reclaim all tx queue resources. */ static void ath_tx_cleanup(struct ath_softc *sc) { int i; ATH_TXBUF_LOCK_DESTROY(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->sc_txq[i]); } /* * Return h/w rate index for an IEEE rate (w/o basic rate bit) * using the current rates in sc_rixmap. */ int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate) { int rix = sc->sc_rixmap[rate]; /* NB: return lowest rix for invalid rate */ return (rix == 0xff ? 0 : rix); } static void ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; struct ieee80211com *ic = &sc->sc_ic; int sr, lr, pri; if (ts->ts_status == 0) { u_int8_t txant = ts->ts_antenna; sc->sc_stats.ast_ant_tx[txant]++; sc->sc_ant_tx[txant]++; if (ts->ts_finaltsi != 0) sc->sc_stats.ast_tx_altrate++; /* XXX TODO: should do per-pri conuters */ pri = M_WME_GETAC(bf->bf_m); if (pri >= WME_AC_VO) ic->ic_wme.wme_hipri_traffic++; if ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ni->ni_inact = ni->ni_inact_reload; } else { if (ts->ts_status & HAL_TXERR_XRETRY) sc->sc_stats.ast_tx_xretries++; if (ts->ts_status & HAL_TXERR_FIFO) sc->sc_stats.ast_tx_fifoerr++; if (ts->ts_status & HAL_TXERR_FILT) sc->sc_stats.ast_tx_filtered++; if (ts->ts_status & HAL_TXERR_XTXOP) sc->sc_stats.ast_tx_xtxop++; if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED) sc->sc_stats.ast_tx_timerexpired++; if (bf->bf_m->m_flags & M_FF) sc->sc_stats.ast_ff_txerr++; } /* XXX when is this valid? */ if (ts->ts_flags & HAL_TX_DESC_CFG_ERR) sc->sc_stats.ast_tx_desccfgerr++; /* * This can be valid for successful frame transmission! * If there's a TX FIFO underrun during aggregate transmission, * the MAC will pad the rest of the aggregate with delimiters. * If a BA is returned, the frame is marked as "OK" and it's up * to the TX completion code to notice which frames weren't * successfully transmitted. */ if (ts->ts_flags & HAL_TX_DATA_UNDERRUN) sc->sc_stats.ast_tx_data_underrun++; if (ts->ts_flags & HAL_TX_DELIM_UNDERRUN) sc->sc_stats.ast_tx_delim_underrun++; sr = ts->ts_shortretry; lr = ts->ts_longretry; sc->sc_stats.ast_tx_shortretry += sr; sc->sc_stats.ast_tx_longretry += lr; } /* * The default completion. If fail is 1, this means * "please don't retry the frame, and just return -1 status * to the net80211 stack. */ void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) { struct ath_tx_status *ts = &bf->bf_status.ds_txstat; int st; if (fail == 1) st = -1; else st = ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ? ts->ts_status : HAL_TXERR_XRETRY; #if 0 if (bf->bf_state.bfs_dobaw) device_printf(sc->sc_dev, "%s: bf %p: seqno %d: dobaw should've been cleared!\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno)); #endif if (bf->bf_next != NULL) device_printf(sc->sc_dev, "%s: bf %p: seqno %d: bf_next not NULL!\n", __func__, bf, SEQNO(bf->bf_state.bfs_seqno)); /* * Check if the node software queue is empty; if so * then clear the TIM. * * This needs to be done before the buffer is freed as * otherwise the node reference will have been released * and the node may not actually exist any longer. * * XXX I don't like this belonging here, but it's cleaner * to do it here right now then all the other places * where ath_tx_default_comp() is called. * * XXX TODO: during drain, ensure that the callback is * being called so we get a chance to update the TIM. */ if (bf->bf_node) { ATH_TX_LOCK(sc); ath_tx_update_tim(sc, bf->bf_node, 0); ATH_TX_UNLOCK(sc); } /* * Do any tx complete callback. Note this must * be done before releasing the node reference. * This will free the mbuf, release the net80211 * node and recycle the ath_buf. */ ath_tx_freebuf(sc, bf, st); } /* * Update rate control with the given completion status. */ void ath_tx_update_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_rc_series *rc, struct ath_tx_status *ts, int frmlen, int rc_framelen, int nframes, int nbad) { struct ath_node *an; /* Only for unicast frames */ if (ni == NULL) return; an = ATH_NODE(ni); ATH_NODE_UNLOCK_ASSERT(an); /* * XXX TODO: teach the rate control about TXERR_FILT and * see about handling it (eg see how many attempts were * made before it got filtered and account for that.) */ if ((ts->ts_status & HAL_TXERR_FILT) == 0) { ATH_NODE_LOCK(an); ath_rate_tx_complete(sc, an, rc, ts, frmlen, rc_framelen, nframes, nbad); ATH_NODE_UNLOCK(an); } } /* * Process the completion of the given buffer. * * This calls the rate control update and then the buffer completion. * This will either free the buffer or requeue it. In any case, the * bf pointer should be treated as invalid after this function is called. */ void ath_tx_process_buf_completion(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; ATH_TX_UNLOCK_ASSERT(sc); ATH_TXQ_UNLOCK_ASSERT(txq); /* If unicast frame, update general statistics */ if (ni != NULL) { /* update statistics */ ath_tx_update_stats(sc, ts, bf); } /* * Call the completion handler. * The completion handler is responsible for * calling the rate control code. * * Frames with no completion handler get the * rate control code called here. */ if (bf->bf_comp == NULL) { if ((ts->ts_status & HAL_TXERR_FILT) == 0 && (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) { /* * XXX assume this isn't an aggregate * frame. * * XXX TODO: also do this for filtered frames? * Once rate control knows about them? */ ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, ts, bf->bf_state.bfs_pktlen, bf->bf_state.bfs_pktlen, 1, (ts->ts_status == 0 ? 0 : 1)); } ath_tx_default_comp(sc, bf, 0); } else bf->bf_comp(sc, bf, 0); } /* * Process completed xmit descriptors from the specified queue. * Kick the packet scheduler if needed. This can occur from this * particular task. */ static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; struct ath_desc *ds; struct ath_tx_status *ts; struct ieee80211_node *ni; #ifdef IEEE80211_SUPPORT_SUPERG struct ieee80211com *ic = &sc->sc_ic; #endif /* IEEE80211_SUPPORT_SUPERG */ int nacked; HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n", __func__, txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link); ATH_KTR(sc, ATH_KTR_TXCOMP, 4, "ath_tx_processq: txq=%u head %p link %p depth %p", txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link, txq->axq_depth); nacked = 0; for (;;) { ATH_TXQ_LOCK(txq); txq->axq_intrcnt = 0; /* reset periodic desc intr count */ bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } ds = bf->bf_lastds; /* XXX must be setup correctly! */ ts = &bf->bf_status.ds_txstat; status = ath_hal_txprocdesc(ah, ds, ts); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) ath_printtxbuf(sc, bf, txq->axq_qnum, 0, status == HAL_OK); else if ((sc->sc_debug & ATH_DEBUG_RESET) && (dosched == 0)) ath_printtxbuf(sc, bf, txq->axq_qnum, 0, status == HAL_OK); #endif #ifdef ATH_DEBUG_ALQ if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS)) { if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS, sc->sc_tx_statuslen, (char *) ds); } #endif if (status == HAL_EINPROGRESS) { ATH_KTR(sc, ATH_KTR_TXCOMP, 3, "ath_tx_processq: txq=%u, bf=%p ds=%p, HAL_EINPROGRESS", txq->axq_qnum, bf, ds); ATH_TXQ_UNLOCK(txq); break; } ATH_TXQ_REMOVE(txq, bf, bf_list); /* * Sanity check. */ if (txq->axq_qnum != bf->bf_state.bfs_tx_queue) { device_printf(sc->sc_dev, "%s: TXQ=%d: bf=%p, bfs_tx_queue=%d\n", __func__, txq->axq_qnum, bf, bf->bf_state.bfs_tx_queue); } if (txq->axq_qnum != bf->bf_last->bf_state.bfs_tx_queue) { device_printf(sc->sc_dev, "%s: TXQ=%d: bf_last=%p, bfs_tx_queue=%d\n", __func__, txq->axq_qnum, bf->bf_last, bf->bf_last->bf_state.bfs_tx_queue); } #if 0 if (txq->axq_depth > 0) { /* * More frames follow. Mark the buffer busy * so it's not re-used while the hardware may * still re-read the link field in the descriptor. * * Use the last buffer in an aggregate as that * is where the hardware may be - intermediate * descriptors won't be "busy". */ bf->bf_last->bf_flags |= ATH_BUF_BUSY; } else txq->axq_link = NULL; #else bf->bf_last->bf_flags |= ATH_BUF_BUSY; #endif if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth--; ni = bf->bf_node; ATH_KTR(sc, ATH_KTR_TXCOMP, 5, "ath_tx_processq: txq=%u, bf=%p, ds=%p, ni=%p, ts_status=0x%08x", txq->axq_qnum, bf, ds, ni, ts->ts_status); /* * If unicast frame was ack'd update RSSI, * including the last rx time used to * workaround phantom bmiss interrupts. */ if (ni != NULL && ts->ts_status == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { nacked++; sc->sc_stats.ast_tx_rssi = ts->ts_rssi; ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, ts->ts_rssi); ATH_RSSI_LPF(ATH_NODE(ni)->an_node_stats.ns_avgtxrssi, ts->ts_rssi); } ATH_TXQ_UNLOCK(txq); /* * Update statistics and call completion */ ath_tx_process_buf_completion(sc, txq, ts, bf); /* XXX at this point, bf and ni may be totally invalid */ } #ifdef IEEE80211_SUPPORT_SUPERG /* * Flush fast-frame staging queue when traffic slows. */ if (txq->axq_depth <= 1) ieee80211_ff_flush(ic, txq->axq_ac); #endif /* Kick the software TXQ scheduler */ if (dosched) { ATH_TX_LOCK(sc); ath_txq_sched(sc, txq); ATH_TX_UNLOCK(sc); } ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_processq: txq=%u: done", txq->axq_qnum); return nacked; } #define TXQACTIVE(t, q) ( (t) & (1 << (q))) /* * Deferred processing of transmit interrupt; special-cased * for a single hardware transmit queue (e.g. 5210 and 5211). */ static void ath_tx_proc_q0(void *arg, int npending) { struct ath_softc *sc = arg; uint32_t txqs; ATH_PCU_LOCK(sc); sc->sc_txproc_cnt++; txqs = sc->sc_txq_active; sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0: txqs=0x%08x", txqs); if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1)) /* XXX why is lastrx updated in tx code? */ sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) ath_tx_processq(sc, sc->sc_cabq, 1); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ath_tx_kick(sc); } /* * Deferred processing of transmit interrupt; special-cased * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). */ static void ath_tx_proc_q0123(void *arg, int npending) { struct ath_softc *sc = arg; int nacked; uint32_t txqs; ATH_PCU_LOCK(sc); sc->sc_txproc_cnt++; txqs = sc->sc_txq_active; sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0123: txqs=0x%08x", txqs); /* * Process each active queue. */ nacked = 0; if (TXQACTIVE(txqs, 0)) nacked += ath_tx_processq(sc, &sc->sc_txq[0], 1); if (TXQACTIVE(txqs, 1)) nacked += ath_tx_processq(sc, &sc->sc_txq[1], 1); if (TXQACTIVE(txqs, 2)) nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1); if (TXQACTIVE(txqs, 3)) nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1); if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum)) ath_tx_processq(sc, sc->sc_cabq, 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ath_tx_kick(sc); } /* * Deferred processing of transmit interrupt. */ static void ath_tx_proc(void *arg, int npending) { struct ath_softc *sc = arg; int i, nacked; uint32_t txqs; ATH_PCU_LOCK(sc); sc->sc_txproc_cnt++; txqs = sc->sc_txq_active; sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* * Process each active queue. */ nacked = 0; for (i = 0; i < HAL_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i)) nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1); if (nacked) sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah); sc->sc_wd_timer = 0; if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ath_tx_kick(sc); } #undef TXQACTIVE /* * Deferred processing of TXQ rescheduling. */ static void ath_txq_sched_tasklet(void *arg, int npending) { struct ath_softc *sc = arg; int i; /* XXX is skipping ok? */ ATH_PCU_LOCK(sc); #if 0 if (sc->sc_inreset_cnt > 0) { device_printf(sc->sc_dev, "%s: sc_inreset_cnt > 0; skipping\n", __func__); ATH_PCU_UNLOCK(sc); return; } #endif sc->sc_txproc_cnt++; ATH_PCU_UNLOCK(sc); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); ATH_TX_LOCK(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { ath_txq_sched(sc, &sc->sc_txq[i]); } } ATH_TX_UNLOCK(sc); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); } void ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf) { ATH_TXBUF_LOCK_ASSERT(sc); if (bf->bf_flags & ATH_BUF_MGMT) TAILQ_INSERT_TAIL(&sc->sc_txbuf_mgmt, bf, bf_list); else { TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); sc->sc_txbuf_cnt++; if (sc->sc_txbuf_cnt > ath_txbuf) { device_printf(sc->sc_dev, "%s: sc_txbuf_cnt > %d?\n", __func__, ath_txbuf); sc->sc_txbuf_cnt = ath_txbuf; } } } void ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf) { ATH_TXBUF_LOCK_ASSERT(sc); if (bf->bf_flags & ATH_BUF_MGMT) TAILQ_INSERT_HEAD(&sc->sc_txbuf_mgmt, bf, bf_list); else { TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); sc->sc_txbuf_cnt++; if (sc->sc_txbuf_cnt > ATH_TXBUF) { device_printf(sc->sc_dev, "%s: sc_txbuf_cnt > %d?\n", __func__, ATH_TXBUF); sc->sc_txbuf_cnt = ATH_TXBUF; } } } /* * Free the holding buffer if it exists */ void ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq) { ATH_TXBUF_UNLOCK_ASSERT(sc); ATH_TXQ_LOCK_ASSERT(txq); if (txq->axq_holdingbf == NULL) return; txq->axq_holdingbf->bf_flags &= ~ATH_BUF_BUSY; ATH_TXBUF_LOCK(sc); ath_returnbuf_tail(sc, txq->axq_holdingbf); ATH_TXBUF_UNLOCK(sc); txq->axq_holdingbf = NULL; } /* * Add this buffer to the holding queue, freeing the previous * one if it exists. */ static void ath_txq_addholdingbuf(struct ath_softc *sc, struct ath_buf *bf) { struct ath_txq *txq; txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; ATH_TXBUF_UNLOCK_ASSERT(sc); ATH_TXQ_LOCK_ASSERT(txq); /* XXX assert ATH_BUF_BUSY is set */ /* XXX assert the tx queue is under the max number */ if (bf->bf_state.bfs_tx_queue > HAL_NUM_TX_QUEUES) { device_printf(sc->sc_dev, "%s: bf=%p: invalid tx queue (%d)\n", __func__, bf, bf->bf_state.bfs_tx_queue); bf->bf_flags &= ~ATH_BUF_BUSY; ath_returnbuf_tail(sc, bf); return; } ath_txq_freeholdingbuf(sc, txq); txq->axq_holdingbf = bf; } /* * Return a buffer to the pool and update the 'busy' flag on the * previous 'tail' entry. * * This _must_ only be called when the buffer is involved in a completed * TX. The logic is that if it was part of an active TX, the previous * buffer on the list is now not involved in a halted TX DMA queue, waiting * for restart (eg for TDMA.) * * The caller must free the mbuf and recycle the node reference. * * XXX This method of handling busy / holding buffers is insanely stupid. * It requires bf_state.bfs_tx_queue to be correctly assigned. It would * be much nicer if buffers in the processq() methods would instead be * always completed there (pushed onto a txq or ath_bufhead) so we knew * exactly what hardware queue they came from in the first place. */ void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf) { struct ath_txq *txq; txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; KASSERT((bf->bf_node == NULL), ("%s: bf->bf_node != NULL\n", __func__)); KASSERT((bf->bf_m == NULL), ("%s: bf->bf_m != NULL\n", __func__)); /* * If this buffer is busy, push it onto the holding queue. */ if (bf->bf_flags & ATH_BUF_BUSY) { ATH_TXQ_LOCK(txq); ath_txq_addholdingbuf(sc, bf); ATH_TXQ_UNLOCK(txq); return; } /* * Not a busy buffer, so free normally */ ATH_TXBUF_LOCK(sc); ath_returnbuf_tail(sc, bf); ATH_TXBUF_UNLOCK(sc); } /* * This is currently used by ath_tx_draintxq() and * ath_tx_tid_free_pkts(). * * It recycles a single ath_buf. */ void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status) { struct ieee80211_node *ni = bf->bf_node; struct mbuf *m0 = bf->bf_m; /* * Make sure that we only sync/unload if there's an mbuf. * If not (eg we cloned a buffer), the unload will have already * occurred. */ if (bf->bf_m != NULL) { bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); } bf->bf_node = NULL; bf->bf_m = NULL; /* Free the buffer, it's not needed any longer */ ath_freebuf(sc, bf); /* Pass the buffer back to net80211 - completing it */ ieee80211_tx_complete(ni, m0, status); } static struct ath_buf * ath_tx_draintxq_get_one(struct ath_softc *sc, struct ath_txq *txq) { struct ath_buf *bf; ATH_TXQ_LOCK_ASSERT(txq); /* * Drain the FIFO queue first, then if it's * empty, move to the normal frame queue. */ bf = TAILQ_FIRST(&txq->fifo.axq_q); if (bf != NULL) { /* * Is it the last buffer in this set? * Decrement the FIFO counter. */ if (bf->bf_flags & ATH_BUF_FIFOEND) { if (txq->axq_fifo_depth == 0) { device_printf(sc->sc_dev, "%s: Q%d: fifo_depth=0, fifo.axq_depth=%d?\n", __func__, txq->axq_qnum, txq->fifo.axq_depth); } else txq->axq_fifo_depth--; } ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list); return (bf); } /* * Debugging! */ if (txq->axq_fifo_depth != 0 || txq->fifo.axq_depth != 0) { device_printf(sc->sc_dev, "%s: Q%d: fifo_depth=%d, fifo.axq_depth=%d\n", __func__, txq->axq_qnum, txq->axq_fifo_depth, txq->fifo.axq_depth); } /* * Now drain the pending queue. */ bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { txq->axq_link = NULL; return (NULL); } ATH_TXQ_REMOVE(txq, bf, bf_list); return (bf); } void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) { #ifdef ATH_DEBUG struct ath_hal *ah = sc->sc_ah; #endif struct ath_buf *bf; u_int ix; /* * NB: this assumes output has been stopped and * we do not need to block ath_tx_proc */ for (ix = 0;; ix++) { ATH_TXQ_LOCK(txq); bf = ath_tx_draintxq_get_one(sc, txq); if (bf == NULL) { ATH_TXQ_UNLOCK(txq); break; } if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth--; #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { struct ieee80211com *ic = &sc->sc_ic; int status = 0; /* * EDMA operation has a TX completion FIFO * separate from the TX descriptor, so this * method of checking the "completion" status * is wrong. */ if (! sc->sc_isedma) { status = (ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); } ath_printtxbuf(sc, bf, txq->axq_qnum, ix, status); ieee80211_dump_pkt(ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } #endif /* ATH_DEBUG */ /* * Since we're now doing magic in the completion * functions, we -must- call it for aggregation * destinations or BAW tracking will get upset. */ /* * Clear ATH_BUF_BUSY; the completion handler * will free the buffer. */ ATH_TXQ_UNLOCK(txq); bf->bf_flags &= ~ATH_BUF_BUSY; if (bf->bf_comp) bf->bf_comp(sc, bf, 1); else ath_tx_default_comp(sc, bf, 1); } /* * Free the holding buffer if it exists */ ATH_TXQ_LOCK(txq); ath_txq_freeholdingbuf(sc, txq); ATH_TXQ_UNLOCK(txq); /* * Drain software queued frames which are on * active TIDs. */ ath_tx_txq_drain(sc, txq); } static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; ATH_TXQ_LOCK_ASSERT(txq); DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, active=%d, hwpending=%d, flags 0x%08x, " "link %p, holdingbf=%p\n", __func__, txq->axq_qnum, (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), (int) (!! ath_hal_txqenabled(ah, txq->axq_qnum)), (int) ath_hal_numtxpending(ah, txq->axq_qnum), txq->axq_flags, txq->axq_link, txq->axq_holdingbf); (void) ath_hal_stoptxdma(ah, txq->axq_qnum); /* We've stopped TX DMA, so mark this as stopped. */ txq->axq_flags &= ~ATH_TXQ_PUTRUNNING; #ifdef ATH_DEBUG if ((sc->sc_debug & ATH_DEBUG_RESET) && (txq->axq_holdingbf != NULL)) { ath_printtxbuf(sc, txq->axq_holdingbf, txq->axq_qnum, 0, 0); } #endif } int ath_stoptxdma(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; int i; /* XXX return value */ if (sc->sc_invalid) return 0; if (!sc->sc_invalid) { /* don't touch the hardware if marked invalid */ DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", __func__, sc->sc_bhalq, (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq), NULL); /* stop the beacon queue */ (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); /* Stop the data queues */ for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { ATH_TXQ_LOCK(&sc->sc_txq[i]); ath_tx_stopdma(sc, &sc->sc_txq[i]); ATH_TXQ_UNLOCK(&sc->sc_txq[i]); } } } return 1; } #ifdef ATH_DEBUG void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; int i = 0; if (! (sc->sc_debug & ATH_DEBUG_RESET)) return; device_printf(sc->sc_dev, "%s: Q%d: begin\n", __func__, txq->axq_qnum); TAILQ_FOREACH(bf, &txq->axq_q, bf_list) { ath_printtxbuf(sc, bf, txq->axq_qnum, i, ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); i++; } device_printf(sc->sc_dev, "%s: Q%d: end\n", __func__, txq->axq_qnum); } #endif /* ATH_DEBUG */ /* * Drain the transmit queues and reclaim resources. */ void ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf_last; int i; (void) ath_stoptxdma(sc); /* * Dump the queue contents */ for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { /* * XXX TODO: should we just handle the completed TX frames * here, whether or not the reset is a full one or not? */ if (ATH_TXQ_SETUP(sc, i)) { #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) ath_tx_dump(sc, &sc->sc_txq[i]); #endif /* ATH_DEBUG */ if (reset_type == ATH_RESET_NOLOSS) { ath_tx_processq(sc, &sc->sc_txq[i], 0); ATH_TXQ_LOCK(&sc->sc_txq[i]); /* * Free the holding buffer; DMA is now * stopped. */ ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]); /* * Setup the link pointer to be the * _last_ buffer/descriptor in the list. * If there's nothing in the list, set it * to NULL. */ bf_last = ATH_TXQ_LAST(&sc->sc_txq[i], axq_q_s); if (bf_last != NULL) { ath_hal_gettxdesclinkptr(ah, bf_last->bf_lastds, &sc->sc_txq[i].axq_link); } else { sc->sc_txq[i].axq_link = NULL; } ATH_TXQ_UNLOCK(&sc->sc_txq[i]); } else ath_tx_draintxq(sc, &sc->sc_txq[i]); } } #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RESET) { struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf); if (bf != NULL && bf->bf_m != NULL) { ath_printtxbuf(sc, bf, sc->sc_bhalq, 0, ath_hal_txprocdesc(ah, bf->bf_lastds, &bf->bf_status.ds_txstat) == HAL_OK); ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len, 0, -1); } } #endif /* ATH_DEBUG */ sc->sc_wd_timer = 0; } /* * Update internal state after a channel change. */ static void ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) { enum ieee80211_phymode mode; /* * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ mode = ieee80211_chan2mode(chan); if (mode != sc->sc_curmode) ath_setcurmode(sc, mode); sc->sc_curchan = chan; } /* * Set/change channels. If the channel is really being changed, * it's done by resetting the chip. To accomplish this we must * first cleanup any pending DMA, then restart stuff after a la * ath_init. */ static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; int ret = 0; /* Treat this as an interface reset */ ATH_PCU_UNLOCK_ASSERT(sc); ATH_UNLOCK_ASSERT(sc); /* (Try to) stop TX/RX from occurring */ taskqueue_block(sc->sc_tq); ATH_PCU_LOCK(sc); /* Disable interrupts */ ath_hal_intrset(ah, 0); /* Stop new RX/TX/interrupt completion */ if (ath_reset_grablock(sc, 1) == 0) { device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", __func__); } /* Stop pending RX/TX completion */ ath_txrx_stop_locked(sc); ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n", __func__, ieee80211_chan2ieee(ic, chan), chan->ic_freq, chan->ic_flags); if (chan != sc->sc_curchan) { HAL_STATUS status; /* * To switch channels clear any pending DMA operations; * wait long enough for the RX fifo to drain, reset the * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ #if 0 ath_hal_intrset(ah, 0); /* disable interrupts */ #endif ath_stoprecv(sc, 1); /* turn off frame recv */ /* * First, handle completed TX/RX frames. */ ath_rx_flush(sc); ath_draintxq(sc, ATH_RESET_NOLOSS); /* * Next, flush the non-scheduled frames. */ ath_draintxq(sc, ATH_RESET_FULL); /* clear pending tx frames */ ath_update_chainmasks(sc, chan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, HAL_RESET_NORMAL, &status)) { device_printf(sc->sc_dev, "%s: unable to reset " "channel %u (%u MHz, flags 0x%x), hal status %u\n", __func__, ieee80211_chan2ieee(ic, chan), chan->ic_freq, chan->ic_flags, status); ret = EIO; goto finish; } sc->sc_diversity = ath_hal_getdiversity(ah); ATH_RX_LOCK(sc); sc->sc_rx_stopped = 1; sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); /* Quiet time handling - ensure we resync */ ath_vap_clear_quiet_ie(sc); /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, chan); /* Let spectral at in case spectral is enabled */ ath_spectral_enable(sc, chan); /* * Let bluetooth coexistence at in case it's needed for this * channel */ ath_btcoex_enable(sc, ic->ic_curchan); /* * If we're doing TDMA, enforce the TXOP limitation for chips * that support it. */ if (sc->sc_hasenforcetxop && sc->sc_tdma) ath_hal_setenforcetxop(sc->sc_ah, 1); else ath_hal_setenforcetxop(sc->sc_ah, 0); /* * Re-enable rx framework. */ if (ath_startrecv(sc) != 0) { device_printf(sc->sc_dev, "%s: unable to restart recv logic\n", __func__); ret = EIO; goto finish; } /* * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ ath_chan_change(sc, chan); /* * Reset clears the beacon timers; reset them * here if needed. */ if (sc->sc_beacons) { /* restart beacons */ #ifdef IEEE80211_SUPPORT_TDMA if (sc->sc_tdma) ath_tdma_config(sc, NULL); else #endif ath_beacon_config(sc, NULL); } /* * Re-enable interrupts. */ #if 0 ath_hal_intrset(ah, sc->sc_imask); #endif } finish: ATH_PCU_LOCK(sc); sc->sc_inreset_cnt--; /* XXX only do this if sc_inreset_cnt == 0? */ ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); ath_txrx_start(sc); /* XXX ath_start? */ return ret; } /* * Periodically recalibrate the PHY to account * for temperature/environment changes. */ static void ath_calibrate(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; struct ieee80211com *ic = &sc->sc_ic; HAL_BOOL longCal, isCalDone = AH_TRUE; HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; ATH_LOCK_ASSERT(sc); /* * Force the hardware awake for ANI work. */ ath_power_set_power_state(sc, HAL_PM_AWAKE); /* Skip trying to do this if we're in reset */ if (sc->sc_inreset_cnt) goto restart; if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); aniCal = (ticks - sc->sc_lastani >= ath_anicalinterval*hz/1000); if (sc->sc_doresetcal) shortCal = (ticks - sc->sc_lastshortcal >= ath_shortcalinterval*hz/1000); DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: shortCal=%d; longCal=%d; aniCal=%d\n", __func__, shortCal, longCal, aniCal); if (aniCal) { sc->sc_stats.ast_ani_cal++; sc->sc_lastani = ticks; ath_hal_ani_poll(ah, sc->sc_curchan); } if (longCal) { sc->sc_stats.ast_per_cal++; sc->sc_lastlongcal = ticks; if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { /* * Rfgain is out of bounds, reset the chip * to load new gain values. */ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: rfgain change\n", __func__); sc->sc_stats.ast_per_rfgain++; sc->sc_resetcal = 0; sc->sc_doresetcal = AH_TRUE; taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); ath_power_restore_power_state(sc); return; } /* * If this long cal is after an idle period, then * reset the data collection state so we start fresh. */ if (sc->sc_resetcal) { (void) ath_hal_calreset(ah, sc->sc_curchan); sc->sc_lastcalreset = ticks; sc->sc_lastshortcal = ticks; sc->sc_resetcal = 0; sc->sc_doresetcal = AH_TRUE; } } /* Only call if we're doing a short/long cal, not for ANI calibration */ if (shortCal || longCal) { isCalDone = AH_FALSE; if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) { if (longCal) { /* * Calibrate noise floor data again in case of change. */ ath_hal_process_noisefloor(ah); } } else { DPRINTF(sc, ATH_DEBUG_ANY, "%s: calibration of channel %u failed\n", __func__, sc->sc_curchan->ic_freq); sc->sc_stats.ast_per_calfail++; } /* * XXX TODO: get the NF calibration results from the HAL. * If we failed NF cal then schedule a hard reset to potentially * un-freeze the PHY. * * Note we have to be careful here to not get stuck in an * infinite NIC restart. Ideally we'd not restart if we * failed the first NF cal - that /can/ fail sometimes in * a noisy environment. + * + * Instead, we should likely temporarily shorten the longCal + * period to happen pretty quickly and if a subsequent one + * fails, do a full reset. */ if (shortCal) sc->sc_lastshortcal = ticks; } if (!isCalDone) { restart: /* * Use a shorter interval to potentially collect multiple * data samples required to complete calibration. Once * we're told the work is done we drop back to a longer * interval between requests. We're more aggressive doing * work when operating as an AP to improve operation right * after startup. */ sc->sc_lastshortcal = ticks; nextcal = ath_shortcalinterval*hz/1000; if (sc->sc_opmode != HAL_M_HOSTAP) nextcal *= 10; sc->sc_doresetcal = AH_TRUE; } else { /* nextcal should be the shortest time for next event */ nextcal = ath_longcalinterval*hz; if (sc->sc_lastcalreset == 0) sc->sc_lastcalreset = sc->sc_lastlongcal; else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz) sc->sc_resetcal = 1; /* setup reset next trip */ sc->sc_doresetcal = AH_FALSE; } /* ANI calibration may occur more often than short/long/resetcal */ if (ath_anicalinterval > 0) nextcal = MIN(nextcal, ath_anicalinterval*hz/1000); if (nextcal != 0) { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n", __func__, nextcal, isCalDone ? "" : "!"); callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); /* NB: don't rearm timer */ } /* * Restore power state now that we're done. */ ath_power_restore_power_state(sc); } static void ath_scan_start(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; /* XXX calibration timer? */ /* XXXGL: is constant ieee80211broadcastaddr a correct choice? */ ATH_LOCK(sc); sc->sc_scanning = 1; sc->sc_syncbeacon = 0; rfilt = ath_calcrxfilter(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); ath_hal_setrxfilter(ah, rfilt); ath_hal_setassocid(ah, ieee80211broadcastaddr, 0); ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n", __func__, rfilt, ether_sprintf(ieee80211broadcastaddr)); } static void ath_scan_end(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; u_int32_t rfilt; ATH_LOCK(sc); sc->sc_scanning = 0; rfilt = ath_calcrxfilter(sc); ATH_UNLOCK(sc); ATH_PCU_LOCK(sc); ath_hal_setrxfilter(ah, rfilt); ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); ath_hal_process_noisefloor(ah); ATH_PCU_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); } #ifdef ATH_ENABLE_11N /* * For now, just do a channel change. * * Later, we'll go through the hard slog of suspending tx/rx, changing rate * control state and resetting the hardware without dropping frames out * of the queue. * * The unfortunate trouble here is making absolutely sure that the * channel width change has propagated enough so the hardware * absolutely isn't handed bogus frames for it's current operating * mode. (Eg, 40MHz frames in 20MHz mode.) Since TX and RX can and * does occur in parallel, we need to make certain we've blocked * any further ongoing TX (and RX, that can cause raw TX) * before we do this. */ static void ath_update_chw(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; //DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); device_printf(sc->sc_dev, "%s: called\n", __func__); /* * XXX TODO: schedule a tasklet that stops things without freeing, * walks the now stopped TX queue(s) looking for frames to retry * as if we TX filtered them (whch may mean dropping non-ampdu frames!) * but okay) then place them back on the software queue so they * can have the rate control lookup done again. */ ath_set_channel(ic); } #endif /* ATH_ENABLE_11N */ /* * This is called by the beacon parsing routine in the receive * path to update the current quiet time information provided by * an AP. * * This is STA specific, it doesn't take the AP TBTT/beacon slot * offset into account. * * The quiet IE doesn't control the /now/ beacon interval - it * controls the upcoming beacon interval. So, when tbtt=1, * the quiet element programming shall be for the next beacon * interval. There's no tbtt=0 behaviour defined, so don't. * * Since we're programming the next quiet interval, we have * to keep in mind what we will see when the next beacon * is received with potentially a quiet IE. For example, if * quiet_period is 1, then we are always getting a quiet interval * each TBTT - so if we just program it in upon each beacon received, * it will constantly reflect the "next" TBTT and we will never * let the counter stay programmed correctly. * * So: * + the first time we see the quiet IE, program it and store * the details somewhere; * + if the quiet parameters don't change (ie, period/duration/offset) * then just leave the programming enabled; * + (we can "skip" beacons, so don't try to enforce tbttcount unless * you're willing to also do the skipped beacon math); * + if the quiet IE is removed, then halt quiet time. */ static int ath_set_quiet_ie(struct ieee80211_node *ni, uint8_t *ie) { struct ieee80211_quiet_ie *q; struct ieee80211vap *vap = ni->ni_vap; struct ath_vap *avp = ATH_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; if (vap->iv_opmode != IEEE80211_M_STA) return (0); /* Verify we have a quiet time IE */ if (ie == NULL) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: called; NULL IE, disabling\n", __func__); ath_hal_set_quiet(sc->sc_ah, 0, 0, 0, HAL_QUIET_DISABLE); memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); return (0); } /* If we do, verify it's actually legit */ if (ie[0] != IEEE80211_ELEMID_QUIET) return 0; if (ie[1] != 6) return 0; /* Note: this belongs in net80211, parsed out and everything */ q = (void *) ie; /* * Compare what we have stored to what we last saw. * If they're the same then don't program in anything. */ if ((q->period == avp->quiet_ie.period) && (le16dec(&q->duration) == le16dec(&avp->quiet_ie.duration)) && (le16dec(&q->offset) == le16dec(&avp->quiet_ie.offset))) return (0); DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: called; tbttcount=%d, period=%d, duration=%d, offset=%d\n", __func__, (int) q->tbttcount, (int) q->period, (int) le16dec(&q->duration), (int) le16dec(&q->offset)); /* * Don't program in garbage values. */ if ((le16dec(&q->duration) == 0) || (le16dec(&q->duration) >= ni->ni_intval)) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid duration (%d)\n", __func__, le16dec(&q->duration)); return (0); } /* * Can have a 0 offset, but not a duration - so just check * they don't exceed the intval. */ if (le16dec(&q->duration) + le16dec(&q->offset) >= ni->ni_intval) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid duration + offset (%d+%d)\n", __func__, le16dec(&q->duration), le16dec(&q->offset)); return (0); } if (q->tbttcount == 0) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid tbttcount (0)\n", __func__); return (0); } if (q->period == 0) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: invalid period (0)\n", __func__); return (0); } /* * This is a new quiet time IE config, so wait until tbttcount * is equal to 1, and program it in. */ if (q->tbttcount == 1) { DPRINTF(sc, ATH_DEBUG_QUIETIE, "%s: programming\n", __func__); ath_hal_set_quiet(sc->sc_ah, q->period * ni->ni_intval, /* convert to TU */ le16dec(&q->duration), /* already in TU */ le16dec(&q->offset) + ni->ni_intval, HAL_QUIET_ENABLE | HAL_QUIET_ADD_CURRENT_TSF); /* * Note: no HAL_QUIET_ADD_SWBA_RESP_TIME; as this is for * STA mode */ /* Update local state */ memcpy(&avp->quiet_ie, ie, sizeof(struct ieee80211_quiet_ie)); } return (0); } static void ath_set_channel(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); (void) ath_chan_set(sc, ic->ic_curchan); /* * If we are returning to our bss channel then mark state * so the next recv'd beacon's tsf will be used to sync the * beacon timers. Note that since we only hear beacons in * sta/ibss mode this has no effect in other operating modes. */ ATH_LOCK(sc); if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) sc->sc_syncbeacon = 1; ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } /* * Walk the vap list and check if there any vap's in RUN state. */ static int ath_isanyrunningvaps(struct ieee80211vap *this) { struct ieee80211com *ic = this->iv_ic; struct ieee80211vap *vap; IEEE80211_LOCK_ASSERT(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap != this && vap->iv_state >= IEEE80211_S_RUN) return 1; } return 0; } static int ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp = ATH_VAP(vap); struct ath_hal *ah = sc->sc_ah; struct ieee80211_node *ni = NULL; int i, error, stamode; u_int32_t rfilt; int csa_run_transition = 0; enum ieee80211_state ostate = vap->iv_state; static const HAL_LED_STATE leds[] = { HAL_LED_INIT, /* IEEE80211_S_INIT */ HAL_LED_SCAN, /* IEEE80211_S_SCAN */ HAL_LED_AUTH, /* IEEE80211_S_AUTH */ HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ HAL_LED_RUN, /* IEEE80211_S_CAC */ HAL_LED_RUN, /* IEEE80211_S_RUN */ HAL_LED_RUN, /* IEEE80211_S_CSA */ HAL_LED_RUN, /* IEEE80211_S_SLEEP */ }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); /* * net80211 _should_ have the comlock asserted at this point. * There are some comments around the calls to vap->iv_newstate * which indicate that it (newstate) may end up dropping the * lock. This and the subsequent lock assert check after newstate * are an attempt to catch these and figure out how/why. */ IEEE80211_LOCK_ASSERT(ic); /* Before we touch the hardware - wake it up */ ATH_LOCK(sc); /* * If the NIC is in anything other than SLEEP state, * we need to ensure that self-generated frames are * set for PWRMGT=0. Otherwise we may end up with * strange situations. * * XXX TODO: is this actually the case? :-) */ if (nstate != IEEE80211_S_SLEEP) ath_power_setselfgen(sc, HAL_PM_AWAKE); /* * Now, wake the thing up. */ ath_power_set_power_state(sc, HAL_PM_AWAKE); /* * And stop the calibration callout whilst we have * ATH_LOCK held. */ callout_stop(&sc->sc_cal_ch); ATH_UNLOCK(sc); if (ostate == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; ath_hal_setledstate(ah, leds[nstate]); /* set LED */ if (nstate == IEEE80211_S_SCAN) { /* * Scanning: turn off beacon miss and don't beacon. * Mark beacon state so when we reach RUN state we'll * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ /* Ensure we stay awake during scan */ ATH_LOCK(sc); ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE, 1); ATH_UNLOCK(sc); ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); sc->sc_beacons = 0; taskqueue_unblock(sc->sc_tq); } ni = ieee80211_ref_node(vap->iv_bss); rfilt = ath_calcrxfilter(sc); stamode = (vap->iv_opmode == IEEE80211_M_STA || vap->iv_opmode == IEEE80211_M_AHDEMO || vap->iv_opmode == IEEE80211_M_IBSS); /* * XXX Dont need to do this (and others) if we've transitioned * from SLEEP->RUN. */ if (stamode && nstate == IEEE80211_S_RUN) { sc->sc_curaid = ni->ni_associd; IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid); } DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n", __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid); ath_hal_setrxfilter(ah, rfilt); /* XXX is this to restore keycache on resume? */ if (vap->iv_opmode != IEEE80211_M_STA && (vap->iv_flags & IEEE80211_F_PRIVACY)) { for (i = 0; i < IEEE80211_WEP_NKID; i++) if (ath_hal_keyisvalid(ah, i)) ath_hal_keysetmac(ah, i, ni->ni_bssid); } /* * Invoke the parent method to do net80211 work. */ error = avp->av_newstate(vap, nstate, arg); if (error != 0) goto bad; /* * See above: ensure av_newstate() doesn't drop the lock * on us. */ IEEE80211_LOCK_ASSERT(ic); /* * XXX TODO: if nstate is _S_CAC, then we should disable * ACK processing until CAC is completed. */ /* * XXX TODO: if we're on a passive channel, then we should * not allow any ACKs or self-generated frames until we hear * a beacon. Unfortunately there isn't a notification from * net80211 so perhaps we could slot that particular check * into the mgmt receive path and just ensure that we clear * it on RX of beacons in passive mode (and only clear it * once, obviously.) */ /* * XXX TODO: net80211 should be tracking whether channels * have heard beacons and are thus considered "OK" for * transmitting - and then inform the driver about this * state change. That way if we hear an AP go quiet * (and nothing else is beaconing on a channel) the * channel can go back to being passive until another * beacon is heard. */ /* * XXX TODO: if nstate is _S_CAC, then we should disable * ACK processing until CAC is completed. */ /* * XXX TODO: if we're on a passive channel, then we should * not allow any ACKs or self-generated frames until we hear * a beacon. Unfortunately there isn't a notification from * net80211 so perhaps we could slot that particular check * into the mgmt receive path and just ensure that we clear * it on RX of beacons in passive mode (and only clear it * once, obviously.) */ /* * XXX TODO: net80211 should be tracking whether channels * have heard beacons and are thus considered "OK" for * transmitting - and then inform the driver about this * state change. That way if we hear an AP go quiet * (and nothing else is beaconing on a channel) the * channel can go back to being passive until another * beacon is heard. */ if (nstate == IEEE80211_S_RUN) { /* NB: collect bss node again, it may have changed */ ieee80211_free_node(ni); ni = ieee80211_ref_node(vap->iv_bss); DPRINTF(sc, ATH_DEBUG_STATE, "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s " "capinfo 0x%04x chan %d\n", __func__, vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); switch (vap->iv_opmode) { #ifdef IEEE80211_SUPPORT_TDMA case IEEE80211_M_AHDEMO: if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) break; /* fall thru... */ #endif case IEEE80211_M_HOSTAP: case IEEE80211_M_IBSS: case IEEE80211_M_MBSS: /* * TODO: Enable ACK processing (ie, clear AR_DIAG_ACK_DIS.) * For channels that are in CAC, we may have disabled * this during CAC to ensure we don't ACK frames * sent to us. */ /* * Allocate and setup the beacon frame. * * Stop any previous beacon DMA. This may be * necessary, for example, when an ibss merge * causes reconfiguration; there will be a state * transition from RUN->RUN that means we may * be called with beacon transmission active. */ ath_hal_stoptxdma(ah, sc->sc_bhalq); error = ath_beacon_alloc(sc, ni); if (error != 0) goto bad; /* * If joining an adhoc network defer beacon timer * configuration to the next beacon frame so we * have a current TSF to use. Otherwise we're * starting an ibss/bss so there's no need to delay; * if this is the first vap moving to RUN state, then * beacon state needs to be [re]configured. */ if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf != 0) { sc->sc_syncbeacon = 1; } else if (!sc->sc_beacons) { #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) ath_tdma_config(sc, vap); else #endif ath_beacon_config(sc, vap); sc->sc_beacons = 1; } break; case IEEE80211_M_STA: /* * Defer beacon timer configuration to the next * beacon frame so we have a current TSF to use * (any TSF collected when scanning is likely old). * However if it's due to a CSA -> RUN transition, * force a beacon update so we pick up a lack of * beacons from an AP in CAC and thus force a * scan. * * And, there's also corner cases here where * after a scan, the AP may have disappeared. * In that case, we may not receive an actual * beacon to update the beacon timer and thus we * won't get notified of the missing beacons. */ if (ostate != IEEE80211_S_RUN && ostate != IEEE80211_S_SLEEP) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: STA; syncbeacon=1\n", __func__); sc->sc_syncbeacon = 1; /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); if (csa_run_transition) ath_beacon_config(sc, vap); /* * PR: kern/175227 * * Reconfigure beacons during reset; as otherwise * we won't get the beacon timers reprogrammed * after a reset and thus we won't pick up a * beacon miss interrupt. * * Hopefully we'll see a beacon before the BMISS * timer fires (too often), leading to a STA * disassociation. */ sc->sc_beacons = 1; } break; case IEEE80211_M_MONITOR: /* * Monitor mode vaps have only INIT->RUN and RUN->RUN * transitions so we must re-enable interrupts here to * handle the case of a single monitor mode vap. */ ath_hal_intrset(ah, sc->sc_imask); break; case IEEE80211_M_WDS: break; default: break; } /* * Let the hal process statistics collected during a * scan so it can provide calibrated noise floor data. */ ath_hal_process_noisefloor(ah); /* * Reset rssi stats; maybe not the best place... */ sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; /* * Force awake for RUN mode. */ ATH_LOCK(sc); ath_power_setselfgen(sc, HAL_PM_AWAKE); ath_power_setpower(sc, HAL_PM_AWAKE, 1); /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). */ if (ath_longcalinterval != 0) { /* start periodic recalibration timer */ callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } ATH_UNLOCK(sc); taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); /* * If there are no vaps left in RUN state then * shutdown host/driver operation: * o disable interrupts * o disable the task queue thread * o mark beacon processing as stopped */ if (!ath_isanyrunningvaps(vap)) { sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); /* disable interrupts */ ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL); taskqueue_block(sc->sc_tq); sc->sc_beacons = 0; } /* * For at least STA mode we likely should clear the ANI * and NF calibration state and allow the NIC/HAL to figure * out optimal parameters at runtime. Otherwise if we * disassociate due to interference / deafness it may persist * when we reconnect. * * Note: may need to do this for other states too, not just * _S_INIT. */ #ifdef IEEE80211_SUPPORT_TDMA ath_hal_setcca(ah, AH_TRUE); #endif } else if (nstate == IEEE80211_S_SLEEP) { /* We're going to sleep, so transition appropriately */ /* For now, only do this if we're a single STA vap */ if (sc->sc_nvaps == 1 && vap->iv_opmode == IEEE80211_M_STA) { DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=%d\n", __func__, sc->sc_syncbeacon); ATH_LOCK(sc); /* * Always at least set the self-generated * frame config to set PWRMGT=1. */ ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP); /* * If we're not syncing beacons, transition * to NETWORK_SLEEP. * * We stay awake if syncbeacon > 0 in case * we need to listen for some beacons otherwise * our beacon timer config may be wrong. */ if (sc->sc_syncbeacon == 0) { ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP, 1); } ATH_UNLOCK(sc); } /* * Note - the ANI/calibration timer isn't re-enabled during * network sleep for now. One unfortunate side-effect is that * the PHY/airtime statistics aren't gathered on the channel * but I haven't yet tested to see if reading those registers * CAN occur during network sleep. * * This should be revisited in a future commit, even if it's * just to split out the airtime polling from ANI/calibration. */ } else if (nstate == IEEE80211_S_SCAN) { /* Quiet time handling - ensure we resync */ memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); /* * If we're in scan mode then startpcureceive() is * hopefully being called with "reset ANI" for this channel; * but once we attempt to reassociate we program in the previous * ANI values and.. not do any calibration until we're running. * This may mean we stay deaf unless we can associate successfully. * * So do kick off the cal timer to get NF/ANI going. */ ATH_LOCK(sc); if (ath_longcalinterval != 0) { /* start periodic recalibration timer */ callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); } else { DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } ATH_UNLOCK(sc); } bad: ieee80211_free_node(ni); /* * Restore the power state - either to what it was, or * to network_sleep if it's alright. */ ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return error; } /* * Allocate a key cache slot to the station so we can * setup a mapping from key index to node. The key cache * slot is needed for managing antenna state and for * compression when stations do not use crypto. We do * it uniliaterally here; if crypto is employed this slot * will be reassigned. */ static void ath_setup_stationkey(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_softc; ieee80211_keyix keyix, rxkeyix; /* XXX should take a locked ref to vap->iv_bss */ if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) { /* * Key cache is full; we'll fall back to doing * the more expensive lookup in software. Note * this also means no h/w compression. */ /* XXX msg+statistic */ } else { /* XXX locking? */ ni->ni_ucastkey.wk_keyix = keyix; ni->ni_ucastkey.wk_rxkeyix = rxkeyix; /* NB: must mark device key to get called back on delete */ ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY; IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); /* NB: this will create a pass-thru key entry */ ath_keyset(sc, vap, &ni->ni_ucastkey, vap->iv_bss); } } /* * Setup driver-specific state for a newly associated node. * Note that we're called also on a re-associate, the isnew * param tells us if this is the first time or not. */ static void ath_newassoc(struct ieee80211_node *ni, int isnew) { struct ath_node *an = ATH_NODE(ni); struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_softc; const struct ieee80211_txparam *tp = ni->ni_txparms; an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", __func__, ni->ni_macaddr, ":", isnew, an->an_is_powersave); ATH_NODE_LOCK(an); ath_rate_newassoc(sc, an, isnew); ATH_NODE_UNLOCK(an); if (isnew && (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE) ath_setup_stationkey(ni); /* * If we're reassociating, make sure that any paused queues * get unpaused. * * Now, we may have frames in the hardware queue for this node. * So if we are reassociating and there are frames in the queue, * we need to go through the cleanup path to ensure that they're * marked as non-aggregate. */ if (! isnew) { DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; is_powersave=%d\n", __func__, ni->ni_macaddr, ":", an->an_is_powersave); /* XXX for now, we can't hold the lock across assoc */ ath_tx_node_reassoc(sc, an); /* XXX for now, we can't hold the lock across wakeup */ if (an->an_is_powersave) ath_tx_node_wakeup(sc, an); } } static int ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *reg, int nchans, struct ieee80211_channel chans[]) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: rd %u cc %u location %c%s\n", __func__, reg->regdomain, reg->country, reg->location, reg->ecm ? " ecm" : ""); status = ath_hal_set_channels(ah, chans, nchans, reg->country, reg->regdomain); if (status != HAL_OK) { DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: failed, status %u\n", __func__, status); return EINVAL; /* XXX */ } return 0; } static void ath_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct ath_softc *sc = ic->ic_softc; struct ath_hal *ah = sc->sc_ah; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: use rd %u cc %d\n", __func__, SKU_DEBUG, CTRY_DEFAULT); /* XXX check return */ (void) ath_hal_getchannels(ah, chans, maxchans, nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_DEBUG, AH_TRUE); } static int ath_getchannels(struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; HAL_STATUS status; /* * Collect channel set based on EEPROM contents. */ status = ath_hal_init_channels(ah, ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, HAL_MODE_ALL, CTRY_DEFAULT, SKU_NONE, AH_TRUE); if (status != HAL_OK) { device_printf(sc->sc_dev, "%s: unable to collect channel list from hal, status %d\n", __func__, status); return EINVAL; } (void) ath_hal_getregdomain(ah, &sc->sc_eerd); ath_hal_getcountrycode(ah, &sc->sc_eecc); /* NB: cannot fail */ /* XXX map Atheros sku's to net80211 SKU's */ /* XXX net80211 types too small */ ic->ic_regdomain.regdomain = (uint16_t) sc->sc_eerd; ic->ic_regdomain.country = (uint16_t) sc->sc_eecc; ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */ ic->ic_regdomain.isocc[1] = ' '; ic->ic_regdomain.ecm = 1; ic->ic_regdomain.location = 'I'; DPRINTF(sc, ATH_DEBUG_REGDOMAIN, "%s: eeprom rd %u cc %u (mapped rd %u cc %u) location %c%s\n", __func__, sc->sc_eerd, sc->sc_eecc, ic->ic_regdomain.regdomain, ic->ic_regdomain.country, ic->ic_regdomain.location, ic->ic_regdomain.ecm ? " ecm" : ""); return 0; } static int ath_rate_setup(struct ath_softc *sc, u_int mode) { struct ath_hal *ah = sc->sc_ah; const HAL_RATE_TABLE *rt; switch (mode) { case IEEE80211_MODE_11A: rt = ath_hal_getratetable(ah, HAL_MODE_11A); break; case IEEE80211_MODE_HALF: rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE); break; case IEEE80211_MODE_QUARTER: rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE); break; case IEEE80211_MODE_11B: rt = ath_hal_getratetable(ah, HAL_MODE_11B); break; case IEEE80211_MODE_11G: rt = ath_hal_getratetable(ah, HAL_MODE_11G); break; case IEEE80211_MODE_TURBO_A: rt = ath_hal_getratetable(ah, HAL_MODE_108A); break; case IEEE80211_MODE_TURBO_G: rt = ath_hal_getratetable(ah, HAL_MODE_108G); break; case IEEE80211_MODE_STURBO_A: rt = ath_hal_getratetable(ah, HAL_MODE_TURBO); break; case IEEE80211_MODE_11NA: rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20); break; case IEEE80211_MODE_11NG: rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20); break; default: DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", __func__, mode); return 0; } sc->sc_rates[mode] = rt; return (rt != NULL); } static void ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) { /* NB: on/off times from the Atheros NDIS driver, w/ permission */ static const struct { u_int rate; /* tx/rx 802.11 rate */ u_int16_t timeOn; /* LED on time (ms) */ u_int16_t timeOff; /* LED off time (ms) */ } blinkrates[] = { { 108, 40, 10 }, { 96, 44, 11 }, { 72, 50, 13 }, { 48, 57, 14 }, { 36, 67, 16 }, { 24, 80, 20 }, { 22, 100, 25 }, { 18, 133, 34 }, { 12, 160, 40 }, { 10, 200, 50 }, { 6, 240, 58 }, { 4, 267, 66 }, { 2, 400, 100 }, { 0, 500, 130 }, /* XXX half/quarter rates */ }; const HAL_RATE_TABLE *rt; int i, j; memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); rt = sc->sc_rates[mode]; KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); for (i = 0; i < rt->rateCount; i++) { uint8_t ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; if (rt->info[i].phy != IEEE80211_T_HT) sc->sc_rixmap[ieeerate] = i; else sc->sc_rixmap[ieeerate | IEEE80211_RATE_MCS] = i; } memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); for (i = 0; i < nitems(sc->sc_hwmap); i++) { if (i >= rt->rateCount) { sc->sc_hwmap[i].ledon = (500 * hz) / 1000; sc->sc_hwmap[i].ledoff = (130 * hz) / 1000; continue; } sc->sc_hwmap[i].ieeerate = rt->info[i].dot11Rate & IEEE80211_RATE_VAL; if (rt->info[i].phy == IEEE80211_T_HT) sc->sc_hwmap[i].ieeerate |= IEEE80211_RATE_MCS; sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; if (rt->info[i].shortPreamble || rt->info[i].phy == IEEE80211_T_OFDM) sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE; sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags; for (j = 0; j < nitems(blinkrates)-1; j++) if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate) break; /* NB: this uses the last entry if the rate isn't found */ /* XXX beware of overlow */ sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000; sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000; } sc->sc_currates = rt; sc->sc_curmode = mode; /* * All protection frames are transmitted at 2Mb/s for * 11g, otherwise at 1Mb/s. */ if (mode == IEEE80211_MODE_11G) sc->sc_protrix = ath_tx_findrix(sc, 2*2); else sc->sc_protrix = ath_tx_findrix(sc, 2*1); /* NB: caller is responsible for resetting rate control state */ } static void ath_watchdog(void *arg) { struct ath_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; int do_reset = 0; ATH_LOCK_ASSERT(sc); if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { uint32_t hangs; ath_power_set_power_state(sc, HAL_PM_AWAKE); if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { device_printf(sc->sc_dev, "%s hang detected (0x%x)\n", hangs & 0xff ? "bb" : "mac", hangs); } else device_printf(sc->sc_dev, "device timeout\n"); do_reset = 1; counter_u64_add(ic->ic_oerrors, 1); sc->sc_stats.ast_watchdog++; ath_power_restore_power_state(sc); } /* * We can't hold the lock across the ath_reset() call. * * And since this routine can't hold a lock and sleep, * do the reset deferred. */ if (do_reset) { taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); } callout_schedule(&sc->sc_wd_ch, hz); } static void ath_parent(struct ieee80211com *ic) { struct ath_softc *sc = ic->ic_softc; int error = EDOOFUS; ATH_LOCK(sc); if (ic->ic_nrunning > 0) { /* * To avoid rescanning another access point, * do not call ath_init() here. Instead, * only reflect promisc mode settings. */ if (sc->sc_running) { ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_mode_init(sc); ath_power_restore_power_state(sc); } else if (!sc->sc_invalid) { /* * Beware of being called during attach/detach * to reset promiscuous mode. In that case we * will still be marked UP but not RUNNING. * However trying to re-init the interface * is the wrong thing to do as we've already * torn down much of our state. There's * probably a better way to deal with this. */ error = ath_init(sc); } } else { ath_stop(sc); if (!sc->sc_invalid) ath_power_setpower(sc, HAL_PM_FULL_SLEEP, 1); } ATH_UNLOCK(sc); if (error == 0) { #ifdef ATH_TX99_DIAG if (sc->sc_tx99 != NULL) sc->sc_tx99->start(sc->sc_tx99); else #endif ieee80211_start_all(ic); } } /* * Announce various information on device/driver attach. */ static void ath_announce(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; device_printf(sc->sc_dev, "%s mac %d.%d RF%s phy %d.%d\n", ath_hal_mac_name(ah), ah->ah_macVersion, ah->ah_macRev, ath_hal_rf_name(ah), ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); device_printf(sc->sc_dev, "2GHz radio: 0x%.4x; 5GHz radio: 0x%.4x\n", ah->ah_analog2GhzRev, ah->ah_analog5GhzRev); if (bootverbose) { int i; for (i = 0; i <= WME_AC_VO; i++) { struct ath_txq *txq = sc->sc_ac2q[i]; device_printf(sc->sc_dev, "Use hw queue %u for %s traffic\n", txq->axq_qnum, ieee80211_wme_acnames[i]); } device_printf(sc->sc_dev, "Use hw queue %u for CAB traffic\n", sc->sc_cabq->axq_qnum); device_printf(sc->sc_dev, "Use hw queue %u for beacons\n", sc->sc_bhalq); } if (ath_rxbuf != ATH_RXBUF) device_printf(sc->sc_dev, "using %u rx buffers\n", ath_rxbuf); if (ath_txbuf != ATH_TXBUF) device_printf(sc->sc_dev, "using %u tx buffers\n", ath_txbuf); if (sc->sc_mcastkey && bootverbose) device_printf(sc->sc_dev, "using multicast key search\n"); } static void ath_dfs_tasklet(void *p, int npending) { struct ath_softc *sc = (struct ath_softc *) p; struct ieee80211com *ic = &sc->sc_ic; /* * If previous processing has found a radar event, * signal this to the net80211 layer to begin DFS * processing. */ if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { /* DFS event found, initiate channel change */ /* * XXX TODO: immediately disable ACK processing * on the current channel. This would be done * by setting AR_DIAG_ACK_DIS (AR5212; may be * different for others) until we are out of * CAC. */ /* * XXX doesn't currently tell us whether the event * XXX was found in the primary or extension * XXX channel! */ IEEE80211_LOCK(ic); ieee80211_dfs_notify_radar(ic, sc->sc_curchan); IEEE80211_UNLOCK(ic); } } /* * Enable/disable power save. This must be called with * no TX driver locks currently held, so it should only * be called from the RX path (which doesn't hold any * TX driver locks.) */ static void ath_node_powersave(struct ieee80211_node *ni, int enable) { #ifdef ATH_SW_PSQ struct ath_node *an = ATH_NODE(ni); struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_vap *avp = ATH_VAP(ni->ni_vap); /* XXX and no TXQ locks should be held here */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n", __func__, ni->ni_macaddr, ":", !! enable); /* Suspend or resume software queue handling */ if (enable) ath_tx_node_sleep(sc, an); else ath_tx_node_wakeup(sc, an); /* Update net80211 state */ avp->av_node_ps(ni, enable); #else struct ath_vap *avp = ATH_VAP(ni->ni_vap); /* Update net80211 state */ avp->av_node_ps(ni, enable); #endif/* ATH_SW_PSQ */ } /* * Notification from net80211 that the powersave queue state has * changed. * * Since the software queue also may have some frames: * * + if the node software queue has frames and the TID state * is 0, we set the TIM; * + if the node and the stack are both empty, we clear the TIM bit. * + If the stack tries to set the bit, always set it. * + If the stack tries to clear the bit, only clear it if the * software queue in question is also cleared. * * TODO: this is called during node teardown; so let's ensure this * is all correctly handled and that the TIM bit is cleared. * It may be that the node flush is called _AFTER_ the net80211 * stack clears the TIM. * * Here is the racy part. Since it's possible >1 concurrent, * overlapping TXes will appear complete with a TX completion in * another thread, it's possible that the concurrent TIM calls will * clash. We can't hold the node lock here because setting the * TIM grabs the net80211 comlock and this may cause a LOR. * The solution is either to totally serialise _everything_ at * this point (ie, all TX, completion and any reset/flush go into * one taskqueue) or a new "ath TIM lock" needs to be created that * just wraps the driver state change and this call to avp->av_set_tim(). * * The same race exists in the net80211 power save queue handling * as well. Since multiple transmitting threads may queue frames * into the driver, as well as ps-poll and the driver transmitting * frames (and thus clearing the psq), it's quite possible that * a packet entering the PSQ and a ps-poll being handled will * race, causing the TIM to be cleared and not re-set. */ static int ath_node_set_tim(struct ieee80211_node *ni, int enable) { #ifdef ATH_SW_PSQ struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; struct ath_node *an = ATH_NODE(ni); struct ath_vap *avp = ATH_VAP(ni->ni_vap); int changed = 0; ATH_TX_LOCK(sc); an->an_stack_psq = enable; /* * This will get called for all operating modes, * even if avp->av_set_tim is unset. * It's currently set for hostap/ibss modes; but * the same infrastructure is used for both STA * and AP/IBSS node power save. */ if (avp->av_set_tim == NULL) { ATH_TX_UNLOCK(sc); return (0); } /* * If setting the bit, always set it here. * If clearing the bit, only clear it if the * software queue is also empty. * * If the node has left power save, just clear the TIM * bit regardless of the state of the power save queue. * * XXX TODO: although atomics are used, it's quite possible * that a race will occur between this and setting/clearing * in another thread. TX completion will occur always in * one thread, however setting/clearing the TIM bit can come * from a variety of different process contexts! */ if (enable && an->an_tim_set == 1) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, tim_set=1, ignoring\n", __func__, ni->ni_macaddr, ":", enable); ATH_TX_UNLOCK(sc); } else if (enable) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, enabling TIM\n", __func__, ni->ni_macaddr, ":", enable); an->an_tim_set = 1; ATH_TX_UNLOCK(sc); changed = avp->av_set_tim(ni, enable); } else if (an->an_swq_depth == 0) { /* disable */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, an_swq_depth == 0, disabling\n", __func__, ni->ni_macaddr, ":", enable); an->an_tim_set = 0; ATH_TX_UNLOCK(sc); changed = avp->av_set_tim(ni, enable); } else if (! an->an_is_powersave) { /* * disable regardless; the node isn't in powersave now */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, an_pwrsave=0, disabling\n", __func__, ni->ni_macaddr, ":", enable); an->an_tim_set = 0; ATH_TX_UNLOCK(sc); changed = avp->av_set_tim(ni, enable); } else { /* * psq disable, node is currently in powersave, node * software queue isn't empty, so don't clear the TIM bit * for now. */ ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d, an_swq_depth > 0, ignoring\n", __func__, ni->ni_macaddr, ":", enable); changed = 0; } return (changed); #else struct ath_vap *avp = ATH_VAP(ni->ni_vap); /* * Some operating modes don't set av_set_tim(), so don't * update it here. */ if (avp->av_set_tim == NULL) return (0); return (avp->av_set_tim(ni, enable)); #endif /* ATH_SW_PSQ */ } /* * Set or update the TIM from the software queue. * * Check the software queue depth before attempting to do lock * anything; that avoids trying to obtain the lock. Then, * re-check afterwards to ensure nothing has changed in the * meantime. * * set: This is designed to be called from the TX path, after * a frame has been queued; to see if the swq > 0. * * clear: This is designed to be called from the buffer completion point * (right now it's ath_tx_default_comp()) where the state of * a software queue has changed. * * It makes sense to place it at buffer free / completion rather * than after each software queue operation, as there's no real * point in churning the TIM bit as the last frames in the software * queue are transmitted. If they fail and we retry them, we'd * just be setting the TIM bit again anyway. */ void ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni, int enable) { #ifdef ATH_SW_PSQ struct ath_node *an; struct ath_vap *avp; /* Don't do this for broadcast/etc frames */ if (ni == NULL) return; an = ATH_NODE(ni); avp = ATH_VAP(ni->ni_vap); /* * And for operating modes without the TIM handler set, let's * just skip those. */ if (avp->av_set_tim == NULL) return; ATH_TX_LOCK_ASSERT(sc); if (enable) { if (an->an_is_powersave && an->an_tim_set == 0 && an->an_swq_depth != 0) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: swq_depth>0, tim_set=0, set!\n", __func__, ni->ni_macaddr, ":"); an->an_tim_set = 1; (void) avp->av_set_tim(ni, 1); } } else { /* * Don't bother grabbing the lock unless the queue is empty. */ if (an->an_swq_depth != 0) return; if (an->an_is_powersave && an->an_stack_psq == 0 && an->an_tim_set == 1 && an->an_swq_depth == 0) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: swq_depth=0, tim_set=1, psq_set=0," " clear!\n", __func__, ni->ni_macaddr, ":"); an->an_tim_set = 0; (void) avp->av_set_tim(ni, 0); } } #else return; #endif /* ATH_SW_PSQ */ } /* * Received a ps-poll frame from net80211. * * Here we get a chance to serve out a software-queued frame ourselves * before we punt it to net80211 to transmit us one itself - either * because there's traffic in the net80211 psq, or a NULL frame to * indicate there's nothing else. */ static void ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m) { #ifdef ATH_SW_PSQ struct ath_node *an; struct ath_vap *avp; struct ieee80211com *ic = ni->ni_ic; struct ath_softc *sc = ic->ic_softc; int tid; /* Just paranoia */ if (ni == NULL) return; /* * Unassociated (temporary node) station. */ if (ni->ni_associd == 0) return; /* * We do have an active node, so let's begin looking into it. */ an = ATH_NODE(ni); avp = ATH_VAP(ni->ni_vap); /* * For now, we just call the original ps-poll method. * Once we're ready to flip this on: * * + Set leak to 1, as no matter what we're going to have * to send a frame; * + Check the software queue and if there's something in it, * schedule the highest TID thas has traffic from this node. * Then make sure we schedule the software scheduler to * run so it picks up said frame. * * That way whatever happens, we'll at least send _a_ frame * to the given node. * * Again, yes, it's crappy QoS if the node has multiple * TIDs worth of traffic - but let's get it working first * before we optimise it. * * Also yes, there's definitely latency here - we're not * direct dispatching to the hardware in this path (and * we're likely being called from the packet receive path, * so going back into TX may be a little hairy!) but again * I'd like to get this working first before optimising * turn-around time. */ ATH_TX_LOCK(sc); /* * Legacy - we're called and the node isn't asleep. * Immediately punt. */ if (! an->an_is_powersave) { DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: not in powersave?\n", __func__, ni->ni_macaddr, ":"); ATH_TX_UNLOCK(sc); avp->av_recv_pspoll(ni, m); return; } /* * We're in powersave. * * Leak a frame. */ an->an_leak_count = 1; /* * Now, if there's no frames in the node, just punt to * recv_pspoll. * * Don't bother checking if the TIM bit is set, we really * only care if there are any frames here! */ if (an->an_swq_depth == 0) { ATH_TX_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: SWQ empty; punting to net80211\n", __func__, ni->ni_macaddr, ":"); avp->av_recv_pspoll(ni, m); return; } /* * Ok, let's schedule the highest TID that has traffic * and then schedule something. */ for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) { struct ath_tid *atid = &an->an_tid[tid]; /* * No frames? Skip. */ if (atid->axq_depth == 0) continue; ath_tx_tid_sched(sc, atid); /* * XXX we could do a direct call to the TXQ * scheduler code here to optimise latency * at the expense of a REALLY deep callstack. */ ATH_TX_UNLOCK(sc); taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask); DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: leaking frame to TID %d\n", __func__, ni->ni_macaddr, ":", tid); return; } ATH_TX_UNLOCK(sc); /* * XXX nothing in the TIDs at this point? Eek. */ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: TIDs empty, but ath_node showed traffic?!\n", __func__, ni->ni_macaddr, ":"); avp->av_recv_pspoll(ni, m); #else avp->av_recv_pspoll(ni, m); #endif /* ATH_SW_PSQ */ } MODULE_VERSION(ath_main, 1); MODULE_DEPEND(ath_main, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(ath_main, ath_rate, 1, 1, 1); MODULE_DEPEND(ath_main, ath_dfs, 1, 1, 1); MODULE_DEPEND(ath_main, ath_hal, 1, 1, 1); #if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) || defined(ATH_DEBUG_ALQ) MODULE_DEPEND(ath_main, alq, 1, 1, 1); #endif Index: head/sys/dev/ath/if_ath_misc.h =================================================================== --- head/sys/dev/ath/if_ath_misc.h (revision 361485) +++ head/sys/dev/ath/if_ath_misc.h (revision 361486) @@ -1,153 +1,154 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef __IF_ATH_MISC_H__ #define __IF_ATH_MISC_H__ /* * This is where definitions for "public things" in if_ath.c * will go for the time being. * * Anything in here should eventually be moved out of if_ath.c * and into something else. */ extern int ath_rxbuf; extern int ath_txbuf; extern int ath_txbuf_mgmt; extern int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate); extern struct ath_buf * ath_getbuf(struct ath_softc *sc, ath_buf_type_t btype); extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype); extern struct ath_buf * ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf); /* XXX change this to NULL the buffer pointer? */ extern void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf); extern void ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf); extern void ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf); -extern int ath_reset(struct ath_softc *, ATH_RESET_TYPE); +extern int ath_reset(struct ath_softc *, ATH_RESET_TYPE, + HAL_RESET_TYPE ah_reset_type); extern void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail); extern void ath_tx_update_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_rc_series *rc, struct ath_tx_status *ts, int frmlen, int rc_framelen, int nframes, int nbad); extern int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs); extern void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status); extern void ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq); extern void ath_txqmove(struct ath_txq *dst, struct ath_txq *src); extern void ath_mode_init(struct ath_softc *sc); extern void ath_setdefantenna(struct ath_softc *sc, u_int antenna); extern void ath_setslottime(struct ath_softc *sc); extern void ath_legacy_attach_comp_func(struct ath_softc *sc); extern void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq); extern void ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type); extern void ath_tx_process_buf_completion(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf); extern int ath_stoptxdma(struct ath_softc *sc); extern void ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni, int enable); /* * This is only here so that the RX proc function can call it. * It's very likely that the "start TX after RX" call should be * done via something in if_ath.c, moving "rx tasklet" into * if_ath.c and do the ath_start() call there. Once that's done, * we can kill this. */ extern void ath_start(struct ifnet *ifp); extern void ath_start_task(void *arg, int npending); extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq); /* * Power state tracking. */ extern void _ath_power_setpower(struct ath_softc *sc, int power_state, int selfgen, const char *file, int line); extern void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line); extern void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line); extern void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line); #define ath_power_setpower(sc, ps, sg) _ath_power_setpower(sc, ps, sg, \ __FILE__, __LINE__) #define ath_power_setselfgen(sc, ps) _ath_power_set_selfgen(sc, ps, \ __FILE__, __LINE__) #define ath_power_set_power_state(sc, ps) \ _ath_power_set_power_state(sc, ps, __FILE__, __LINE__) #define ath_power_restore_power_state(sc) \ _ath_power_restore_power_state(sc, __FILE__, __LINE__) /* * Kick the frame TX task. */ static inline void ath_tx_kick(struct ath_softc *sc) { /* XXX NULL for now */ } /* * Kick the software TX queue task. */ static inline void ath_tx_swq_kick(struct ath_softc *sc) { taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask); } #endif Index: head/sys/dev/ath/if_ath_sysctl.c =================================================================== --- head/sys/dev/ath/if_ath_sysctl.c (revision 361485) +++ head/sys/dev/ath/if_ath_sysctl.c (revision 361486) @@ -1,1359 +1,1361 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * Driver for the Atheros Wireless LAN controller. * * This software is derived from work of Atsushi Onoe; his contribution * is greatly appreciated. */ #include "opt_inet.h" #include "opt_ath.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #ifdef INET #include #include #endif #include #include /* XXX for softled */ #include #include #include #include #include #include #ifdef ATH_TX99_DIAG #include #endif #ifdef ATH_DEBUG_ALQ #include #endif static int ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int slottime; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); slottime = ath_hal_getslottime(sc->sc_ah); ATH_UNLOCK(sc); error = sysctl_handle_int(oidp, &slottime, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return error; } static int ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int acktimeout; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); acktimeout = ath_hal_getacktimeout(sc->sc_ah); ATH_UNLOCK(sc); error = sysctl_handle_int(oidp, &acktimeout, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int ctstimeout; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ctstimeout = ath_hal_getctstimeout(sc->sc_ah); ATH_UNLOCK(sc); error = sysctl_handle_int(oidp, &ctstimeout, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_softled(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int softled = sc->sc_softled; int error; error = sysctl_handle_int(oidp, &softled, 0, req); if (error || !req->newptr) return error; softled = (softled != 0); if (softled != sc->sc_softled) { if (softled) { /* NB: handle any sc_ledpin change */ ath_led_config(sc); } sc->sc_softled = softled; } return 0; } static int ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int ledpin = sc->sc_ledpin; int error; error = sysctl_handle_int(oidp, &ledpin, 0, req); if (error || !req->newptr) return error; if (ledpin != sc->sc_ledpin) { sc->sc_ledpin = ledpin; if (sc->sc_softled) { ath_led_config(sc); } } return 0; } static int ath_sysctl_hardled(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int hardled = sc->sc_hardled; int error; error = sysctl_handle_int(oidp, &hardled, 0, req); if (error || !req->newptr) return error; hardled = (hardled != 0); if (hardled != sc->sc_hardled) { if (hardled) { /* NB: handle any sc_ledpin change */ ath_led_config(sc); } sc->sc_hardled = hardled; } return 0; } static int ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int txantenna; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); txantenna = ath_hal_getantennaswitch(sc->sc_ah); error = sysctl_handle_int(oidp, &txantenna, 0, req); if (!error && req->newptr) { /* XXX assumes 2 antenna ports */ if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B) { error = EINVAL; goto finish; } ath_hal_setantennaswitch(sc->sc_ah, txantenna); /* * NB: with the switch locked this isn't meaningful, * but set it anyway so things like radiotap get * consistent info in their data. */ sc->sc_txantenna = txantenna; } finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int defantenna; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); defantenna = ath_hal_getdefantenna(sc->sc_ah); ATH_UNLOCK(sc); error = sysctl_handle_int(oidp, &defantenna, 0, req); if (!error && req->newptr) ath_hal_setdefantenna(sc->sc_ah, defantenna); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_diversity(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int diversity; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); diversity = ath_hal_getdiversity(sc->sc_ah); error = sysctl_handle_int(oidp, &diversity, 0, req); if (error || !req->newptr) goto finish; if (!ath_hal_setdiversity(sc->sc_ah, diversity)) { error = EINVAL; goto finish; } sc->sc_diversity = diversity; error = 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_diag(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int32_t diag; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); if (!ath_hal_getdiag(sc->sc_ah, &diag)) { error = EINVAL; goto finish; } error = sysctl_handle_int(oidp, &diag, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int32_t scale; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); (void) ath_hal_gettpscale(sc->sc_ah, &scale); error = sysctl_handle_int(oidp, &scale, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : - (sc->sc_running) ? ath_reset(sc, ATH_RESET_NOLOSS) : 0; + (sc->sc_running) ? ath_reset(sc, ATH_RESET_NOLOSS, + HAL_RESET_NORMAL) : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_tpc(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int tpc; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); tpc = ath_hal_gettpc(sc->sc_ah); error = sysctl_handle_int(oidp, &tpc, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; struct ath_hal *ah = sc->sc_ah; u_int rfkill; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); rfkill = ath_hal_getrfkill(ah); error = sysctl_handle_int(oidp, &rfkill, 0, req); if (error || !req->newptr) goto finish; if (rfkill == ath_hal_getrfkill(ah)) { /* unchanged */ error = 0; goto finish; } if (!ath_hal_setrfkill(ah, rfkill)) { error = EINVAL; goto finish; } - error = sc->sc_running ? ath_reset(sc, ATH_RESET_FULL) : 0; + error = sc->sc_running ? ath_reset(sc, ATH_RESET_FULL, + HAL_RESET_NORMAL) : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_txagg(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int i, t, param = 0; int error; struct ath_buf *bf; error = sysctl_handle_int(oidp, ¶m, 0, req); if (error || !req->newptr) return error; if (param != 1) return 0; printf("no tx bufs (empty list): %d\n", sc->sc_stats.ast_tx_getnobuf); printf("no tx bufs (was busy): %d\n", sc->sc_stats.ast_tx_getbusybuf); printf("aggr single packet: %d\n", sc->sc_aggr_stats.aggr_single_pkt); printf("aggr single packet w/ BAW closed: %d\n", sc->sc_aggr_stats.aggr_baw_closed_single_pkt); printf("aggr non-baw packet: %d\n", sc->sc_aggr_stats.aggr_nonbaw_pkt); printf("aggr aggregate packet: %d\n", sc->sc_aggr_stats.aggr_aggr_pkt); printf("aggr single packet low hwq: %d\n", sc->sc_aggr_stats.aggr_low_hwq_single_pkt); printf("aggr single packet RTS aggr limited: %d\n", sc->sc_aggr_stats.aggr_rts_aggr_limited); printf("aggr sched, no work: %d\n", sc->sc_aggr_stats.aggr_sched_nopkt); for (i = 0; i < 64; i++) { printf("%2d: %10d ", i, sc->sc_aggr_stats.aggr_pkts[i]); if (i % 4 == 3) printf("\n"); } printf("\n"); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { printf("HW TXQ %d: axq_depth=%d, axq_aggr_depth=%d, " "axq_fifo_depth=%d, holdingbf=%p\n", i, sc->sc_txq[i].axq_depth, sc->sc_txq[i].axq_aggr_depth, sc->sc_txq[i].axq_fifo_depth, sc->sc_txq[i].axq_holdingbf); } } i = t = 0; ATH_TXBUF_LOCK(sc); TAILQ_FOREACH(bf, &sc->sc_txbuf, bf_list) { if (bf->bf_flags & ATH_BUF_BUSY) { printf("Busy: %d\n", t); i++; } t++; } ATH_TXBUF_UNLOCK(sc); printf("Total TX buffers: %d; Total TX buffers busy: %d (%d)\n", t, i, sc->sc_txbuf_cnt); i = t = 0; ATH_TXBUF_LOCK(sc); TAILQ_FOREACH(bf, &sc->sc_txbuf_mgmt, bf_list) { if (bf->bf_flags & ATH_BUF_BUSY) { printf("Busy: %d\n", t); i++; } t++; } ATH_TXBUF_UNLOCK(sc); printf("Total mgmt TX buffers: %d; Total mgmt TX buffers busy: %d\n", t, i); ATH_RX_LOCK(sc); for (i = 0; i < 2; i++) { printf("%d: fifolen: %d/%d; head=%d; tail=%d; m_pending=%p, m_holdbf=%p\n", i, sc->sc_rxedma[i].m_fifo_depth, sc->sc_rxedma[i].m_fifolen, sc->sc_rxedma[i].m_fifo_head, sc->sc_rxedma[i].m_fifo_tail, sc->sc_rxedma[i].m_rxpending, sc->sc_rxedma[i].m_holdbf); } i = 0; TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { i++; } printf("Total RX buffers in free list: %d buffers\n", i); ATH_RX_UNLOCK(sc); return 0; } static int ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int rfsilent; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); (void) ath_hal_getrfsilent(sc->sc_ah, &rfsilent); error = sysctl_handle_int(oidp, &rfsilent, 0, req); if (error || !req->newptr) goto finish; if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) { error = EINVAL; goto finish; } /* * Earlier chips (< AR5212) have up to 8 GPIO * pins exposed. * * AR5416 and later chips have many more GPIO * pins (up to 16) so the mask is expanded to * four bits. */ sc->sc_rfsilentpin = rfsilent & 0x3c; sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; error = 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_tpack(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int32_t tpack; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); (void) ath_hal_gettpack(sc->sc_ah, &tpack); error = sysctl_handle_int(oidp, &tpack, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; u_int32_t tpcts; int error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); (void) ath_hal_gettpcts(sc->sc_ah, &tpcts); error = sysctl_handle_int(oidp, &tpcts, 0, req); if (error || !req->newptr) goto finish; error = !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } static int ath_sysctl_intmit(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int intmit, error; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); intmit = ath_hal_getintmit(sc->sc_ah); error = sysctl_handle_int(oidp, &intmit, 0, req); if (error || !req->newptr) goto finish; /* reusing error; 1 here means "good"; 0 means "fail" */ error = ath_hal_setintmit(sc->sc_ah, intmit); if (! error) { error = EINVAL; goto finish; } /* * Reset the hardware here - disabling ANI in the HAL * doesn't reset ANI related registers, so it'll leave * things in an inconsistent state. */ if (sc->sc_running) - ath_reset(sc, ATH_RESET_NOLOSS); + ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_NORMAL); error = 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } #ifdef IEEE80211_SUPPORT_TDMA static int ath_sysctl_setcca(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int setcca, error; setcca = sc->sc_setcca; error = sysctl_handle_int(oidp, &setcca, 0, req); if (error || !req->newptr) return error; sc->sc_setcca = (setcca != 0); return 0; } #endif /* IEEE80211_SUPPORT_TDMA */ static int ath_sysctl_forcebstuck(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int val = 0; int error; error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; if (val == 0) return 0; taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); val = 0; return 0; } static int ath_sysctl_hangcheck(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int val = 0; int error; uint32_t mask = 0xffffffff; uint32_t *sp; uint32_t rsize; struct ath_hal *ah = sc->sc_ah; error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; if (val == 0) return 0; ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); /* Do a hang check */ if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS, &mask, sizeof(mask), (void *) &sp, &rsize)) { error = 0; goto finish; } device_printf(sc->sc_dev, "%s: sp=0x%08x\n", __func__, *sp); val = 0; error = 0; finish: ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); } #ifdef ATH_DEBUG_ALQ static int ath_sysctl_alq_log(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int error, enable; enable = (sc->sc_alq.sc_alq_isactive); error = sysctl_handle_int(oidp, &enable, 0, req); if (error || !req->newptr) return (error); else if (enable) error = if_ath_alq_start(&sc->sc_alq); else error = if_ath_alq_stop(&sc->sc_alq); return (error); } /* * Attach the ALQ debugging if required. */ static void ath_sysctl_alq_attach(struct ath_softc *sc) { struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "alq", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Atheros ALQ logging parameters"); child = SYSCTL_CHILDREN(tree); SYSCTL_ADD_STRING(ctx, child, OID_AUTO, "filename", CTLFLAG_RW, sc->sc_alq.sc_alq_filename, 0, "ALQ filename"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_alq_log, "I", ""); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debugmask", CTLFLAG_RW, &sc->sc_alq.sc_alq_debug, 0, "ALQ debug mask"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "numlost", CTLFLAG_RW, &sc->sc_alq.sc_alq_numlost, 0, "number lost"); } #endif /* ATH_DEBUG_ALQ */ void ath_sysctlattach(struct ath_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct ath_hal *ah = sc->sc_ah; SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "countrycode", CTLFLAG_RD, &sc->sc_eecc, 0, "EEPROM country code"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "regdomain", CTLFLAG_RD, &sc->sc_eerd, 0, "EEPROM regdomain code"); #ifdef ATH_DEBUG SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, "control debugging printfs"); #endif #ifdef ATH_DEBUG_ALQ SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ktrdebug", CTLFLAG_RW, &sc->sc_ktrdebug, "control debugging KTR"); #endif /* ATH_DEBUG_ALQ */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "slottime", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_slottime, "I", "802.11 slot time (us)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "acktimeout", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ctstimeout", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "softled", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_softled, "I", "enable/disable software LED support"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledpin", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_ledpin, "I", "GPIO pin connected to LED"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledon", CTLFLAG_RW, &sc->sc_ledon, 0, "setting to turn LED on"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, "idle time for inactivity LED (ticks)"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "hardled", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_hardled, "I", "enable/disable hardware LED support"); /* XXX Laziness - configure pins, then flip hardled off/on */ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "led_net_pin", CTLFLAG_RW, &sc->sc_led_net_pin, 0, "MAC Network LED pin, or -1 to disable"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "led_pwr_pin", CTLFLAG_RW, &sc->sc_led_pwr_pin, 0, "MAC Power LED pin, or -1 to disable"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "txantenna", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_txantenna, "I", "antenna switch"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "rxantenna", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_rxantenna, "I", "default/rx antenna"); if (ath_hal_hasdiversity(ah)) SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "diversity", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_diversity, "I", "antenna diversity"); sc->sc_txintrperiod = ATH_TXINTR_PERIOD; SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0, "tx descriptor batching"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "diag", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_diag, "I", "h/w diagnostic control"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tpscale", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_tpscale, "I", "tx power scaling"); if (ath_hal_hastpc(ah)) { SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tpc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_tpc, "I", "enable/disable per-packet TPC"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tpack", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_tpack, "I", "tx power for ack frames"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tpcts", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_tpcts, "I", "tx power for cts frames"); } if (ath_hal_hasrfsilent(ah)) { SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "rfsilent", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_rfsilent, "I", "h/w RF silent config"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "rfkill", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_rfkill, "I", "enable/disable RF kill switch"); } SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "txagg", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_txagg, "I", ""); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "forcebstuck", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_forcebstuck, "I", ""); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "hangcheck", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_hangcheck, "I", ""); if (ath_hal_hasintmit(ah)) { SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "intmit", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_intmit, "I", "interference mitigation"); } sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC; SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "monpass", CTLFLAG_RW, &sc->sc_monpass, 0, "mask of error frames to pass when monitoring"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "hwq_limit_nonaggr", CTLFLAG_RW, &sc->sc_hwq_limit_nonaggr, 0, "Hardware non-AMPDU queue depth before software-queuing TX frames"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "hwq_limit_aggr", CTLFLAG_RW, &sc->sc_hwq_limit_aggr, 0, "Hardware AMPDU queue depth before software-queuing TX frames"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tid_hwq_lo", CTLFLAG_RW, &sc->sc_tid_hwq_lo, 0, ""); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tid_hwq_hi", CTLFLAG_RW, &sc->sc_tid_hwq_hi, 0, ""); /* Aggregate length twiddles */ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "aggr_limit", CTLFLAG_RW, &sc->sc_aggr_limit, 0, "Maximum A-MPDU size, or 0 for 'default'"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "rts_aggr_limit", CTLFLAG_RW, &sc->sc_rts_aggr_limit, 0, "Maximum A-MPDU size for RTS-protected frames, or '0' " "for default"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "delim_min_pad", CTLFLAG_RW, &sc->sc_delim_min_pad, 0, "Enforce a minimum number of delimiters per A-MPDU " " sub-frame"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "txq_data_minfree", CTLFLAG_RW, &sc->sc_txq_data_minfree, 0, "Minimum free buffers before adding a data frame" " to the TX queue"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "txq_mcastq_maxdepth", CTLFLAG_RW, &sc->sc_txq_mcastq_maxdepth, 0, "Maximum buffer depth for multicast/broadcast frames"); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "txq_node_maxdepth", CTLFLAG_RW, &sc->sc_txq_node_maxdepth, 0, "Maximum buffer depth for a single node"); #if 0 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "cabq_enable", CTLFLAG_RW, &sc->sc_cabq_enable, 0, "Whether to transmit on the CABQ or not"); #endif #ifdef IEEE80211_SUPPORT_TDMA if (ath_hal_macversion(ah) > 0x78) { sc->sc_tdmadbaprep = 2; SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "dbaprep", CTLFLAG_RW, &sc->sc_tdmadbaprep, 0, "TDMA DBA preparation time"); sc->sc_tdmaswbaprep = 10; SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "swbaprep", CTLFLAG_RW, &sc->sc_tdmaswbaprep, 0, "TDMA SWBA preparation time"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "guardtime", CTLFLAG_RW, &sc->sc_tdmaguard, 0, "TDMA slot guard time"); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "superframe", CTLFLAG_RD, &sc->sc_tdmabintval, 0, "TDMA calculated super frame"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "setcca", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_setcca, "I", "enable CCA control"); } #endif #ifdef ATH_DEBUG_ALQ ath_sysctl_alq_attach(sc); #endif } static int ath_sysctl_clearstats(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; int val = 0; int error; error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; if (val == 0) return 0; /* Not clearing the stats is still valid */ memset(&sc->sc_stats, 0, sizeof(sc->sc_stats)); memset(&sc->sc_aggr_stats, 0, sizeof(sc->sc_aggr_stats)); memset(&sc->sc_intr_stats, 0, sizeof(sc->sc_intr_stats)); val = 0; return 0; } static void ath_sysctl_stats_attach_rxphyerr(struct ath_softc *sc, struct sysctl_oid_list *parent) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); int i; char sn[8]; tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx_phy_err", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Per-code RX PHY Errors"); child = SYSCTL_CHILDREN(tree); for (i = 0; i < 64; i++) { snprintf(sn, sizeof(sn), "%d", i); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD, &sc->sc_stats.ast_rx_phy[i], 0, ""); } } static void ath_sysctl_stats_attach_intr(struct ath_softc *sc, struct sysctl_oid_list *parent) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); int i; char sn[8]; tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "sync_intr", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Sync interrupt statistics"); child = SYSCTL_CHILDREN(tree); for (i = 0; i < 32; i++) { snprintf(sn, sizeof(sn), "%d", i); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD, &sc->sc_intr_stats.sync_intr[i], 0, ""); } } void ath_sysctl_stats_attach(struct ath_softc *sc) { struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); /* Create "clear" node */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "clear_stats", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ath_sysctl_clearstats, "I", "clear stats"); /* Create stats node */ tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Statistics"); child = SYSCTL_CHILDREN(tree); /* This was generated from if_athioctl.h */ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_watchdog", CTLFLAG_RD, &sc->sc_stats.ast_watchdog, 0, "device reset by watchdog"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_hardware", CTLFLAG_RD, &sc->sc_stats.ast_hardware, 0, "fatal hardware error interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bmiss", CTLFLAG_RD, &sc->sc_stats.ast_bmiss, 0, "beacon miss interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bmiss_phantom", CTLFLAG_RD, &sc->sc_stats.ast_bmiss_phantom, 0, "beacon miss interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_bstuck", CTLFLAG_RD, &sc->sc_stats.ast_bstuck, 0, "beacon stuck interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rxorn", CTLFLAG_RD, &sc->sc_stats.ast_rxorn, 0, "rx overrun interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rxeol", CTLFLAG_RD, &sc->sc_stats.ast_rxeol, 0, "rx eol interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_txurn", CTLFLAG_RD, &sc->sc_stats.ast_txurn, 0, "tx underrun interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_mib", CTLFLAG_RD, &sc->sc_stats.ast_mib, 0, "mib interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_intrcoal", CTLFLAG_RD, &sc->sc_stats.ast_intrcoal, 0, "interrupts coalesced"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_packets", CTLFLAG_RD, &sc->sc_stats.ast_tx_packets, 0, "packet sent on the interface"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mgmt", CTLFLAG_RD, &sc->sc_stats.ast_tx_mgmt, 0, "management frames transmitted"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_discard", CTLFLAG_RD, &sc->sc_stats.ast_tx_discard, 0, "frames discarded prior to assoc"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_qstop", CTLFLAG_RD, &sc->sc_stats.ast_tx_qstop, 0, "output stopped 'cuz no buffer"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_encap", CTLFLAG_RD, &sc->sc_stats.ast_tx_encap, 0, "tx encapsulation failed"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nonode", CTLFLAG_RD, &sc->sc_stats.ast_tx_nonode, 0, "tx failed 'cuz no node"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nombuf", CTLFLAG_RD, &sc->sc_stats.ast_tx_nombuf, 0, "tx failed 'cuz no mbuf"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nomcl", CTLFLAG_RD, &sc->sc_stats.ast_tx_nomcl, 0, "tx failed 'cuz no cluster"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_linear", CTLFLAG_RD, &sc->sc_stats.ast_tx_linear, 0, "tx linearized to cluster"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nodata", CTLFLAG_RD, &sc->sc_stats.ast_tx_nodata, 0, "tx discarded empty frame"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_busdma", CTLFLAG_RD, &sc->sc_stats.ast_tx_busdma, 0, "tx failed for dma resrcs"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_xretries", CTLFLAG_RD, &sc->sc_stats.ast_tx_xretries, 0, "tx failed 'cuz too many retries"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_fifoerr", CTLFLAG_RD, &sc->sc_stats.ast_tx_fifoerr, 0, "tx failed 'cuz FIFO underrun"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_filtered", CTLFLAG_RD, &sc->sc_stats.ast_tx_filtered, 0, "tx failed 'cuz xmit filtered"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_shortretry", CTLFLAG_RD, &sc->sc_stats.ast_tx_shortretry, 0, "tx on-chip retries (short)"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_longretry", CTLFLAG_RD, &sc->sc_stats.ast_tx_longretry, 0, "tx on-chip retries (long)"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_badrate", CTLFLAG_RD, &sc->sc_stats.ast_tx_badrate, 0, "tx failed 'cuz bogus xmit rate"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_noack", CTLFLAG_RD, &sc->sc_stats.ast_tx_noack, 0, "tx frames with no ack marked"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_rts", CTLFLAG_RD, &sc->sc_stats.ast_tx_rts, 0, "tx frames with rts enabled"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_cts", CTLFLAG_RD, &sc->sc_stats.ast_tx_cts, 0, "tx frames with cts enabled"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_shortpre", CTLFLAG_RD, &sc->sc_stats.ast_tx_shortpre, 0, "tx frames with short preamble"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_altrate", CTLFLAG_RD, &sc->sc_stats.ast_tx_altrate, 0, "tx frames with alternate rate"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_protect", CTLFLAG_RD, &sc->sc_stats.ast_tx_protect, 0, "tx frames with protection"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ctsburst", CTLFLAG_RD, &sc->sc_stats.ast_tx_ctsburst, 0, "tx frames with cts and bursting"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ctsext", CTLFLAG_RD, &sc->sc_stats.ast_tx_ctsext, 0, "tx frames with cts extension"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_nombuf", CTLFLAG_RD, &sc->sc_stats.ast_rx_nombuf, 0, "rx setup failed 'cuz no mbuf"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_busdma", CTLFLAG_RD, &sc->sc_stats.ast_rx_busdma, 0, "rx setup failed for dma resrcs"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_orn", CTLFLAG_RD, &sc->sc_stats.ast_rx_orn, 0, "rx failed 'cuz of desc overrun"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_crcerr", CTLFLAG_RD, &sc->sc_stats.ast_rx_crcerr, 0, "rx failed 'cuz of bad CRC"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_fifoerr", CTLFLAG_RD, &sc->sc_stats.ast_rx_fifoerr, 0, "rx failed 'cuz of FIFO overrun"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_badcrypt", CTLFLAG_RD, &sc->sc_stats.ast_rx_badcrypt, 0, "rx failed 'cuz decryption"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_badmic", CTLFLAG_RD, &sc->sc_stats.ast_rx_badmic, 0, "rx failed 'cuz MIC failure"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_phyerr", CTLFLAG_RD, &sc->sc_stats.ast_rx_phyerr, 0, "rx failed 'cuz of PHY err"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_tooshort", CTLFLAG_RD, &sc->sc_stats.ast_rx_tooshort, 0, "rx discarded 'cuz frame too short"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_toobig", CTLFLAG_RD, &sc->sc_stats.ast_rx_toobig, 0, "rx discarded 'cuz frame too large"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_packets", CTLFLAG_RD, &sc->sc_stats.ast_rx_packets, 0, "packet recv on the interface"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_mgt", CTLFLAG_RD, &sc->sc_stats.ast_rx_mgt, 0, "management frames received"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_ctl", CTLFLAG_RD, &sc->sc_stats.ast_rx_ctl, 0, "rx discarded 'cuz ctl frame"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_xmit", CTLFLAG_RD, &sc->sc_stats.ast_be_xmit, 0, "beacons transmitted"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_nombuf", CTLFLAG_RD, &sc->sc_stats.ast_be_nombuf, 0, "beacon setup failed 'cuz no mbuf"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_cal", CTLFLAG_RD, &sc->sc_stats.ast_per_cal, 0, "periodic calibration calls"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_calfail", CTLFLAG_RD, &sc->sc_stats.ast_per_calfail, 0, "periodic calibration failed"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_per_rfgain", CTLFLAG_RD, &sc->sc_stats.ast_per_rfgain, 0, "periodic calibration rfgain reset"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_calls", CTLFLAG_RD, &sc->sc_stats.ast_rate_calls, 0, "rate control checks"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_raise", CTLFLAG_RD, &sc->sc_stats.ast_rate_raise, 0, "rate control raised xmit rate"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rate_drop", CTLFLAG_RD, &sc->sc_stats.ast_rate_drop, 0, "rate control dropped xmit rate"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ant_defswitch", CTLFLAG_RD, &sc->sc_stats.ast_ant_defswitch, 0, "rx/default antenna switches"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ant_txswitch", CTLFLAG_RD, &sc->sc_stats.ast_ant_txswitch, 0, "tx antenna switches"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_cabq_xmit", CTLFLAG_RD, &sc->sc_stats.ast_cabq_xmit, 0, "cabq frames transmitted"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_cabq_busy", CTLFLAG_RD, &sc->sc_stats.ast_cabq_busy, 0, "cabq found busy"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_raw", CTLFLAG_RD, &sc->sc_stats.ast_tx_raw, 0, "tx frames through raw api"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_txok", CTLFLAG_RD, &sc->sc_stats.ast_ff_txok, 0, "fast frames tx'd successfully"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_txerr", CTLFLAG_RD, &sc->sc_stats.ast_ff_txerr, 0, "fast frames tx'd w/ error"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_rx", CTLFLAG_RD, &sc->sc_stats.ast_ff_rx, 0, "fast frames rx'd"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ff_flush", CTLFLAG_RD, &sc->sc_stats.ast_ff_flush, 0, "fast frames flushed from staging q"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_qfull", CTLFLAG_RD, &sc->sc_stats.ast_tx_qfull, 0, "tx dropped 'cuz of queue limit"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nobuf", CTLFLAG_RD, &sc->sc_stats.ast_tx_nobuf, 0, "tx dropped 'cuz no ath buffer"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_update", CTLFLAG_RD, &sc->sc_stats.ast_tdma_update, 0, "TDMA slot timing updates"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_timers", CTLFLAG_RD, &sc->sc_stats.ast_tdma_timers, 0, "TDMA slot update set beacon timers"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_tsf", CTLFLAG_RD, &sc->sc_stats.ast_tdma_tsf, 0, "TDMA slot update set TSF"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tdma_ack", CTLFLAG_RD, &sc->sc_stats.ast_tdma_ack, 0, "TDMA tx failed 'cuz ACK required"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_raw_fail", CTLFLAG_RD, &sc->sc_stats.ast_tx_raw_fail, 0, "raw tx failed 'cuz h/w down"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nofrag", CTLFLAG_RD, &sc->sc_stats.ast_tx_nofrag, 0, "tx dropped 'cuz no ath frag buffer"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_be_missed", CTLFLAG_RD, &sc->sc_stats.ast_be_missed, 0, "number of -missed- beacons"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_ani_cal", CTLFLAG_RD, &sc->sc_stats.ast_ani_cal, 0, "number of ANI polls"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_agg", CTLFLAG_RD, &sc->sc_stats.ast_rx_agg, 0, "number of aggregate frames received"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_halfgi", CTLFLAG_RD, &sc->sc_stats.ast_rx_halfgi, 0, "number of frames received with half-GI"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_2040", CTLFLAG_RD, &sc->sc_stats.ast_rx_2040, 0, "number of HT/40 frames received"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_pre_crc_err", CTLFLAG_RD, &sc->sc_stats.ast_rx_pre_crc_err, 0, "number of delimeter-CRC errors detected"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_post_crc_err", CTLFLAG_RD, &sc->sc_stats.ast_rx_post_crc_err, 0, "number of post-delimiter CRC errors detected"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_decrypt_busy_err", CTLFLAG_RD, &sc->sc_stats.ast_rx_decrypt_busy_err, 0, "number of frames received w/ busy decrypt engine"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_hi_rx_chain", CTLFLAG_RD, &sc->sc_stats.ast_rx_hi_rx_chain, 0, ""); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_htprotect", CTLFLAG_RD, &sc->sc_stats.ast_tx_htprotect, 0, "HT tx frames with protection"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_hitqueueend", CTLFLAG_RD, &sc->sc_stats.ast_rx_hitqueueend, 0, "RX hit queue end"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_timeout", CTLFLAG_RD, &sc->sc_stats.ast_tx_timeout, 0, "TX Global Timeout"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_cst", CTLFLAG_RD, &sc->sc_stats.ast_tx_cst, 0, "TX Carrier Sense Timeout"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_xtxop", CTLFLAG_RD, &sc->sc_stats.ast_tx_xtxop, 0, "TX exceeded TXOP"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_timerexpired", CTLFLAG_RD, &sc->sc_stats.ast_tx_timerexpired, 0, "TX exceeded TX_TIMER register"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_desccfgerr", CTLFLAG_RD, &sc->sc_stats.ast_tx_desccfgerr, 0, "TX Descriptor Cfg Error"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swretries", CTLFLAG_RD, &sc->sc_stats.ast_tx_swretries, 0, "TX software retry count"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swretrymax", CTLFLAG_RD, &sc->sc_stats.ast_tx_swretrymax, 0, "TX software retry max reached"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_data_underrun", CTLFLAG_RD, &sc->sc_stats.ast_tx_data_underrun, 0, ""); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_delim_underrun", CTLFLAG_RD, &sc->sc_stats.ast_tx_delim_underrun, 0, ""); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_failall", CTLFLAG_RD, &sc->sc_stats.ast_tx_aggr_failall, 0, "Number of aggregate TX failures (whole frame)"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_ok", CTLFLAG_RD, &sc->sc_stats.ast_tx_aggr_ok, 0, "Number of aggregate TX OK completions (subframe)"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_fail", CTLFLAG_RD, &sc->sc_stats.ast_tx_aggr_fail, 0, "Number of aggregate TX failures (subframe)"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_intr", CTLFLAG_RD, &sc->sc_stats.ast_rx_intr, 0, "RX interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_intr", CTLFLAG_RD, &sc->sc_stats.ast_tx_intr, 0, "TX interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mcastq_overflow", CTLFLAG_RD, &sc->sc_stats.ast_tx_mcastq_overflow, 0, "Number of multicast frames exceeding maximum mcast queue depth"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_keymiss", CTLFLAG_RD, &sc->sc_stats.ast_rx_keymiss, 0, ""); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swfiltered", CTLFLAG_RD, &sc->sc_stats.ast_tx_swfiltered, 0, ""); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_nodeq_overflow", CTLFLAG_RD, &sc->sc_stats.ast_tx_nodeq_overflow, 0, "tx dropped 'cuz nodeq overflow"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_stbc", CTLFLAG_RD, &sc->sc_stats.ast_rx_stbc, 0, "Number of STBC frames received"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_stbc", CTLFLAG_RD, &sc->sc_stats.ast_tx_stbc, 0, "Number of STBC frames transmitted"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_ldpc", CTLFLAG_RD, &sc->sc_stats.ast_tx_ldpc, 0, "Number of LDPC frames transmitted"); /* Attach the RX phy error array */ ath_sysctl_stats_attach_rxphyerr(sc, child); /* Attach the interrupt statistics array */ ath_sysctl_stats_attach_intr(sc, child); } /* * This doesn't necessarily belong here (because it's HAL related, not * driver related). */ void ath_sysctl_hal_attach(struct ath_softc *sc) { struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "hal", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Atheros HAL parameters"); child = SYSCTL_CHILDREN(tree); sc->sc_ah->ah_config.ah_debug = 0; SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_debug, 0, "Atheros HAL debugging printfs"); sc->sc_ah->ah_config.ah_ar5416_biasadj = 0; SYSCTL_ADD_INT(ctx, child, OID_AUTO, "ar5416_biasadj", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_ar5416_biasadj, 0, "Enable 2GHz AR5416 direction sensitivity bias adjust"); sc->sc_ah->ah_config.ah_dma_beacon_response_time = 2; SYSCTL_ADD_INT(ctx, child, OID_AUTO, "dma_brt", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_dma_beacon_response_time, 0, "Atheros HAL DMA beacon response time"); sc->sc_ah->ah_config.ah_sw_beacon_response_time = 10; SYSCTL_ADD_INT(ctx, child, OID_AUTO, "sw_brt", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_sw_beacon_response_time, 0, "Atheros HAL software beacon response time"); sc->sc_ah->ah_config.ah_additional_swba_backoff = 0; SYSCTL_ADD_INT(ctx, child, OID_AUTO, "swba_backoff", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_additional_swba_backoff, 0, "Atheros HAL additional SWBA backoff time"); sc->sc_ah->ah_config.ah_force_full_reset = 0; SYSCTL_ADD_INT(ctx, child, OID_AUTO, "force_full_reset", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_force_full_reset, 0, "Force full chip reset rather than a warm reset"); /* * This is initialised by the driver. */ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "serialise_reg_war", CTLFLAG_RW, &sc->sc_ah->ah_config.ah_serialise_reg_war, 0, "Force register access serialisation"); }