📄 bcm43xx_phy.c
字号:
struct bcm43xx_radioinfo *radio = bcm->current_core->radio; struct bcm43xx_phyinfo *phy = bcm->current_core->phy; if (phy->savedpctlreg == 0xFFFF) return; if ((bcm->board_type == 0x0416) && (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM)) return; switch (phy->type) { case BCM43xx_PHYTYPE_A: { TODO(); //TODO: Nothing for A PHYs yet :-/ break; } case BCM43xx_PHYTYPE_B: case BCM43xx_PHYTYPE_G: { u16 tmp; u16 txpower; s8 v0, v1, v2, v3; s8 average; u8 max_pwr; s16 desired_pwr, estimated_pwr, pwr_adjust; s16 radio_att_delta, baseband_att_delta; s16 radio_attenuation, baseband_attenuation; unsigned long phylock_flags; tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0058); v0 = (s8)(tmp & 0x00FF); v1 = (s8)((tmp & 0xFF00) >> 8); tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005A); v2 = (s8)(tmp & 0x00FF); v3 = (s8)((tmp & 0xFF00) >> 8); tmp = 0; if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0070); v0 = (s8)(tmp & 0x00FF); v1 = (s8)((tmp & 0xFF00) >> 8); tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0072); v2 = (s8)(tmp & 0x00FF); v3 = (s8)((tmp & 0xFF00) >> 8); if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) return; v0 = (v0 + 0x20) & 0x3F; v1 = (v1 + 0x20) & 0x3F; v2 = (v2 + 0x20) & 0x3F; v3 = (v3 + 0x20) & 0x3F; tmp = 1; } bcm43xx_radio_clear_tssi(bcm); average = (v0 + v1 + v2 + v3 + 2) / 4; if (tmp && (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005E) & 0x8)) average -= 13; estimated_pwr = bcm43xx_phy_estimate_power_out(bcm, average); max_pwr = bcm->sprom.maxpower_bgphy; if ((bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) && (phy->type == BCM43xx_PHYTYPE_G)) max_pwr -= 0x3; /*TODO: max_pwr = min(REG - bcm->sprom.antennagain_bgphy - 0x6, max_pwr) where REG is the max power as per the regulatory domain */ desired_pwr = limit_value(radio->txpower_desired, 0, max_pwr); /* Check if we need to adjust the current power. */ pwr_adjust = desired_pwr - estimated_pwr; radio_att_delta = -(pwr_adjust + 7) >> 3; baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { bcm43xx_phy_lo_mark_current_used(bcm); return; } /* Calculate the new attenuation values. */ baseband_attenuation = radio->txpower[0]; baseband_attenuation += baseband_att_delta; radio_attenuation = radio->txpower[1]; radio_attenuation += radio_att_delta; /* Get baseband and radio attenuation values into their permitted ranges. * baseband 0-11, radio 0-9. * Radio attenuation affects power level 4 times as much as baseband. */ if (radio_attenuation < 0) { baseband_attenuation -= (4 * -radio_attenuation); radio_attenuation = 0; } else if (radio_attenuation > 9) { baseband_attenuation += (4 * (radio_attenuation - 9)); radio_attenuation = 9; } else { while (baseband_attenuation < 0 && radio_attenuation > 0) { baseband_attenuation += 4; radio_attenuation--; } while (baseband_attenuation > 11 && radio_attenuation < 9) { baseband_attenuation -= 4; radio_attenuation++; } } baseband_attenuation = limit_value(baseband_attenuation, 0, 11); txpower = radio->txpower[2]; if ((radio->version == 0x2050) && (radio->revision == 2)) { if (radio_attenuation <= 1) { if (txpower == 0) { txpower = 3; radio_attenuation += 2; baseband_attenuation += 2; } else if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) { baseband_attenuation += 4 * (radio_attenuation - 2); radio_attenuation = 2; } } else if (radio_attenuation > 4 && txpower != 0) { txpower = 0; if (baseband_attenuation < 3) { radio_attenuation -= 3; baseband_attenuation += 2; } else { radio_attenuation -= 2; baseband_attenuation -= 2; } } } radio->txpower[2] = txpower; baseband_attenuation = limit_value(baseband_attenuation, 0, 11); radio_attenuation = limit_value(radio_attenuation, 0, 9); bcm43xx_phy_lock(bcm, phylock_flags); bcm43xx_radio_lock(bcm); bcm43xx_radio_set_txpower_bg(bcm, baseband_attenuation, radio_attenuation, txpower); bcm43xx_phy_lo_mark_current_used(bcm); bcm43xx_radio_unlock(bcm); bcm43xx_phy_unlock(bcm, phylock_flags); break; } default: assert(0); }}static inlines32 bcm43xx_tssi2dbm_ad(s32 num, s32 den){ if (num < 0) return num/den; else return (num+den/2)/den;}static inlines8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2){ s32 m1, m2, f = 256, q, delta; s8 i = 0; m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32); m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1); do { if (i > 15) return -EINVAL; q = bcm43xx_tssi2dbm_ad(f * 4096 - bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048); delta = abs(q - f); f = q; i++; } while (delta >= 2); entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128); return 0;}/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm){ struct bcm43xx_phyinfo *phy = bcm->current_core->phy; struct bcm43xx_radioinfo *radio = bcm->current_core->radio; s16 pab0, pab1, pab2; u8 idx; s8 *dyn_tssi2dbm; if (phy->type == BCM43xx_PHYTYPE_A) { pab0 = (s16)(bcm->sprom.pa1b0); pab1 = (s16)(bcm->sprom.pa1b1); pab2 = (s16)(bcm->sprom.pa1b2); } else { pab0 = (s16)(bcm->sprom.pa0b0); pab1 = (s16)(bcm->sprom.pa0b1); pab2 = (s16)(bcm->sprom.pa0b2); } if ((bcm->chip_id == 0x4301) && (radio->version != 0x2050)) { phy->idle_tssi = 0x34; phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; return 0; } if (pab0 != 0 && pab1 != 0 && pab2 != 0 && pab0 != -1 && pab1 != -1 && pab2 != -1) { /* The pabX values are set in SPROM. Use them. */ if (phy->type == BCM43xx_PHYTYPE_A) { if ((s8)bcm->sprom.idle_tssi_tgt_aphy != 0 && (s8)bcm->sprom.idle_tssi_tgt_aphy != -1) phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_aphy); else phy->idle_tssi = 62; } else { if ((s8)bcm->sprom.idle_tssi_tgt_bgphy != 0 && (s8)bcm->sprom.idle_tssi_tgt_bgphy != -1) phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_bgphy); else phy->idle_tssi = 62; } dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); if (dyn_tssi2dbm == NULL) { printk(KERN_ERR PFX "Could not allocate memory" "for tssi2dbm table\n"); return -ENOMEM; } for (idx = 0; idx < 64; idx++) if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) { phy->tssi2dbm = NULL; printk(KERN_ERR PFX "Could not generate " "tssi2dBm table\n"); return -ENODEV; } phy->tssi2dbm = dyn_tssi2dbm; phy->dyn_tssi_tbl = 1; } else { /* pabX values not set in SPROM. */ switch (phy->type) { case BCM43xx_PHYTYPE_A: /* APHY needs a generated table. */ phy->tssi2dbm = NULL; printk(KERN_ERR PFX "Could not generate tssi2dBm " "table (wrong SPROM info)!\n"); return -ENODEV; case BCM43xx_PHYTYPE_B: phy->idle_tssi = 0x34; phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; break; case BCM43xx_PHYTYPE_G: phy->idle_tssi = 0x34; phy->tssi2dbm = bcm43xx_tssi2dbm_g_table; break; } } return 0;}int bcm43xx_phy_init(struct bcm43xx_private *bcm){ struct bcm43xx_phyinfo *phy = bcm->current_core->phy; int err = -ENODEV; unsigned long flags; /* We do not want to be preempted while calibrating * the hardware. */ local_irq_save(flags); switch (phy->type) { case BCM43xx_PHYTYPE_A: if (phy->rev == 2 || phy->rev == 3) { bcm43xx_phy_inita(bcm); err = 0; } break; case BCM43xx_PHYTYPE_B: switch (phy->rev) { case 2: bcm43xx_phy_initb2(bcm); err = 0; break; case 4: bcm43xx_phy_initb4(bcm); err = 0; break; case 5: bcm43xx_phy_initb5(bcm); err = 0; break; case 6: bcm43xx_phy_initb6(bcm); err = 0; break; } break; case BCM43xx_PHYTYPE_G: bcm43xx_phy_initg(bcm); err = 0; break; } local_irq_restore(flags); if (err) printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n"); return err;}void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm){ struct bcm43xx_phyinfo *phy = bcm->current_core->phy; u16 antennadiv; u16 offset; u16 value; u32 ucodeflags; antennadiv = phy->antenna_diversity; if (antennadiv == 0xFFFF) antennadiv = 3; assert(antennadiv <= 3); ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET); bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET, ucodeflags & ~BCM43xx_UCODEFLAG_AUTODIV); switch (phy->type) { case BCM43xx_PHYTYPE_A: case BCM43xx_PHYTYPE_G: if (phy->type == BCM43xx_PHYTYPE_A) offset = 0x0000; else offset = 0x0400; if (antennadiv == 2) value = (3/*automatic*/ << 7); else value = (antennadiv << 7); bcm43xx_phy_write(bcm, offset + 1, (bcm43xx_phy_read(bcm, offset + 1) & 0x7E7F) | value); if (antennadiv >= 2) { if (antennadiv == 2) value = (antennadiv << 7); else value = (0/*force0*/ << 7); bcm43xx_phy_write(bcm, offset + 0x2B, (bcm43xx_phy_read(bcm, offset + 0x2B) & 0xFEFF) | value); } if (phy->type == BCM43xx_PHYTYPE_G) { if (antennadiv >= 2) bcm43xx_phy_write(bcm, 0x048C, bcm43xx_phy_read(bcm, 0x048C) | 0x2000); else bcm43xx_phy_write(bcm, 0x048C, bcm43xx_phy_read(bcm, 0x048C) & ~0x2000); if (phy->rev >= 2) { bcm43xx_phy_write(bcm, 0x0461, bcm43xx_phy_read(bcm, 0x0461) | 0x0010); bcm43xx_phy_write(bcm, 0x04AD, (bcm43xx_phy_read(bcm, 0x04AD) & 0x00FF) | 0x0015); if (phy->rev == 2) bcm43xx_phy_write(bcm, 0x0427, 0x0008); else bcm43xx_phy_write(bcm, 0x0427, (bcm43xx_phy_read(bcm, 0x0427) & 0x00FF) | 0x0008); } else if (phy->rev >= 6) bcm43xx_phy_write(bcm, 0x049B, 0x00DC); } else { if (phy->rev < 3) bcm43xx_phy_write(bcm, 0x002B, (bcm43xx_phy_read(bcm, 0x002B) & 0x00FF) | 0x0024); else { bcm43xx_phy_write(bcm, 0x0061, bcm43xx_phy_read(bcm, 0x0061) | 0x0010); if (phy->rev == 3) { bcm43xx_phy_write(bcm, 0x0093, 0x001D); bcm43xx_phy_write(bcm, 0x0027, 0x0008); } else { bcm43xx_phy_write(bcm, 0x0093, 0x003A); bcm43xx_phy_write(bcm, 0x0027, (bcm43xx_phy_read(bcm, 0x0027) & 0x00FF) | 0x0008); } } } break; case BCM43xx_PHYTYPE_B: if (bcm->current_core->rev == 2) value = (3/*automatic*/ << 7); else value = (antennadiv << 7); bcm43xx_phy_write(bcm, 0x03E2, (bcm43xx_phy_read(bcm, 0x03E2) & 0xFE7F) | value); break; default: assert(0); } if (antennadiv >= 2) { ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET); bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET, ucodeflags | BCM43xx_UCODEFLAG_AUTODIV); } phy->antenna_diversity = antennadiv;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -