void* etc_attach(void *et, uint vendor, uint device, uint unit, void *osh, void *regsva) { etc_info_t *etc; ET_TRACE(("et%d: etc_attach: vendor 0x%x device 0x%x\n", unit, vendor, device)); /* some code depends on packed structures */ ASSERT(sizeof(struct ether_addr) == ETHER_ADDR_LEN); ASSERT(sizeof(struct ether_header) == ETHER_HDR_LEN); /* allocate etc_info_t state structure */ if ((etc = (etc_info_t*) MALLOC(osh, sizeof(etc_info_t))) == NULL) { ET_ERROR(("et%d: etc_attach: out of memory, malloced %d bytes\n", unit, MALLOCED(osh))); return (NULL); } bzero((char*)etc, sizeof(etc_info_t)); etc->et = et; etc->unit = unit; etc->osh = osh; etc->vendorid = (uint16) vendor; etc->deviceid = (uint16) device; etc->forcespeed = ET_AUTO; etc->linkstate = FALSE; /* set chip opsvec */ etc->chops = etc_chipmatch(vendor, device); ASSERT(etc->chops); /* chip attach */ if ((etc->ch = (*etc->chops->attach)(etc, osh, regsva)) == NULL) { ET_ERROR(("et%d: chipattach error\n", unit)); goto fail; } return ((void*)etc); fail: etc_detach(etc); return (NULL); }
static void chipphyreset(struct bcm4xxx *ch, uint phyaddr) { ASSERT(phyaddr < MAXEPHY); if (phyaddr == EPHY_NOREG) return; chipphywr(ch, phyaddr, 0, CTL_RESET); OSL_DELAY(100); if (chipphyrd(ch, phyaddr, 0) & CTL_RESET) { ET_ERROR(("et%d: chipphyreset: reset not complete\n", ch->etc->unit)); } chipphyinit(ch, phyaddr); }
/* dma transmit */ static bool BCMFASTPATH chiptx(struct bcm4xxx *ch, void *p0) { int error; ET_TRACE(("et%d: chiptx\n", ch->etc->unit)); ET_LOG("et%d: chiptx", ch->etc->unit, 0); error = dma_txfast(ch->di, p0, TRUE); if (error) { ET_ERROR(("et%d: chiptx: out of txds\n", ch->etc->unit)); ch->etc->txnobuf++; return FALSE; } return TRUE; }
static uint16 chipphyrd(struct bcm4xxx *ch, uint phyaddr, uint reg) { bcmenetregs_t *regs; ASSERT(phyaddr < MAXEPHY); /* * BCM5222 dualphy shared mdio contortion. * remote phy: another emac controls our phy. */ if (ch->etc->mdcport != ch->etc->coreunit) { if (ch->etphy == NULL) { ch->etphy = et_phyfind(ch->et, ch->etc->mdcport); /* first time reset */ if (ch->etphy) chipphyreset(ch, ch->etc->phyaddr); } if (ch->etphy) return (et_phyrd(ch->etphy, phyaddr, reg)); else return (0xffff); } /* local phy: our emac controls our phy */ regs = ch->regs; /* clear mii_int */ W_REG(ch->osh, ®s->emacintstatus, EI_MII); /* issue the read */ W_REG(ch->osh, ®s->mdiodata, (MD_SB_START | MD_OP_READ | (phyaddr << MD_PMD_SHIFT) | (reg << MD_RA_SHIFT) | MD_TA_VALID)); /* wait for it to complete */ SPINWAIT(((R_REG(ch->osh, ®s->emacintstatus) & EI_MII) == 0), 100); if ((R_REG(ch->osh, ®s->emacintstatus) & EI_MII) == 0) { ET_ERROR(("et%d: chipphyrd: did not complete\n", ch->etc->unit)); } return (R_REG(ch->osh, ®s->mdiodata) & MD_DATA_MASK); }
/* Allocate private resource */ adm_info_t * adm_attach(si_t *sih, char *vars) { adm_info_t *adm; int gpio; /* Allocate private data */ if (!(adm = MALLOC(si_osh(sih), sizeof(adm_info_t)))) { ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih)))); return NULL; } bzero((char *) adm, sizeof(adm_info_t)); adm->sih = sih; adm->vars = vars; /* Init GPIO mapping. Default GPIO: 2, 3, 4 */ gpio = getgpiopin(vars, "adm_eecs", 2); ET_ERROR(("adm_attach: got %d as adm_eecs", gpio)); if (gpio == GPIO_PIN_NOTDEFINED) { ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use")); goto error; } adm->eecs = 1 << gpio; gpio = getgpiopin(vars, "adm_eesk", 3); ET_ERROR(("adm_attach: got %d as adm_eesk", gpio)); if (gpio == GPIO_PIN_NOTDEFINED) { ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use")); goto error; } adm->eesk = 1 << gpio; gpio = getgpiopin(vars, "adm_eedi", 4); ET_ERROR(("adm_attach: got %d as adm_eedi", gpio)); if (gpio == GPIO_PIN_NOTDEFINED) { ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use")); goto error; } adm->eedi = 1 << gpio; return adm; error: adm_detach(adm); return NULL; }
/* return true of caller should re-initialize, otherwise false */ static bool BCMFASTPATH chiperrors(struct bcm4xxx *ch) { uint32 intstatus; etc_info_t *etc; etc = ch->etc; intstatus = ch->intstatus; ch->intstatus &= ~(I_ERRORS); ET_TRACE(("et%d: chiperrors: intstatus 0x%x\n", etc->unit, intstatus)); if (intstatus & I_PC) { ET_ERROR(("et%d: descriptor error\n", etc->unit)); etc->dmade++; } if (intstatus & I_PD) { ET_ERROR(("et%d: data error\n", etc->unit)); etc->dmada++; } if (intstatus & I_DE) { ET_ERROR(("et%d: descriptor protocol error\n", etc->unit)); etc->dmape++; } /* NOTE : this ie NOT an error. It becomes an error only * when the rx fifo overflows */ if (intstatus & I_RU) { ET_ERROR(("et%d: receive descriptor underflow\n", etc->unit)); etc->rxdmauflo++; } if (intstatus & I_RO) { ET_ERROR(("et%d: receive fifo overflow\n", etc->unit)); etc->rxoflo++; } if (intstatus & I_XU) { ET_ERROR(("et%d: transmit fifo underflow\n", etc->unit)); etc->txuflo++; } /* if overflows or decriptors underflow, don't report it * as an error and provoque a reset */ if (intstatus & ~(I_RU) & I_ERRORS) return (TRUE); return FALSE; }
static void chipphywr(struct bcm4xxx *ch, uint phyaddr, uint reg, uint16 v) { bcmenetregs_t *regs; ASSERT(phyaddr < MAXEPHY); /* * BCM5222 dualphy shared mdio contortion. * remote phy: another emac controls our phy. */ if (ch->etc->mdcport != ch->etc->coreunit) { if (ch->etphy == NULL) ch->etphy = et_phyfind(ch->et, ch->etc->mdcport); if (ch->etphy) et_phywr(ch->etphy, phyaddr, reg, v); return; } /* local phy: our emac controls our phy */ regs = ch->regs; /* clear mii_int */ W_REG(ch->osh, ®s->emacintstatus, EI_MII); ASSERT((R_REG(ch->osh, ®s->emacintstatus) & EI_MII) == 0); /* issue the write */ W_REG(ch->osh, ®s->mdiodata, (MD_SB_START | MD_OP_WRITE | (phyaddr << MD_PMD_SHIFT) | (reg << MD_RA_SHIFT) | MD_TA_VALID | v)); /* wait for it to complete */ SPINWAIT(((R_REG(ch->osh, ®s->emacintstatus) & EI_MII) == 0), 100); if ((R_REG(ch->osh, ®s->emacintstatus) & EI_MII) == 0) { ET_ERROR(("et%d: chipphywr: did not complete\n", ch->etc->unit)); } }
static void chipreset(struct bcm4xxx *ch) { bcmenetregs_t *regs; uint32 clk, mdc; ET_TRACE(("et%d: chipreset\n", ch->etc->unit)); regs = ch->regs; if (!si_iscoreup(ch->sih)) { if (!ch->etc->nicmode) si_pci_setup(ch->sih, (1 << si_coreidx(ch->sih))); /* power on reset: reset the enet core */ si_core_reset(ch->sih, 0, 0); goto chipinreset; } /* read counters before resetting the chip */ if (ch->mibgood) chipstatsupd(ch); /* reset the tx dma engine */ if (ch->di) dma_txreset(ch->di); /* set emac into loopback mode to ensure no rx traffic */ W_REG(ch->osh, ®s->rxconfig, ERC_LE); OSL_DELAY(1); /* reset the rx dma engine */ if (ch->di) dma_rxreset(ch->di); /* reset core */ si_core_reset(ch->sih, 0, 0); chipinreset: /* must clear mib registers by hand */ W_REG(ch->osh, ®s->mibcontrol, EMC_RZ); (void) R_REG(ch->osh, ®s->mib.tx_broadcast_pkts); (void) R_REG(ch->osh, ®s->mib.tx_multicast_pkts); (void) R_REG(ch->osh, ®s->mib.tx_len_64); (void) R_REG(ch->osh, ®s->mib.tx_len_65_to_127); (void) R_REG(ch->osh, ®s->mib.tx_len_128_to_255); (void) R_REG(ch->osh, ®s->mib.tx_len_256_to_511); (void) R_REG(ch->osh, ®s->mib.tx_len_512_to_1023); (void) R_REG(ch->osh, ®s->mib.tx_len_1024_to_max); (void) R_REG(ch->osh, ®s->mib.tx_jabber_pkts); (void) R_REG(ch->osh, ®s->mib.tx_oversize_pkts); (void) R_REG(ch->osh, ®s->mib.tx_fragment_pkts); (void) R_REG(ch->osh, ®s->mib.tx_underruns); (void) R_REG(ch->osh, ®s->mib.tx_total_cols); (void) R_REG(ch->osh, ®s->mib.tx_single_cols); (void) R_REG(ch->osh, ®s->mib.tx_multiple_cols); (void) R_REG(ch->osh, ®s->mib.tx_excessive_cols); (void) R_REG(ch->osh, ®s->mib.tx_late_cols); (void) R_REG(ch->osh, ®s->mib.tx_defered); (void) R_REG(ch->osh, ®s->mib.tx_carrier_lost); (void) R_REG(ch->osh, ®s->mib.tx_pause_pkts); (void) R_REG(ch->osh, ®s->mib.rx_broadcast_pkts); (void) R_REG(ch->osh, ®s->mib.rx_multicast_pkts); (void) R_REG(ch->osh, ®s->mib.rx_len_64); (void) R_REG(ch->osh, ®s->mib.rx_len_65_to_127); (void) R_REG(ch->osh, ®s->mib.rx_len_128_to_255); (void) R_REG(ch->osh, ®s->mib.rx_len_256_to_511); (void) R_REG(ch->osh, ®s->mib.rx_len_512_to_1023); (void) R_REG(ch->osh, ®s->mib.rx_len_1024_to_max); (void) R_REG(ch->osh, ®s->mib.rx_jabber_pkts); (void) R_REG(ch->osh, ®s->mib.rx_oversize_pkts); (void) R_REG(ch->osh, ®s->mib.rx_fragment_pkts); (void) R_REG(ch->osh, ®s->mib.rx_missed_pkts); (void) R_REG(ch->osh, ®s->mib.rx_crc_align_errs); (void) R_REG(ch->osh, ®s->mib.rx_undersize); (void) R_REG(ch->osh, ®s->mib.rx_crc_errs); (void) R_REG(ch->osh, ®s->mib.rx_align_errs); (void) R_REG(ch->osh, ®s->mib.rx_symbol_errs); (void) R_REG(ch->osh, ®s->mib.rx_pause_pkts); (void) R_REG(ch->osh, ®s->mib.rx_nonpause_pkts); ch->mibgood = TRUE; /* * We want the phy registers to be accessible even when * the driver is "downed" so initialize MDC preamble, frequency, * and whether internal or external phy here. */ /* default: 100Mhz SI clock and external phy */ W_REG(ch->osh, ®s->mdiocontrol, 0x94); if (ch->etc->deviceid == BCM47XX_ENET_ID) { /* 47xx chips: find out the clock */ if ((clk = si_clock(ch->sih)) != 0) { mdc = 0x80 | ((clk + (MDC_RATIO / 2)) / MDC_RATIO); W_REG(ch->osh, ®s->mdiocontrol, mdc); } else { ET_ERROR(("et%d: chipreset: Could not figure out backplane clock, " "using 100Mhz\n", ch->etc->unit)); } } /* some chips have internal phy, some don't */ if (!(R_REG(ch->osh, ®s->devcontrol) & DC_IP)) { W_REG(ch->osh, ®s->enetcontrol, EC_EP); } else if (R_REG(ch->osh, ®s->devcontrol) & DC_ER) { AND_REG(ch->osh, ®s->devcontrol, ~DC_ER); OSL_DELAY(100); chipphyinit(ch, ch->etc->phyaddr); } /* clear persistent sw intstatus */ ch->intstatus = 0; }
static void * chipattach(etc_info_t *etc, void *osh, void *regsva) { struct bcm4xxx *ch; bcmenetregs_t *regs; char name[16]; char *var; uint boardflags, boardtype; ET_TRACE(("et%d: chipattach: regsva 0x%lx\n", etc->unit, (ulong)regsva)); if ((ch = (struct bcm4xxx *)MALLOC(osh, sizeof(struct bcm4xxx))) == NULL) { ET_ERROR(("et%d: chipattach: out of memory, malloced %d bytes\n", etc->unit, MALLOCED(osh))); return (NULL); } bzero((char *)ch, sizeof(struct bcm4xxx)); ch->etc = etc; ch->et = etc->et; ch->osh = osh; /* store the pointer to the sw mib */ etc->mib = (void *)&ch->mib; /* get si handle */ if ((ch->sih = si_attach(etc->deviceid, ch->osh, regsva, PCI_BUS, NULL, &ch->vars, &ch->vars_size)) == NULL) { ET_ERROR(("et%d: chipattach: si_attach error\n", etc->unit)); goto fail; } /* We used to have an assert here like: * si_coreid(ch->sih) == ENET_CORE_ID * but srom-less systems and simulators don't have a way to * provide a default bar0window so we were relying on nvram * variables. At some point we decided that we could do away * with that since the wireless driver was simply doing a * setcore in attach. So we need to do the same here for * the ethernet. */ if ((regs = (bcmenetregs_t *)si_setcore(ch->sih, ENET_CORE_ID, etc->unit)) == NULL) { ET_ERROR(("et%d: chipattach: Could not setcore to the ENET core\n", etc->unit)); goto fail; } ch->regs = regs; etc->chip = ch->sih->chip; etc->chiprev = ch->sih->chiprev; etc->coreid = si_coreid(ch->sih); etc->corerev = si_corerev(ch->sih); etc->nicmode = !(ch->sih->bustype == SI_BUS); etc->coreunit = si_coreunit(ch->sih); etc->boardflags = getintvar(ch->vars, "boardflags"); etc->hwrxoff = HWRXOFF; boardflags = etc->boardflags; boardtype = ch->sih->boardtype; /* Backplane clock ticks per microsecs: used by gptimer, intrecvlazy */ etc->bp_ticks_usec = si_clock(ch->sih) / 1000000; /* get our local ether addr */ sprintf(name, "et%dmacaddr", etc->coreunit); var = getvar(ch->vars, name); if (var == NULL) { ET_ERROR(("et%d: chipattach: NVRAM_GET(%s) not found\n", etc->unit, name)); goto fail; } bcm_ether_atoe(var, &etc->perm_etheraddr); if (ETHER_ISNULLADDR(&etc->perm_etheraddr)) { ET_ERROR(("et%d: chipattach: invalid format: %s=%s\n", etc->unit, name, var)); goto fail; } bcopy((char *)&etc->perm_etheraddr, (char *)&etc->cur_etheraddr, ETHER_ADDR_LEN); /* * Too much can go wrong in scanning MDC/MDIO playing "whos my phy?" . * Instead, explicitly require the environment var "et<coreunit>phyaddr=<val>". */ /* get our phyaddr value */ sprintf(name, "et%dphyaddr", etc->coreunit); var = getvar(ch->vars, name); if (var == NULL) { ET_ERROR(("et%d: chipattach: NVRAM_GET(%s) not found\n", etc->unit, name)); goto fail; } etc->phyaddr = bcm_atoi(var) & EPHY_MASK; /* nvram says no phy is present */ if (etc->phyaddr == EPHY_NONE) { ET_ERROR(("et%d: chipattach: phy not present\n", etc->unit)); goto fail; } /* get our mdc/mdio port number */ sprintf(name, "et%dmdcport", etc->coreunit); var = getvar(ch->vars, name); if (var == NULL) { ET_ERROR(("et%d: chipattach: NVRAM_GET(%s) not found\n", etc->unit, name)); goto fail; } etc->mdcport = bcm_atoi(var); /* configure pci core */ si_pci_setup(ch->sih, (1 << si_coreidx(ch->sih))); /* reset the enet core */ chipreset(ch); /* dma attach */ sprintf(name, "et%d", etc->coreunit); if ((ch->di = dma_attach(osh, name, ch->sih, (void *)®s->dmaregs.xmt, (void *)®s->dmaregs.rcv, NTXD, NRXD, RXBUFSZ, -1, NRXBUFPOST, HWRXOFF, &et_msg_level)) == NULL) { ET_ERROR(("et%d: chipattach: dma_attach failed\n", etc->unit)); goto fail; } etc->txavail[TX_Q0] = (uint *)&ch->di->txavail; /* set default sofware intmask */ ch->intmask = DEF_INTMASK; /* * For the 5222 dual phy shared mdio contortion, our phy is * on someone elses mdio pins. This other enet enet * may not yet be attached so we must defer the et_phyfind(). */ /* if local phy: reset it once now */ if (etc->mdcport == etc->coreunit) chipphyreset(ch, etc->phyaddr); #ifdef ETROBO /* * Broadcom Robo ethernet switch. */ if ((boardflags & BFL_ENETROBO) && (etc->phyaddr == EPHY_NOREG)) { /* Attach to the switch */ if (!(etc->robo = bcm_robo_attach(ch->sih, ch, ch->vars, (miird_f)bcm47xx_et_chops.phyrd, (miiwr_f)bcm47xx_et_chops.phywr))) { ET_ERROR(("et%d: chipattach: robo_attach failed\n", etc->unit)); goto fail; } /* Enable the switch and set it to a known good state */ if (bcm_robo_enable_device(etc->robo)) { ET_ERROR(("et%d: chipattach: robo_enable_device failed\n", etc->unit)); goto fail; } /* Configure the switch to do VLAN */ if ((boardflags & BFL_ENETVLAN) && bcm_robo_config_vlan(etc->robo, etc->perm_etheraddr.octet)) { ET_ERROR(("et%d: chipattach: robo_config_vlan failed\n", etc->unit)); goto fail; } /* Enable switching/forwarding */ if (bcm_robo_enable_switch(etc->robo)) { ET_ERROR(("et%d: chipattach: robo_enable_switch failed\n", etc->unit)); goto fail; } } #endif /* ETROBO */ #ifdef ETADM /* * ADMtek ethernet switch. */ if (boardflags & BFL_ENETADM) { /* Attach to the device */ if (!(ch->adm = adm_attach(ch->sih, ch->vars))) { ET_ERROR(("et%d: chipattach: adm_attach failed\n", etc->unit)); goto fail; } /* Enable the external switch and set it to a known good state */ if (adm_enable_device(ch->adm)) { ET_ERROR(("et%d: chipattach: adm_enable_device failed\n", etc->unit)); goto fail; } /* Configure the switch */ if ((boardflags & BFL_ENETVLAN) && adm_config_vlan(ch->adm)) { ET_ERROR(("et%d: chipattach: adm_config_vlan failed\n", etc->unit)); goto fail; } } #endif /* ETADM */ return ((void *)ch); fail: chipdetach(ch); return (NULL); }
/* called once per second */ void etc_watchdog(etc_info_t *etc) { uint16 control; uint16 status; uint16 adv; uint16 lpa; etc->now++; /* no local phy registers */ if (etc->phyaddr == EPHY_NOREG) { control = CTL_SPEED | CTL_DUPLEX; status = STAT_LINK; } else { control = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 0); status = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 1); } /* check for bad mdio read */ if (control == 0xffff || status == 0xffff) { ET_ERROR(("et%d: etc_watchdog: bad mdio read: phyaddr %d mdcport %d\n", etc->unit, etc->phyaddr, etc->mdcport)); return; } /* update current speed and duplex */ if (control & CTL_ANENAB) { adv = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 4); lpa = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 5); if ((adv & ADV_100FULL) && (lpa & LPA_100FULL)) { etc->speed = 100; etc->duplex = 1; } else if ((adv & ADV_100HALF) && (lpa & LPA_100HALF)) { etc->speed = 100; etc->duplex = 0; } else if ((adv & ADV_10FULL) && (lpa & LPA_10FULL)) { etc->speed = 10; etc->duplex = 1; } else { etc->speed = 10; etc->duplex = 0; } } else { etc->speed = (control & CTL_SPEED) ? 100 : 10; etc->duplex = (control & CTL_DUPLEX) ? 1 : 0; } /* monitor link state */ if (!etc->linkstate && (status & STAT_LINK)) { etc->linkstate = TRUE; if (etc->pm_modechange) etc->pm_modechange = FALSE; else { et_link_up(etc->et); } } else if (etc->linkstate && !(status & STAT_LINK)) { etc->linkstate = FALSE; if (!etc->pm_modechange) { et_link_down(etc->et); } } /* keep emac txcontrol duplex bit consistent with current phy duplex */ (*etc->chops->duplexupd)(etc->ch); /* check for remote fault error */ if (status & STAT_REMFAULT) { ET_ERROR(("et%d: remote fault\n", etc->unit)); } /* check for jabber error */ if (status & STAT_JAB) { ET_ERROR(("et%d: jabber\n", etc->unit)); } /* * Read chip mib counters occationally before the 16bit ones can wrap. * We don't use the high-rate mib counters. */ if ((etc->now % 30) == 0) (*etc->chops->statsupd)(etc->ch); }