/* centralized clkreq control policy */ static void pcie_clkreq_upd(pcicore_info_t *pi, uint state) { si_t *sih = pi->sih; ASSERT(PCIE(sih)); switch (state) { case SI_DOATTACH: /* XXX PR42780 WAR: Disable clk req when coming up */ if (PCIE_ASPM(sih)) pcie_clkreq((void *)pi, 1, 0); break; case SI_PCIDOWN: if (sih->buscorerev == 6) { /* turn on serdes PLL down */ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_addr), ~0, 0); si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_data), ~0x40, 0); } else if (pi->pcie_pr42767) { /* When the driver going down, enable clkreq if PR42767 has been applied. * Also, adjust the state as system could hibernate, so Serdes PLL WAR is * a must before doing this */ pcie_clkreq((void *)pi, 1, 1); } break; case SI_PCIUP: if (sih->buscorerev == 6) { /* turn off serdes PLL down */ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_addr), ~0, 0); si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_data), ~0x40, 0x40); } else if (PCIE_ASPM(sih)) { /* disable clkreq */ pcie_clkreq((void *)pi, 1, 0); } break; default: ASSERT(0); break; } }
/* centralized clkreq control policy */ static void pcie_clkreq_upd(pcicore_info_t *pi, uint state) { si_t *sih = pi->sih; ASSERT(PCIE(sih)); switch (state) { case SI_DOATTACH: if (PCIE_ASPM(sih)) pcie_clkreq((void *)pi, 1, 0); break; case SI_PCIDOWN: if (sih->buscorerev == 6) { /* turn on serdes PLL down */ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_addr), ~0, 0); si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_data), ~0x40, 0); } else if (pi->pcie_pr42767) { pcie_clkreq((void *)pi, 1, 1); } break; case SI_PCIUP: if (sih->buscorerev == 6) { /* turn off serdes PLL down */ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_addr), ~0, 0); si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_data), ~0x40, 0x40); } else if (PCIE_ASPM(sih)) { /* disable clkreq */ pcie_clkreq((void *)pi, 1, 0); } break; default: ASSERT(0); break; } }
void pcie_war_ovr_aspm_update(void *pch, uint8 aspm) { pcicore_info_t *pi = (pcicore_info_t *)pch; if (!PCIE_ASPM(pi->sih)) return; /* Validate */ if (aspm > PCIE_ASPM_ENAB) return; pi->pcie_war_aspm_ovr = aspm; /* Update the current state */ pcie_war_aspm_clkreq(pi); }
/* Needs to happen when coming out of 'standby'/'hibernate' */ static void pcie_war_pci_setup(pcicore_info_t *pi) { si_t *sih = pi->sih; osl_t *osh = pi->osh; sbpcieregs_t *pcieregs = pi->regs.pcieregs; uint32 w; /* PR 29224 enable_9715_fix bit in the TLP workaround register should be set */ if ((sih->buscorerev == 0) || (sih->buscorerev == 1)) { w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_TLP_WORKAROUNDSREG); w |= 0x8; pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_TLP_WORKAROUNDSREG, w); } /* PR 34651 set bit6 to enable pcie-pm power mgmt in DLLP LC Reg, default is off */ if (sih->buscorerev == 1) { w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG); w |= (0x40); pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG, w); } if (sih->buscorerev == 0) { /* PR30841 WAR */ pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_TIMER1, 0x8128); pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDR, 0x0100); pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDRBW, 0x1466); } else if (PCIE_ASPM(sih)) { /* PR42766 WAR */ /* Change the L1 threshold for better performance */ w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG); w &= ~(PCIE_L1THRESHOLDTIME_MASK); w |= (PCIE_L1THRESHOLD_WARVAL << PCIE_L1THRESHOLDTIME_SHIFT); pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w); pcie_war_serdes(pi); pcie_war_aspm_clkreq(pi); } else if (pi->sih->buscorerev == 7) pcie_war_noplldown(pi); /* Note that the fix is actually in the SROM, that's why this is open-ended */ if (pi->sih->buscorerev >= 6) pcie_misc_config_fixup(pi); }
/* Needs to happen when update to shadow SROM is needed * : Coming out of 'standby'/'hibernate' * : If pcie_war_aspm_ovr state changed */ static void pcie_war_aspm_clkreq(pcicore_info_t *pi) { sbpcieregs_t *pcieregs = pi->regs.pcieregs; si_t *sih = pi->sih; uint16 val16, *reg16; uint32 w; if (!PCIE_ASPM(sih)) return; /* bypass this on QT or VSIM */ if (!ISSIM_ENAB(sih)) { reg16 = &pcieregs->sprom[SRSH_ASPM_OFFSET]; val16 = R_REG(pi->osh, reg16); val16 &= ~SRSH_ASPM_ENB; if (pi->pcie_war_aspm_ovr == PCIE_ASPM_ENAB) val16 |= SRSH_ASPM_ENB; else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L1_ENAB) val16 |= SRSH_ASPM_L1_ENB; else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L0s_ENAB) val16 |= SRSH_ASPM_L0s_ENB; W_REG(pi->osh, reg16, val16); w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32)); w &= ~PCIE_ASPM_ENAB; w |= pi->pcie_war_aspm_ovr; OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32), w); } reg16 = &pcieregs->sprom[SRSH_CLKREQ_OFFSET_REV5]; val16 = R_REG(pi->osh, reg16); if (pi->pcie_war_aspm_ovr != PCIE_ASPM_DISAB) { val16 |= SRSH_CLKREQ_ENB; pi->pcie_pr42767 = TRUE; } else val16 &= ~SRSH_CLKREQ_ENB; W_REG(pi->osh, reg16, val16); }
/* centralized clkreq control policy */ static void pcie_clkreq_upd(struct pcicore_info *pi, uint state) { struct si_pub *sih = pi->sih; switch (state) { case SI_DOATTACH: if (PCIE_ASPM(sih)) pcie_clkreq(pi, 1, 0); break; case SI_PCIDOWN: /* turn on serdes PLL down */ if (ai_get_buscorerev(sih) == 6) { ai_cc_reg(sih, offsetof(struct chipcregs, chipcontrol_addr), ~0, 0); ai_cc_reg(sih, offsetof(struct chipcregs, chipcontrol_data), ~0x40, 0); } else if (pi->pcie_pr42767) {
/* Needs to happen when update to shadow SROM is needed * : Coming out of 'standby'/'hibernate' * : If pcie_war_aspm_ovr state changed */ static void pcie_war_aspm_clkreq(pcicore_info_t *pi) { sbpcieregs_t *pcieregs = pi->regs.pcieregs; si_t *sih = pi->sih; uint16 val16, *reg16; uint32 w; if (!PCIE_ASPM(sih)) return; /* PR43448 WAR: Enable ASPM in the shadow SROM and Link control */ /* bypass this on QT or VSIM */ if (sih->chippkg != HDLSIM_PKG_ID && sih->chippkg != HWSIM_PKG_ID) { reg16 = &pcieregs->sprom[SRSH_ASPM_OFFSET]; val16 = R_REG(pi->osh, reg16); if (!pi->pcie_war_aspm_ovr) val16 |= SRSH_ASPM_ENB; else val16 &= ~SRSH_ASPM_ENB; W_REG(pi->osh, reg16, val16); w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32)); if (!pi->pcie_war_aspm_ovr) w |= PCIE_ASPM_ENAB; else w &= ~PCIE_ASPM_ENAB; OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32), w); } /* PR42767 WAR: if clockreq is not advertized in SROM, advertize it */ reg16 = &pcieregs->sprom[SRSH_CLKREQ_OFFSET_REV5]; val16 = R_REG(pi->osh, reg16); if (!pi->pcie_war_aspm_ovr) { val16 |= SRSH_CLKREQ_ENB; pi->pcie_pr42767 = TRUE; } else val16 &= ~SRSH_CLKREQ_ENB; W_REG(pi->osh, reg16, val16); }
/* When the device is going to enter D3 state (or the system is going to enter S3/S4 states */ void pcicore_sleep(void *pch) { pcicore_info_t *pi = (pcicore_info_t *)pch; uint32 w; if (!pi || !PCIE_ASPM(pi->sih)) return; /* PR43448: Clear ASPM L1 when going to sleep as while coming out of standby/sleep, * ASPM L1 should not be accidentally enabled before driver has a chance to apply * the WAR */ w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32)); w &= ~PCIE_CAP_LCREG_ASPML1; OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32), w); /* Clear the state for PR42767 to make sure it's applied on the way up */ pi->pcie_pr42767 = FALSE; }
/* Needs to happen when coming out of 'standby'/'hibernate' */ static void pcie_war_pci_setup(pcicore_info_t *pi) { si_t *sih = pi->sih; osl_t *osh = pi->osh; sbpcieregs_t *pcieregs = pi->regs.pcieregs; uint32 w; if ((sih->buscorerev == 0) || (sih->buscorerev == 1)) { w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_TLP_WORKAROUNDSREG); w |= 0x8; pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_TLP_WORKAROUNDSREG, w); } if (sih->buscorerev == 1) { w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG); w |= (0x40); pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG, w); } if (sih->buscorerev == 0) { pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_TIMER1, 0x8128); pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDR, 0x0100); pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDRBW, 0x1466); } else if (PCIE_ASPM(sih)) { /* Change the L1 threshold for better performance */ w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG); w &= ~(PCIE_L1THRESHOLDTIME_MASK); w |= (PCIE_L1THRESHOLD_WARVAL << PCIE_L1THRESHOLDTIME_SHIFT); pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w); pcie_war_serdes(pi); pcie_war_aspm_clkreq(pi); } else if (pi->sih->buscorerev == 7) pcie_war_noplldown(pi); /* Note that the fix is actually in the SROM, that's why this is open-ended */ if (pi->sih->buscorerev >= 6) pcie_misc_config_fixup(pi); }
/* ***** Functions called during driver state changes ***** */ void BCMATTACHFN(pcicore_attach)(void *pch, char *pvars, int state) { pcicore_info_t *pi = (pcicore_info_t *)pch; si_t *sih = pi->sih; if (PCIE_ASPM(sih)) { if (((sih->boardvendor == VENDOR_APPLE) && ((uint8)getintvar(pvars, "sromrev") == 4) && ((uint8)getintvar(pvars, "boardrev") <= 0x71)) || ((uint32)getintvar(pvars, "boardflags2") & BFL2_PCIEWAR_OVR)) { pi->pcie_war_aspm_ovr = PCIE_ASPM_DISAB; } else { pi->pcie_war_aspm_ovr = PCIE_ASPM_ENAB; } } pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_128B; if (BCM4331_CHIP_ID == CHIPID(sih->chip)) pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_512B; /* These need to happen in this order only */ pcie_war_polarity(pi); pcie_war_serdes(pi); pcie_war_aspm_clkreq(pi); pcie_clkreq_upd(pi, state); /* Alter default TX drive strength setting */ if (sih->boardvendor == VENDOR_APPLE) { if (sih->boardtype == 0x8d) /* change the TX drive strength to max */ pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x7f); else if (BCM4331_CHIP_ID == CHIPID(sih->chip)) /* change the drive strength for X19b & X28 to 700mv */ pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x70); } }
/* When the device is going to enter D3 state (or the system is going to enter S3/S4 states */ void pcicore_sleep(void *pch) { pcicore_info_t *pi = (pcicore_info_t *)pch; uint32 w; if (!pi || !PCIE(pi->sih)) return; pcie_power_save_upd(pi, FALSE); if (!PCIE_ASPM(pi->sih)) return; w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32)); w &= ~PCIE_CAP_LCREG_ASPML1; OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32), w); pi->pcie_pr42767 = FALSE; }