diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2004-12-31 01:00:24 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2004-12-31 01:00:24 +0000 |
commit | fffb2b6a064dd9b85c1f3dbb35e0c073a9c8547f (patch) | |
tree | 704da8bd9d855a3dc8a01e4e557c2fe5e8e8805e /sys/dev/ic/ar5xxx.c | |
parent | cc318892241217ab5fefef4687486649c95a1f86 (diff) |
sync with the latest work and add some stuff needed by the upcoming ar5211/ar5212
support. some further cleanups and changes will follow.
Diffstat (limited to 'sys/dev/ic/ar5xxx.c')
-rw-r--r-- | sys/dev/ic/ar5xxx.c | 986 |
1 files changed, 936 insertions, 50 deletions
diff --git a/sys/dev/ic/ar5xxx.c b/sys/dev/ic/ar5xxx.c index ec0b6777fa9..8b8ad0b8f53 100644 --- a/sys/dev/ic/ar5xxx.c +++ b/sys/dev/ic/ar5xxx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ar5xxx.c,v 1.5 2004/11/11 20:11:28 reyk Exp $ */ +/* $OpenBSD: ar5xxx.c,v 1.6 2004/12/31 01:00:23 reyk Exp $ */ /* * Copyright (c) 2004 Reyk Floeter <reyk@vantronix.net>. @@ -27,7 +27,7 @@ /* * HAL interface for Atheros Wireless LAN devices. - * (Please have a look at ar5k.h for further information) + * (Please have a look at ar5xxx.h for further information) */ #include <dev/pci/pcidevs.h> @@ -35,7 +35,6 @@ #include <dev/ic/ar5xxx.h> extern ar5k_attach_t ar5k_ar5210_attach; - #ifdef notyet extern ar5k_attach_t ar5k_ar5211_attach; extern ar5k_attach_t ar5k_ar5212_attach; @@ -61,6 +60,7 @@ static const struct { ar5k_ar5210_attach }, { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5210_DEFAULT, ar5k_ar5210_attach }, + #ifdef notyet { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5211, ar5k_ar5211_attach }, @@ -85,8 +85,32 @@ static const struct { { PCI_VENDOR_3COM2, PCI_PRODUCT_3COM2_3CRPAG175, ar5k_ar5212_attach }, #endif + }; +int ar5k_eeprom_read_ants(struct ath_hal *, u_int32_t *, u_int); +int ar5k_eeprom_read_modes(struct ath_hal *, u_int32_t *, u_int); +u_int16_t ar5k_eeprom_bin2freq(struct ath_hal *, u_int16_t, u_int); + +HAL_BOOL ar5k_ar5110_channel(struct ath_hal *, HAL_CHANNEL *); +HAL_BOOL ar5k_ar5110_chan2athchan(HAL_CHANNEL *); +HAL_BOOL ar5k_ar5111_channel(struct ath_hal *, HAL_CHANNEL *); +HAL_BOOL ar5k_ar5111_chan2athchan(u_int, struct ar5k_athchan_2ghz *); +HAL_BOOL ar5k_ar5112_channel(struct ath_hal *, HAL_CHANNEL *); + +HAL_BOOL ar5k_ar5111_rfregs(struct ath_hal *, HAL_CHANNEL *, u_int); +HAL_BOOL ar5k_ar5112_rfregs(struct ath_hal *, HAL_CHANNEL *, u_int); +int ar5k_rfregs_set(u_int32_t *, u_int32_t, u_int32_t, u_int32_t, + u_int32_t, u_int32_t); + +/* + * Initial register for the radio chipsets + */ +static const struct ar5k_ini_rf ar5111_rf[] = + AR5K_AR5111_INI_RF; +static const struct ar5k_ini_rf ar5112_rf[] = + AR5K_AR5112_INI_RF; + /* * Perform a lookup if the device is supported by the HAL */ @@ -120,10 +144,12 @@ ath_hal_attach(device, sc, st, sh, status) bus_space_handle_t sh; int *status; { + ieee80211_regdomain_t ieee_regdomain; HAL_RATE_TABLE rt_11a = AR5K_RATES_11A; HAL_RATE_TABLE rt_11b = AR5K_RATES_11B; HAL_RATE_TABLE rt_11g = AR5K_RATES_11G; HAL_RATE_TABLE rt_turbo = AR5K_RATES_TURBO; + u_int16_t regdomain; struct ath_hal *hal = NULL; ar5k_attach_t *attach = NULL; u_int8_t mac[IEEE80211_ADDR_LEN]; @@ -166,16 +192,19 @@ ath_hal_attach(device, sc, st, sh, status) */ hal->ah_abi = HAL_ABI_VERSION; hal->ah_country_code = CTRY_DEFAULT; + hal->ah_capabilities.cap_regdomain.reg_current = AR5K_TUNE_REGDOMAIN; hal->ah_op_mode = HAL_M_STA; hal->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT; - hal->ah_capabilities.cap_eeprom.ee_regdomain = DMN_DEFAULT; hal->ah_turbo = AH_FALSE; + hal->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; + hal->ah_txpower.txp_max = AR5K_TUNE_MAX_TXPOWER; hal->ah_imr = 0; hal->ah_atim_window = 0; hal->ah_aifs = AR5K_TUNE_AIFS; hal->ah_cw_min = AR5K_TUNE_CWMIN; hal->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; hal->ah_software_retry = AH_FALSE; + hal->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY; if (attach(device, hal, st, sh, status) == NULL) goto failed; @@ -184,12 +213,36 @@ ath_hal_attach(device, sc, st, sh, status) * Get card capabilities, values, ... */ + if (ar5k_eeprom_init(hal) != 0) { + AR5K_PRINT("unable to init EEPROM\n"); + goto failed; + } + + /* Set regulation domain */ + if ((regdomain = + (u_int16_t)hal->ah_capabilities.cap_eeprom.ee_regdomain) != 0) { + ieee_regdomain = *ar5k_regdomain_to_ieee(regdomain); + memcpy(&hal->ah_capabilities.cap_regdomain.reg_current, + &ieee_regdomain, sizeof(ieee80211_regdomain_t)); + } else { + ieee_regdomain = + hal->ah_capabilities.cap_regdomain.reg_current; + + /* Try to write default regulation domain to EEPROM */ + ar5k_eeprom_regulation_domain(hal, AH_TRUE, &ieee_regdomain); + } + + memcpy(&hal->ah_capabilities.cap_regdomain.reg_hw, + &ieee_regdomain, sizeof(ieee80211_regdomain_t)); + + /* Get misc capabilities */ if (hal->ah_get_capabilities(hal) != AH_TRUE) { AR5K_PRINTF("unable to get device capabilities: 0x%04x\n", device); goto failed; } + /* Get MAC address */ if ((*status = ar5k_eeprom_read_mac(hal, mac)) != HAL_OK) { AR5K_PRINTF("unable to read address from EEPROM: 0x%04x\n", device); @@ -198,6 +251,7 @@ ath_hal_attach(device, sc, st, sh, status) hal->ah_setMacAddress(hal, mac); + /* Get rate tables */ if (hal->ah_capabilities.cap_mode & HAL_MODE_11A) ar5k_rt_copy(&hal->ah_rt_11a, &rt_11a); if (hal->ah_capabilities.cap_mode & HAL_MODE_11B) @@ -419,60 +473,19 @@ ar5k_radar_alert(hal) hal->ah_radar.r_last_alert, hal->ah_current_channel.channel); } -int -ar5k_eeprom_read_mac(hal, mac) - struct ath_hal *hal; - u_int8_t *mac; -{ - u_int32_t total, offset; - u_int16_t data; - int octet; - u_int8_t mac_d[IEEE80211_ADDR_LEN]; - - bzero(mac, IEEE80211_ADDR_LEN); - bzero(&mac_d, IEEE80211_ADDR_LEN); - - if (hal->ah_eeprom_is_busy(hal)) - return (EBUSY); - - /* - * XXX Does this work with newer EEPROMs? - */ - if (hal->ah_eeprom_read(hal, 0x20, &data) != 0) - return (EIO); - - for (offset = 0x1f, octet = 0, total = 0; - offset >= 0x1d; offset--) { - if (hal->ah_eeprom_read(hal, offset, &data) != 0) - return (EIO); - - total += data; - mac_d[octet + 1] = data & 0xff; - mac_d[octet] = data >> 8; - octet += 2; - } - - memcpy(mac, &mac_d, IEEE80211_ADDR_LEN); - - if ((!total) || total == (3 * 0xffff)) - return (EINVAL); - - return (0); -} - -u_int8_t +u_int16_t ar5k_regdomain_from_ieee(regdomain) ieee80211_regdomain_t *regdomain; { /* * XXX Fix */ - return ((u_int8_t)*regdomain); + return ((u_int16_t)*regdomain); } ieee80211_regdomain_t * ar5k_regdomain_to_ieee(regdomain) - u_int8_t regdomain; + u_int16_t regdomain; { /* * XXX Fix @@ -530,11 +543,13 @@ ar5k_register_timeout(hal, reg, flag, val, is_set) HAL_BOOL is_set; { int i; + u_int32_t data; for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) { - if ((is_set == AH_TRUE) && (AR5K_REG_READ(reg) & flag)) + data = AR5K_REG_READ(reg); + if ((is_set == AH_TRUE) && (data & flag)) break; - else if ((AR5K_REG_READ(reg) & flag) == val) + else if ((data & flag) == val) break; AR5K_DELAY(15); } @@ -544,3 +559,874 @@ ar5k_register_timeout(hal, reg, flag, val, is_set) return (AH_TRUE); } + +/* + * Common ar5xx EEPROM access functions + */ + +u_int16_t +ar5k_eeprom_bin2freq(hal, bin, mode) + struct ath_hal *hal; + u_int16_t bin; + u_int mode; +{ + u_int16_t val; + + if (bin == AR5K_EEPROM_CHANNEL_DIS) + return (bin); + + if (mode == AR5K_EEPROM_MODE_11A) { + if (hal->ah_ee_version > AR5K_EEPROM_VERSION_3_2) + val = (5 * bin) + 4800; + else + val = bin > 62 ? + (10 * 62) + (5 * (bin - 62)) + 5100 : + (bin * 10) + 5100; + } else { + if (hal->ah_ee_version > AR5K_EEPROM_VERSION_3_2) + val = bin + 2300; + else + val = bin + 2400; + } + + return (val); +} + +#define EEPROM_READ_VAL(_o, _v) { \ + if ((ret = hal->ah_eeprom_read(hal, (_o), \ + &(_v))) != 0) \ + return (ret); \ +} + +#define EEPROM_READ_HDR(_o, _v) { \ + if ((ret = hal->ah_eeprom_read(hal, (_o), \ + &hal->ah_capabilities.cap_eeprom.##_v)) != 0) \ + return (ret); \ +} + +int +ar5k_eeprom_read_ants(hal, offset, mode) + struct ath_hal *hal; + u_int32_t *offset; + u_int mode; +{ + struct ar5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u_int32_t o = *offset; + u_int16_t val; + int ret, i = 0; + + EEPROM_READ_VAL(o++, val); + ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; + ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; + + EEPROM_READ_VAL(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; + ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; + ee->ee_ant_control[mode][i++] = val & 0x3f; + + EEPROM_READ_VAL(o++, val); + ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f; + ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 2) & 0x3f; + + EEPROM_READ_VAL(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3; + ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f; + ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; + + EEPROM_READ_VAL(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; + ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; + ee->ee_ant_control[mode][i++] = val & 0x3f; + + /* Get antenna modes */ + hal->ah_antenna[mode][0] = + (ee->ee_ant_control[mode][0] << 4) | 0x1; + hal->ah_antenna[mode][HAL_ANT_FIXED_A] = + ee->ee_ant_control[mode][1] | + (ee->ee_ant_control[mode][2] << 6) | + (ee->ee_ant_control[mode][3] << 12) | + (ee->ee_ant_control[mode][4] << 18) | + (ee->ee_ant_control[mode][5] << 24); + hal->ah_antenna[mode][HAL_ANT_FIXED_B] = + ee->ee_ant_control[mode][6] | + (ee->ee_ant_control[mode][7] << 6) | + (ee->ee_ant_control[mode][8] << 12) | + (ee->ee_ant_control[mode][9] << 18) | + (ee->ee_ant_control[mode][10] << 24); + + /* return new offset */ + *offset = o; + + return (0); +} + +int +ar5k_eeprom_read_modes(hal, offset, mode) + struct ath_hal *hal; + u_int32_t *offset; + u_int mode; +{ + struct ar5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u_int32_t o = *offset; + u_int16_t val; + int ret; + + EEPROM_READ_VAL(o++, val); + ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; + ee->ee_thr_62[mode] = val & 0xff; + + if (hal->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) + ee->ee_thr_62[mode] = + mode == AR5K_EEPROM_MODE_11A ? 15 : 28; + + EEPROM_READ_VAL(o++, val); + ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff; + ee->ee_tx_frm2xpa_enable[mode] = val & 0xff; + + EEPROM_READ_VAL(o++, val); + ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff; + + if ((val & 0xff) & 0x80) + ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); + else + ee->ee_noise_floor_thr[mode] = val & 0xff; + + if (hal->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) + ee->ee_noise_floor_thr[mode] = + mode == AR5K_EEPROM_MODE_11A ? -54 : -1; + + EEPROM_READ_VAL(o++, val); + ee->ee_xlna_gain[mode] = (val >> 5) & 0xff; + ee->ee_x_gain[mode] = (val >> 1) & 0xf; + ee->ee_xpd[mode] = val & 0x1; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) + ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { + EEPROM_READ_VAL(o++, val); + ee->ee_false_detect[mode] = (val >> 6) & 0x7f; + + if (mode == AR5K_EEPROM_MODE_11A) + ee->ee_xr_power[mode] = val & 0x3f; + else { + ee->ee_ob[mode][0] = val & 0x7; + ee->ee_db[mode][0] = (val >> 3) & 0x7; + } + } + + if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { + ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; + ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; + } else { + ee->ee_i_gain[mode] = (val >> 13) & 0x7; + + EEPROM_READ_VAL(o++, val); + ee->ee_i_gain[mode] |= (val << 3) & 0x38; + + if (mode == AR5K_EEPROM_MODE_11G) + ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; + } + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && + mode == AR5K_EEPROM_MODE_11A) { + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + } + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 && + mode == AR5K_EEPROM_MODE_11G) + ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; + + /* return new offset */ + *offset = o; + + return (0); +} + +int +ar5k_eeprom_init(hal) + struct ath_hal *hal; +{ + struct ar5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u_int32_t offset; + u_int16_t val; + int ret, i; + u_int mode; + + /* Check if EEPROM is busy */ + if (hal->ah_eeprom_is_busy(hal) == AH_TRUE) + return (EBUSY); + + /* Initial TX thermal adjustment values */ + ee->ee_tx_clip = 4; + ee->ee_pwd_84 = ee->ee_pwd_90 = 1; + ee->ee_gain_select = 1; + + /* + * Read values from EEPROM and store them in the capability structure + */ + EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); + EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); + EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); + EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); + EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); + + /* Return if we have an old EEPROM */ + if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_0) + return (0); + + EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(hal->ah_ee_version), ee_ant_gain); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); + EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); + } + + if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { + EEPROM_READ_VAL(AR5K_EEPROM_OBDB0_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; + + EEPROM_READ_VAL(AR5K_EEPROM_OBDB1_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; + } + + /* + * Get conformance test limit values + */ + offset = AR5K_EEPROM_CTL(hal->ah_ee_version); + ee->ee_ctls = AR5K_EEPROM_N_CTLS(hal->ah_ee_version); + + for (i = 0; i < ee->ee_ctls; i++) { + EEPROM_READ_VAL(offset++, val); + ee->ee_ctl[i] = (val >> 8) & 0xff; + ee->ee_ctl[i + 1] = val & 0xff; + } + + /* + * Get values for 802.11a (5GHz) + */ + mode = AR5K_EEPROM_MODE_11A; + + ee->ee_turbo_max_power[mode] = + AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); + + offset = AR5K_EEPROM_MODES_11A(hal->ah_ee_version); + + if ((ret = ar5k_eeprom_read_ants(hal, &offset, mode)) != 0) + return (ret); + + EEPROM_READ_VAL(offset++, val); + ee->ee_adc_desired_size[mode] = (int8_t)((val >> 8) & 0xff); + ee->ee_ob[mode][3] = (val >> 5) & 0x7; + ee->ee_db[mode][3] = (val >> 2) & 0x7; + ee->ee_ob[mode][2] = (val << 1) & 0x7; + + EEPROM_READ_VAL(offset++, val); + ee->ee_ob[mode][2] |= (val >> 15) & 0x1; + ee->ee_db[mode][2] = (val >> 12) & 0x7; + ee->ee_ob[mode][1] = (val >> 9) & 0x7; + ee->ee_db[mode][1] = (val >> 6) & 0x7; + ee->ee_ob[mode][0] = (val >> 3) & 0x7; + ee->ee_db[mode][0] = val & 0x7; + + if ((ret = ar5k_eeprom_read_modes(hal, &offset, mode)) != 0) + return (ret); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { + EEPROM_READ_VAL(offset++, val); + ee->ee_margin_tx_rx[mode] = val & 0x3f; + } + + /* + * Get values for 802.11b (2.4GHz) + */ + mode = AR5K_EEPROM_MODE_11B; + offset = AR5K_EEPROM_MODES_11B(hal->ah_ee_version); + + if ((ret = ar5k_eeprom_read_ants(hal, &offset, mode)) != 0) + return (ret); + + EEPROM_READ_VAL(offset++, val); + ee->ee_adc_desired_size[mode] = (int8_t)((val >> 8) & 0xff); + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; + + if ((ret = ar5k_eeprom_read_modes(hal, &offset, mode)) != 0) + return (ret); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + EEPROM_READ_VAL(offset++, val); + ee->ee_cal_pier[mode][0] = + ar5k_eeprom_bin2freq(hal, val & 0xff, mode); + ee->ee_cal_pier[mode][1] = + ar5k_eeprom_bin2freq(hal, (val >> 8) & 0xff, mode); + + EEPROM_READ_VAL(offset++, val); + ee->ee_cal_pier[mode][2] = + ar5k_eeprom_bin2freq(hal, val & 0xff, mode); + } + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + } + + /* + * Get values for 802.11g (2.4GHz) + */ + mode = AR5K_EEPROM_MODE_11G; + offset = AR5K_EEPROM_MODES_11G(hal->ah_ee_version); + + if ((ret = ar5k_eeprom_read_ants(hal, &offset, mode)) != 0) + return (ret); + + EEPROM_READ_VAL(offset++, val); + ee->ee_adc_desired_size[mode] = (int8_t)((val >> 8) & 0xff); + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; + + if ((ret = ar5k_eeprom_read_modes(hal, &offset, mode)) != 0) + return (ret); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + EEPROM_READ_VAL(offset++, val); + ee->ee_cal_pier[mode][0] = + ar5k_eeprom_bin2freq(hal, val & 0xff, mode); + ee->ee_cal_pier[mode][1] = + ar5k_eeprom_bin2freq(hal, (val >> 8) & 0xff, mode); + + EEPROM_READ_VAL(offset++, val); + ee->ee_turbo_max_power[mode] = val & 0x7f; + ee->ee_xr_power[mode] = (val >> 7) & 0x3f; + + EEPROM_READ_VAL(offset++, val); + ee->ee_cal_pier[mode][2] = + ar5k_eeprom_bin2freq(hal, val & 0xff, mode); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + } + + EEPROM_READ_VAL(offset++, val); + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { + EEPROM_READ_VAL(offset++, val); + ee->ee_cck_ofdm_gain_delta = val & 0xff; + } + } + + /* + * Read 5GHz EEPROM channels + */ + + return (0); +} + +#undef EEPROM_READ_VAL +#undef EEPROM_READ_HDR + +int +ar5k_eeprom_read_mac(hal, mac) + struct ath_hal *hal; + u_int8_t *mac; +{ + u_int32_t total, offset; + u_int16_t data; + int octet; + u_int8_t mac_d[IEEE80211_ADDR_LEN]; + + bzero(mac, IEEE80211_ADDR_LEN); + bzero(&mac_d, IEEE80211_ADDR_LEN); + + if (hal->ah_eeprom_is_busy(hal)) + return (EBUSY); + + if (hal->ah_eeprom_read(hal, 0x20, &data) != 0) + return (EIO); + + for (offset = 0x1f, octet = 0, total = 0; + offset >= 0x1d; offset--) { + if (hal->ah_eeprom_read(hal, offset, &data) != 0) + return (EIO); + + total += data; + mac_d[octet + 1] = data & 0xff; + mac_d[octet] = data >> 8; + octet += 2; + } + + memcpy(mac, &mac_d, IEEE80211_ADDR_LEN); + + if ((!total) || total == (3 * 0xffff)) + return (EINVAL); + + return (0); +} + +HAL_BOOL +ar5k_eeprom_regulation_domain(hal, write, regdomain) + struct ath_hal *hal; + HAL_BOOL write; + ieee80211_regdomain_t *regdomain; +{ + /* Read current value */ + if (write != AH_TRUE) { + memcpy(regdomain, + &hal->ah_capabilities.cap_regdomain.reg_current, + sizeof(ieee80211_regdomain_t)); + return (AH_TRUE); + } + + /* Try to write a new value */ + memcpy(&hal->ah_capabilities.cap_regdomain.reg_current, regdomain, + sizeof(ieee80211_regdomain_t)); + + if (hal->ah_capabilities.cap_eeprom.ee_protect & + AR5K_EEPROM_PROTECT_WR_128_191) + return (AH_FALSE); + + hal->ah_capabilities.cap_eeprom.ee_regdomain = + ar5k_regdomain_from_ieee(regdomain); + + if (hal->ah_eeprom_write(hal, AR5K_EEPROM_REG_DOMAIN, + hal->ah_capabilities.cap_eeprom.ee_regdomain) != 0) + return (AH_FALSE); + + return (AH_TRUE); +} + +/* + * PHY/RF access functions + */ + +HAL_BOOL +ar5k_channel(hal, channel) + struct ath_hal *hal; + HAL_CHANNEL *channel; +{ + HAL_BOOL ret; + AR5K_TRACE; + + /* + * Check bounds supported by the PHY + * (don't care about regulation restrictions at this point) + */ + if ((channel->channel < hal->ah_capabilities.cap_range.range_2ghz_min || + channel->channel > hal->ah_capabilities.cap_range.range_2ghz_max) && + (channel->channel < hal->ah_capabilities.cap_range.range_5ghz_min || + channel->channel > hal->ah_capabilities.cap_range.range_5ghz_max)) { + AR5K_PRINTF("channel out of supported range (%u MHz)\n", + channel->channel); + return (AH_FALSE); + } + + /* + * Set the channel and wait + */ + if (hal->ah_radio == AR5K_AR5110) { + ret = ar5k_ar5110_channel(hal, channel); + } else if (hal->ah_radio == AR5K_AR5111) { + ret = ar5k_ar5111_channel(hal, channel); + } else { + ret = ar5k_ar5112_channel(hal, channel); + } + + if (ret == AH_FALSE) + return (ret); + + hal->ah_current_channel.c_channel = channel->c_channel; + hal->ah_current_channel.c_channel_flags = channel->c_channel_flags; + hal->ah_turbo = channel->c_channel_flags == CHANNEL_T ? + AH_TRUE : AH_FALSE; + + return (AH_TRUE); +} + +u_int32_t +ar5k_ar5110_chan2athchan(channel) + HAL_CHANNEL *channel; +{ + u_int32_t athchan; + + /* + * Convert IEEE channel/MHz to an internal channel value used + * by the AR5210 chipset. This has not been verified with + * newer chipsets like the AR5212A who have a completely + * different RF/PHY part. + */ + athchan = (ar5k_bitswap((ieee80211_mhz2ieee(channel->c_channel, + channel->c_channel_flags) - 24) + / 2, 5) << 1) | (1 << 6) | 0x1; + + return (athchan); +} + +HAL_BOOL +ar5k_ar5110_channel(hal, channel) + struct ath_hal *hal; + HAL_CHANNEL *channel; +{ + u_int32_t data; + + /* + * Set the channel and wait + */ + data = ar5k_ar5110_chan2athchan(channel); + AR5K_PHY_WRITE(0x27, data); + AR5K_PHY_WRITE(0x30, 0); + AR5K_DELAY(1000); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5111_chan2athchan(ieee, athchan) + u_int ieee; + struct ar5k_athchan_2ghz *athchan; +{ + int channel; + + /* Cast this value to catch negative channel numbers (>= -19) */ + channel = (int)ieee; + + /* + * Map 2GHz IEEE channel to 5GHz Atheros channel + */ + if (channel <= 13) { + athchan->a2_athchan = 115 + channel; + athchan->a2_flags = 0x46; + } else if (channel == 14) { + athchan->a2_athchan = 124; + athchan->a2_flags = 0x44; + } else if (channel >= 15 && channel <= 26) { + athchan->a2_athchan = ((channel - 14) * 4) + 132; + athchan->a2_flags = 0x46; + } else + return (AH_FALSE); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5111_channel(hal, channel) + struct ath_hal *hal; + HAL_CHANNEL *channel; +{ + u_int ieee_channel, ath_channel; + u_int32_t data0, data1, clock; + struct ar5k_athchan_2ghz ath_channel_2ghz; + + AR5K_TRACE; + + /* + * Set the channel on the AR5111 radio + */ + data0 = 0; + ath_channel = ieee_channel = ath_hal_mhz2ieee(channel->c_channel, + channel->c_channel_flags); + + if (channel->c_channel_flags & IEEE80211_CHAN_2GHZ) { + /* Map 2GHz channel to 5GHz Atheros channel ID */ + if (ar5k_ar5111_chan2athchan(ieee_channel, + &ath_channel_2ghz) == AH_FALSE) + return (AH_FALSE); + + ath_channel = ath_channel_2ghz.a2_athchan; + data0 = ((ar5k_bitswap(ath_channel_2ghz.a2_flags, 8) & 0xff) + << 5) | (1 << 4); + } + + if (ath_channel < 145 || !(ath_channel & 1)) { + clock = 1; + data1 = ((ar5k_bitswap(ath_channel - 24, 8) & 0xff) << 2) + | (clock << 1) | (1 << 10) | 1; + } else { + clock = 0; + data1 = ((ar5k_bitswap((ath_channel - 24) / 2, 8) & 0xff) << 2) + | (clock << 1) | (1 << 10) | 1; + } + + AR5K_PHY_WRITE(0x27, (data1 & 0xff) | ((data0 & 0xff) << 8)); + AR5K_PHY_WRITE(0x34, ((data1 >> 8) & 0xff) | (data0 & 0xff00)); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5112_channel(hal, channel) + struct ath_hal *hal; + HAL_CHANNEL *channel; +{ + u_int32_t data, data0, data1, data2; + u_int16_t c; + + AR5K_TRACE; + + c = channel->c_channel; + + /* + * Set the channel on the AR5112 or newer + */ + if (c < 4800) { + if (!((c - 2224) % 5)) { + data0 = ((2 * (c - 704)) - 3040) / 10; + data1 = 1; + } else if (!((c - 2192) % 5)) { + data0 = ((2 * (c - 672)) - 3040) / 10; + data1 = 0; + } else + return (AH_FALSE); + + data0 = ar5k_bitswap((data0 << 2) & 0xff, 8); + } else { + if (!(c % 20) && c >= 5120) { + data0 = ar5k_bitswap(((c - 4800) / 20 << 2), 8); + data2 = ar5k_bitswap(3, 2); + } else if (!(c % 10)) { + data0 = ar5k_bitswap(((c - 4800) / 10 << 1), 8); + data2 = ar5k_bitswap(2, 2); + } else if (!(c % 5)) { + data0 = ar5k_bitswap((c - 4800) / 5, 8); + data2 = ar5k_bitswap(1, 2); + } else + return (AH_FALSE); + } + + data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; + + AR5K_PHY_WRITE(0x27, data & 0xff); + AR5K_PHY_WRITE(0x36, (data >> 8) & 0x7f); + + return (AH_TRUE); +} + +int +ar5k_rfregs_set(rf, offset, reg, bits, first, col) + u_int32_t *rf; + u_int32_t offset, reg, bits, first, col; +{ + u_int32_t tmp, mask, entry, last; + int32_t position, left; + int i; + + if (!(col <= 3 && bits <= 32 && first + bits <= 319)) { + AR5K_PRINTF("invalid values at offset %u\n", offset); + return (-1); + } + + tmp = ar5k_bitswap(reg, bits); + entry = ((first - 1) / 8) + offset; + position = (first - 1) % 8; + + for (i = 0, left = bits; left > 0; position = 0, entry++, i++) { + last = (position + left > 8) ? 8 : position + left; + mask = (((1 << last) - 1) ^ ((1 << position) - 1)) << + (col * 8); + rf[entry] &= ~mask; + rf[entry] |= ((tmp << position) << (col * 8)) & mask; + left -= 8 - position; + tmp >>= (8 - position); + } + + return (i); +} + +HAL_BOOL +ar5k_rfregs(hal, channel, mode) + struct ath_hal *hal; + HAL_CHANNEL *channel; + u_int mode; +{ + if (hal->ah_radio < AR5K_AR5111) + return (AH_FALSE); + else if (hal->ah_radio < AR5K_AR5112) + return (ar5k_ar5111_rfregs(hal, channel, mode)); + + return (ar5k_ar5112_rfregs(hal, channel, mode)); +} + +HAL_BOOL +ar5k_ar5111_rfregs(hal, channel, mode) + struct ath_hal *hal; + HAL_CHANNEL *channel; + u_int mode; +{ + struct ar5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + const u_int rf_size = AR5K_ELEMENTS(ar5111_rf); + u_int32_t rf[rf_size]; + int i, obdb = -1, bank = -1; + u_int32_t ee_mode, offset[AR5K_AR5111_INI_RF_MAX_BANKS]; + + AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + + /* Copy values to modify them */ + for (i = 0; i < rf_size; i++) { + if (ar5111_rf[i].rf_bank >= + AR5K_AR5111_INI_RF_MAX_BANKS) { + AR5K_PRINT("invalid bank\n"); + return (AH_FALSE); + } + + if (bank != ar5111_rf[i].rf_bank) { + bank = ar5111_rf[i].rf_bank; + offset[bank] = i; + } + + rf[i] = ar5111_rf[i].rf_value[mode]; + } + + if (channel->c_channel_flags & IEEE80211_CHAN_2GHZ) { + if (channel->c_channel_flags & IEEE80211_CHAN_B) + ee_mode = AR5K_EEPROM_MODE_11B; + else + ee_mode = AR5K_EEPROM_MODE_11G; + obdb = 0; + + if (ar5k_rfregs_set(rf, offset[0], + ee->ee_ob[ee_mode][obdb], 3, 119, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[0], + ee->ee_ob[ee_mode][obdb], 3, 122, 0) < 0) + return (AH_FALSE); + + obdb = 1; + } else { + /* For 11a, Turbo and XR */ + ee_mode = AR5K_EEPROM_MODE_11A; + obdb = channel->c_channel >= 5725 ? 3 : + (channel->c_channel >= 5500 ? 2 : + (channel->c_channel >= 5260 ? 1 : + (channel->c_channel > 4000 ? 0 : -1))); + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_pwd_84, 1, 51, 3) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_pwd_90, 1, 45, 3) < 0) + return (AH_FALSE); + } + + if (ar5k_rfregs_set(rf, offset[6], + !ee->ee_xpd[ee_mode], 1, 95, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_x_gain[ee_mode], 4, 96, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[6], + obdb >= 0 ? ee->ee_ob[ee_mode][obdb] : 0, 3, 104, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[6], + obdb >= 0 ? ee->ee_db[ee_mode][obdb] : 0, 3, 107, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[7], + ee->ee_i_gain[ee_mode], 6, 29, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[7], + ee->ee_xpd[ee_mode], 1, 4, 0) < 0) + return (AH_FALSE); + + /* Write RF values */ + for (i = 0; i < rf_size; i++) { + AR5K_REG_WRITE(ar5111_rf[i].rf_register, rf[i]); + AR5K_DELAY(1); + } + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5112_rfregs(hal, channel, mode) + struct ath_hal *hal; + HAL_CHANNEL *channel; + u_int mode; +{ + struct ar5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + const u_int rf_size = AR5K_ELEMENTS(ar5112_rf); + u_int32_t rf[rf_size]; + int i, obdb = -1, bank = -1; + u_int32_t ee_mode, offset[AR5K_AR5112_INI_RF_MAX_BANKS]; + + AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + + /* Copy values to modify them */ + for (i = 0; i < rf_size; i++) { + if (ar5112_rf[i].rf_bank >= + AR5K_AR5112_INI_RF_MAX_BANKS) { + AR5K_PRINT("invalid bank\n"); + return (AH_FALSE); + } + + if (bank != ar5112_rf[i].rf_bank) { + bank = ar5112_rf[i].rf_bank; + offset[bank] = i; + } + + rf[i] = ar5112_rf[i].rf_value[mode]; + } + + if (channel->c_channel_flags & IEEE80211_CHAN_2GHZ) { + if (channel->c_channel_flags & IEEE80211_CHAN_B) + ee_mode = AR5K_EEPROM_MODE_11B; + else + ee_mode = AR5K_EEPROM_MODE_11G; + obdb = 0; + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_ob[ee_mode][obdb], 3, 287, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_ob[ee_mode][obdb], 3, 290, 0) < 0) + return (AH_FALSE); + } else { + /* For 11a, Turbo and XR */ + ee_mode = AR5K_EEPROM_MODE_11A; + obdb = channel->c_channel >= 5725 ? 3 : + (channel->c_channel >= 5500 ? 2 : + (channel->c_channel >= 5260 ? 1 : + (channel->c_channel > 4000 ? 0 : -1))); + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_ob[ee_mode][obdb], 3, 279, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_ob[ee_mode][obdb], 3, 282, 0) < 0) + return (AH_FALSE); + } + +#ifdef notyet + ar5k_rfregs_set(rf, offset[6], ee->ee_x_gain[ee_mode], 2, 270, 0); + ar5k_rfregs_set(rf, offset[6], ee->ee_x_gain[ee_mode], 2, 257, 0); +#endif + + if (ar5k_rfregs_set(rf, offset[6], + ee->ee_xpd[ee_mode], 1, 302, 0) < 0) + return (AH_FALSE); + + if (ar5k_rfregs_set(rf, offset[7], + ee->ee_i_gain[ee_mode], 6, 14, 0) < 0) + return (AH_FALSE); + + /* Write RF values */ + for (i = 0; i < rf_size; i++) { + AR5K_REG_WRITE(ar5112_rf[i].rf_register, rf[i]); + AR5K_DELAY(1); + } + + return (AH_TRUE); +} |