uint pcie_writereg(si_t *sih, sbpcieregs_t *pcieregs, uint addrtype, uint offset, uint val) { osl_t *osh = si_osh(sih); ASSERT(pcieregs != NULL); BCM_REFERENCE(osh); if ((BUSTYPE(sih->bustype) == SI_BUS) || PCIE_GEN1(sih)) { switch (addrtype) { case PCIE_CONFIGREGS: W_REG(osh, (&pcieregs->configaddr), offset); W_REG(osh, (&pcieregs->configdata), val); break; case PCIE_PCIEREGS: W_REG(osh, (&pcieregs->u.pcie1.pcieindaddr), offset); W_REG(osh, (&pcieregs->u.pcie1.pcieinddata), val); break; default: ASSERT(0); break; } } else if (PCIE_GEN2(sih)) { W_REG(osh, (&pcieregs->configaddr), offset); W_REG(osh, (&pcieregs->configdata), val); } return 0; }
/* ***** Support functions ***** */ static uint32 pcie_devcontrol_mrrs(void *pch, uint32 mask, uint32 val) { pcicore_info_t *pi = (pcicore_info_t *)pch; uint32 reg_val; uint8 offset; offset = pi->pciecap_devctrl_offset; if (!offset) return 0; reg_val = OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32)); /* set operation */ if (mask) { if (val > PCIE_CAP_DEVCTRL_MRRS_128B) { if (PCIE_GEN1(pi->sih) && (pi->sih->buscorerev < 18)) { PCI_ERROR(("%s pcie corerev %d doesn't support >128B MRRS", __FUNCTION__, pi->sih->buscorerev)); val = PCIE_CAP_DEVCTRL_MRRS_128B; } } reg_val &= ~PCIE_CAP_DEVCTRL_MRRS_MASK; reg_val |= (val << PCIE_CAP_DEVCTRL_MRRS_SHIFT) & PCIE_CAP_DEVCTRL_MRRS_MASK; OSL_PCI_WRITE_CONFIG(pi->osh, offset, sizeof(uint32), reg_val); reg_val = OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32)); } return reg_val; }
void pcicore_hwup(void *pch) { pcicore_info_t *pi = (pcicore_info_t *)pch; if (!pi || !PCIE_GEN1(pi->sih)) return; pcie_power_save_upd(pi, TRUE); if (pi->sih->boardtype == CB2_4321_BOARD || pi->sih->boardtype == CB2_4321_AG_BOARD) pcicore_fixlatencytimer(pch, 0x20); pcie_war_pci_setup(pi); /* Alter default TX drive strength setting */ if (pi->sih->boardvendor == VENDOR_APPLE) { if (pi->sih->boardtype == 0x8d) /* change the TX drive strength to max */ pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x7f); else if (PCIE_DRIVE_STRENGTH_OVERRIDE(pi->sih)) /* change the drive strength to 700mv */ pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x70); } }
uint32 pcie_ltr_reg(void *pch, uint32 reg, uint32 mask, uint32 val) { pcicore_info_t *pi = (pcicore_info_t *)pch; uint32 reg_val; uint32 offset; if (PCIE_GEN1(pi->sih)) return 0; if (reg == PCIE_CAP_LTR0_REG) offset = pi->pciecap_ltr0_reg_offset; else if (reg == PCIE_CAP_LTR1_REG) offset = pi->pciecap_ltr1_reg_offset; else if (reg == PCIE_CAP_LTR2_REG) offset = pi->pciecap_ltr2_reg_offset; else { PCI_ERROR(("pcie_ltr_reg: unsupported LTR register offset %d\n", reg)); return 0; } if (!offset) return 0; if (mask) { /* set operation */ reg_val = val; pcie_writereg(pi->sih, pi->regs.pcieregs, PCIE_CONFIGREGS, offset, reg_val); } else { /* get operation */ reg_val = pcie_readreg(pi->sih, pi->regs.pcieregs, PCIE_CONFIGREGS, offset); } return reg_val; }
void pcie_set_request_size(void *pch, uint16 size) { pcicore_info_t *pi = (pcicore_info_t *)pch; si_t *sih; if (!pi) return; sih = pi->sih; if (size == 128) pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_128B; else if (size == 256) pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_256B; else if (size == 512) pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_512B; else if (size == 1024) pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_1024B; else return; if (PCIE_GEN1(sih)) { if (pi->sih->buscorerev == 18 || pi->sih->buscorerev == 19) pcie_devcontrol_mrrs(pi, PCIE_CAP_DEVCTRL_MRRS_MASK, (uint32)pi->pcie_reqsize); } else if (PCIE_GEN2(sih)) { pcie_devcontrol_mrrs(pi, PCIE_CAP_DEVCTRL_MRRS_MASK, (uint32)pi->pcie_reqsize); } else ASSERT(0); }
static int pciegen1_mdioop(pcicore_info_t *pi, uint physmedia, uint regaddr, bool write, uint *val) { sbpcieregs_t *pcieregs = pi->regs.pcieregs; uint mdiodata; uint i = 0; uint pcie_serdes_spinwait = 10; if (!PCIE_GEN1(pi->sih)) ASSERT(0); /* enable mdio access to SERDES */ W_REG(pi->osh, (&pcieregs->u.pcie1.mdiocontrol), MDIOCTL_PREAM_EN | MDIOCTL_DIVISOR_VAL); if (pi->sih->buscorerev >= 10) { /* new serdes is slower in rw, using two layers of reg address mapping */ if (!pcie_mdiosetblock(pi, physmedia)) return 1; mdiodata = (MDIODATA_DEV_ADDR << MDIODATA_DEVADDR_SHF) | (regaddr << MDIODATA_REGADDR_SHF); pcie_serdes_spinwait *= 20; } else { mdiodata = (physmedia << MDIODATA_DEVADDR_SHF_OLD) | (regaddr << MDIODATA_REGADDR_SHF_OLD); } if (!write) mdiodata |= (MDIODATA_START | MDIODATA_READ | MDIODATA_TA); else mdiodata |= (MDIODATA_START | MDIODATA_WRITE | MDIODATA_TA | *val); W_REG(pi->osh, &pcieregs->u.pcie1.mdiodata, mdiodata); PR28829_DELAY(); /* retry till the transaction is complete */ while (i < pcie_serdes_spinwait) { if (R_REG(pi->osh, &(pcieregs->u.pcie1.mdiocontrol)) & MDIOCTL_ACCESS_DONE) { if (!write) { PR28829_DELAY(); *val = (R_REG(pi->osh, &(pcieregs->u.pcie1.mdiodata)) & MDIODATA_MASK); } /* Disable mdio access to SERDES */ W_REG(pi->osh, (&pcieregs->u.pcie1.mdiocontrol), 0); return 0; } OSL_DELAY(1000); i++; } PCI_ERROR(("pcie_mdioop: timed out op: %d\n", write)); /* Disable mdio access to SERDES */ W_REG(pi->osh, (&pcieregs->u.pcie1.mdiocontrol), 0); return 1; }
static int pcie_mdioop(pcicore_info_t *pi, uint physmedia, uint regaddr, bool write, uint *val) { if (PCIE_GEN1(pi->sih)) return (pciegen1_mdioop(pi, physmedia, regaddr, write, val)); else if (PCIE_GEN2(pi->sih)) return (pciegen2_mdioop(pi, physmedia, regaddr, write, val, 0)); else return 0xFFFFFFFF; }
/* Dump PCIE Info */ int pcicore_dump_pcieinfo(void *pch, struct bcmstrbuf *b) { pcicore_info_t *pi = (pcicore_info_t *)pch; if (!PCIE_GEN1(pi->sih) && !PCIE_GEN2(pi->sih)) return BCME_ERROR; bcm_bprintf(b, "PCIE link speed: %d\n", pcie_get_link_speed(pch)); return 0; }
/* ***** 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_GEN1(sih)) { if ((BCM4360_CHIP_ID == CHIPID(sih->chip)) || (BCM43460_CHIP_ID == CHIPID(sih->chip)) || (BCM4350_CHIP_ID == CHIPID(sih->chip)) || (BCM4352_CHIP_ID == CHIPID(sih->chip)) || (BCM4335_CHIP_ID == CHIPID(sih->chip))) pi->pcie_reqsize = PCIE_CAP_DEVCTRL_MRRS_1024B; return; } if (PCIEGEN1_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; bzero(pi->pcie_configspace, PCI_CONFIG_SPACE_SIZE); /* 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); pcie_war_pmebits(pi); /* 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 (PCIE_DRIVE_STRENGTH_OVERRIDE(sih)) /* change the drive strength to 700mv */ pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x70); } }
void pcie_disable_TL_clk_gating(void *pch) { /* disable TL clk gating is located in bit 4 of PCIEControl (Offset 0x000) */ pcicore_info_t *pi = (pcicore_info_t *)pch; si_t *sih = pi->sih; if (!PCIE_GEN1(sih) && !PCIE_GEN2(sih)) return; si_corereg(sih, sih->buscoreidx, 0, 0x10, 0x10); }
void pcicore_down(void *pch, int state) { pcicore_info_t *pi = (pcicore_info_t *)pch; if (!pi || !PCIE_GEN1(pi->sih)) return; pcie_clkreq_upd(pi, state); /* Reduce L1 timer for better power savings */ pcie_extendL1timer(pi, FALSE); pcie_power_save_upd(pi, FALSE); }
void pcie_set_L1_entry_time(void *pch, uint32 val) { /* L1 entry time is located in bits [22:16] of register 0x1004 (pdl_control_1) */ pcicore_info_t *pi = (pcicore_info_t *)pch; si_t *sih = pi->sih; sbpcieregs_t *pcieregs = pi->regs.pcieregs; uint32 data; if (!PCIE_GEN1(sih) && !PCIE_GEN2(sih)) return; if (val > 0x7F) return; data = pcie_readreg(sih, pcieregs, PCIE_CONFIGREGS, 0x1004); pcie_writereg(pch, pcieregs, PCIE_CONFIGREGS, 0x1004, (data & ~0x7F0000) | (val << 16)); }
/* mode : 0 -- reset, 1 -- tx, 2 -- rx */ void pcie_set_error_injection(void *pch, uint32 mode) { /* through reg_phy_ctl_7 - 0x181c */ pcicore_info_t *pi = (pcicore_info_t *)pch; si_t *sih = pi->sih; sbpcieregs_t *pcieregs = pi->regs.pcieregs; if (!PCIE_GEN1(sih) && !PCIE_GEN2(sih)) return; if (mode == 0) pcie_writereg(pch, pcieregs, PCIE_CONFIGREGS, 0x181c, 0); else if (mode == 1) pcie_writereg(pch, pcieregs, PCIE_CONFIGREGS, 0x181c, 0x14031); else pcie_writereg(pch, pcieregs, PCIE_CONFIGREGS, 0x181c, 0x2c031); }
static void pcie_extendL1timer(pcicore_info_t *pi, bool extend) { uint32 w; si_t *sih = pi->sih; sbpcieregs_t *pcieregs = pi->regs.pcieregs; if (!PCIE_GEN1(sih)) return; w = pcie_readreg(sih, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG); if (extend && sih->buscorerev >= 7) w |= PCIE_ASPMTIMER_EXTEND; else w &= ~PCIE_ASPMTIMER_EXTEND; pcie_writereg(sih, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w); w = pcie_readreg(sih, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG); }
void pcie_war_ovr_aspm_update(void *pch, uint8 aspm) { pcicore_info_t *pi = (pcicore_info_t *)pch; if (!PCIE_GEN1(pi->sih)) return; if (!PCIEGEN1_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); }
/* centralized clkreq control policy */ static void pcie_clkreq_upd(pcicore_info_t *pi, uint state) { si_t *sih = pi->sih; ASSERT(PCIE(sih)); if (!PCIE_GEN1(sih)) return; switch (state) { case SI_DOATTACH: if (PCIEGEN1_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 (PCIEGEN1_ASPM(sih)) { /* disable clkreq */ pcie_clkreq((void *)pi, 1, 0); } break; default: ASSERT(0); break; } }
uint32 pcicore_pciereg(void *pch, uint32 offset, uint32 mask, uint32 val, uint type) { uint32 reg_val = 0; pcicore_info_t *pi = (pcicore_info_t *)pch; sbpcieregs_t *pcieregs = pi->regs.pcieregs; if (mask) { PCI_ERROR(("PCIEREG: 0x%x writeval 0x%x\n", offset, val)); pcie_writereg(pi->sih, pcieregs, type, offset, val); } /* Should not read register 0x154 */ if (PCIE_GEN1(pi->sih) && pi->sih->buscorerev <= 5 && offset == PCIE_DLLP_PCIE11 && type == PCIE_PCIEREGS) return reg_val; reg_val = pcie_readreg(pi->sih, pcieregs, type, offset); PCI_ERROR(("PCIEREG: 0x%x readval is 0x%x\n", offset, reg_val)); return reg_val; }
uint32 pcieltrhysteresiscnt_reg(void *pch, uint32 mask, uint32 val) { pcicore_info_t *pi = (pcicore_info_t *)pch; si_t *sih = pi->sih; sbpcieregs_t *pcieregs = pi->regs.pcieregs; uint32 retval; if (PCIE_GEN1(sih)) return 0; ASSERT(pcieregs != NULL); if (mask) { /* set operation */ retval = val; W_REG(pi->osh, &(pcieregs->ltrhysteresiscnt), val); } else { /* get operation */ retval = R_REG(pi->osh, &(pcieregs->ltrhysteresiscnt)); } return retval; }
/* 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_GEN1(pi->sih)) return; pcie_power_save_upd(pi, FALSE); if (!PCIEGEN1_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; }
/* Dump PCIE PLP/DLLP/TLP diagnostic registers */ int pcicore_dump_pcieregs(void *pch, struct bcmstrbuf *b) { pcicore_info_t *pi = (pcicore_info_t *)pch; sbpcieregs_t *pcieregs = pi->regs.pcieregs; si_t *sih = pi->sih; uint reg_val = 0; char *bitfield_dump_buf; if (!PCIE_GEN1(pi->sih)) return BCME_ERROR; if (!(bitfield_dump_buf = MALLOC(pi->osh, BITFIELD_DUMP_SIZE))) { printf("bitfield dump allocation failed\n"); return BCME_NOMEM; } bcm_bprintf(b, "PLPRegs \t"); bcmdumpfields(si_pcie_readreg, (void *)(uintptr)pi->sih, PCIE_PCIEREGS, (struct fielddesc *)(uintptr)pcie_plp_regdesc, bitfield_dump_buf, BITFIELD_DUMP_SIZE); bcm_bprintf(b, "%s", bitfield_dump_buf); bzero(bitfield_dump_buf, BITFIELD_DUMP_SIZE); bcm_bprintf(b, "\n"); bcm_bprintf(b, "DLLPRegs \t"); bcmdumpfields(si_pcie_readreg, (void *)(uintptr)pi->sih, PCIE_PCIEREGS, (struct fielddesc *)(uintptr)pcie_dllp_regdesc, bitfield_dump_buf, BITFIELD_DUMP_SIZE); bcm_bprintf(b, "%s", bitfield_dump_buf); bzero(bitfield_dump_buf, BITFIELD_DUMP_SIZE); bcm_bprintf(b, "\n"); bcm_bprintf(b, "TLPRegs \t"); bcmdumpfields(si_pcie_readreg, (void *)(uintptr)pi->sih, PCIE_PCIEREGS, (struct fielddesc *)(uintptr)pcie_tlp_regdesc, bitfield_dump_buf, BITFIELD_DUMP_SIZE); bcm_bprintf(b, "%s", bitfield_dump_buf); bzero(bitfield_dump_buf, BITFIELD_DUMP_SIZE); bcm_bprintf(b, "\n"); /* enable mdio access to SERDES */ W_REG(pi->osh, (&pcieregs->u.pcie1.mdiocontrol), MDIOCTL_PREAM_EN | MDIOCTL_DIVISOR_VAL); bcm_bprintf(b, "SERDES regs \n"); if (sih->buscorerev >= 10) { pcie_mdioread(pi, MDIO_DEV_IEEE0, 0x2, ®_val); bcm_bprintf(b, "block IEEE0, offset 2: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_IEEE0, 0x3, ®_val); bcm_bprintf(b, "block IEEE0, offset 2: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_IEEE1, 0x08, ®_val); bcm_bprintf(b, "block IEEE1, lanestatus: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_IEEE1, 0x0a, ®_val); bcm_bprintf(b, "block IEEE1, lanestatus2: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_BLK4, 0x16, ®_val); bcm_bprintf(b, "MDIO_DEV_BLK4, lanetest0: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_TXPLL, 0x11, ®_val); bcm_bprintf(b, "MDIO_DEV_TXPLL, pllcontrol: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_TXPLL, 0x12, ®_val); bcm_bprintf(b, "MDIO_DEV_TXPLL, plltimer1: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_TXPLL, 0x13, ®_val); bcm_bprintf(b, "MDIO_DEV_TXPLL, plltimer2: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_TXPLL, 0x14, ®_val); bcm_bprintf(b, "MDIO_DEV_TXPLL, plltimer3: 0x%x\n", reg_val); pcie_mdioread(pi, MDIO_DEV_TXPLL, 0x17, ®_val); bcm_bprintf(b, "MDIO_DEV_TXPLL, freqdetcounter: 0x%x\n", reg_val); } else { pcie_mdioread(pi, MDIODATA_DEV_RX, SERDES_RX_TIMER1, ®_val); bcm_bprintf(b, "rxtimer1 0x%x ", reg_val); pcie_mdioread(pi, MDIODATA_DEV_RX, SERDES_RX_CDR, ®_val); bcm_bprintf(b, "rxCDR 0x%x ", reg_val); pcie_mdioread(pi, MDIODATA_DEV_RX, SERDES_RX_CDRBW, ®_val); bcm_bprintf(b, "rxCDRBW 0x%x\n", reg_val); } /* disable mdio access to SERDES */ W_REG(pi->osh, (&pcieregs->u.pcie1.mdiocontrol), 0); MFREE(pi->osh, bitfield_dump_buf, BITFIELD_DUMP_SIZE); return 0; }